# HG changeset patch # User Bryan O'Sullivan # Date 1233295207 28800 # Node ID 51b5d56744c5ffd62ee5bf2e3c02d121c29c2784 # Parent 73b094b764ecf9bd6d103139e7771420b43f567b# Parent b35930ce7a70319961a65d32ea806d48fd60d519 Merge Spanish version diff -r 73b094b764ec -r 51b5d56744c5 en/99defs.tex --- a/en/99defs.tex Thu Jan 15 10:13:51 2009 +0100 +++ b/en/99defs.tex Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 en/collab.tex --- a/en/collab.tex Thu Jan 15 10:13:51 2009 +0100 +++ b/en/collab.tex Thu Jan 29 22:00:07 2009 -0800 @@ -461,7 +461,7 @@ take a look at your system documentation to figure out how to install it. -On Windows, you'll first need to choose download a suitable ssh +On Windows, you'll first need to download a suitable ssh client. There are two alternatives. \begin{itemize} \item Simon Tatham's excellent PuTTY package~\cite{web:putty} provides @@ -495,7 +495,7 @@ When you generate a key pair, it's usually \emph{highly} advisable to protect it with a passphrase. (The only time that you might not want -to do this id when you're using the ssh protocol for automated tasks +to do this is when you're using the ssh protocol for automated tasks on a secure network.) Simply generating a key pair isn't enough, however. You'll need to @@ -737,7 +737,7 @@ named something like \dirname{public\_html} in their home directory, from which they can serve up web pages. A file named \filename{foo} in this directory will be accessible at a URL of the form -\texttt{http://www.example.com/\~username/foo}. +\texttt{http://www.example.com/\~{}username/foo}. To get started, find the \sfilename{hgweb.cgi} script that should be present in your Mercurial installation. If you can't quickly find a diff -r 73b094b764ec -r 51b5d56744c5 en/concepts.tex --- a/en/concepts.tex Thu Jan 15 10:13:51 2009 +0100 +++ b/en/concepts.tex Thu Jan 29 22:00:07 2009 -0800 @@ -309,7 +309,7 @@ changesets to see which one introduced a bug. In cases like this, the natural thing to do is update the working directory to the changeset you're interested in, and then examine the files in the working -directory directly to see their contents as they werea when you +directory directly to see their contents as they were when you committed that changeset. The effect of this is shown in figure~\ref{fig:concepts:wdir-pre-branch}. diff -r 73b094b764ec -r 51b5d56744c5 en/examples/bisect --- a/en/examples/bisect Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/bisect Thu Jan 29 22:00:07 2009 -0800 @@ -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. diff -r 73b094b764ec -r 51b5d56744c5 en/examples/bisect.commits.out --- a/en/examples/bisect.commits.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/bisect.commits.out Thu Jan 29 22:00:07 2009 -0800 @@ -1,16 +1,3 @@ - - - - - - - - - - - - - @@ -21,25 +8,3 @@ - - - - - - - - - - - - - - - - - - - - - - diff -r 73b094b764ec -r 51b5d56744c5 en/examples/bisect.help.out --- a/en/examples/bisect.help.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/bisect.help.out Thu Jan 29 22:00:07 2009 -0800 @@ -19,3 +19,5 @@ + + diff -r 73b094b764ec -r 51b5d56744c5 en/examples/bisect.init.out --- a/en/examples/bisect.init.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/bisect.init.out Thu Jan 29 22:00:07 2009 -0800 @@ -1,3 +1,2 @@ - diff -r 73b094b764ec -r 51b5d56744c5 en/examples/bisect.search.bad-init.out --- a/en/examples/bisect.search.bad-init.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/bisect.search.bad-init.out Thu Jan 29 22:00:07 2009 -0800 @@ -1,22 +1,1 @@ - - - - - - - - - - - - - - - - - - - - - diff -r 73b094b764ec -r 51b5d56744c5 en/examples/bisect.search.good-init.out --- a/en/examples/bisect.search.good-init.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/bisect.search.good-init.out Thu Jan 29 22:00:07 2009 -0800 @@ -1,22 +1,3 @@ - - - - - - - - - - - - - - - - - - - diff -r 73b094b764ec -r 51b5d56744c5 en/examples/bisect.search.init.out --- a/en/examples/bisect.search.init.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/bisect.search.init.out Thu Jan 29 22:00:07 2009 -0800 @@ -1,22 +1,5 @@ - - - - - - - - - - - - - - - - - diff -r 73b094b764ec -r 51b5d56744c5 en/examples/bisect.search.reset.out --- a/en/examples/bisect.search.reset.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/bisect.search.reset.out Thu Jan 29 22:00:07 2009 -0800 @@ -1,22 +1,1 @@ - - - - - - - - - - - - - - - - - - - - - diff -r 73b094b764ec -r 51b5d56744c5 en/examples/bisect.search.rest.out --- a/en/examples/bisect.search.rest.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/bisect.search.rest.out Thu Jan 29 22:00:07 2009 -0800 @@ -1,22 +1,3 @@ - - - - - - - - - - - - - - - - - - - @@ -33,37 +14,3 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -r 73b094b764ec -r 51b5d56744c5 en/examples/bisect.search.step1.out --- a/en/examples/bisect.search.step1.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/bisect.search.step1.out Thu Jan 29 22:00:07 2009 -0800 @@ -1,7 +1,3 @@ - - - - @@ -13,18 +9,3 @@ - - - - - - - - - - - - - - - diff -r 73b094b764ec -r 51b5d56744c5 en/examples/bisect.search.step2.out --- a/en/examples/bisect.search.step2.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/bisect.search.step2.out Thu Jan 29 22:00:07 2009 -0800 @@ -1,23 +1,4 @@ - - - - - - - - - - - - - - - - - - - diff -r 73b094b764ec -r 51b5d56744c5 en/examples/branch-repo.bugfix.out --- a/en/examples/branch-repo.bugfix.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/branch-repo.bugfix.out Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 en/examples/branching.stable.out --- a/en/examples/branching.stable.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/branching.stable.out Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 en/examples/daily.copy.merge.out --- a/en/examples/daily.copy.merge.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/daily.copy.merge.out Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 en/examples/tour-merge-conflict.commit.out --- a/en/examples/tour-merge-conflict.commit.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/tour-merge-conflict.commit.out Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 en/examples/tour-merge-conflict.merge.out --- a/en/examples/tour-merge-conflict.merge.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/tour-merge-conflict.merge.out Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 en/examples/tour.help.out --- a/en/examples/tour.help.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/tour.help.out Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 en/examples/tour.ls.out --- a/en/examples/tour.ls.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/tour.ls.out Thu Jan 29 22:00:07 2009 -0800 @@ -1,5 +1,5 @@ $ \textbf{ls -l} -total 4 + $ \textbf{ls hello} Makefile hello.c diff -r 73b094b764ec -r 51b5d56744c5 en/examples/tour.version.out --- a/en/examples/tour.version.out Thu Jan 15 10:13:51 2009 +0100 +++ b/en/examples/tour.version.out Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 en/hook.tex --- a/en/hook.tex Thu Jan 15 10:13:51 2009 +0100 +++ b/en/hook.tex Thu Jan 29 22:00:07 2009 -0800 @@ -509,7 +509,7 @@ whitespace from a file. This is concise and useful enough that I will reproduce it here. \begin{codesample2} - perl -pi -e 's,\\s+\$,,' filename + perl -pi -e 's,\textbackslash{}s+\$,,' filename \end{codesample2} \section{Bundled hooks} diff -r 73b094b764ec -r 51b5d56744c5 en/mq-collab.tex --- a/en/mq-collab.tex Thu Jan 15 10:13:51 2009 +0100 +++ b/en/mq-collab.tex Thu Jan 29 22:00:07 2009 -0800 @@ -275,7 +275,7 @@ 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 +determining what kind of source tree I want to end up with. For most situations, the selection of appropriate guards is automated during the build process, but I can manually tune the guards to use for less common circumstances. diff -r 73b094b764ec -r 51b5d56744c5 en/mq.tex --- a/en/mq.tex Thu Jan 15 10:13:51 2009 +0100 +++ b/en/mq.tex Thu Jan 29 22:00:07 2009 -0800 @@ -525,7 +525,7 @@ modifies \filename{foo} with six hunks, and one of them fails to apply, you will have: an unmodified \filename{foo.orig}, a \filename{foo.rej} containing one hunk, and \filename{foo}, containing -the changes made by the five successful five hunks. +the changes made by the five successful hunks. \subsection{Some quirks of patch representation} diff -r 73b094b764ec -r 51b5d56744c5 en/template.tex --- a/en/template.tex Thu Jan 15 10:13:51 2009 +0100 +++ b/en/template.tex Thu Jan 29 22:00:07 2009 -0800 @@ -244,7 +244,7 @@ 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. + of a changeset hash, i.e.~a 12-character hexadecimal string. \item[\tplkwfilt{date}{shortdate}] \tplkword{date} keyword. Render the year, month, and day of the date. Yields a string like ``\Verb+2006-09-04+''. @@ -452,7 +452,7 @@ \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. + that counts the number of lines the template generates. \end{itemize} It took me no more than a minute or two of work to replace literal text from an example of Subversion's output with some keywords and diff -r 73b094b764ec -r 51b5d56744c5 en/tour-merge.tex diff -r 73b094b764ec -r 51b5d56744c5 es/00book.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/00book.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,81 @@ +% The use of oneside here is a temporary hack; \marginpar entries +% don't show up on odd pages of PDF output without it. Sigh. +\documentclass[oneside]{book} +\usepackage[spanish]{babel} +\usepackage{enumerate} +\usepackage{fullpage} +\usepackage{makeidx} +\usepackage{ifpdf} +\usepackage{graphicx} +\usepackage{pslatex} +\usepackage{fancyvrb} +\usepackage[utf8]{inputenc} %accents in spanish +% leave hyperref until last +\ifpdf +\usepackage[colorlinks=true,bookmarks=true,pdftitle={Distributed + revision control with Mercurial},pdfsubject={Revision + control},pdfkeywords={Mercurial, Revision control, Distributed + revision control},pdfauthor={Bryan O'Sullivan}]{hyperref} +\fi + +\include{99defs} + +\title{Control Distribuido de Revisiones con Mercurial} \author{Bryan + O'Sullivan} +\date{Copyright \copyright\ 2006, 2007 Bryan O'Sullivan.\\ + Este material puede distribuirse únicamente bajo los términos y + condiciones establecidos en la versión 1.0 de la Licencia de Publicación + Abierta (OPL). Refiérase por favor al apéndice~\ref{cha:opl} para encontrar el + texto de la licencia.\\ + Este libro fue preparado a partir de + \href{http://mercurial.intuxication.org/hg/mercurial_book_es}{rev~\input{build_id}} + usando Mercurial \href{http://www.selenic.com/hg/}{rev~\input{hg_id}}.} + +\makeindex + +\begin{document} +\spanishdeactivate{<>"~} +\maketitle + +\addcontentsline{toc}{chapter}{Índice general} +\pagenumbering{roman} +\tableofcontents +\listoffigures +%\listoftables + +\pagenumbering{arabic} + +\include{preface} +\include{intro} +\include{tour-basic} +\include{tour-merge} +\include{concepts} +\include{daily} +\include{collab} +\include{filenames} +\include{branch} +\include{undo} +\include{hook} +\include{template} +\include{mq} +\include{mq-collab} +\include{hgext} + +\appendix +\include{cmdref} +\include{mq-ref} +\include{srcinstall} +\include{license} +\addcontentsline{toc}{chapter}{Bibliografía} +\bibliographystyle{alpha} +\bibliography{99book} + +\addcontentsline{toc}{chapter}{Índice alfabético} +\printindex + +\end{document} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: t +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/99book.bib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/99book.bib Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,1 @@ +../en/99book.bib \ No newline at end of file diff -r 73b094b764ec -r 51b5d56744c5 es/99defs.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/99defs.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,146 @@ +% Bug ID. +\newcommand{\bug}[1]{\index{Base de datos de fallos de Mercurial + !\href{http://www.selenic.com/mercurial/bts/issue#1}{fallo + ~#1}}\href{http://www.selenic.com/mercurial/bts/issue#1}{Fallo de + Mercurial No.~#1}} + +% File name in the user's home directory. +\newcommand{\tildefile}[1]{\texttt{\~{}/#1}} + +% File name. +\newcommand{\filename}[1]{\texttt{#1}} + +% Directory name. +\newcommand{\dirname}[1]{\texttt{#1}} + +% File name, with index entry. +% The ``s'' prefix comes from ``special''. +\newcommand{\sfilename}[1]{\index{\texttt{#1}, fichero}\texttt{#1}} + +% Directory name, with index entry. +\newcommand{\sdirname}[1]{\index{\texttt{#1}, directorio}\texttt{#1}} + +% Mercurial extension. +\newcommand{\hgext}[1]{\index{\texttt{#1}, extensi\'on}\texttt{#1}} + +% Command provided by a Mercurial extension. +\newcommand{\hgxcmd}[2]{\index{\texttt{#2}, comando (extensi\'on +\texttt{#1})}\index{\texttt{#1}, extensi\'on!comando \texttt{#2}}``\texttt{hg #2}''} + +% Mercurial command. +\newcommand{\hgcmd}[1]{\index{\texttt{#1}, comando}``\texttt{hg #1}''} + +% Mercurial command, with arguments. +\newcommand{\hgcmdargs}[2]{\index{\texttt{#1}, comando}``\texttt{hg #1 #2}''} + +\newcommand{\tplkword}[1]{\index{\texttt{#1}, palabra clave de +plantilla}\index{palabras clave de plantilla!\texttt{#1}}\texttt{#1}} + +\newcommand{\tplkwfilt}[2]{\index{\texttt{#1}, palabra clave de plantilla!filtro +\texttt{#2}}\index{filtros de plantilla!\texttt{#2}}\index{\texttt{#2}, filtro +de plantilla}\texttt{#2}} + +\newcommand{\tplfilter}[1]{\index{filtros de +plantilla!\texttt{#1}}\index{\texttt{#1}, filtro de plantilla}\texttt{#1}} + +% Shell/system command. +\newcommand{\command}[1]{\index{\texttt{#1}, comando de sistema}\texttt{#1}} + +% Shell/system command, with arguments. +\newcommand{\cmdargs}[2]{\index{\texttt{#1} comando de sistema}``\texttt{#1 #2}''} + +% Mercurial command option. +\newcommand{\hgopt}[2]{\index{\texttt{#1}, comando!opción \texttt{#2}}\texttt{#2}} + +% Mercurial command option, provided by an extension command. +\newcommand{\hgxopt}[3]{\index{\texttt{#2}, comando (extensión +\texttt{#1})!opción \texttt{#3}}\index{\texttt{#1}, extensión!comando +\texttt{#2}!opción\texttt{#3}}\texttt{#3}} + +% Mercurial global option. +\newcommand{\hggopt}[1]{\index{opciones globales!opción \texttt{#1}}\texttt{#1}} + +% Shell/system command option. +\newcommand{\cmdopt}[2]{\index{\texttt{#1}, comando!opción \texttt{#2}}\texttt{#2}} + +% Command option. +\newcommand{\option}[1]{\texttt{#1}} + +% Software package. +\newcommand{\package}[1]{\index{\texttt{#1}, paquete}\texttt{#1}} + +% Section name from a hgrc file. +\newcommand{\rcsection}[1]{\index{\texttt{hgrc}, fichero!sección \texttt{#1}}\texttt{[#1]}} + +% Named item in a hgrc file section. +\newcommand{\rcitem}[2]{\index{\texttt{hgrc}, fichero!sección +\texttt{#1}!entrada \texttt{#2}}\texttt{#2}} + +% hgrc file. +\newcommand{\hgrc}{\index{fichero de configuración!\texttt{hgrc} + (Linux/Unix)}\index{\texttt{hgrc}, fichero de configuración}\texttt{hgrc}} + +% Mercurial.ini file. +\newcommand{\hgini}{\index{fichero de configuración!\texttt{Mercurial.ini} + (Windows)}\index{\texttt{Mercurial.ini}, fichero de configuración}\texttt{Mercurial.ini}} + +% Hook name. +\newcommand{\hook}[1]{\index{\texttt{#1}, gancho}\index{ganchos!\texttt{#1}}\texttt{#1}} + +% Environment variable. +\newcommand{\envar}[1]{\index{\texttt{#1}, variable de entorno}\index{variables +de entorno!\texttt{#1}}\texttt{#1}} + +% Python module. +\newcommand{\pymod}[1]{\index{\texttt{#1}, módulo}\texttt{#1}} + +% Python class in a module. +\newcommand{\pymodclass}[2]{\index{\texttt{#1}, módulo!clase \texttt{#2}}\texttt{#1.#2}} + +% Python function in a module. +\newcommand{\pymodfunc}[2]{\index{\texttt{#1}, módulo!función \texttt{#2}}\texttt{#1.#2}} + +% Note: blah blah. +\newsavebox{\notebox} +\newenvironment{note}% + {\begin{lrbox}{\notebox}\begin{minipage}{0.7\textwidth}\textbf{Nota:}\space}% + {\end{minipage}\end{lrbox}\fbox{\usebox{\notebox}}} +\newenvironment{caution}% + {\begin{lrbox}{\notebox}\begin{minipage}{0.7\textwidth}\textbf{Precaució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}, comando}} + +% Reference entry for a command option with long and short forms. +\newcommand{\optref}[3]{\subsubsection{\hgopt{#1}{--#3}, también \hgopt{#1}{-#2}}} + +% Reference entry for a command option with only long form. +\newcommand{\loptref}[2]{\subsubsection{opción \hgopt{#1}{--#2}}} + +% command to generate a footnote to be used as a translator's note +\newcommand{\ndt}[1]{\footnote{\textbf{N. del T.} #1}} + + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/Leame.1st --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/Leame.1st Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,508 @@ += Parámetros de Organización = + + * Se mantienen los nombres de los archivos + * Se traduce solamente el contenido + * Copie los archivos de en a es y tradúzcalos + * Las gráficas son tan importantes como los archivos + de texto, ya han sido traducidas + * Encoding UTF-8 para las tildes, eñes y demás + * Ancho de línea de 70 caracteres + += ¿Cómo contribuir? = +Obtenga la copia : +hg clone http://mercurial.intuxication.org/hg/mercurial_book_es/ + +Esto le ofrecerá un clon del repositorio en el directorio recién +creado '''mercurial_book_es''': + +mercurial_book_es +| +|-- en +|-- es +|-- examples +|-- html +`-- sillybench + +El directorio de trabajo es '''es'''. + + +Una vez que haya traducido o aplicado correcciones a los archivos de +su copia local, haga un commit + + hg commit -m "comentario descriptivo de lo que hizo" + +Siempre mantenga actualizado su repositorio local + hg pull + hg update + +Hay dos formas de hacer la contribución, primero envíe un correo a +igor@tamarapatino.org indicando lo que desea hacer, se le puede +otorgar permiso de escritura en el repositorio, o si lo prefiere, +puede enviar un parche (patch). Describimos a continuación los dos +procedimientos : Repositorio Público y Parches. Es preferible el +repositorio público frente a los parches, puesto que estos segundos +pueden tardar en propagarse más. + +== Repositorio Público == +Este sería el método preferido para que los cambios que usted haga +automáticamente queden en el repositorio y todos los traductores +podamos contar con la información rápidamente. + +Una vez que usted haya recibido la información necesaria, habiendo +elegido su usuario y su clave podrá "publicar" (push). + +Como este es un sistema distribuido, después de hacer la +consignación (commit), deberá publicarlo. + + hg push + +Se le solicitará su usuario y clave. + +== Parches == +Este método exige que alguien reciba el parche y haga manualmente la +aplicación del mismo, ese alguien es igor@tamarapatino.org por ahora, +después de haber hecho commit en su repositorio local, revise su log. + + hg log | head + +Esta última orden le permitirá establecer la última revisión que se +consignó en su repositorio local, su identificador de revisión tendrá +el formato número:hash. Generaría el archivo +/tmp/patchparahgbook.patch con la orden + + hg -o /tmp/patchparahgbook.patch REV + +donde REV es el identificador de revisión que debió haber encontrado. + += Traducción/Revisión = + +En esta sección indicamos quienes están traduciendo +y quienes revisando lo traducido. Coloque su nombre +para que los demás colaboradores sepan en dónde +enfocar sus esfuerzos. + +Indique qué archivos está traduciendo y/o revisando en +la lista siguiente. Cada archivo debe ser traducido y +revisado antes de poder considerarlo terminado. El revisor +no puede ser la misma persona que hizo la traducción. + +Cada traductor puede traducir o revisar el archivo que +desee, teniendo siempre en cuenta los archivos que ya tengan +alguien a cargo y escogiendo en la medida de lo posible +otros que no lo tengan. Los arreglos de 'typos' y problemas +de ortografía son siempre bienvenidos. + +== Archivos en proceso de traducción == +||'''archivo''' ||'''traductor'''||'''Estado'''||'''Inicio'''|| '''Fin''' || +|| 00book.tex || Igor Támara || 100% || 16/10/2008 || 16/10/2008 || +|| preface.tex || Javier Rojas || 100% || 18/10/2008 || 19/10/2008 || +|| intro.tex || Igor Támara || 100% || 08/11/2008 || 09/11/2008 || +|| tour-basic.tex || Javier Rojas || 100% || 19/10/2008 || 27/10/2008 || +|| tour-merge.tex || Javier Rojas || 100% || 28/10/2008 || 03/11/2008 || +|| concepts.tex || Javier Rojas || 100% || 03/11/2008 || 23/11/2008 || +|| daily.tex || Igor Támara || 100% || 19/10/2008 || 26/10/2008 || +|| collab.tex || Igor Támara || 100% || 10/11/2008 || 06/12/2008 || +|| filenames.tex || Javier Rojas || 100% || 27/11/2008 || 12/01/2008 || +|| branch.tex || Igor Támara || 100% || 16/10/2008 || 19/10/2008 || +|| undo.tex || Igor Támara || 100% || 26/10/2008 || 07/11/2008 || +|| hook.tex || Javier Rojas || 100% || 01/12/2008 || 04/01/2009 || +|| template.tex || Igor Támara || 100% || 27/12/2008 || 01/01/2009 || +|| mq.tex || Igor Támara || 100% || 06/12/2008 || 13/12/2008 || +|| mq-collab.tex || Javier Rojas || 100% || 04/01/2009 || 08/01/2009 || +|| hgext.tex || Igor Támara || 100% || 13/12/2008 || 16/12/2008 || +|| cmdref.tex || Igor Támara || 100% || 01/01/2009 || 01/01/2009 || +|| mq-ref.tex || Igor Támara || 100% || 06/01/2009 || 10/01/2009 || +|| srcinstall.tex || Igor Támara || 100% || 01/01/2009 || 01/01/2009 || +|| license.tex || Igor Támara || 100% || 16/12/2008 || 16/12/2008 || + +== Archivos en proceso de revisión == +||'''archivo''' || '''revisor''' ||'''Estado'''||'''Inicio'''|| '''Fin''' || +|| 00book.tex || Javier Rojas || 100% || 18/01/2009 || 18/01/2009 || +|| branch.tex || Javier Rojas || 100% || 25/01/2009 || 25/01/2009 || +|| preface.tex || || || || || +|| daily.tex || Javier Rojas || 100% || 25/01/2009 || 29/01/2009 || +|| tour-basic.tex || || || || || +|| undo.tex || Javier Rojas || || || || +|| tour-merge.tex || || || || || +|| concepts.tex || || || || || +|| intro.tex || Javier Rojas || 100% || 12/01/2009 || 12/01/2009 || +|| collab.tex || Javier Rojas || 0% || 29/01/2009 || || +|| mq.tex || || || || || +|| hgext.tex || || || || || +|| template.tex || || || || || +|| mq-collab.tex || || || || || +|| mq-ref.tex || || || || || +|| cmdref.tex || || || || || +|| license.tex || || || || || +|| srcinstall.tex || || || || || + +== Archivos terminados == +||'''archivo''' ||'''Inicio'''|| '''Fin''' || +|| 00book.tex || 16/10/2008 || 18/01/2009 || +|| intro.tex || 08/11/2008 || 12/01/2009 || +|| branch.tex || 16/10/2008 || 25/01/2009 || +|| daily.tex || 19/10/2008 || 29/01/2009 || + += Unificación de Términos de Traducción = +Por favor mantenga esta lista en orden alfabético + +La mayor parte del texto a continuación fue tomado del glosario de la +traducción del libro de subversion al idioma español. + +Pequeño glosario de términos traducidos. Aquí deben ponerse esos +"bonitos palabros" que tanto nos ha costado traducir para evitar +inconsistencias en la traducción por varias personas. Normalmente +son técnicos, pero puede incluirse cualquier "giro" o expresión que +consideremos útil mantener y repetir a lo largo de la traducción. +En el libro final posiblemente se añada una versión de este fichero +como apéndice. + +Para incluir algo, hay que especificar la expresión en inglés, su +versión traducida, y una pequeña explicación como justificación. + + Alice: Alicia + Anne: Ana + Back out: Retroceder + Binary test: Prueba binaria + Bob : Roberto + Branch: Rama + Bug: Fallo + Build Script: Guión de construcción + Builtin: integrada/o + Bundle: Agrupamiento + Bundled: Incluído o agrupado + Changelog: Bitácora de Cambios + Changeset: Conjunto de Cambios + Command: Orden + Commit: Consignar + Core: alma + Directory: Directorio + Escape Sequence: Secuencia de control + File: fichero + Filelog: fichero de registro + Fold: Integrar + Fork: Bifurcación + Hash: No se traduce + Head: Principal. En el contexto de revisiones HEAD se sugiere usar "frente" + Hook: Gancho + Merge: Fusión + Milestone: Etapa + Mistake: Equivocación, cometida por un humano + Output: salida o despliegue + Patch: Parche + Path: Ruta de archivo + Pointer: apuntador + Pop: Sustraer, la contraparte push, será publicar + Probe: Sondeo + Pull: Jalar + Push: Publicar. En el contexto de parches introducir. + Queue: Cola + Release: Versión o liberación de versión + Revlog: Bitácora de revisiones + Roll back: NO se traduce Ver más abajo + Snapshot: instantánea + Snippet: Recorte de código + Stack: pila + Stripped: + Sprint: sprint + Tarball: paquete de cambios + Timestamp : marca de tiempo + Tip: punta + Update: actualización + Upstream: principal, mantenedor principal. De acuerdo al contexto. + +abort -> cancelar + +ancestry -> ascendencia + La traducción literal concuerda con el significado que se le + da al mismo término en la jerga de Subversion. + +API, GUI -> no se traduce + La primera vez que aparecen, poner nota del traductor indicando + qué significan las siglas y su traducción al castellano. De + hecho, verificar la aparición de las notas de traductor en el + sitio correcto debería ser una entrada del fichero TODO... + +back-end -> ??? + Se refiere al término opuesto de front-end. En el octavo + capítulo se usa al menos tres veces para referirse al "motor" + que hay detrás de la capa de base de datos. Hmmm... pero motor + suena raro... + +backup -> copia de seguridad + Obtenido del glosario ORCA. + +Blanket Access Control -> Control de acceso simple + Por ahora no se me ocurre mejor traducción. Aquí blanket se + refiere a un control muy genérico, con poca granularidad. + +branching and merging -> crear ramas y fusionarlas + Aunque branch está bien traducido como rama o rama de desarrollo + (en su versión "verbose"), no hay forma de hacer de un sustantivo + un verbo. Por lo tanto, se crean, borran y fusionan ramas. + +browse -> navegar + Usado con frecuencia para navegar por directorios o repositorios. + +build -> comodín + No es que se traduzca como comodín, sino que en función del + contexto es una de esas palabras que significan muchas cosas y + no es posible traducirla literalmente. Por ejemplo, cuando se + usa como verbo se suele referir a compilar código fuente. En + cambio, cuando se usa como sustantivo se suele referir a una + versión particular del fichero binario ejecutable de un software, + resultado de una compilación previa. Cuidado con esto. Anotar + aquí traducciones realizadas para comparar. + + ...using and building Subversion... + ...usando y compilando Subversion... + +changeset -> ??? + Aparentemente conjunto de cambios. No puede ser traducido + como parche, el libro inglés indica que un "changeset" es un + parche con nombre único. Por ahora dejar en "changeset", ya se + buscará algo en el futuro. Para ver la descripción del libro, + buscar en ch04.xml la frase "Subversion y los changesets". + +cheap copy -> copia ligera + Otras posibilidades barajadas son copia barata o liviana. + Veremos si en el futuro éstas suenan mejor. + +click -> haga clic, pulse + Traducción obtenida del glosario. Parece ser que click sólo se + deja tal cual cuando se refiere a un sonido mecánico. Cuando + se refiere a pulsar el botón del ratón se traduce como clic. + +CVS -> No se traduce + +DAV share -> recurso DAV compartido + No tengo ni idea de lo que es un DAV share, así que cuando me + entere (o alguien lo haga), que cambie esta entrada del glosario. + +directory -> directorio + Entre carpeta y directorio se prefiere directorio. + +email -> correo electrónico + Entre las posibilidades de dejar la palabra tal cual, añadir un + guión (e-mail) y poner la traducción, se prefiere la traducción + completa. + +FAQ -> FAQ + Se deja tal cual pues se explica en el primer párrafo del prólogo + como una nota del traductor, y porque es muy frecuente ver su + uso en español. PyRF, PUFs o PFs son realmente desconcertantes. + +file -> fichero + Entre archivo y fichero se prefiere fichero. + +file path -> ruta del fichero + Cuando se ve path a secas, la forma más común de traducirlo + es decir ruta a secas igualmente. Bueno, el glosario de ORCA + también da como válidos camino y trayectoria. A ser posible + usar ruta si el contexto de la frase así lo favorece, en caso + contrario probar con camino o trayectoria. + +hash table -> tabla hash + Sugerido por Miguel Pérez Ibars. También encontrado en el + glosario ORCA. + +history -> Depende del contexto. Cuando se use en el contexto del repositorio + (the history of the repository), debe usarse el término historial. En otro + caso, historia. Valga anotar que ambas traducciones son aceptadas (al menos + en wordreference.com). + +hook, to hook -> gancho, enganchar + Usado en terminología de programación para indicar que el usuario + tiene un mecanismo estándar para modificar el comportamiento de + un dispositivo existente. Un ejemplo a nivel de programación + son las funciones "callback" que se pasan como parámetros, + o a nivel de sistema, scripts que existen en un directorio + concreto y que se ejecutan por el servidor de Subversion para + realizar tareas personalizadas por el administrador. + +ignore pattern -> ignorar patrones, pero patrones de exclusión + Subversion permite que ciertas opciones almacenen patrones + con los que se ignoran ficheros no versionados. Cuando la + frase original usa el verbo, se puede traducir como ignorar + directamente, pero cuando se usa a modo de sustantivo, + es mejor traducirlo como "patrón de exclusión" en lugar de + "patrón a ignorar". + +language -> lenguaje o idioma + Precisamente para diferenciar si nos estamos refiriendo a un + lenguaje de programación o a un lenguaje hablado por humanos, + se usará idioma en este último caso. + +language binding, SWIG binding, wrapper -> interfaz, enlace, ligadura, envoltorio... + Dependiendo del contexto, y desde un punto de vista personal, + se puede traducir esta palabra de muchas maneras. Una manera + genérica es decir "Interfaz con el lenguaje X", o "Ligadura con + el lenguaje X". Habitualmente, cuando se habla de una interfaz, + se está hablando de un binding ligero, o que únicamente permite + a hacer llamadas a funciones del lenguaje de programación A + desde el lenguaje de programación B. + + En cambio, se suele hablar de wrapper cuando el código entre + el lenguaje A y B es algo más complejo, o adapta el estilo de + programación del lenguaje A al del lenguaje B. Un ejemplo de esto + último sería una librería en C cuyo binding en Perl/Ruby/Python + fuese orientado a objetos. + + Aparte, wrapper también se usa cuando se habla de una función + o librería que engloba o asimila otra menor, con el propósito + de extender su funcionalidad o hacerla más fácil de usar + de cara al usuario, sin necesidad de cruzar ninguna barrera + "intra-lenguaje". + + Por lo tanto, hay que decidir con cuidado el contexto de la + palabra binding o wrapper, y escoger una. En el capítulo octavo + hay varios usos, aunque como son relativos a SWIG, se habla de + interfaces o envoltorios simplificados, puesto que se generan + de manera automática y no hay ninguna "conversión" en el estilo + de uso. + +lazy copy -> copia vaga + Horrible traducción literal. ¿Sugerencias? + copia perezosa + +location (repository) -> ubicación (del repositorio) + Cuidado, no traducir como localización, que es la acción y + efecto de localizar. + +log, log message -> informe de cambios, mensaje del informe de cambios + Traducción extraída del manual de CVS en español. La traducción + de "log message" es muy larga, pero únicamente porque no se + tiene contexto alguno. Con contexto suele ser posible omitir + la palabra informe si se considera apropiado. + +memory pool -> área de memoria + Traducción temporal hasta que se revise algún libro en castellano + de programación que use el mismo término. + +merge -> fusionar cambios, fusión + Obtenida referencia del manual de CVS en castellano, la otra + alternativa obvia para traducir merge es mezclar. No obstante, + mezclar tiene una connotación de azar al realizar la mezcla. Se + mezclan líquidos, ingredientes, etc. En cambio, un "merge" es + cualquier cosa menos un proceso realizado al azar (especialmente + si ha habido conflictos). En caso de traducir como sustantivo, + fusión vuelve a "sonar mejor" que mezcla, que por alguna razón me + suena a un combustible especial usado en vehículos de transporte. + +namespace -> espacio de nombrado, + Tecnicismo del C++, obtenido de la traducción del libro + "Pensar en C++", en concreto la sección 3.2 disponible en + http://arco.inf-cr.uclm.es/~dvilla/pensar_en_C++/ch02s03.html#id2589039. + +open source -> código fuente abierto + Referencia: http://es.tldp.org/ORCA/glosario.html#O + +plugins -> módulos + El término fue extraído del glosario de ORCA. + +repository -> repositorio + No hay mucha alternativa, ¿verdad? + +roll back -> No se traduce + El significado igual que en los ambientes + de sistemas manejadores de bases de datos se refiere a la atomicidad + e integridad al devolver un conjunto de acciones que permitan dejar + el repositorio en un estado consistente previo. + +repository layou t-> estructura del repositorio En referencia a cómo + están organizados los directorios. + +schedule -> programa o planifica + Parece más correcta la opción programa, en el sentido de pensar en + hacer algo. + schedule foo to be added -> programa la adición de foo + +switch -> cambiar + Únicamente cuando se habla del comando svn switch, no como la + palabra switch aislada, que significa parámetro o interruptor. + En el contexto de svn switch, traducirlo como cambiar. Quizás + traducir como reubicar una vez haya suficiente material traducido + y una lectura final sugiera una u otra traducción. + +tag, tagging -> etiqueta, etiquetar + Expresión ya común en español. + +three-way differencing program -> programa de diferenciación a tres bandas + Una diferenciación "normal" es generar las diferencias entre + dos ficheros. Una diferenciación a tres bandas es comparar las + diferencias entre tres ficheros, y se usa cuando se intentan + fusionar los cambios de una copia local junto con los cambios + recibidos del repositorio (por ejemplo, otra persona ha cambiado + el fichero sobre el que trabajábamos). + +timestamp, datestamp -> marca de tiempo, fecha y hora + Esa traducción viene del ORCA. No obstante en muchos casos es + más claro traducirla como "fecha de fichero" o "fecha" a secas. + Ejemplo: ...will show the modification timestamp. -> mostrará + la fecha de modificación. Decir "mostrará la marca de tiempo de + la modificación" o algo así es una traducción demasiado literal, + así que ojo con el contexto. + +track -> seguir, monitorear + este término suele ser usado cuando se habla de archivos, y de llevar la + pista o monitorear los cambios en un archivo. + +trunk -> tronco + Se refiere al nombre que recibe la línea de desarrollo + principal del repositorio que no está asociada a ninguna rama + en particular. Traducción obtenida del manual de CVS en español. + +Unix-like systems -> sistemas tipo unix + +URL -> No se traduce + +working copy -> copia (de trabajo) local/activa + Traducción similar a la de "commit data" (razonamiento + cliente-servidor). + += Términos a no-usar = +Evite el uso de estos términos en la traducción aún si son de uso +común para usted; el consenso general de la comunidad hispana puede +ser diferente del que usted tenga ;) + + * "archivo". Use "fichero" en su lugar. + * "carpeta". Use "directorio". + * "la historia". Use "el historial" (por supuesto, cuando se refiera al + historial del repositorio) + += Diferencias de redacción = +Hay varias expresiones que pueden terminar siendo traducidas de formas +diferentes por los traductores, y que aún siendo ambas correctas, ponen en +evidencia que el trabajo fue llevado a cabo por varias personas y que el estilo +que ellas usan difiere. Una vez terminada la traducción habrá que buscar cuáles +son éstos términos y decidirse por uno solo de ellos. A continuación se presenta +una lista de los términos o expresiones encontrados hasta ahora + + * comando - orden. Ambos son la traducción para "command". Parece que la + traducción más adecuada es "comando" + (http://www.wordreference.com/es/translation.asp?tranword=command) + * kernel - núcleo. + * Colas de Mercurial - colas de Mercurial. Creo que lo mejor es revisar qué + usó el autor (y rogar que él no haya sido inconsistente :P) + * armar - compilar - construir. Build, compile. Más que todo "build" + * daemonio - demonio. daemon + * kernel - núcleo. + * la URL - el URL + += Notas del traductor = +Por favor use el comando \ndt para insertar notas del traductor. Este +comando requiere un argumento. Por ejemplo: \ndt{Del inglés del original.} + += Para compilar = +He aquí algunas dependencias para Debian: + +apt-get install texlive-latex-extra tex4ht diffstat patchutils \ + inkscape graphviz texlive-pdfetex + += Traductores = +Por favor mantenga esta lista en orden alfabético de acuerdo al +apellido. + + * Javier Rojas + * Igor Támara + * Su nombre diff -r 73b094b764ec -r 51b5d56744c5 es/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/Makefile Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,223 @@ +# This makefile requires GNU make. + +sources := \ + 00book.tex \ + 99book.bib \ + 99defs.tex \ + build_id.tex \ + branch.tex \ + cmdref.tex \ + collab.tex \ + concepts.tex \ + daily.tex \ + filenames.tex \ + hg_id.tex \ + hgext.tex \ + hook.tex \ + intro.tex \ + mq.tex \ + mq-collab.tex \ + mq-ref.tex \ + preface.tex \ + srcinstall.tex \ + template.tex \ + tour-basic.tex \ + tour-merge.tex \ + undo.tex + +image-sources := \ + feature-branches.dot \ + filelog.svg \ + kdiff3.png \ + metadata.svg \ + mq-stack.svg \ + note.png \ + revlog.svg \ + snapshot.svg \ + tour-history.svg \ + tour-merge-conflict.svg \ + tour-merge-merge.svg \ + tour-merge-pull.svg \ + tour-merge-sep-repos.svg \ + undo-manual.dot \ + undo-manual-merge.dot \ + undo-non-tip.dot \ + undo-simple.dot \ + wdir.svg \ + wdir-after-commit.svg \ + wdir-branch.svg \ + wdir-merge.svg \ + wdir-pre-branch.svg + +image-dot := $(filter %.dot,$(image-sources)) +image-svg := $(filter %.svg,$(image-sources)) +image-png := $(filter %.png,$(image-sources)) + +image-pdf := $(image-dot:%.dot=%.pdf) $(image-svg:%.svg=%.pdf) $(image-png) +image-html := $(image-dot:%.dot=%.png) $(image-svg:%.svg=%.png) $(image-png) + +example-sources := \ + backout \ + bisect \ + branching \ + branch-named \ + branch-repo \ + cmdref \ + daily.copy \ + daily.files \ + daily.rename \ + daily.revert \ + extdiff \ + filenames \ + hook.msglen \ + hook.simple \ + hook.ws \ + issue29 \ + mq.guards \ + mq.qinit-help \ + mq.dodiff \ + mq.id \ + mq.tarball \ + mq.tools \ + mq.tutorial \ + rename.divergent \ + rollback \ + tag \ + template.simple \ + template.svnstyle \ + tour \ + tour-merge-conflict + +example-prereqs := \ + /usr/bin/merge + +dist-sources := \ + ../html/hgicon.png \ + ../html/index.html.var \ + ../html/index.en.html \ + ../html/index.es.html + +latex-options = \ + -interaction batchmode \ + -output-directory $(dir $(1)) \ + -jobname $(basename $(notdir $(1))) + +hg = $(shell which hg) + +hg-id = $(shell hg parents --template '{node|short}, fechado {date|isodate},\n') + +hg-version = $(shell hg version -q | \ + sed 's,.*(versión \(unknown\|[a-f0-9+]*\)),\1,') + +all: pdf html + +pdf: pdf/hgbook.pdf + +define pdf + mkdir -p $(dir $@) + TEXINPUTS=$(dir $<): pdflatex $(call latex-options,$@) $< || (rm -f $@; exit 1) + cp 99book.bib $(dir $@) + cd $(dir $@) && bibtex $(basename $(notdir $@)) + cd $(dir $@) && makeindex $(basename $(notdir $@)) + TEXINPUTS=$(dir $<): pdflatex $(call latex-options,$@) $< || (rm -f $@; exit 1) + TEXINPUTS=$(dir $<): pdflatex $(call latex-options,$@) $< || (rm -f $@; exit 1) + if grep 'Reference.*undefined' $(@:.pdf=.log); then exit 1; fi +endef + +pdf/hgbook.pdf: $(sources) examples $(image-pdf) + $(call pdf) + +html: onepage split + +onepage: $(htlatex) html/onepage/hgbook.html html/onepage/hgbook.css $(image-html:%=html/onepage/%) + +html/onepage/%: % + cp $< $@ + +split: $(htlatex) html/split/hgbook.html html/split/hgbook.css $(image-html:%=html/split/%) + +html/split/%: % + cp $< $@ + +# This is a horrible hack to work around the fact that the htlatex +# command in tex4ht is itself a horrible hack. I really don't want to +# include verbatim the big wad of TeX that is repeated in that script, +# but I've given up and run a hacked copy as htlatex.book here. + +define htlatex + mkdir -p $(dir $(1)) + cp 99book.bib $(dir $(1)) + TEXINPUTS=$(dir $(2)): ./htlatex.book $(2) "bookhtml,html4-uni,$(3)" " -cunihtf -utf8" "$(dir $(1))" "$(call latex-options,$(1))" || (rm -f $(1); exit 1) + cd $(dir $(1)) && tex4ht -f/$(basename $(notdir $(1))) -cvalidate -cunihtf + cd $(dir $(1)) && t4ht -f/$(basename $(notdir $(1))) + ./fixhtml.py $(dir $(1))/*.html + rm $(dir $(1))/hgbook.css +endef + +html/onepage/hgbook.html: $(sources) examples $(image-html) bookhtml.cfg + $(call htlatex,$@,$<) + +html/split/hgbook.html: $(sources) examples bookhtml.cfg + $(call htlatex,$@,$<,2) + +# Produce 90dpi PNGs for the web. + +%.png: %.svg fixsvg + ./fixsvg $< + inkscape -D -e $@ $<-tmp.svg + rm $<-tmp.svg + +%.svg: %.dot + dot -Tsvg -o $@ $< + +# Produce eps & pdf for the pdf + +%.pdf: %.eps + epstopdf $< + +%.eps: %.svg + ./fixsvg $< + inkscape -E $@ $<-tmp.svg + rm $<-tmp.svg + +%.eps: %.dot + dot -Tps -o $@ $< + +examples: $(example-prereqs) examples/.run + +examples/.run: $(example-sources:%=examples/%.run) + touch examples/.run + +examples/%.run: examples/% examples/run-example + cd examples && ./run-example $(notdir $<) + +changelog := $(wildcard ../.hg/store/00changelog.[id]) +ifeq ($(changelog),) +changelog := $(wildcard ../.hg/00changelog.[id]) +endif + +build_id.tex: $(changelog) + echo -n '$(hg-id)' > build_id.tex + +hg_id.tex: $(hg) + echo -n '$(hg-version)' > hg_id.tex + +clean: + rm -rf dist html pdf \ + $(image-dot:%.dot=%.pdf) \ + $(image-dot:%.dot=%.png) \ + $(image-svg:%.svg=%.pdf) \ + $(image-svg:%.svg=%.png) \ + examples/*.{lxo,run} examples/.run build_id.tex hg_id.tex + +install: pdf split $(dist-sources) + rm -rf dist + mkdir -p dist + cp pdf/hgbook.pdf dist + cp html/split/*.{css,html,png} dist + cp html/onepage/hgbook.html dist/onepage.html + ln -s index.es.html dist/index.html + cp $(dist-sources) dist + +rsync: install + rsync -avz --delete dist/ ikks@sulaco.devnull.li:public_html/hgbook/ diff -r 73b094b764ec -r 51b5d56744c5 es/bookhtml.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/bookhtml.cfg Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,1 @@ +../en/bookhtml.cfg \ No newline at end of file diff -r 73b094b764ec -r 51b5d56744c5 es/branch.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/branch.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,412 @@ +%% vim: tw=70 encoding=utf8 +\chapter{Administración de versiones y desarrollo ramificado} +\label{chap:branch} + +Mercurial ofrece varios mecanismos que le permiten administrar un +proyecto que avanza en múltiples frentes simultáneamente. Para +entender estos mecanismos, demos un vistazo a la estructura usual de +un proyecto de software. + +Muchos proyectos de software liberan una versión ``mayor'' que contiene +nuevas características substanciales. En paralelo, pueden liberar +versiones ``menores''. Usualmente éstas son idénticas a las +versiones mayores en las cuales están basadas, pero con arreglos para +algunos fallos. + +En este capítulo, comenzaremos hablando de cómo mantener registro de +etapas del proyecto como las liberaciones de una +versión. Continuaremos hablando del flujo de trabajo entre las +diferentes fases de un proyecto, y cómo puede ayudar Mercurial a +aislar y administrar tal trabajo. + +\section{Dar un nombre persistente a una revisión} + +Cuando usted decide otorgar a una revisión el nombre particular de una +``versión'', es buena idea grabar la identidad de tal revisión. +Esto le permitirá reproducir dicha versión en una fecha posterior, +para cualquiera que sea el +propósito que se tenga en ese momento (reproducir un fallo, portar +a una nueva plataforma, etc). +\interaction{tag.init} + +Mercurial le permite dar un nombre permanente a cualquier revisión +usando la orden \hgcmd{tag}. Sin causa de sorpresa, esos nombres se llaman +``tags'' (etiquetas). +\interaction{tag.tag} + +Una etiqueta no es más que un ``nombre simbólico'' para una revisión. Las +etiquetas existen únicamente para su conveniencia, brindándole una forma +permanente y sencilla de referirse a una revisión; Mercurial no +interpreta de ninguna manera los nombres de las etiquetas que usted use. +Mercurial tampoco impone restricción alguna al nombre de una etiqueta, más +allá de lo necesario para asegurar que una etiqueta pueda procesarse sin +ambigüedades. El nombre de una etiqueta no puede tener ninguno de los +siguientes caracteres: +\begin{itemize} +\item Dos puntos (ASCII 58, ``\texttt{:}'') +\item Retorno de carro (return) (ASCII 13, ``\Verb+\r+'') +\item Nueva línea (ASCII 10, ``\Verb+\n+'') +\end{itemize} + +Puede usar la orden \hgcmd{tags} para ver las etiquetas presentes en +su repositorio. Al desplegarse, cada revisión marcada se identifica +primero con su nombre, después con el número de revisión y finalmente con +un hash único de la revisión. +\interaction{tag.tags} +Note que \texttt{tip} aparece en en listado generado por \hgcmd{tags}. La etiqueta +\texttt{tip} es una etiqueta ``flotante'' especial, que identifica siempre +la revisión más reciente en el repositorio. + +Al desplegar la orden \hgcmd{tags}, las etiquetas se listan en orden +inverso, por número de revisión. Lo que significa usualmente que las +etiquetas más recientes se listan antes que las más antiguas. También +significa que la etiqueta \texttt{tip} siempre aparecerá como primera +etiqueta listada al desplegar la orden \hgcmd{tags}. + +Cuando usted ejecuta \hgcmd{log}, si se muestra una revisión que tenga +etiquetas asociadas a ella, se imprimirán tales etiquetas. +\interaction{tag.log} + +Siempre que requiera indicar un~ID de revisión a una orden de +Mercurial, aceptará un nombre de etiqueta en su lugar. Internamente, +Mercurial traducirá su nombre de etiqueta en el~ID de revisión +correspondiente, y lo usará. +\interaction{tag.log.v1.0} + +No hay límites en la cantidad de etiquetas por repositorio, o la cantidad +de etiquetas que una misma revisión pueda tener. Siendo prácticos, no es +muy buena idea tener ``demasiadas'' (la cantidad variará de un +proyecto a otro), debido a que la intención es ayudarle a encontrar +revisiones. Si tiene demasiadas etiquetas, la facilidad de usarlas +para identificar revisiones disminuirá rápidamente. + +Por ejemplo, si su proyecto tiene etapas (milestones) frecuentes, de pocos +días, es perfectamente razonable asignarle una etiqueta a cada una de +ellas. Pero si tiene un sistema de construcción automática de binarios +que asegura que cada revisión puede generarse limpiamente, estaría +introduciendo mucho ruido si se usara una etiqueta para cada generación +exitosa. Más bien, podría usar tags para generaciones fallidas +(\textexclamdown en +caso de que estas sean raras!), o simplemente evitar las etiquetas para +llevar cuenta de la posibilidad de generación de binarios. + + +Si quiere eliminar una etiqueta que no desea, use +\hgcmdargs{tag}{--remove}. +\interaction{tag.remove} +También puede modificar una etiqueta en cualquier momento, para que +identifique una revisión distinta, simplemente usando una nueva orden +\hgcmd{tag}. Deberá usar la opción \hgopt{tag}{-f} para indicarle a +Mercurial que \emph{realmente} desea actualizar la etiqueta. +\interaction{tag.replace} +De todas maneras habrá un registro permanente de la antigua identidad +de la etiqueta, pero Mercurial no la usará. Por lo tanto no hay +problema al marcar con una etiqueta una revisión incorrecta; lo único +que debe hacer es mover la etiqueta hacia la revisión correcta tan +pronto como localice el error. + +Mercurial almacena las etiquetas en un fichero controlado por revisiones en +su repositorio. Si ha creado etiquetas, las encontrará en un fichero +llamado \sfilename{.hgtags}. Cuando invoca la orden \hgcmd{tag}, +Mercurial modifica este fichero, y hace la consignación del cambio al +mismo automáticamente. Esto significa que cada vez que ejecuta +\hgcmd{tag}, verá un conjunto de cambios correspondiente en la salida +de \hgcmd{log}. +\interaction{tag.tip} + +\subsection{Manejo de conflictos entre etiquetas durante una fusión} + +Usualmente no tendrá que preocuparse por el fichero \sfilename{.hgtags}, +pero a veces hace su aparición durante una fusión. El formato del +fichero es sencillo: Consiste de una serie de líneas. Cada línea +comienza con un hash de conjunto de cambios, seguido por un espacio, +seguido por el nombre de una etiqueta. + +Si está resolviendo un conflicto en el fichero \sfilename{.hgtags} +durante una fusión, hay un detalle para tener en cuenta al modificar +el fichero \sfilename{.hgtags}: +cuando Mercurial procesa las etiquetas en el repositorio, \emph{nunca} +lee la copia de trabajo del fichero \sfilename{.hgtags}. En cambio, +lee la versión \emph{consignada más reciente} del fichero. + +Una consecuencia desafortunada de este diseño es que usted no puede +verificar que su fichero \sfilename{.hgtags} fusionado sea correcto hasta +\emph{después} de haber consignado un cambio. Así que si se +encuentra resolviendo un conflicto en \sfilename{.hgtags} durante una +fusión, asegúrese de ejecutar la orden \hgcmd{tags} después de +consignar. Si encuentra un error en el fichero \sfilename{.hgtags}, +la orden reportará el lugar del error, que podrá arreglar y después +consignar. Posteriormente ejecute de nuevo la orden \hgcmd{tags} para +asegurarse de que su arreglo fue aplicado correctamente . + +\subsection{Etiquetas y clonado} + +Puede haber notado que la orden \hgcmd{clone} tiene la opción +\hgopt{clone}{-r} que le permite clonar una copia exacta del +repositorio hasta un conjunto de cambios específico. El nuevo clon no +tendrá historial posterior a la revisión que usted haya +especificado. Esto tiene una interacción con etiquetas que puede +sorprender a los desprevenidos. + +Recuerde que una etiqueta se almacena como una revisión al fichero +\sfilename{.hgtags}, así que cuando usted crea una etiqueta, el +conjunto de cambios en el cual ésta se almacena necesariamente se +refiere a un conjunto de cambios anterior. Cuando ejecuta +\hgcmdargs{clone}{-r foo} para clonar un repositorio hasta la etiqueta +\texttt{foo}, el nuevo clon \emph{no contendrá el historial que creo +la etiqueta} que usó para clonar el repositorio. El resultado es que tendrá +exactamente el subconjunto correcto del historial del proyecto en el +nuevo repositorio, pero, \emph{no} la etiqueta que podría haber esperado. + +\subsection{Cuando las etiquetas permanentes son demasiado} + +Dado que las etiquetas de Mercurial están controladas por revisiones y se +llevan en el historial del proyecto, todas las personas involucradas +verán las etiquetas que usted haya creado. El hecho de dar nombres a las +revisiones tiene usos más allá que simplemente hacer notar que la +revisión \texttt{4237e45506ee} es realmente \texttt{v2.0.2}. Si está +tratando de encontrar un fallo sutil, posiblemente desearía colocar una +etiqueta recordándole algo como ``Ana vio los síntomas en esta revisión''. + +Para estos casos, lo que usted posiblemente desearía serían etiquetas +\emph{locales}. Puede crear una etiqueta local con la opción~\hgopt{tag}{-l} +de la orden \hgcmd{tag}. Esto guardará la etiqueta en un fichero llamado +\sfilename{.hg/localtags}. A diferencia de \sfilename{.hgtags}, +\sfilename{.hg/localtags} no está controlado por revisiones. +Cualquier etiqueta que usted cree usando \hgopt{tag}{-l} se mantendrá +local al repositorio en el que esté trabajando en ese momento. + +\section{El flujo de cambios---El gran cuadro vs. el pequeño} + +Retomando lo mencionado en el comienzo de un capítulo, pensemos en el +hecho de que un proyecto tiene muchas piezas concurrentes de trabajo +en desarrollo al mismo tiempo. + +Puede haber prisa por una nueva versión ``principal''; una nueva +versión con un arreglo de fallo a la última versión; y una versión de +``mantenimiento correctivo'' a una versión antigua que ha entrado en +modo de mantenimiento. + +Usualmente la gente se refiere a esas direcciones +concurrentes de desarrollo como ``ramas''. Sin embargo, ya hemos visto que +en varias ocasiones Mercurial trata a \emph{todo el historial} como +una serie de ramas y fusiones. Realmente lo que tenemos aquí es dos +ideas que se relacionan periféricamente, pero que en esencia comparten +un nombre. +\begin{itemize} +\item ``El gran cuadro'' Las ramas representan un barrido de la + evolución del proyecto; la gente les da nombres y hablan acerca de + ellas en sus conversaciones. +\item ``El cuadro pequeño'' Las ramas son artefactos de las + actividades diarias de desarrollar y fusionar cambios. Exponen la + narrativa de cómo se desarrolló el código. +\end{itemize} + +\section{Administrar ramas en repositorios estilo gran cuadro} + +En Mercurial la forma más sencilla de aislar una rama del ``gran +cuadro'' es a través de un repositorio dedicado. Si cuenta con un +repositorio compartido existente ---llamémoslo +\texttt{myproject}---que alcanzó la etapa ``1.0'', puede comenzar a +prepararse para versiones de mantenimiento futuras a partir de la +versión~1.0 marcando con una etiqueta la revisión con la cual preparó la versión~1.0. +\interaction{branch-repo.tag} +Ahora puede clonar un repositorio compartido nuevo +\texttt{myproject-1.0.1} con tal etiqueta. +\interaction{branch-repo.clone} + +Posteriormente, si alguien necesita trabajar en la reparación de un +fallo debería dirigirse a la liberación de versión~1.0.1 que viene en +camino, ellos clonarían el repositorio \texttt{myproject-1.0.1}, +harían sus cambios y los empujarían de vuelta. +\interaction{branch-repo.bugfix} +Mientras tanto, el desarrollo para la siguiente versión mayor puede +continuar aislado e incólume, en el repositorio \texttt{myproject}. +\interaction{branch-repo.new} + +\section{No repita trabajo: fusión entre ramas} + +En muchos casos, cuando tiene un fallo para arreglar en una rama de +mantenimiento, es muy probable que el fallo también esté en la rama +principal (y posiblemente en otras ramas de mantenimiento +también). Solamente un desarrollador extraño desearía corregir el +mismo fallo muchas veces, por tanto, veremos varias alternativas con +las que Mercurial puede ayudarle a administrar tales arreglos de fallo +sin duplicar su trabajo. + +En el caso más sencillo, basta con jalar los cambios de la rama de +mantenimiento a la rama objetivo en su clon local. +\interaction{branch-repo.pull} +A continuación deberá mezclar las cabezas de las dos ramas, y empujar +de nuevo a la rama principal. +\interaction{branch-repo.merge} + +\section{Nombrar ramas dentro de un repositorio} + +La aproximación correcta en casi todas las oportunidades es aislar las +ramas en los repositorios. Es fácil de entender gracias a su +simplicidad; y es difícil cometer errores. Hay una relación uno a uno +entre las ramas y los directorios con los que está trabajando en su +sistema. Esto le permite usar emplear herramientas usuales (que no son +conscientes de Mercurial) para trabajar con los ficheros dentro de una +rama/repositorio. + +Si se encuentra más en la categoría ``usuario diestro'' (\emph{y} sus +colaboradores también), puede considerar otra alternativa para +administrar las ramas. He mencionado con anterioridad la distinción a +nivel humano entre las ramas estilo ``cuadro pequeño'' y ``gran +cuadro''. Mientras que Mercurial trabaja con muchas ramas del estilo +``cuadro pequeño'' en el repositorio todo el tiempo (por ejemplo cuando +usted jala cambios, pero antes de fusionarlos), \emph{también} puede +trabajar con varias ramas del ``cuadro grande''. + +El truco para trabajar de esta forma en Mercurial se logra gracias a +que puede asignar un \emph{nombre} persistente a una rama. Siempre +existe una rama llamada \texttt{default}. Incluso antes de que +empiece a nombrar ramas por su cuenta, puede encontrar indicios de la +rama \texttt{default} si los busca. + +Por ejemplo, cuando invoca la orden \hgcmd{commit}, y se lanza su +editor para introducir el mensaje de la consignación, busque la línea +que contiene el texto ``\texttt{HG: branch default}'' al final. Le +está indicando que su consignación ocurrirá en la rama llamada +\texttt{default}. + +Use la orden \hgcmd{branches} para empezar a trabajar con ramas +nombradas. Esta orden mostrará las ramas presentes en su repositorio, +indicándole qué conjunto de cambios es la punta de cada una. +\interaction{branch-named.branches} +Dado que todavía no ha creado ramas nombradas, la única que verá será +\texttt{default}. + +Para hallar cuál es la rama ``actual'', invoque la orden +\hgcmd{branch}, sin argumento alguno. Le informará en qué rama se +encuentra el padre del conjunto de cambios actual. +\interaction{branch-named.branch} + +Para crear una nueva rama, invoque la orden \hgcmd{branch} de +nuevo. En esta oportunidad, ofrezca un argumento: el nombre de la rama +que desea crear. +\interaction{branch-named.create} + +Después de crear la rama, usted podría desear ver el efecto que tuvo +la orden \hgcmd{branch}. ¿Qué reportan las ordenes \hgcmd{status} y +\hgcmd{tip}? +\interaction{branch-named.status} +Nada cambia en el directorio actual, y no se ha añadido nada al +historial. Esto sugiere que al ejecutar la orden \hgcmd{branch} no hay +un efecto permanente; solamente le indica a que nombre de rama usará +la \emph{próxima} vez que consigne un conjunto de cambios. + +Cuando consigna un cambio, Mercurial almacena el nombre de la rama en +la cual consignó. Una vez que haya cambiado de la rama \texttt{default} +y haya consignado, verá que el nombre de la nueva rama se mostrará +cuando use la orden \hgcmd{log}, \hgcmd{tip}, y otras órdenes que +desplieguen la misma clase de información. +\interaction{branch-named.commit} +Las órdenes del tipo \hgcmd{log} imprimirán el nombre de la rama de +cualquier conjunto de cambios que no esté en la rama +\texttt{default}. Como resultado, si nunca usa ramas nombradas, nunca +verá esta información. + +Una vez que haya nombrado una rama y consignado un cambio con ese +nombre, todas las consignaciones subsecuentes que desciendan de ese +cambio heredarán el mismo nombre de rama. Puede cambiar el nombre de +una rama en cualquier momento con la orden \hgcmd{branch}. +\interaction{branch-named.rebranch} +Esto es algo que no hará muy seguido en la práctica, debido que los +nombres de las ramas tienden a tener vidas largas. (Esto no es una +regla, solamente una observación.) + +\section{Tratamiento de varias ramas nombradas en un repositorio} + +Si tiene más de una rama nombrada en un repositorio, Mercurial +recordará la rama en la cual está su directorio de trabajo cuando +invoque una orden como \hgcmd{update} o \hgcmdargs{pull}{-u}. Se +actualizará su directorio de trabajo actual a la punta de esta rama, sin +importar cuál sea la punta ``a lo largo del repositorio''. Para +actualizar a una revisión que está en una rama con distinto nombre, +puede necesitar la opción \hgopt{update}{-C} de \hgcmd{update}. + +Este comportamiento puede ser sutil, así que veámoslo en acción. Primero, +recordemos en qué rama estamos trabajando, y qué ramas están en +nuestro repositorio. +\interaction{branch-named.parents} +Estamos en la rama \texttt{bar}, pero existe otra rama más antigua +llamada \hgcmd{foo}. + +Podemos hacer \hgcmd{update} entre los tipos de las ramas \texttt{foo} +y \texttt{bar} sin necesidad de usar la opción \hgopt{update}{-C}, +puesto que esto solamente implica ir linealmente hacia adelante y +atrás en nuestro historial de cambios. +\interaction{branch-named.update-switchy} + +Si volvemos a la rama \texttt{foo} e invocamos la orden \hgcmd{update}, +nos mantendrá en \texttt{foo}, sin movernos a la punta de \texttt{bar}. +\interaction{branch-named.update-nothing} + +Al consignar un cambio a la rama \texttt{foo} se introducirá una nueva +cabeza. +\interaction{branch-named.foo-commit} + +\section{Nombres de ramas y fusiones} + +Posiblemente ha notado que las fusiones en Mercurial no son simétricas. +Supongamos que su repositorio tiene dos cabezas, 17 y 23. Si yo invoco +\hgcmd{update} a 17 y aplico \hgcmd{merge} a 23, Mercurial almacena 17 +como el primer padre de la fusión, y 23 como el segundo. Mientras que +si hago \hgcmd{update} a 23 y después aplico \hgcmd{merge} con 17, +grabará a 23 como el primer padre, y 17 como el segundo. + +Esto afecta el cómo elige Mercurial el nombre de la rama cuando usted +hace la fusión. Después de una fusión, Mercurial mantendrá el nombre de la +rama del primer padre cuando consigne el resultado de la fusión. Si +el primer nombre de su padre es \texttt{foo}, y fusiona con +\texttt{bar}, el nombre de la rama continuará siendo \texttt{foo} +después de fusionar. + +No es inusual que un repositorio contenga varias cabezas, cada una con +el mismo nombre de rama. Digamos que estoy trabajando en la rama +\texttt{foo}, y usted también. Consignamos cambios distintos; yo jalo +sus cambios; Ahora tengo dos cabezas, cada una afirmando estar en la +rama \texttt{foo}. El resultado de una fusión será una única cabeza +en la rama \texttt{foo} como usted esperaría. + +Pero si estoy trabajando en la rama \texttt{bar}, y fusiono el trabajo +de la rama \texttt{foo}, el resultado permanecerá en la rama +\texttt{bar}. +\interaction{branch-named.merge} + +En un ejemplo más concreto, si yo estoy trabajando en la rama +\texttt{bleeding-edge}, y deseo traer los arreglos más recientes de la +rama \texttt{estable}, Mercurial elegirá el nombre de rama ``correcto'' +(\texttt{bleeding-edge}) cuando yo jale una fusión desde \texttt{estable}. + +\section{Normalmente es útil nombrar ramas} + +No debería considerar que las ramas nombradas son aplicables +únicamente en situaciones con muchas ramas de larga vida cohabitando +en un mismo repositorio. Son muy útiles incluso en los casos de +una rama por repositorio. + +En el caso más sencillo, dar un nombre a cada rama ofrece un registro +permanente acerca de en qué conjunto de cambios se generó la rama. +Esto le ofrece más contexto cuando esté tratando de seguir el +historial de un proyecto ramificado de larga vida. + +Si está trabajando con repositorios compartidos, puede configurar el gancho +\hook{pretxnchangegroup} para que cada uno bloquee los cambios con +nombres de rama ``incorrectos'' que están por adicionarse. Este +provee una defensa sencilla, pero efectiva, para evitar que la gente +publique accidentalmente cambios de una rama ``super nueva'' a la rama +``estable''. Tal gancho podría verse de la siguiente forma dentro de +un repositorio compartido de \hgrc. +\begin{codesample2} + [hooks] + pretxnchangegroup.branch = hg heads --template '{branches} ' | grep mybranch +\end{codesample2} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/cmdref.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/cmdref.py Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,1 @@ +../en/cmdref.py \ No newline at end of file diff -r 73b094b764ec -r 51b5d56744c5 es/cmdref.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/cmdref.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,186 @@ +\chapter{Referencia de Órdenes} +\label{cmdref} + +\cmdref{add}{Añade ficheros en la próxima consignación} +\optref{add}{I}{include} +\optref{add}{X}{exclude} +\optref{add}{n}{dry-run} + +\cmdref{diff}{imprime los cambios en el historial o el directorio actual} + +Mostrar las diferencias entre revisiones para ficheros especificados o +directorios, con el formato unificado diff. Si desea ver una +descripción del formato unificado diff, ver la sección~\ref{sec:mq:patch}. + +De forma predeterminada, esta orden no imprime las diferencias para +los ficheros binarios que Mercurial esté siguiendo. Para controlar +este comportamiento, vea las opciones \hgopt{diff}{-a} y +\hgopt{diff}{--git}. + +\subsection{Options} + +\loptref{diff}{nodates} + +Omite la fecha y hora cuando se muestran los encabezados de las +diferencias. + +\optref{diff}{B}{ignore-blank-lines} + +No imprime los cambios que solamente insertan o eliminan líneas en +blanco. Una línea que contiene espacios en blanco no se considera +como una línea en blanco. + +\optref{diff}{I}{include} + +Incluye ficheros y directorios cuyos nombres coinciden con los +patrones elegidos. + +\optref{diff}{X}{exclude} + +Excluye los ficheros y directorios cuyos nombres coinciden con los +patrones elegidos. + +\optref{diff}{a}{text} + +Si no especifica esta opción, \hgcmd{diff} no mostrará las diferencias +de los ficheros que detecte como binarios. Al especificar \hgopt{diff}{-a} +se forza a \hgcmd{diff} a tratar los ficheros como texto, y generar +diferencias para todos. + +Esta opción es útil para los ficherso que son ``texto en mayor +medida'' pero que tienen caracteres NUL. Si lo usa en ficheros que +contienen muchos datos binarios, la salida será incomprensible. + +\optref{diff}{b}{ignore-space-change} + +No imprime si el único cambio que en la línea es la cantidad de +espacio en blanco. + +\optref{diff}{g}{git} + +Mostrar diferencias compatibles con \command{git}. XXX reference a format +description. + +\optref{diff}{p}{show-function} + +Mostrar el nombre de la función que contiene el código en una porción +del encabzado usando una heurística simple. Esta funcionalidad se +habilita de forma predeterminada, así que la opción \hgopt{diff}{-p} +no tiene efectos a menos que cambie el valor de +\rcitem{diff}{showfunc} en la configuración, como en el ejemplo +siguiente. +\interaction{cmdref.diff-p} + +\optref{diff}{r}{rev} + +Especifique una o más revisiones para comparar. La orden \hgcmd{diff} +acepta hasta dos opciones \hgopt{diff}{-r} para especificar las +revisiones a comparar. + +\begin{enumerate} +\setcounter{enumi}{0} +\item Despliega las diferencias entre la revisión padre y del directorio + de trabajo. +\item Despliega las diferencias entre el conjunto de cambios + especificados y el directorio de trabajo. +\item Despliega las diferencias entre dos conjuntos de cambios + especificados. +\end{enumerate} + +Puede especificar dos revisiones usando o bien sea las opciones +\hgopt{diff}{-r} o la notación de rango. Por ejemplo, las dos +especificaciones de revisiones a continuación son equivalentes: +\begin{codesample2} + hg diff -r 10 -r 20 + hg diff -r10:20 +\end{codesample2} + +Cuando especifica dos revisiones, esto tiene significado para +Mercurial. Esto significa que \hgcmdargs{diff}{-r10:20} producirá un +diff que transformará los ficheros desde los contenidos en la revisión +10 a los contenidos de la revisión 20, mientras que +\hgcmdargs{diff}{-r20:10} significa lo opuesto: el diff que +transformaría los contenidos de los ficheros de la revisión 20 a los +contenidos de la revisión 10. No puede invertir el orden de esta +forma si está haciendo un diff frente al directorio de trabajo. + +\optref{diff}{w}{ignore-all-space} + +\cmdref{version}{imprime la información de versión y derechos de reproducción} + +Esta orden despliega la versión de Mercurial que está usando, y su +nota de derechos de reproducción. Hay cuatro clases de cadenas de +versión posibles: +\begin{itemize} +\item La cadena ``\texttt{unknown}''. Esta versión de Mercurial no fue + construida en un repositorio de Mercurial, y no puede determinar su + propia versión. +\item Una cadena numérica corta, tal como ``\texttt{1.1}''. Esta es + una construcción de una versión de Mercurial que se identifica con + una etiqueta específica en el repositorio en el cual fue + armada (Esto no significa necesariamente que está ejecutando una + versión oficial; alguien pudo haber añadido tal etiqueta a cualquier + versión del repositorio en el cual armaron Mercurial). +\item Una cadena hexadecimal, tal como ``\texttt{875489e31abe}''. + Esta es una construcción de una revisión dada de Mercurial. +\item Una cadena hexadecimal seguida por una fecha, tal como + ``\texttt{875489e31abe+20070205}''. Esta construcción de la + revisión de Mercurial fue la construcción de un repositorio que tuvo + cambios locales que no han sido consignados. +\end{itemize} + +\subsection{Consejos y trucos} + +\subsubsection{¿Por qué difieren los resultados de \hgcmd{diff} y + \hgcmd{status}?} +\label{cmdref:diff-vs-status} + +Cuando ejecuta la orden \hgcmd{status}, verá una lista de ficheros +para los cuales Mercurial almacenará cambios la próxima vez que +consigne. Si ejecuta la orden \hgcmd{diff}, verá que imprime +diferencias solamente para un \emph{subconjunto} de los ficheros que +\hgcmd{status} liste. Hay dos posibles razones para este comportamiento: + +La primera es que \hgcmd{status} imprime cierta clase de +modificaciones que \hgcmd{diff} no despliega normalmente. La orden +\hgcmd{diff} usualmente despliega diferencias unificadas, las cuales +no tienen la habilidad de representar algunos cambios que Mercurial +puede seguir. Lo más notable es que las diferencias tradicionales no +pueden representar un cambio acerca de la ejecutabilidad de un +fichero, pero Mercurial sí almacena esta información. + +Si usa la opción \hgopt{diff}{--git} de \hgcmd{diff}, mostrará +diferencias compatibles con \command{git} que \emph{pueden} desplegar +esta información adicional. + +La segunda razón posible para que \hgcmd{diff} esté imprimiendo +diferencias para un subconjunto de ficheros de lo que muestra +\hgcmd{status} es que si usted le invoca sin argumento alguno, +\hgcmd{diff} imprime diferencias frente al primer padre del directorio +de trabajo. Si ha ejecutado \hgcmd{merge} para fusionar dos conjuntos +de cambios, pero no ha consignado aún los resultados de la fusión, su +directorio de trabajo tiene dos padres (use \hgcmd{parents} para +verlos). Mientras que \hgcmd{status} imprime modificaciones relativas +a \emph{ambos} padres después de una fusión que no se ha consignado, +\hgcmd{diff} opera aún relativo solamente al primer padre. Puede +lograr que imprima las diferencias relativas al segundo padre +especificando tal padre con la opción \hgopt{diff}{-r}. No hay forma +de hacer que imprima las diferencias relativas a los dos padres. + +\subsubsection{Generar diferencias seguras en binarios} + +Si usa la opción \hgopt{diff}{-a} para forzar que Mercurial imprima +las diferencias de los ficheros que so o bien ``casi completamente +texto'' o contienen muchos datos binarios, tales diferencias no pueden +aplicarse subsecuentemente a la orden \hgcmd{import} de Mercurial o a +la orden \command{patch} del sistema. + +Si desea generar una diferencia de un fichero binario que es seguro +para usarlo como entrada a la orden \hgcmd{import}, use la opción +\hgcmd{diff}{--git} cuando genere el parche. La orden \command{patch} +del sistema no puede tratar con parches binarios. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/collab.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/collab.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,1059 @@ +\chapter{Colaborar con otros} +\label{cha:collab} + +Debido a su naturaleza descentralizada, Mercurial no impone política +alguna de cómo deben trabajar los grupos de personas. Sin embargo, si +usted es nuevo al control distribuido de versiones, es bueno tener +herramientas y ejemplos a la mano al pensar en posibles modelos de +flujo de trabajo. + +\section{La interfaz web de Mercurial} + +Mercurial tiene una poderosa interfaz web que provee bastantes +capacidades útiles. + +Para uso interactivo, la interfaz le permite visualizar uno o varios +repositorios. Puede ver el historial de un repositorio, examinar cada +cambio (comentarios y diferencias), y ver los contenidos de cada +directorio y fichero. + +Adicionalmente la interfaz provee notificaciones RSS de los cambios de los +repositorios. Que le permite ``subscribirse''a un repositorio usando +su herramienta de lectura de notificaciones favorita, y ser notificado +automáticamente de la actividad en el repositorio tan pronto como +sucede. Me gusta mucho más este modelo que el estar suscrito a una +lista de correo a la cual se envían las notificaciones, dado que no +requiere configuración adicional de parte de quien sea que está +administrando el repositorio. + +La interfaz web también permite clonar repositorios a los usuarios +remotos, jalar cambios, y (cuando el servidor está configurado para +permitirlo) publicar cambios en el mismo. El protocolo de entunelamiento +de Mercurial comprime datos agresivamente, de forma que trabaja +eficientemente incluso con conexiones de red con poco ancho de banda. + +La forma más sencilla de iniciarse con la interfaz web es usar su +navegador para visitar un repositorio existente, como por ejemplo el +repositorio principal de Mercurial \url{http://www.selenic.com/repo/hg?style=gitweb}. + +Si está interesado en proveer una interfaz web a sus propios +repositorios, Mercurial provee dos formas de hacerlo. La primera es +usando la orden \hgcmd{serve}, que está enfocada a servir ``de forma +liviana'' y por intervalos cortos. Para más detalles de cómo usar +esta orden vea la sección~\ref{sec:collab:serve} más adelante. Si +tiene un repositorio que desea hacer permanente, Mercurial tiene +soporte embebido del \command{ssh} para publicar cambios con seguridad +al repositorio central, como se documenta en la +sección~\ref{sec:collab:ssh}. Es muy usual que se publique una copia +de sólo lectura en el repositorio que está corriendo sobre HTTP usando +CGI, como en la sección~\ref{sec:collab:cgi}. Publicar sobre HTTP +satisface las necesidades de la gente que no tiene permisos de +publicación y de aquellos que quieren usar navegadores web para +visualizar el historial del repositorio. + +\subsection{Trabajo con muchas ramas} + +Los proyectos de cierta talla tienden naturalmente a progresar de +forma simultánea en varios frentes. En el caso del software, es común +que un proyecto tenga versiones periódicas oficiales. Una versión +puede entrar a ``modo mantenimiento'' por un tiempo después de su +primera publicación; las versiones de mantenimiento tienden a contener +solamente arreglos de fallos, pero no nuevas características. En +paralelo con las versiones de mantenimiento puede haber una o muchas +versiones futuras pueden estar en desarrollo. La gente usa normalmente +la palabra ``rama'' para referirse a una de las direcciones +ligeramente distintas en las cuales procede el desarrollo. + +Mercurial está especialmente preparado para administrar un buen número +de ramas simultáneas pero no idénticas. Cada ``dirección de +desarrollo'' puede vivir en su propio repositorio central, y puede +mezclar los cambios de una a otra de acuerdo con las necesidades. Dado +que los repositorios son independientes, uno del otro, los cambios +inestables de una rama de desarrollo nunca afectarán una rama estable +a menos que alguien explícitamente mezcle los cambios. + +A continuación un ejemplo de cómo podría hacerse esto en la +práctica. Digamos que tiene una ``rama principal'' en un servidor +central. +\interaction{branching.init} +Alguien lo clona, hace cambios locales, los prueba, y los publica allí +mismo. + +Una vez que la rama principal alcanza una estado de versión se puede +usar la orden \hgcmd{tag} para dar un nombre permanente a la revisión. +\interaction{branching.tag} +Digamos que en la rama principal ocurre más desarrollo. +\interaction{branching.main} +Cuando se usa la etiqueta con que se identificó la versión, la gente +puede clonar el repositorio en cualquier momento en el futuro +empleando \hgcmd{update} para obtener una copia del directorio de +trabajo exacta como cuando se creó la etiqueta de la revisión que se +consignó. +\interaction{branching.update} + +Adicionalmente, justo después de que la rama principal se etiquete, +alguien puede clonarla en el servidor a una nueva rama ``estable'', +también en el servidor. +\interaction{branching.clone} + +Alguien que requiera hacer un cambio en la rama estable puede clonar +\emph{ese} repositorio, hacer sus cambios, consignar y publicarlos +posteriormente al inicial. +\interaction{branching.stable} +Puesto que los repositorios de Mercurial son independientes, y que +Mercurial no mueve los cambios de un lado a otro automáticamente, las +ramas estable y principal están \emph{aisladas} la una de la otra. +Los cambios que haga en la rama principal no ``se filtran'' a la rama +estable o vice versa. + +Es usual que los arreglos de fallos de la rama estable deban hacerse +aparecer en la rama principal también. En lugar de reescribir el +arreglo del fallo en la rama principal, puede jalar y mezclar los +cambios de la rama estable a la principal, Mercurial traerá tales +arreglos por usted. +\interaction{branching.merge} +La rama principal contendrá aún los cambios que no están en la +estable y contendrá además todos los arreglos de fallos de la rama +estable. La rama estable permanece incólume a tales cambios. + +\subsection{Ramas de Características} + +En proyectos grandes, una forma efectiva de administrar los cambios es +dividir el equipo en grupos más pequeños. Cada grupo tiene una rama +compartida, clonada de una rama ``principal'' que conforma el proyecto +completo. Aquellos que trabajan en ramas individuales típicamente +están aislados de los desarrollos de otras ramas. + +\begin{figure}[ht] + \centering + \grafix{feature-branches} + \caption{Ramas de Características} + \label{fig:collab:feature-branches} +\end{figure} + +Cuando una rama particular alcanza un estado deseado, alguien del +equipo de características jala y fusiona de la rama principal hacia +la rama de características y publica posteriormente a la rama principal. + +\subsection{El tren de publicación} + +Algunos proyectos se organizan al estilo``tren'': Una versión se +planifica para ser liberada cada cierto tiempo, y las características +que estén listas cuando ha llegado el momento ``tren'', se incorporan. + +Este modelo tiene cierta similitud a las ramas de características. La +diferencia es que cuando una característica pierde el tren, alguien en +el equipo de características jala y fusiona los cambios que se fueron +en la versión liberada hacia la rama de característica, y el trabajo +continúa sobre lo fusionado para que la característica logre estar en +la próxima versión. + +\subsection{El modelo del kernel linux} + +El desarrollo del Kernel Linux tiene una estructura jerárquica +bastante horizontal, rodeada de una nube de caos aparente. Dado que la +mayoría de desarrolladores usan \command{git}, una herramienta distribuida +de control de versiones con capacidades similares a Mercurial, resulta +de utilidad describir la forma en que el trabajo fluye en tal +ambiente; si le gustan las ideas, la aproximación se traduce bien +entre Git y Mercurial. + +En el centro de la comunidad está Linus Torvalds, el creador de Linux. +Él publica un único repositorio que es considerado el árbol +``oficial'' actual por la comunidad completa de +desarrolladores. Cualquiera puede clonar el árbol de Linus, pero él es +muy selectivo acerca de los árboles de los cuales jala. + +Linus tiene varios ``lugartenientes confiables''. Como regla, él jala +todos los cambios que ellos publican, en la mayoría de los casos sin +siquiera revisarlos. Algunos de sus lugartenientes generalmente +aceptan ser los ``mantenedores'', responsables de subsistemas +específicos dentro del kernel. Si un hacker cualquiera desea hacer un +cambio a un subsistema y busca que termine en el árbol de Linus, debe +encontrar quién es el mantenedor del subsistema y solicitarle que +tenga en cuenta su cambio. Si el mantenedor revisa los cambios y está +de acuerdo en tomarlos, estos pasarán al árbol de Linus de acuerdo a +lo expuesto. + +Cada lugarteniente tiene su forma particular de revisar, aceptar y +publicar los cambios; y para decidir cuando hacerlos presentes a +Linus. Adicionalmente existen varias ramas conocidas que mucha gente +usa para propósitos distintos. Por ejemplo, pocas personas mantienen +repositorios ``estables'' de versiones anteriores del kernel, a los +cuales aplican arreglos de fallos críticos necesarios. Algunos +mantenedores publican varios árboles: uno para cambios +experimentales; uno para cambios que van a ofrecer al mantenedor +principal; y así sucesivamente. Otros publican un solo árbol. + +Este modelo tiene dos características notables. La primera es que son +de ``jalar exclusivamente''. Usted debe solicitar, convencer o +incluso rogar a otro desarrollador para que tome sus cambios, porque +casi no hay árboles en los cuales más de una persona pueda publicar, y +no hay forma de publicar cambios en un árbol que otra persona controla. + +El segundo está basado en reputación y meritocracia. Si usted es un +desconocido, Linus probablemente ignorará sus cambios, sin siquiera +responderle. Pero un mantenedor de un subsistema probablemente los +revisara, y los acogerá en caso de que aprueben su criterio de +aplicabilidad. A medida que usted ofrezca ``mejores'' cambios a un +mantenedor, habrá más posibilidad de que se confíe en su juicio y se +acepten los cambios. Si usted es reconocido y mantiene una rama +durante bastante tiempo para algo que Linus no ha aceptado, personas +con intereses similares pueden jalar sus cambios regularmente para +estar al día con su trabajo. + +La reputación y meritocracia no necesariamente es transversal entre +``personas'' de diferentes subsistemas. Si usted es respetado pero es +un hacker en almacenamiento y trata de arreglar un fallo de redes, +tal cambio puede recibir un nivel de escrutinio de un mantenedor de +redes comparable con el que se le haría a un completo extraño. + +Personas que vienen de proyectos con un ordenamiento distinto, sienten +que el proceso comparativamente caótico del Kernel Linux es +completamente lunático. Es objeto de los caprichos individuales; la +gente desecha cambios cuando lo desean; y la fase de desarrollo es +alucinante. A pesar de eso Linux es una pieza de software exitosa y +bien reconocida. + +\subsection{Solamente jalar frente a colaboración pública} + +Una fuente perpetua de discusiones en la comunidad de código abierto +yace en el modelo de desarrollo en el cual la gente solamente jala +cambios de otros ``es mejor que'' uno en el cual muchas personas +pueden publicar cambios a un repositorio compartido. + +Típicamente los partidarios del modelo de publicar usan las herramientas +que se apegan a este modelo. Si usted usa una herramienta +centralizada de control de versiones como Subversion, no hay forma de +elegir qué modelo va a usar: La herramienta le ofrece publicación +compartida, y si desea hacer cualquier otra cosa, va a tener que +aplicar una aproximación artificial (tal como aplicar parches a mano). + +Una buena herramienta distribuida de control de versiones, tal como +Mercurial soportará los dos modelos. Usted y sus colaboradores +pueden estructurar cómo trabajarán juntos basados en sus propias +necesidades y preferencias, sin depender de las peripecias que la +herramienta les obligue a hacer. + +\subsection{Cuando la colaboración encuentra la administración ramificada} + +Una vez que usted y su equipo configurar algunos repositorios +compartidos y comienzan a propagar cambios entre sus repositorios +locales y compartidos, comenzará a encarar un reto relacionado, pero +un poco distinto: Administrar las direcciones en las cuales su equipo +puede moverse. A pesar de que está íntimamente ligado acerca de cómo +interactúa su equipo, es lo suficientemente denso para ameritar un +tratamiento en el capítulo~\ref{chap:branch}. + +\section{Aspectos técnicos de la colaboración} + +Lo que resta del capítulo lo dedicamos a las cuestiones de servir +datos a sus colaboradores. + +\section{Compartir informalmente con \hgcmd{serve}} +\label{sec:collab:serve} + +La orden \hgcmd{serve} de Mercurial satisface de forma espectacular +las necesidades de un grupo pequeño, acoplado y de corto +tiempo. Se constituye en una demostración de cómo se siente usar los +comandos usando la red. + +Ejecute \hgcmd{serve} dentro de un repositorio, y en pocos segundos +iniciará un servidor HTTP especializado; aceptará conexiones desde +cualquier cliente y servirá datos de este repositorio mientrs lo +mantenga funcionando. Todo el que sepa el URL del servidor que ha +iniciado, y que puede comunicarse con su computador por la red, puede +usar un navegador web o Mercurial para leer datos del repositorio. Un +URL para una instancia de \hgcmd{serve} ejecutándose en un portátil +debería lucir algo \Verb|http://my-laptop.local:8000/|. + +La orden \hgcmd{serve} \emph{no} es un servidor web de propósito +general. Solamente puede hacer dos cosas: +\begin{itemize} +\item Permitir que se pueda visualizar el historial del repositorio que + está sirviendo desde navegadores web. +\item Hablar el protocolo de conexión de Mercurial para que puedan hacer + \hgcmd{clone} o \hgcmd{pull} (jalar) cambios de tal repositorio. +\end{itemize} +En particular, \hgcmd{serve} no permitirá que los usuarios remotos +puedan \emph{modificar} su repositorio. Es de tipo solo lectura. + +Si está comenzando con Mercurial, no hay nada que le impida usar +\hgcmd{serve} para servir un repositorio en su propio computador, y +usar posteriormente órdenes como \hgcmd{clone}, \hgcmd{incoming}, para +comunicarse con el servidor como si el repositorio estuviera alojado +remotamente. Lo que además puede ayudarle a adecuarse rápidamente para +usar comandos en repositorios alojados en la red. + +\subsection{Cuestiones adicionales para tener en cuenta} + +Dado que permite lectura sin autenticación a todos sus clientes, +debería usar \hgcmd{serve} exclusivamente en ambientes en los cuáles +no tenga problema en que otros vean, o en los cuales tenga control +completo acerca de quien puede acceder a su red y jalar cambios de su +repositorio. + +La orden \hgcmd{serve} no tiene conocimiento acerca de programas +cortafuegos que puedan estar instalados en su sistema o en su red. No +puede detectar o controlar sus cortafuegos. Si otras personas no +pueden acceder a su instancia \hgcmd{serve}, lo siguiente que debería hacer +(\emph{después} de asegurarse que tienen el URL correcto) es verificar +su configuración de cortafuegos. + +De forma predeterminada, \hgcmd{serve} escucha conexiones entrantes en +el puerto~8000. Si otro proceso está escuchando en tal puerto, usted +podrá especificar un puerto distinto para escuchar con la opción +\hgopt{serve}{-p}. + +Normalmente, cuando se inicia \hgcmd{serve}, no imprime nada, lo cual +puede ser desconcertante. Si desea confirmar que en efecto está +ejecutándose correctamente, y darse cuenta qué URL debería enviar a +sus colaboradores, inícielo con la opción \hggopt{-v}. + +\section{Uso del protocolo Secure Shell (ssh)} +\label{sec:collab:ssh} + +Usted puede publicar y jalar cambios en la red de forma segura usando +el protocolo Secure Shell (\texttt{ssh}). Para usarlo satisfactoriamente, +tendrá que hacer algo de configuración a nivel de cliente o el +servidor. + +Si no está familiarizado con ssh, es un protocolo de red que le permite +comunicarse con seguridad con otro computador. Para usarlo con +Mercurial, estará estableciendo una o más cuentas de usuario en un +servidor de forma tal que los usuarios remotos puedan entrar y +ejecutar órdenes. + +(Si ssh le \emph{es} familiar, encontrará probablemente elemental una +porción del material a continuación.) + +\subsection{Cómo leer y escribir URLs de ssh} + +Los URLs de ssh tienden a lucir de la siguiente forma: +\begin{codesample2} + ssh://bos@hg.serpentine.com:22/hg/hgbook +\end{codesample2} +\begin{enumerate} +\item La parte ``\texttt{ssh://}'' indica a Mercurial que use el + protocolo ssh. +\item El componente ``\texttt{bos@}'' indica el nombre del usuario que + está entrando al servidor. Puede omitirlo si el usuario remoto + coincide con el usuario local. +\item ``\texttt{hg.serpentine.com}'' es el nombre del servidor al cual + se desea entrar. +\item El ``:22'' identifica el número del puerto en el servidor al cual + se conectará. El predeterminado es el~22, así que solamente + necesitará especificar esa porción si \emph{no} está usando el + puerto~22. +\item La última porción del URL es la ruta local al repositorio en el + servidor. +\end{enumerate} + +El componente de la ruta del URL para ssh es una fuente de confusión, +puesto que no hay una forma estándar para que las herramientas puedan +interpretarlo. Algunos programas se comportan de manera distinta a +otros cuando manipulan estas rutas. No es la situación ideal, pero +es muy poco probable que vaya a cambiar. Por favor lea los párrafos +siguientes cuidadosamente. + +Mercurial trata la ruta al repositorio en el servidor como relativo al +directorio personal del usuario remoto. Por ejemplo, si el usuario +\texttt{foo} en el servidor tiene el directorio casa +\dirname{/home/foo}, +entonces un URL ssh que contenga en su ruta a \dirname{bar} +\emph{realmente} se refiere al directorio \dirname{/home/foo/bar}. + +Si desea especificar una ruta relativa a otro directorio de usuario, +puede usar una ruta que comience con un caracter tildado, seguido del +nombre del usuario (llamémosle \texttt{otrousuario}, así +\begin{codesample2} + ssh://server/~otrousuario/hg/repo +\end{codesample2} + +Y si realmente desea especifica una ruta \emph{absoluta} en el +servidor, comience con el componente de la ruta con dos barras como +en el siguiente ejemplo: +\begin{codesample2} + ssh://server//absolute/path +\end{codesample2} + +\subsection{Encontrar un cliente ssh para su sistema} + +Casi todos los sistemas tipo Unix vienen con OpenSSH preinstalado. Si +usted está usando un sistema de estos, ejecute \Verb|which ssh| para +identificar dónde está instalada la orden \command{ssh} (usualmente +estará en \dirname{/usr/bin}). Si por casualidad no está presente, +vea la documentación de sus sistema para lograr instalarlo. + +En Windows, tendrá que escoger primero un cliente adecuado para +descargarlo. Hay dos alternativas: +\begin{itemize} +\item El excelente paquete PuTTY~\cite{web:putty} de Simon Tatham, que + ofrece un suite completo de órdenes de cliente ssh. +\item Si tiene alta tolerancia al dolor, puede usar el porte de Cygwin + para OpenSSH. +\end{itemize} +En cualquier caso, tendrá que editar su fichero \hgini\ para indicarle +a Mercurial dónde encontrar la orden real del cliente. Por ejemplo, si +está usando PuTTY, tendrá que usar la orden \command{plink} como un +cliente de línea de órdenes. +\begin{codesample2} + [ui] + ssh = C:/ruta/a/plink.exe -ssh -i "C:/ruta/a/mi/llave/privada" +\end{codesample2} + +\begin{note} + La ruta a \command{plink} no debería contener espacios o caracteres + en blanco, o Mercurial no podrá encontrarlo correctamente (por lo + tanto, probablemente no sería buena idea colocarlo en + \dirname{C:\\Program Files} +\end{note} + +\subsection{Generar un par de llaves} + +Para evitar la necesidad de teclear una clave de forma repetitiva cada +vez que necesita usar el cliente, recomiendo generar un par de llaves. +En un sistema tipo Unix, la orden \command{ssh-keygen} también se +comportará bien. En Windows, si está usando PuTTY, la orden +\command{puttygen} es la que necesitará. + +Cuando genera un par de llaves, se aconseja \emph{comedidamente} +protegerlas con una frase de clave. (La única oportunidad en la cual +usted querría identificarse una única vez, es cuando está usando +el protocolo ssh para tareas automatizadas en una red segura.) + +No basta con generar un par de llaves. Se requiere adicionar una llave +pública al conjunto de llaves autorizadas para todos los usuarios +remotos que se vayan a autenticar. Para aquellos servidores que usen +OpenSSH (la gran mayoría), significará añadir la llave pública a la +lista en el fichero llamado \sfilename{authorized\_keys} en su +directorio \sdirname{.ssh}. + +En sistemas tipo Unix, su llave pública tendrá la extensión +\filename{.pub}. Si usa \command{puttygen} en Windows, puede +guardar la llave pública en un fichero de su elección, o pegarla desde +la ventana en la cual se despliega directamente en el fichero +\sfilename{authorized\_keys}. + +\subsection{Uso de un agente de autenticación} + +Un agente de autenticación es un demonio que almacena frases clave en +memoria (olvidará las frases clave si sale y vuelve a entrar). Un cliente +ssh notará si está corriendo, y solicitará una frase clave. Si no hay +un agente de autenticación corriendo, o el agente no almacena la frase +clave necesaria, tendrá que teclear su frase clave cada vez que +Mercurial intente comunicarse con un servidor para usted (p.e.~cada vez +que jale o publique cambios). + +El problema de almacenar frases claves en un agente es que es posible +para un atacante bien preparado recuperar el texto plano de su frase +clave, en algunos casos incluso si su sistema sea muy alternante. +Es su decisión si es un riesgo aceptable. Lo que si es seguro es que +evita reteclear. + +En sistemas tipo Unix, el agente se llama \command{ssh-agent}, y +usualmente se ejecuta automáticamente cuando usted entra. Tendrá que +usar la orden \command{ssh-add} para añadir frases claves al agente. En +Windows, si está usando PuTTY, la orden \command{pageant} actúa como +el agente. Añade un icono a su barra del sistema que le permitirá +almacenar frases clave. + +\subsection{Configurar el lado del servidor apropiadamente} + +Dado que puede ser dispendioso configurar ssh si usted es nuevo, hay +una variedad de cosas que podrían ir mal. Añada piense primero en +Mercurial y hay mucho más en qué pensar. La mayor parte de estos +problemas potenciales ocurren en el lado del servidor, no en el cliente. +Las buenas noticias es que una vez tiene una configuración funcional, +usualmente continuará trabajando indefinidamente. + +Antes de intentar que Mercurial hable con un servidor ssh, es mejor +asegurarse que puede usar la orden normal \command{ssh} o \command{putty} +para comunicarse con el servidor primero. Si tiene problemas usando +estas órdenes directamente, de seguro Mercurial no funcionará. Pero aún, +esconderá el problema subyacente. Cuando desee revisar un problema +relacionado con ssh y Mercurial, debería asegurarse primero que las +órdenes de ssh en el lado del cliente funcionan primero, \emph{antes} +de preocuparse por si existe un problema con Mercurial. + +Lo primero para asegurar en el lado del servidor es que puede entrar +desde otra máquina. Si no puede entrar con \command{ssh} o +\command{putty}, el mensaje de error que obtenga le puede dar pistas +de qué ha ido mal. Los problemas más comunes son los siguientes: +\begin{itemize} +\item Si obtiene un error de ``conexión rehusada'', es posible que no + haya un demonio SSH corriendo en el servidor o que no pueda accederse + a él por configuraciones de cortafuegos. +\item Si obtiene un error de ``no hay ruta hasta el servidor'', puede + tener la dirección del servidor incorrecta o un cortafuegos con + bloqueo agresivo que no permitirá su existencia. +\item Si obtiene un mensaje de ``permiso denegado'', puede que haya + tecleado mal el usuario en el servidor, o que haya tecleado + incorrectamente la frase clave o la clave del usuario remoto. +\end{itemize} +En resumen, si tiene problemas al comunicarse con el demonio ssh del +servidor, primero asegúrese de que está corriendo. En muchos sistemas +estará instalado, pero deshabilitado de forma predeterminada. Una vez +que haya hecho este paso tendrá que revisar si el cortafuegos del +servidor está configurado para recibir conexiones entrantes en el +puerto en el cual el demonio de ssh está escuchando (usualmente el~22). +No trate de buscar otras posibilidades exóticas o configuraciones +erradas hasta que haya revisado primero estas dos. + +Si está usando un agente de autenticación en el lado del cliente para +almacenar las frase claves de sus contraseñas, debería poder entrar al +servidor sin necesidad de que se le solicite frases claves o +contraseñas. Si se le pregunta alguna, a continuación algunas +posibilidades: +\begin{itemize} +\item Puede haber olvidado usar \command{ssh-add} o + \command{pageant} para guardar la frase clave. +\item Puede haber almacenado una frase clave errónea para la llave. +\end{itemize} +Si se le solicita la clave del usuario remoto, hay otras posibilidades +que deben revisarse: +\begin{itemize} +\item O bien el directorio del usuario o su directorio \sdirname{.ssh} + tiene permisos excesivamente abiertos. Como resultado el daemonio + ssh no creerá o leerá su fichero \sfilename{authorized\_keys}. + Por ejemplo, un directorio casa o \sdirname{.ssh} causará aveces + este síntoma. +\item El fichero de usuario \sfilename{authorized\_keys} puede tener + un problema. Si alguien distinto al usuario es dueño o puede + escribir el fichero, el demonio ssh no confiará o lo leerá. +\end{itemize} + +En un mundo ideal, debería poder ejecutar la siguiente orden +exitosamente, y debería imprimir exactamente una línea de salida, +la fecha y hora actual. +\begin{codesample2} + ssh miservidor fecha +\end{codesample2} + +Si en su servidor tiene guión que se ejecuta a la entrada e imprime +letreros o cualquier otra cosa, incluso cuando se ejecutan órdenes no +interactivas como esta, debería arreglarlo antes de continuar, de +forma que solamente imprima algo si se ejecuta interactivamente. De +otra forma estos letreros al menos llenarán la salida de Mercurial. +Incluso podrían causar problemas potenciales cuando se ejecuten +órdenes de forma remota. Mercurial intenta detectar e ignorar los +letreros en sesiones no interactivas de \command{ssh}, pero no es +a prueba de tontos. (Si edita sus guiones de entrada en el servidor, +la forma usual de ver si un guión de línea de comandos se ejecuta en un intérprete +interactivo, es verificar el código de retorno de la orden +\Verb|tty -s|.) + +Cuando verifique que el venerado ssh funciona en su servidor, el +paso siguiente es asegurar que Mercurial corre en el servidor. La +orden siguiente debería ejecutarse satisfactoriamente: +\begin{codesample2} + ssh miservidor hg version +\end{codesample2} +Si ve un mensaje de error en lugar de la salida usual de +\hgcmd{version}, será porque no ha instalado Mercurial en +\dirname{/usr/bin}. No se preocupe si este es el caso; no necesita +hacerlo. Pero debería revisar los posibles problemas presentados a +continuación: +\begin{itemize} +\item Está instalado Mercurial en el servidor? Se que suena trivial + pero es mejor revisar! +\item Tal vez la ruta de búsqueda de la interfaz de órdenes + (normalmente vía la variable de ambiente \envar{PATH}) simplemente + está mal configurada. +\item Puede ser que su variable de ambiente \envar{PATH} soalamente + apunte al lugar en el cual está el ejecutable \command{hg} si la + sesión de entrada es interactiva. Puede suceder si establece la + ruta en el guión de línea de comandos de entrada incorrecto. Consulte la + documentación de su línea de órdenes. +\item La variable de ambiente \envar{PYTHONPATH} puede requerir la + ruta a los módulos de Mercurial en Python. Puede que ni siquiera + está establecida; podría estar incorrecta; o puede ser que se + establezca únicamente cuando hay entradas interactivas. +\end{itemize} + +Si puede ejecutar \hgcmd{version} sobre una conexión ssh, +felicitaciones! Ha logrado la interacción entre el cliente y el +servidor. Ahora debería poder acceder a los repositorios de +Mercurial que tiene el usuario en el servidor. Si tiene problemas +con Mercurial y ssh en este punto, intente usar la opción +\hggopt{--debug} para tener información más clara de lo que está +sucediendo. + +\subsection{Compresión con ssh} + +Mercurial no comprime datos cuando usa el protocolo ssh, dado que +el protocolo puede comprimir datos transparentemente. Pero el +comportamiento predeterminado del cliente ssh es \emph{no} +solicitar compresión. + +Sobre cualquier red distinta a una LAN rápida (incluso con una red +inalámbrica), hacer uso de compresión puede mejorar el rendimiento +de las operaciones de Mercurial que involucren la red. Por ejemplo, +sobre WAN, alguien ha medido la compresión reduciendo la cantidad +de tiempo requerido para clonar un repositorio particularmente +grande de~51 minutos a~17 minutos. + +Tanto \command{ssh} como \command{plink} aceptan la opción +\cmdopt{ssh}{-C} que activa la compresión. Puede editar fácilmente +su \hgrc\ para habilitar la compresión para todos los usos de +Mercurial sobre el protocolo ssh. +\begin{codesample2} + [ui] + ssh = ssh -C +\end{codesample2} + +Si usa \command{ssh}, puede reconfigurarlo para que siempre use +compresión cuando se comunique con su servidor. Para hacerlo, +edite su fichero \sfilename{.ssh/config} (que puede no existir +aún), de la siguiente forma: +\begin{codesample2} + Host hg + Compression yes + HostName hg.ejemplo.com +\end{codesample2} +Que define un alias, \texttt{hg}. Cuando lo usa con la orden +\command{ssh} o con una URL de Mercurial con protocolo\texttt{ssh}, +logrará que \command{ssh} se conecte a \texttt{hg.ejemplo.com} +con compresión. Que le dará un nombre más corto para teclear y +compresión, los cuales por derecho propio son buenos. + +\section{Uso de CGI a través de HTTP} +\label{sec:collab:cgi} + +Dependiendo de qué tan ambicioso sea, configurar la interfaz CGI +de Mercurial puede tomar desde unos minutos hasta varias horas. + +Comenzaremos con el ejemplo más sencillo, y nos dirigiremos hacia +configuraciones más complejas. Incluso para el caso más básico +necesitará leer y modificar su configuración del servidor web. + +\begin{note} + Configurar un servidor web es una actividad compleja, engorrosa y + altamente dependiente del sistema. De ninguna manera podremos + cubrir todos los casos posibles con los cuales pueda encontrarse. + Use su discreción y juicio respecto a las secciones siguientes. + Esté preparado para cometer muchas equivocaciones, y emplear + bastante tiempo leyendo sus bitácoras de error del servidor. +\end{note} + +\subsection{Lista de chequeo de la configuración del servidor web} + +Antes de continuar, tómese un tiempo para revisar ciertos aspectos de +la configuración de su sistema: + +\begin{enumerate} +\item ¿Tiene un servidor web? Mac OS X viene con Apache, pero otros + sistemas pueden no tener un servidor web instalado. +\item Si tiene un servidor web instalado, ¿Está ejecutándose? En la + mayoría de sistemas, aunque esté presente, puede no estar habilitado + de forma predeterminada. +\item ¿u servidor está configurado para permitir ejecutar programas + CGI en el directorio donde planea hacerlo? Casi todos los + servidores de forma predeterminada explícitamente inhiben la + habilidad de ejecutar programas CGI. +\end{enumerate} + +Si no tiene un servidor web instalado, y no tiene cierta experiencia +configurando Apache, debería considerar usar el servidor web +\texttt{lighttpd} en lugar de Apache. Apache tiene una reputación +bien ganada por su configuración barroca y confusa. +A pesar de que \texttt{lighttpd} tiene menos características que +Apache en ciertas áreas, las mismas no son relevantes para servir +repositorios de Mercurial. Definitivamente es mucho más sencillo +comenzar con \texttt{lighttpd} que con Apache. + +\subsection{Configuración básica de CGI} + +En sistemas tipo Unix es común que los usuarios tengan un subdirectorio +con un nombre como \dirname{public\_html} en su directorio personal, +desde el cual pueden servir páginas web. Un fichero llamado \filename{foo} +en este directorio será visible en una URL de la forma +\texttt{http://www.example.com/\~username/foo}. + +Para comenzar, encuentre el guión \sfilename{hgweb.cgi} que debería +estar presente en su instalación de Mercurial. Si no puede +encontrarlo rápidamente una copia local en su sistema, puede +descargarlo del repositorio principal de Mercurial en +\url{http://www.selenic.com/repo/hg/raw-file/tip/hgweb.cgi}. + +Tendrá que copiar este guión en su directorio \dirname{public\_html}, +y asegurarse que sea ejecutable. +\begin{codesample2} + cp .../hgweb.cgi ~/public_html + chmod 755 ~/public_html/hgweb.cgi +\end{codesample2} +El argumento \texttt{755} de la orden \command{chmod} es un poco más +general que hacerlo ejecutable: Asegura que el guión sea ejecutable +por cualquiera, y que el ``grupo'' y los ``otros'' \emph{no} tengan +permiso de escritura. Si dejara los permisos de escritura abiertos, +, el subsistema \texttt{suexec} de Apache probablemente se negaría +a ejecutar el guión. De hecho, \texttt{suexec} también insiste en que +el \emph{directorio} en el cual reside el guión no tenga permiso de +escritura para otros. +\begin{codesample2} + chmod 755 ~/public_html +\end{codesample2} + +\subsubsection{¿Qué \emph{podría} resultar mal?} +\label{sec:collab:wtf} + +Cuando haya ubicado el CGI en el sitio correspondiente con un navegador +intente visitar el URL \url{http://myhostname/~myuser/hgweb.cgi}, +\emph{sin} dejarse abatir por un error. Hay una alta probabilidad de +que esta primera visita al URL sea fallida, y hay muchas razones posibles +para este comportamiento. De hecho, podría toparse con cada uno de los +errores que describimos a continuación, así que no deje de leerlos +cuidadosamente. A continuación presento los problemas que yo tuve en +un sistema con Fedora~7, con una instalación nueva de Apache, y una +cuenta de usuario que creé específicamente para desarrollar este +ejercicio. + +Su servidor web puede tener directorios por usuario deshabilitados. Si +usa Apache, busque el fichero de configuración que contenga la +directiva \texttt{UserDir}. Si no está presente en sitio alguno, los +directorios por usuario están deshabilitados. Si la hay, pero su +valor es \texttt{disabled}, los directorios por usuario estarán +deshabilitados. La directiva \texttt{UserDir} en caso contrario tendrá +el nombre del subdirectorio bajo el cual Apache mirará en el +directorio de cada usuario, por ejemplo \dirname{public\_html}. + +Los permisos de sus ficheros pueden ser demasiado restrictivos. El +servidor web debe poder recorrer su directorio personal y los +directorios que estén bajo \dirname{public\_html}, además de tener +permiso para leer aquellos que estén adentro. A continuación una +receta rápida para hacer que sus permisos estén acordes con las +necesidades básicas. +\begin{codesample2} + chmod 755 ~ + find ~/public_html -type d -print0 | xargs -0r chmod 755 + find ~/public_html -type f -print0 | xargs -0r chmod 644 +\end{codesample2} + +Otra posibilidad con los permisos es que obtenga una ventana +completamente en blanco cuando trata de cargar el guión. En cuyo +caso, es posible que los permisos que tiene son \emph{demasiado + permisivos}. El subsistema \texttt{suexec} de Apache no ejecutará un +guión que tenga permisos de escritura para el grupo o el planeta, por +ejemplo. + +Su servidor web puede estar configurado para evitar la ejecución de +programas CGI en los directorios de usuario. A continuación presento +una configuración predeterminada por usuario en mi sistema Fedora. + +\begin{codesample2} + + AllowOverride FileInfo AuthConfig Limit + Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec + + Order allow,deny + Allow from all + + + Order deny,allow + Deny from all + + +\end{codesample2} +Si encuentra un grupo de instrucciones de \texttt{Directory} similares +en su configuración de Apache, la directiva a revisar es \texttt{Options}. +Adicione \texttt{ExecCGI} al final de esta lista en caso de que haga +falta y reinicie su servidor web. + +Si resulta que Apache le muestra el texto del guión CGI en lugar de +ejecutarlo, necesitará o bien descomentar (si se encuentra presente) o +adicionar una directiva como la siguiente: +\begin{codesample2} + AddHandler cgi-script .cgi +\end{codesample2} + +Otra posibilidad es que observe una traza de Python en colores +informando que no puede importar un módulo relacionado con +\texttt{mercurial}. Esto es un gran progreso! El servidor es capaz +de ejecutar su guión CGI. Este error solamente ocurrirá si está +ejecutando una instalación privada de Mercurial en lugar de una +instalación para todo el sistema. Recuerde que el servidor que +ejecuta el programa CGI no cuenta con variables de ambiente de las +cuales usted si dispone en una sesión interactiva. Si este error le +ocurre, edite su copia de \sfilename{hgweb.cgi} y siga las indicaciones +dentro del mismo para establecer de forma adecuada su variable de +ambiente \envar{PYTHONPATH}. + +Finalmente, si encuentra \emph{otra} traza a todo color de Python al visitar +el URL: Esta seguramente se referirá a que no puede encontrar +\dirname{/path/to/repository}. Edite su script \sfilename{hgweb.cgi} +y reemplace la cadena \dirname{/path/to/repository} con la ruta +completa al repositorio que desea servir. + +En este punto, cuando trate de recargar la página, deberá visualizar +una linda vista HTML del historial de su repositorio. Uff! + +\subsubsection{Configuración de lighttpd} + +En mi intención de ser exhaustivo, intenté configurar +\texttt{lighttpd}, un servidor web con creciente aceptación, para +servir los repositorios de la misma forma como lo describí +anteriormente con Apache. Después de superar los problemas que mostré +con Apache, muchos de los cuáles no son específicos del servidor. Por +lo tanto estaba seguro de que mis permisos para directorios y ficheros +eran correctos y que mi guión \sfilename{hgweb.cgi} también lo era. + +Dado que ya Apache estaba en ejecución correctamente, lograr que +\texttt{lighttpd} sirviera mi repositorio fue rápido (en otras +palabras, si está tratando de usar \texttt{lighttpd}, debe leer la +sección de Apache). Primero tuve que editar la sección +\texttt{mod\_access} para habilitar \texttt{mod\_cgi} y +\texttt{mod\_userdir}, los cuales estaban inhabilitados en mi +instalación predeterminada. Añadí posteriormente unas líneas al final +del fichero de configuración, para hacer lo propio con los módulos. +\begin{codesample2} + userdir.path = "public_html" + cgi.assign = ( ".cgi" => "" ) +\end{codesample2} +Hecho esto, \texttt{lighttpd} funcionó inmediatamente para +mí. Configuré \texttt{lighttpd} antes que Apache, tuve casi los mismos +reparos a nivel de configuración del sistema que con Apache. De todas +maneras, considero que \texttt{lighttpd} es bastante más sencillo de +configurar que Apache, a pesar de haber usado Apache por lo menos por +una década, y esta fue mi primera experiencia con \texttt{lighttpd}. + +\subsection{Compartir varios repositorios con un guión CGI} + +El guión \sfilename{hgweb.cgi} permite publicar únicamente un +repositorio, una restricción frustrante. Si desea publicar más de uno +sin complicarse con varias copias del mismo guión, cada una con un +nombre distinto, resulta mucho mejor usar el guión +\sfilename{hgwebdir.cgi}. + +El procedimiento para configurar \sfilename{hgwebdir.cgi} tiene una +porción adicional frente al trabajo requerido con +\sfilename{hgweb.cgi}. Primero se debe obtener una copia del +guión. Si no tiene una a mano, puede descargar una copia del ftp +principal del repositorio de Mercurial en +\url{http://www.selenic.com/repo/hg/raw-file/tip/hgwebdir.cgi}. + +Necesitará una copia del guión en su directorio \dirname{public\_html}, +y asegurarse de que sea ejecutable. +\begin{codesample2} + cp .../hgwebdir.cgi ~/public_html + chmod 755 ~/public_html ~/public_html/hgwebdir.cgi +\end{codesample2} +Con la configuración básica, intente visitar en su navegador +\url{http://myhostname/~myuser/hgwebdir.cgi}. Debería mostrar una +lista vacía de repositorios. Si obtiene una ventana en blanco o un +mensaje de error, verifique la lista de problemas potenciales en la +sección~\ref{sec:collab:wtf}. + +El guión \sfilename{hgwebdir.cgi} se apoya en un fichero externo de +configuración. En principio, busca un fichero llamado +\sfilename{hgweb.config} en el mismo directorio. Tendrá que crear el +fichero, y permitir lectura de todo el mundo. El formato del fichero +es similar a un fichero ``ini'' de Windows, que puede interpretar el módulo +\texttt{ConfigParser}~\cite{web:configparser} de Python. + +La forma más sencilla de configurar \sfilename{hgwebdir.cgi} es con +una sección llamada \texttt{collections}. Esta publicará automáticamente +\emph{todos} los repositorios en los directorios que usted +especifique. La sección debería lucir así: +\begin{codesample2} + [collections] + /mi/ruta = /mi/ruta +\end{codesample2} +Mercurial lo interpreta buscando el nombre del directorio que esté a la +\emph{derecha} del símbolo ``\texttt{=}''; encontrando repositorios en +la jerarquía de directorios; y usando el texto a la \emph{izquierda} +para eliminar el texto de los nombres que mostrará en la interfaz +web. El componente restante de la ruta después de esta eliminación +usualmente se llama ``ruta virtual''. + +Dado el ejemplo de arriba, si tenemos un repositorio cuya ruta local es +\dirname{/mi/ruta/este/repo}, el guión CGI eliminará la porción inicial +\dirname{/mi/ruta} del nombre y publicará el repositorio con una ruta +virtual \dirname{este/repo}. Si el URL base de nuestro guión CGI es +\url{http://myhostname/~myuser/hgwebdir.cgi}, el URL completo al +repositorio será +\url{http://myhostname/~myuser/hgwebdir.cgi/this/repo}. + +Si reemplazamos \dirname{/mi/ruta} en el lado izquierdo de este +ejemplo con \dirname{/mi}, \sfilename{hgwebdir.cgi} eliminará solamente +\dirname{/mi} del nombre del repositorio, y nos ofrecerá la ruta +virtual \dirname{ruta/este/repo} en lugar de \dirname{este/repo}. + +El guión \sfilename{hgwebdir.cgi} buscará recursivamente en cada +directorio listado en la sección \texttt{collections} de su fichero de +configuración, pero \texttt{no} hará el recorrido recursivo dentro de +los repositorios que encuentre. + +El mecanismo de \texttt{collections} permite publicar fácilmente +repositorios de una forma ``hacer y olvidar''. Solamente requiere +configurar el guión CGI y el fichero de configuración una vez. +Después de eso puede publicar y sacar de publicación un repositorio en +cualquier momento incluyéndolo o excluyéndolo de la jerarquía de +directorios en la cual le haya indicado a \sfilename{hgwebdir.cgi} que +mirase. + +\subsubsection{Especificación explícita de los repositorios a publicar} + +Además del mecanismo \texttt{collections}, el guión +\sfilename{hgwebdir.cgi} le permite publicar una lista específica de +repositorios. Para hacerlo, cree una sección \texttt{paths}, con los +contenidos de la siguiente forma: +\begin{codesample2} + [paths] + repo1 = /mi/ruta/a/un/repo + repo2 = /ruta/a/otro/repo +\end{codesample2} +En este caso, la ruta virtual (el componente que aparecerá en el URL) +está en el lado derecho de cada definición, mientras que la ruta al +repositorio está a la derecha. Note que no tiene que haber relación +alguna entre la ruta virtual que elija y el lugar del repositorio en +su sistema de ficheros. + +Si lo desea, puede usar los dos mecanismos \texttt{collections} y +\texttt{paths} simultáneamente en un sólo fichero de configuración. + +\begin{note} + Si varios repositorios tienen la misma ruta virtual, + \sfilename{hgwebdir.cgi} no reportará error. Pero se comportará + impredeciblemente. +\end{note} + +\subsection{Descarga de ficheros fuente} + +La interfaz web de Mercurial permite a los usuarios descargar +un conjunto de cualquier revisión. Este fichero contendrá una réplica +del directorio de trabajo en la revisión en cuestión, pero no +contendrá una copia de los datos del repositorio. + +De forma predeterminada esta característica no está habilitada. Para +lograrlo adicione un \rcitem{web}{allow\_archive} a la sección \rcsection{web} +de su fichero \hgrc. + +\subsection{Opciones de configuración en Web} + +Las interfaces web de Mercurial (la orden \hgcmd{serve}, y los guiones +\sfilename{hgweb.cgi} y \sfilename{hgwebdir.cgi}) tienen varias +opciones de configuración para establecer. Todas ellas en la sección +\rcsection{web}. +\begin{itemize} +\item[\rcitem{web}{allow\_archive}] Determina cuáles tipos de ficheros + de descarga soportará Mercurial. Si habilita esta característica, + los usuarios de la interfaz web podrán descargar una copia de la + revisión del repositorio que estén viendo. Para activar la + característica de descarga de fichero, el valor tendrá una secuencia + de palabras extraídas de la lista de abajo. + \begin{itemize} + \item[\texttt{bz2}] Un fichero \command{tar} con el método de + compresión \texttt{bzip2}. Tiene la mejor tasa de compresión, + pero usa más tiempo de procesamiento en el servidor. + \item[\texttt{gz}] Un fichero \command{tar}, comprimido con + \texttt{gzip}. + \item[\texttt{zip}] Un fichero \command{zip}, comprimido con LZW. + Este formato posee la peor tasa de compresión, pero es muy usado en + el mundo Windows. + \end{itemize} + Si da una lista vacía o no tiene la entrada + \rcitem{web}{allow\_archive}, esta característica se deshabilitará. + A continuación un ejemplo de cómo habilitar los tres formatos soportados. + \begin{codesample4} + [web] + allow_archive = bz2 gz zip + \end{codesample4} +\item[\rcitem{web}{allowpull}] Booleano. Determina si la interfaz web + permite a los usuarios remotos emplear \hgcmd{pull} y \hgcmd{clone} + sobre el repositorio~HTTP. Si se coloca \texttt{no} o + \texttt{false}, solamente la porción de los procesos + ``orientados-a-humanos'' se habilita de la interfaz web. +\item[\rcitem{web}{contact}] Cadena. Una cadena en forma libre (pero + preferiblemente corta) que identifica a la persona o grupo a cargo + del repositorio. Usualmente contiene el nombre y la dirección de + correo electrónico de una persona o de una lista de correo. Aveces + tiene sentido colocar esta opción en el fichero \sfilename{.hg/hgrc} + del repositorio, pero en otras oportunidades en el \hgrc\ global si + todos los repositorios tienen un único mantenedor. +\item[\rcitem{web}{maxchanges}] Entero. La cantidad máxima de + conjuntos de cambios a mostrar de forma predeterminada en cada página. +\item[\rcitem{web}{maxfiles}] Entero. La cantidad máxima + predeterminada de ficheros modificados a desplegar en una página. +\item[\rcitem{web}{stripes}] Entero. Si la interfaz web despliega + ``franjas'' para facilitar la visualización alineada de filas cuando + se ve una tabla, este valor controla la cantidad de filas en cada + franja. +\item[\rcitem{web}{style}] Controla la plantilla que Mercurial usa para + desplegar la interfaz web. Mercurial viene con dos plantillas web, + llamadas \texttt{default} y \texttt{gitweb} (La primera es + visualmente más atractiva). Puede especificar una plantilla propia; + consulte el capítulo~\ref{chap:template}. A continuación mostramos + cómo habilitar el estilo \texttt{gitweb}. + \begin{codesample4} + [web] + style = gitweb + \end{codesample4} +\item[\rcitem{web}{templates}] Ruta. Directorio en el que se buscarán + los ficheros plantilla. De forma predeterminada, busca en el + directorio en el cual fue instalado. +\end{itemize} +Si usa \sfilename{hgwebdir.cgi}, puede añadir otras opciones de +configuración en una sección \section{web} del fichero +\sfilename{hgweb.config} en lugar del fichero \hgrc\, si lo considera +más conveniente. Estas opciones son \rcitem{web}{motd} y +\rcitem{web}{style}. + +\subsubsection{Opciones específicas para repositorios individuales} + +Ciertas opciones de configuración de \rcsection{web} deben estar +ubicadas en el \sfilename{.hg/hgrc} de un repositorio en lugar del +fichero del usuario o el \hgrc global. +\begin{itemize} +\item[\rcitem{web}{description}] Cadena. Una cadena de forma + libre (preferiblemente corta) que describa los contenidos o el + propósito del repositorio. +\item[\rcitem{web}{name}] Cadena. El nombre para visualizar en la + interfaz web del repositorio. Sustituye el nombre predeterminado, el + cual es el último componente de la ruta del repositorio. +\end{itemize} + +\subsubsection{Opciones específicas a la orden \hgcmd{serve}} + +Algunas opciones en la sección \rcsection{web} de un fichero \hgrc\ +son de uso exclusivo para la orden \hgcmd{serve}. +\begin{itemize} +\item[\rcitem{web}{accesslog}] Ruta. El nombre del fichero en el cual + se escribe la bitácora de acceso. En principio, la orden + \hgcmd{serve} escribe esta información a la salida estándar, no a un + fichero. Las líneas de la bitácora se escriben en un formato de + fichero ``combinado'' estándar, usado por casi todos los servidores + web. +\item[\rcitem{web}{address}] Cadena. La dirección local en la cual el + servidor debe escuchar peticiones entrantes. En principio, el + servidor escucha en todas las direcciones. +\item[\rcitem{web}{errorlog}] Ruta. El nombre de un fichero en el + cual escribir la bitácora de error. En principio, la orden + \hgcmd{serve} escribe esta información en la salida de error + estándar, no a un fichero. +\item[\rcitem{web}{ipv6}] Booleano. Si se usa o no el protocolo + IPv6. En principio, IPv6 no se usa. +\item[\rcitem{web}{port}] Entero. El número del puerto~TCP en el cuál + el servidor escuchará. El puerto predeterminado es el~8000. +\end{itemize} + +\subsubsection{Elegir el fichero \hgrc\ correcto para las + configuraciones de \rcsection{web}} + +Es importante recordar que un servidor web como Apache o +\texttt{lighttpd} se ejecutarán bajo el usuario~ID que generalmente no +es el suyo Los guiones CGI ejecutados por su servidor, tales como +\sfilename{hgweb.cgi}, se ejecutarán también con el usuario~ID. + +Si añade opciones \rcsection{web} a su fichero personal \hgrc\, Los +guiones CGI no leerán tal fichero \hgrc\. Tales configuraciones +solamente afectarán el comportamiento de la orden \hgcmd{serve} cuando +usted lo ejecuta. Para logar que los guiones CGI vean sus +configuraciones, o bien cree un fichero \hgrc\ en el directorio hogar +del usuario ID que ejecuta su servidor web, o añada tales +configuraciones al fichero global \hgrc. + + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/concepts.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/concepts.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,650 @@ +\chapter{Tras bambalinas} +\label{chap:concepts} + +A diferencia de varios sistemas de control de revisiones, los +conceptos en los que se fundamenta Mercurial son lo suficientemente +simples como para entender fácilmente cómo funciona el software. +Saber esto no es necesario, pero considero útil tener un ``modelo +mental'' de qué es lo que sucede. + +Comprender esto me da la confianza de que Mercurial ha sido +cuidadosamente diseñado para ser tanto \emph{seguro} como +\emph{eficiente}. Y tal vez con la misma importancia, si es fácil +para mí hacerme a una idea adecuada de qué está haciendo el software +cuando llevo a cabo una tarea relacionada con control de revisiones, +es menos probable que me sosprenda su comportamiento. + +En este capítulo, cubriremos inicialmente los conceptos centrales +del diseño de Mercurial, y luego discutiremos algunos detalles +interesantes de su implementación. + +\section{Registro del historial de Mercurial} + +\subsection{Seguir el historial de un único fichero} + +Cuando Mercurial sigue las modificaciones a un fichero, guarda el +historial de dicho fichero en un objeto de metadatos llamado +\emph{filelog}\ndt{Fichero de registro}. Cada entrada en el fichero +de registro contiene suficiente información para reconstruir una +revisión del fichero que se está siguiendo. Los ficheros de registro +son almacenados como ficheros el el directorio +\sdirname{.hg/store/data}. Un fichero de registro contiene dos tipos +de información: datos de revisiones, y un índice para ayudar a +Mercurial a buscar revisiones eficientemente. + +El fichero de registro de un fichero grande, o con un historial muy +largo, es guardado como ficheros separados para datos (sufijo +``\texttt{.d}'') y para el índice (sufijo ``\texttt{.i}''). Para +ficheros pequeños con un historial pequeño, los datos de revisiones y +el índice son combinados en un único fichero ``\texttt{.i}''. La +correspondencia entre un fichero en el directorio de trabajo y el +fichero de registro que hace seguimiento a su historial en el +repositorio se ilustra en la figura~\ref{fig:concepts:filelog}. + +\begin{figure}[ht] + \centering + \grafix{filelog} + \caption{Relación entre ficheros en el directorio de trabajo y + ficheros de registro en el repositorio} + \label{fig:concepts:filelog} +\end{figure} + +\subsection{Administración de ficheros monitoreados} + +Mercurial usa una estructura llamada \emph{manifiesto} para +% TODO collect together => centralizar +centralizar la información que maneja acerca de los ficheros que +monitorea. Cada entrada en el manifiesto contiene información acerca +de los ficheros involucrados en un único conjunto de cambios. Una +entrada registra qué ficheros están presentes en el conjunto de +cambios, la revisión de cada fichero, y otros cuantos metadatos del +mismo. + +\subsection{Registro de información del conjunto de cambios} + +La \emph{bitácora de cambios} contiene información acerca de cada +conjunto de cambios. Cada revisión indica quién consignó un cambio, el +comentario para el conjunto de cambios, otros datos relacionados con +el conjunto de cambios, y la revisión del manifiesto a usar. + +\subsection{Relaciones entre revisiones} + +Dentro de una bitácora de cambios, un manifiesto, o un fichero de +registro, cada revisión conserva un apuntador a su padre inmediato +(o sus dos padres, si es la revisión de una fusión). Como menciońe +anteriormente, también hay relaciones entre revisiones \emph{a través} +de estas estructuras, y tienen naturaleza jerárquica. + +Por cada conjunto de cambios en un repositorio, hay exactamente una +revisión almacenada en la bitácora de cambios. Cada revisión de la +bitácora de cambios contiene un apuntador a una única revisión del +manifiesto. Una revisión del manifiesto almacena un apuntador a una +única revisión de cada fichero de registro al que se le hacía +seguimiento cuando fue creado el conjunto de cambios. Estas relaciones +se ilustran en la figura~\ref{fig:concepts:metadata}. + +\begin{figure}[ht] + \centering + \grafix{metadata} + \caption{Relaciones entre metadatos} + \label{fig:concepts:metadata} +\end{figure} + +Como lo muestra la figura, \emph{no} hay una relación ``uno a uno'' +entre las revisiones en el conjunto de cambios, el manifiesto, o el +fichero de registro. Si el manifiesto no ha sido modificado de un +conjunto de cambios a otro, las entradas en la bitácora de cambios +para esos conjuntos de cambios apuntarán a la misma revisión del +manifiesto. Si un fichero monitoreado por Mercurial no sufre ningún +cambio de un conjunto de cambios a otro, la entrada para dicho fichero +en las dos revisiones del manifiesto apuntará a la misma revisión de +su fichero de registro. + +\section{Almacenamiento seguro y eficiente} + +La base común de las bitácoras de cambios, los manifiestos, y los +ficheros de registros es provista por una única estructura llamada el +\emph{revlog}\ndt{Contracción de \emph{revision log}, registro de +revisión.}. + +\subsection{Almacenamiento eficiente} + +El revlog provee almacenamiento eficiente de revisiones por medio del +mecanismo de \emph{deltas}\ndt{Diferencias.}. En vez de almacenar una +copia completa del fichero por cada revisión, almacena los cambios +necesarios para transformar una revisión anterior en la nueva +revisión. Para muchos tipos de fichero, estos deltas son típicamente +de una fracción porcentual del tamaño de una copia completa del +fichero. + +Algunos sistemas de control de revisiones obsoletos sólo pueden +manipular deltas de ficheros de texto plano. Ellos o bien almacenan +los ficheros binarios como instantáneas completas, o codificados en +alguna representación de texto plano adecuada, y ambas alternativas +son enfoques que desperdician bastantes recursos. Mercurial puede +manejar deltas de ficheros con contenido binario arbitrario; no +necesita tratar el texto plano como un caso especial. + +\subsection{Operación segura} +\label{sec:concepts:txn} + +Mercurial sólo \emph{añade} datos al final de los ficheros de revlog. Nunca +modifica ninguna sección de un fichero una vez ha sido escrita. Esto es más +robusto y eficiente que otros esquemas que requieren modificar o reescribir +datos. + +Adicionalmente, Mercurial trata cada escritura como parte de una +\emph{transacción}, que puede cubrir varios ficheros. Una transacción es +\emph{atómica}: o bien la transacción tiene éxito y entonces todos sus efectos +son visibles para todos los lectores, o la operación completa es cancelada. +% TODO atomicidad no existe de acuerdo a DRAE, reemplazar +Esta garantía de atomicidad implica que, si usted está ejecutando dos copias de +Mercurial, donde una de ellas está leyendo datos y la otra los está escribiendo, +el lector nunca verá un resultado escrito parcialmente que podría confundirlo. + +El hecho de que Mercurial sólo hace adiciones a los ficheros hace más fácil +proveer esta garantía transaccional. A medida que sea más fácil hacer +operaciones como ésta, más confianza tendrá usted en que sean hechas +correctamente. + +\subsection{Recuperación rápida de datos} + +Mercurial evita ingeniosamente un problema común a todos los sistemas de control +de revisiones anteriores> el problema de la +\emph{recuperación\ndt{\emph{Retrieval}. Recuperación en el sentido de traer los +datos, o reconstruirlos a partir de otros datos, pero no debido a una falla o +calamidad, sino a la operación normal del sistema.} ineficiente de datos}. +Muchos sistemas de control de revisiones almacenan los contenidos de una +revisión como una serie incremental de modificaciones a una ``instantánea''. +Para reconstruir una versión cualquiera, primero usted debe leer la instantánea, +y luego cada una de las revisiones entre la instantánea y su versión objetivo. +Entre más largo sea el historial de un fichero, más revisiones deben ser leídas, +y por tanto toma más tiempo reconstruir una versión particular. + +\begin{figure}[ht] + \centering + \grafix{snapshot} + \caption{Instantánea de un revlog, con deltas incrementales} + \label{fig:concepts:snapshot} +\end{figure} + +La innovación que aplica Mercurial a este problema es simple pero efectiva. +Una vez la cantidad de información de deltas acumulada desde la última +instantánea excede un umbral fijado de antemano, se almacena una nueva +instantánea (comprimida, por supuesto), en lugar de otro delta. Esto hace +posible reconstruir \emph{cualquier} versión de un fichero rápidamente. Este +enfoque funciona tan bien que desde entonces ha sido copiado por otros sistemas +de control de revisiones. + +La figura~\ref{fig:concepts:snapshot} ilustra la idea. En una entrada en el +fichero índice de un revlog, Mercurial almacena el rango de entradas (deltas) +del fichero de datos que se deben leer para reconstruir una revisión en +particular. + +\subsubsection{Nota al margen: la influencia de la compresión de vídeo} + +Si le es familiar la compresión de vídeo, o ha mirado alguna vez una emisión de +TV a través de cable digital o un servicio de satélite, puede que sepa que la +mayor parte de los esquemas de compresión de vídeo almacenan cada cuadro del +mismo como un delta contra el cuadro predecesor. Adicionalmente, estos esquemas +usan técnicas de compresión ``con pérdida'' para aumentar la tasa de +compresión, por lo que los errores visuales se acumulan a lo largo de una +cantidad de deltas inter-cuadros. + +Ya que existe la posibilidad de que un flujo de vídeo se ``pierda'' +ocasionalmente debido a fallas en la señal, y para limitar la acumulación de +errores introducida por la compresión con pérdidas, los codificadores de vídeo +insertan periódicamente un cuadro completo (también llamado ``cuadro clave'') en +el flujo de vídeo; el siguiente delta es generado con respecto a dicho cuadro. +Esto quiere decir que si la señal de vídeo se interrumpe, se reanudará una vez +se reciba el siguiente cuadro clave. Además, la acumulación de errores de +codificación se reinicia con cada cuadro clave. + +\subsection{Identificación e integridad fuerte} + +Además de la información de deltas e instantáneas, una entrada en un +% TODO de pronto aclarar qué diablos es un hash? +revlog contiene un hash criptográfico de los datos que representa. +Esto hace difícil falsificar el contenido de una revisión, y hace +fácil detectar una corrupción accidental. + +Los hashes proveen más que una simple revisión de corrupción: son +usados como los identificadores para las revisiones. +% TODO no entendí completamente la frase a continuación +Los hashes de +identificación de conjuntos de cambios que usted ve como usuario final +son de las revisiones de la bitácora de cambios. Aunque los ficheros +de registro y el manifiesto también usan hashes, Mercurial sólo los +usa tras bambalinas. + +Mercurial verifica que los hashes sean correctos cuando recupera +revisiones de ficheros y cuando jala cambios desde otro repositorio. +Si se encuentra un problema de integridad, Mercurial se quejará y +detendrá cualquier operación que esté haciendo. + +Además del efecto que tiene en la eficiencia en la recuperación, el +uso periódico de instantáneas de Mercurial lo hace más robusto frente +a la corrupción parcial de datos. Si un fichero de registro se +corrompe parcialmente debido a un error de hardware o del sistema, a +menudo es posible reconstruir algunas o la mayoría de las revisiones a +partir de las secciones no corrompidas del fichero de registro, tanto +antes como después de la sección corrompida. Esto no sería posible con +un sistema de almacenamiento basado únicamente en deltas. + +\section{Historial de revisiones, ramas y fusiones} + +Cada entrada en el revlog de Mercurial conoce la identidad de la +revisión de su ancestro inmediato, al que se conoce usualmente como su +\emph{padre}. De hecho, una revisión contiene sitio no sólo para un +padre, sino para dos. Mercurial usa un hash especial, llamado el +``ID nulo'', para representar la idea de ``no hay padre aquí''. Este +hash es simplemente una cadena de ceros. + +En la figura~\ref{fig:concepts:revlog} usted puede ver un ejemplo de +la estructura conceptual de un revlog. Los ficheros de registro, +manifiestos, y bitácoras de cambios comparten la misma estructura; +sólo difieren en el tipo de datos almacenados en cada delta o +instantánea. + +La primera revisión en un revlog (al final de la imagen) tiene como +padre al ID nulo, en las dos ranuras disponibles para padres. En una +revisión normal, la primera ranura para padres contiene el ID de la +revisión padre, y la segunda contiene el ID nulo, señalando así que la +revisión sólo tiene un padre real. Un par de revisiones que tenga el +mismo ID padre son ramas. Una revisión que representa una fusión entre +ramas tiene dos IDs de revisión normales en sus ranuras para padres. + +\begin{figure}[ht] + \centering + \grafix{revlog} + \caption{} + \label{fig:concepts:revlog} +\end{figure} + +\section{El directorio de trabajo} + +% TODO revisar párrafo, no me convence la traducción +En el directorio de trabajo, Mercurial almacena una instantánea de los +ficheros del repositorio como si fueran los de un conjunto de cambios +particular. + +El directorio de trabajo ``sabe'' qué conjunto de cambios contiene. +Cuando usted actualiza el directorio de trabajo para que contenga un +conjunto de cambios particular, Mercurial busca la revisión adecuada +del manifiesto para averiguar qué ficheros estaba monitoreando cuando +se hizo la consignación del conjunto de cambios, y qué revisión de +cada fichero era la actual en ese momento. Luego de eso, recrea una +copia de cada uno de esos ficheros, con los mismos contenidos que +tenían cuando fue consignado el conjunto de cambios. + +El \emph{estado de directorio}\ndt{dirstate, en inglés en el +original.} contiene el conocimiento de Mercurial acerca del directorio +de trabajo. Allí se detalla a qué conjunto de cambios es actualizado +el directorio de trabajo, y todos los ficheros que Mercurial está +monitoreando en este directorio. + +Tal como la revisión de un revlog tiene espacio para dos padres, para +que pueda representar tanto una revisión normal (con un solo padre) o +una fusión de dos revisiones anteriores, el estado de directorio tiene +espacio para dos padres. Cuando usted usa el comando \hgcmd{update}, +el conjunto de cambios al que usted se actualiza es almacenado en la +casilla destinada al ``primer padre'', y un ID nulo es almacenado en +la segunda. Cuando usted hace una fusión (\hgcmd{merge}) con otro +conjunto de cambios, la casilla para el primer padre permanece sin +cambios, y la casilla para el segundo es actualizada con el conjunto +de cambios con el que usted acaba de hacer la fusión. El comando +\hgcmd{parents} le indica cuáles son los padres del estado de +directorio. + +\subsection{Qué pasa en una consignación} + +El estado de directorio almacena información sobre los padres para +algo más que mero registro. Mercurial usa los padres del estado de +directorio como \emph{los padres de un nuevo conjunto de cambios} +cuando usted hace una consignación. + +\begin{figure}[ht] + \centering + \grafix{wdir} + \caption{El directorio de trabajo puede tener dos padres} + \label{fig:concepts:wdir} +\end{figure} + +La figura~\ref{fig:concepts:wdir} muestra el estado normal del +directorio de trabajo, que tiene un único conjunto de cambios como +padre. Dicho conjunto de cambios es la \emph{punta}, el conjunto de +cambios más reciente en el repositorio que no tiene hijos. + +\begin{figure}[ht] + \centering + \grafix{wdir-after-commit} + \caption{El directorio de trabajo obtiene nuevos padres luego de una + consignación} + \label{fig:concepts:wdir-after-commit} +\end{figure} + +Es útil pensar en el directorio de trabajo como en ``el conjunto de +cambios que estoy a punto de enviar''. Cualquier fichero que usted le +diga a Mercurial que fue añadido, borrado, renombrado o copiado, se +verá reflejado en ese conjunto de cambios, como también se verán las +modificaciones a cualquiera de los ficheros que Mercurial ya esté +monitoreando; el nuevo conjunto de cambios dentrá los padres del +directorio de trabajo como propios. + +Luego de una consignación, Mercurial actualizará los padres del +directorio de trabajo, de tal manera que el primer padre sea el ID del +nuevo conjunto de cambios, y el segundo sea el ID nulo. Esto puede +verse en la figura~\ref{fig:concepts:wdir-after-commit}. Mercurial no +toca ninguno de los ficheros del directorio de trabajo cuando usted +hace la consignación; sólo modifica el estado de directorio para +anotar sus nuevos padres. + +\subsection{Creación de un nuevo frente} + +Es perfectamente normal actualizar el directorio de trabajo a un +conjunto de cambios diferente a la punta actual. Por ejemplo, usted +podría desear saber en qué estado se encontraba su proyecto el martes +pasado, o podría estar buscando en todos los conjuntos de cambios para +saber cuándo se introdujo un fallo. En casos como éstos, la acción +natural es actualizar el directorio de trabajo al conjunto de cambios +de su interés, y examinar directamente los ficheros en el directorio +de trabajo para ver sus contenidos tal como estaban en el momento de +hacer la consignación. El efecto que tiene esto se muestra en la +figura~\ref{fig:concepts:wdir-pre-branch}. + +\begin{figure}[ht] + \centering + \grafix{wdir-pre-branch} + \caption{El directorio de trabajo, actualizado a un conjunto de + cambios anterior} + \label{fig:concepts:wdir-pre-branch} +\end{figure} + +Una vez se ha actualizado el directorio de trabajo a un conjunto de +cambios anterior, qué pasa si se hacen cambios, y luego se hace una +consignación? Mercurial se comporta en la misma forma que describí +anteriormente. Los padres del directorio de trabajo se convierten en +los padres del nuevo conjunto de cambios. Este nuevo conjunto de +cambios no tiene hijos, así que se convierte en la nueva punta. Y el +repositorio tiene ahora dos conjuntos de cambios que no tienen hijos; +a éstos los llamamos \emph{frentes}. Usted puede apreciar la +estructura que esto crea en la figura~\ref{fig:concepts:wdir-branch}. + +\begin{figure}[ht] + \centering + \grafix{wdir-branch} + \caption{Después de una consignación hecha mientras se usaba un + conjunto de cambios anterior} + \label{fig:concepts:wdir-branch} +\end{figure} + +\begin{note} + Si usted es nuevo en Mercurial, debería tener en mente un + ``error'' común, que es usar el comando \hgcmd{pull} sin ninguna + opción. Por defecto, el comando \hgcmd{pull} \emph{no} actualiza + el directorio de trabajo, así que usted termina trayendo nuevos + conjuntos de cambios a su repositorio, pero el directorio de + trabajo sigue usando el mismo conjunto de cambios que tenía antes + de jalar. Si usted hace algunos cambios, y luego hace una + consignación, estará creando un nuevo frente, porque su directorio + de trabajo no es sincronizado a cualquiera que sea la nueva punta. + + Pongo la palabra ``error'' en comillas porque todo lo que usted + debe hacer para rectificar la situación es hacer una fusión + (\hgcmd{merge}), y luego una consignación (\hgcmd{commit}). En + otras palabras, esto casi nunca tiene consecuencias negativas; + sólo sorprende a la gente. Discutiré otras formas de evitar este + comportamiento, y porqué Mercurial se comporta de esta forma, + inicialmente sorprendente, más adelante. +\end{note} + +\subsection{Fusión de frentes} + +Cuando usted ejecuta el comando \hgcmd{merge}, Mercurial deja el +primer padre del directorio de trabajo intacto, y escribe como segundo +padre el conjunto de cambios contra el cual usted está haciendo la +fusión, como se muestra en la figura~\ref{fig:concepts:wdir-merge}. + +\begin{figure}[ht] + \centering + \grafix{wdir-merge} + \caption{Fusión de dos frentes} + \label{fig:concepts:wdir-merge} +\end{figure} + +Mercurial también debe modificar el directorio de trabajo, para +fusionar los ficheros que él monitorea en los dos conjuntos de +cambios. Con algunas simplificaciones, el proceso es el siguiente, por +cada fichero en los manifiestos de ambos conjuntos de cambios. +\begin{itemize} +\item Si ningún conjunto de cambios ha modificado un fichero, no se + hace nada con el mismo. +\item Si un conjunto de cambios ha modificado un fichero, y el otro no + lo ha hecho, se crea una copia del fichero con las modificaciones + pertinentes en el directorio de trabajo. +\item Si un conjunto de cambios borra un fichero, y el otro no lo ha + hecho (o también lo borró), se borra dicho fichero del directorio + de trabajo. +\item Si un conjunto de cambios ha borrado un fichero, pero el otro lo ha + modificado, se le pregunta al usuario qué hacer: conservar el + fichero modificado, o borrarlo? +\item Si ambos conjuntos de cambios han modificado un fichero, se + invoca el programa externo de fusión para definir el nuevo + contenido del fichero fusionado. Esto puede requerir interacción + directa de parte del usuario. +\item Si un conjunto de cambios ha modificado un fichero, y el otro ha + renombrado o copiado el mismo, asegurarse de que los cambios sigan + al nuevo nombre de fichero. +\end{itemize} +Hay más detalles---hacer una fusión tiene una gran cantidad de casos +especiales---pero éstas son las elecciones más comunes que se ven +involucradas en una fusión. Como usted puede ver, muchos de los casos +son completamente automáticos, y de hecho la mayoría de las fusiones +terminan automáticamente, sin requerir la interacción del usuario para +resolver ningún conflicto. + +Cuando considere qué pasa cuando usted hace una consignación después +de una fusión, de nuevo el directorio de trabajo es ``el conjunto de +cambios que estoy a punto de consignar''. Una vez termina su trabajo +el comando \hgcmd{merge}, el directorio de trabajo tiene dos padre; +éstos se convertirán en los padres del nuevo conjunto de cambios. + +Mercurial le permite hacer múltiples fusiones, pero usted debe +consignar los resultados de cada fusión sucesivamente. Esto es +necesario porque Mercurial sólo monitorea dos padres, tanto para las +revisiones como para los directorios de trabajo. Aunque técnicamente +es posible fusionar varios conjuntos de trabajo en una sola operación, +la posibilidad de confundir al usuario y crear un desorden terrible en +la fusión se hace incontenible de inmediato. + +\section{Otras características de diseño interesantes} + +En las secciones anteriores, he tratado de resaltar algunos de los +aspectos más importantes del diseño de Mercurial, para mostrar que se +presta gran cuidado y atención a la confiabilidad y el desempeño. Sin +embargo, la atención a los detalles no para ahí. Hay una cantidad de +aspectos de la construcción de Mercurial que encuentro interesantes +personalmente. Detallaré unos cuantos de ellos aquí, aparte de los +elementos ``importantes'' de arriba, para que, si usted está +% TODO the amount of thinking => (la cantidad de) esfuerzo mental +interesado, pueda obetener una idea mejor de la cantidad de esfuerzo +mental invertido en el diseño de un sistema bien diseñado. + + +\subsection{Compresión ingeniosa} + +Cuando es adecuado, Mercurial almacenará tanto las instantáneas como +los deltas en formato comprimido. Lo hace \emph{tratando} siempre de +comprimir una instantánea o delta, y conservando la versión comprimida +sólo si es más pequeña que la versión sin compresión. + +Esto implica que Mercurial hace ``lo correcto'' cuando almacena un +fichero cuyo formato original está comprimido, como un fichero +\texttt{zip} o una imagen JPEG. Cuando estos tipos de ficheros son +comprimidos por segunda vez, el fichero resultante usualmente es más +grande que la versión comprimida una sola vez, por lo que Mercurial +almacenará el fichero \texttt{zip} o JPEG original. + +Los deltas entre revisiones de un fichero comprimido usualmente son +más grandes que las instantáneas del mismo fichero, y Mercurial de +nuevo hace ``lo correcto'' en estos casos. Él encuentra que dicho +delta excede el umbral respecto al cual se debería almacenar una +instantánea completa del fichero, así que almacena la instantánea, +ahorrando espacio de nuevo respecto al enfoque simplista de usar +únicamente deltas. + +\subsubsection{Recompresión de red} + +Cuando almacena las revisiones en disco, Mercurial usa el algoritmo de +compresión ``deflación'' (el mismo usado en el popular formato de +fichero \texttt{zip}), que provee una buena velocidad con una tasa de +compresión respetable. Sin embargo, cuando se transmiten datos de +revisiones a través de una conexión de red, Mercurial descomprime los +datos comprimidos de las revisiones. + +Si la conexión es hecha a través de HTTP, Mercurial recomprime el +flujo completo de datos usando un algoritmo de compresión que brinda +una mejor tasa de compresión (el algoritmo Burrows-Wheeler del +ampliamente usado paquete de compresión \texttt{bzip2}). Esta +combinación de algoritmo y compresión del flujo completo de datos +(en vez de una revisión a la vez) reduce sustancialmente la cantidad +de bytes a transferir, brindando así un mejor desempeño de red sobre +casi todo tipo de redes. + +(Si la conexión se hace sobre \command{ssh}, Mercurial \emph{no} +recomprmime el flujo, porque \command{ssh} puede hacer esto por sí +mismo.) + +\subsection{Reordenado de lectura/escritura y atomicidad} + +Añadir datos al final de un fichero no es todo lo que hace falta para +garantizar que un lector no verá una escritura parcial. Si recuerda la +figura~\ref{fig:concepts:metadata}, las revisiones en la bitácora de +cambios apuntan a revisiones en el manifiesto, y las revisiones en el +manifiesto apuntan a revisiones en ficheros de registro. Esta +jerarquía es deliberada. + +Un escritor inicia una transacción al escribir los datos del ficheros +del fichero de registro y el manifiesto, y no escribe nada en la +bitácora de cambios hasta que dichas escrituras hayan terminado. Un +lector empieza leyendo datos de la bitácora de cambios, luego del +manifiesto, y finalmente del fichero de registro. + +%TODO revisar párrafo completo, no me gusta el resultado +Como el escritor siempre termina de escribir los datos en el fichero +de registro y en el manifiesto antes de escribir a la bitácora de +cambios, un lector nunca verá un apuntador a una versión parcialmente +escrita de revisiones del manifiesto desde la bitácora de cambios, y +nunca leerá un apuntador a una revisión parcialmente escrita del +fichero de registro desde el manifiesto. + +\subsection{Acceso concurrente} + +El reordenado de lectura/escritura y la atomicidad garantizan que +Mercurial nunca necesita \emph{bloquear} un repositorio cuando está +leyendo datos, aún si se está escribiendo al repositorio mientras se +hace la lectura. Esto tiene un gran efecto en la escalabilidad; usted +puede tener cualquier cantidad de procesos Mercurial leyendo datos de +un repositorio de manera segura al mismo tiempo, sin importar si se +está escribiendo al mismo o no. + +La naturaleza carente de bloqueos de la lectura significa que si usted +está compartiendo un repositorio en un sistema multiusuario, no +necesita dar a los usuarios locales permisos de \emph{escritura} a su +repositorio para que ellos puedan clonarlo o jalar cambios; sólo +necesitan permisos de \emph{lectura}. (Esta \emph{no} es una +%TODO signo de admiración de apertura +característica común entre los sistemas de control de revisiones, así +que no la dé por hecha! Muchos de ellos requieren que los lectores +sean capaces de bloquear el repositorio antes de poder leerlo, y esto +requiere acceso de escritura en al menos un directorio, lo que por +supuesto se convierte en una fuente de todo tipo de problemas +administrativos y de seguridad bastante molestos.) + +Mercurial usar bloqueos para asegurarse de que sólo un proceso pueda +escribir a un repositorio al mismo tiempo (el mecanismo de bloqueo es +seguro incluso sobre sistemas de ficheros notoriamente hostiles al +bloqueo, como NFS). Si un repositorio está bloqueado, los escritores +esperarán un buen rato para revisar si el repositorio ya ha sido +desbloqueado, pero si el repositorio sique bloqueado por mucho tiempo, +el proceso que intenta escribir fallará por tiempo de espera máximo. +Esto significa que sus guiones automáticos diarios no se quedarán +esperando para siempre, apilándose si el sistema se cayó sin que nadie +se diera cuenta, por ejemplo. (Sí, el tiempo de espera máximo es +configurable, de cero a infinito). + + +\subsubsection{Acceso seguro al estado de directorio} + +Al igual que con los datos de revisión, Mercurial no requiere un +bloqueo para leer el fichero de estado de directorio; sí se usa un +bloqueo para escribir a él. Para evitar la posibilidad de leer una +copia parcialmente escrita del fichero de estado de directorio, +Mercurial escribe a un fichero con un nombre único en el mismo +directorio del fichero de estado de directorio, y luego renombra +atómicamente este fichero temporal a \filename{dirstate}\ndt{Estado de +directorio.}. Así se garantiza que el fichero llamado +\filename{dirstate} esté completo, y no parcialmente escrito. + +\subsection{Evitar movimientos de brazo} + +Un aspecto crítico para el desempeño de Mercurial es evitar los +movimientos del brazo de lectura del disco duro, ya que cualquier +movimiento de brazo es mucho más costoso que incluso una operación de +lectura relativamente grande. + +Es por esto que, por ejemplo, el estado de directorio es almacenado +como un solo fichero. Si hubiera un estado de directorio por cada +directorio que Mercurial monitorea, el disco haría un movimiento de +brazo por cada directorio. En cambio, Mercurial lee el estado de +directorio completo en un solo paso. + +Mercurial también usa un esquema de ``copiar al escribir'' cuando +clona un repositorio en un mismo medio de almacenamiento local. En vez +de copiar cada fichero de revlog del repositorio viejo al nuevo, se +crea un ``enlace duro'', que es una manera sucinta de decir +``estos dos nombres apuntan al mismo fichero''. Cuando Mercurial está +a punto de escribir a uno de los ficheros de revlog, revisa si la +cantidad de nombres apuntando al fichero es de más de uno. Si lo es, +más de un repositorio está usando el fichero, así que Mercurial hace +una nueva copia del fichero, privada para este repositorio. + +Algunos desarrolladores de control de revisiones han indicado que la +idea de hacer una copia privada completa de un fichero no es eficiente +desde el punto de vista de almacenamiento. Aunque esto es cierto, el +almacenamiento es barato, y este método brinda el máximo rendimiento +al tiempo que delega la mayor parte del trabajo de manejo de ficheros +al sistema operativo. Un esquema alternativo seguramente reduciría el +desempeño y aumentaría la complejidad del software, cada uno de los +cuales es mucho más importante para la ``sensación'' que se tiene del +software en el trabajo día a día. + +\subsection{Otros contenidos del estado de directorio} + +Debido a que Mercurial no lo fuerza a indicar si usted está +modificando un fichero, se usa el estado de directorio para almacenar +información extra para poder determinar efecientemente si usted ha +modificado un fichero. Por cada fichero en el directorio de trabajo, +se almacena el momento en que Mercurial modificó por última vez el +fichero, y el tamaño del fichero en ese momento. + +Cuando usted añade (\hgcmd{add}), remueve (\hgcmd{remove}), renombra +(\hgcmd{rename}) o copia (\hgcmd{copy}) ficheros, Mercurial actualiza +el estado de directorio para saber qué hacer con dichos ficheros +cuando usted haga la consignación. + +Cuando Mercurial está revisando el estado de los ficheros en el +directorio de trabajo, revisa primero la fecha de modificación del +fichero. Si no ha cambiado, el fichero no ha sido modificado. Si el +tamaño del fichero ha cambiado, el fichero ha sido modificado. Sólo en +el caso en que el tiempo de modificación ha cambiado, pero el tamaño +no, es necesario leer el contenido del fichero para revisar si ha +cambiado. Almacenar estos pocos datos reduce dramáticamente la +cantidad de datos que Mercurial debe leer, lo que brinda una mejora en +el rendimiento grande, comparado con otros sistemas de control de +revisiones. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/daily.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/daily.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,404 @@ +\chapter{Mercurial día a día} +\label{chap:daily} + +\section{Cómo indicarle a Mercurial qué ficheros seguir} + +Mercurial no trabaja con ficheros en su repositorio a menos que usted +se lo indique explícitamente. La orden \hgcmd{status} le mostrará +cuáles ficheros son desconocidos para Mercurial; se emplea un +``\texttt{?}'' para mostrar tales ficheros. + +Para indicarle a Mercurial que tenga en cuenta un fichero, emplee la +orden \hgcmd{add}. Una vez que haya adicionado el fichero, la línea +referente al fichero al aplicar la orden \hgcmd{status} para tal +fichero cambia de ``\texttt{?}'' a ``\texttt{A}''. +\interaction{daily.files.add} + +Después de invocar \hgcmd{commit}, los ficheros que haya adicionado +antes de consignar no se listarán en la salida de \hgcmd{status}. La +razón para esto es que \hgcmd{status} solamente le muestra aquellos +ficheros ``interesantes'' ---~los que usted haya modificado o a aquellos +sobre los que usted haya indicado a Mercurial hacer algo--- de forma +predeterminada. Si tiene un repositorio que contiene miles de +ficheros, rara vez deseará saber cuáles de ellos están siendo +seguidos por Mercurial, pero que no han cambiado. (De todas maneras, +puede obtener tal información; más adelante hablaremos de ello.) + + +Cuando usted añade un fichero, Mercurial no hace nada con él inmediatamente. +En cambio, tomará una instantánea del estado del fichero la próxima vez +que usted consigne. Continuará haciendo seguimiento a los cambios que +haga sobre el fichero cada vez que consigne, hasta que usted lo elimine. + +\subsection{Nombramiento explícito e implícito de ficheros} + +Mercurial tiene un comportamiento útil en el cual si a una orden, +le pasa el nombre de un directorio, todas las órdenes lo interpretarán como +``Deseo operar en cada fichero de este directorio y sus +subdirectorios''. +\interaction{daily.files.add-dir} +Tenga en cuenta que en este ejemplo Mercurial imprimió los nombres de +los ficheros que se adicionaron, mientras que no lo hizo en el ejemplo +anterior cuando adicionamos el fichero con nombre \filename{a}. + +En el último caso hicimos explícito el nombre del fichero que +deseábamos adicionar en la línea de órdenes, y Mercurial asume en +tales casos que usted sabe lo que está haciendo y no imprime +información alguna. + +Cuando hacemos \emph{implícitos} los nombres de los ficheros dando el +nombre de un directorio, Mercurial efectúa el paso extra de imprimir +el nombre de cada fichero con el que va a hacer algo. Esto para +aclarar lo que está sucediendo, y reducir en lo posible una sorpresa +silenciosa pero fatal. Este comportamiento es común a la mayoría de +órdenes en Mercurial. + +\subsection{Nota al margen: Mercurial trata ficheros, no directorios} + +Mercurial no da seguimiento a la información de los directorios. En +lugar de eso tiene en cuenta las rutas de los ficheros. Antes de +crear un fichero, primero crea todos los directorios que hagan falta +para completar la ruta del mismo. Después de borrar un fichero, +borra todos los directorios vacíos que estuvieran en la ruta del +fichero borrado. Suena como una diferencia trivial, pero tiene una +consecuencia práctica menor: no es posible representar un directorio +completamente vacío en Mercurial. + +Los directorios vacíos rara vez son útiles, y hay soluciones +alternativas no intrusivas que usted puede emplear para obtener el efecto +apropiado. Los desarrolladores de Mercurial pensaron que la +complejidad necesaria para administrar directorios vacíos no valía la +pena frente al beneficio limitado que esta característica podría traer. + +Si necesita un directorio vacío en su repositorio, hay algunas formas +de lograrlo. Una es crear un directorio, después hacer \hgcmd{add} a +un fichero ``oculto'' dentro de ese directorio. En sistemas tipo +Unix, cualquier fichero cuyo nombre comience con un punto +(``\texttt{.}'') es tratado como oculto por la mayoría de +comandos y herramientas GUI. Esta aproximación se ilustra en la figura~\ref{ex:daily:hidden}. + +\begin{figure}[ht] + \interaction{daily.files.hidden} + \caption{Simular un directorio vacío con un fichero oculto} + \label{ex:daily:hidden} +\end{figure} + +Otra forma de abordar la necesidad de un directorio vacío es +simplemente crear uno en sus guiones de construcción antes de que lo +necesiten. + +\section{Cómo dejar de hacer seguimiento a un fichero} + +Si decide que un fichero no pertenece a su repositorio, use la orden +\hgcmd{remove}; se borrará el fichero y le indicará a Mercurial que +deje de hacerle seguimiento. Los ficheros eliminados se representan +con ``\texttt{R}'' al usar \hgcmd{status}. +\interaction{daily.files.remove} + +Después de hacer \hgcmd{remove} a un fichero, Mercurial dejará de +hacer seguimiento al mismo, incluso si recrea el fichero con el mismo +nombre en su directorio de trabajo. Si decide recrear un fichero con +el mismo nombre y desea que Mercurial le haga seguimiento, basta con +hacerle \hgcmd{add}. Mercurial sabrá que el fichero recientemente +adicionado no está relacionado con el fichero anterior que tenía el +mismo nombre. + +\subsection{Al eliminar un fichero no se afecta su historial} + +Es preciso tener en cuenta que eliminar un fichero tiene sólo dos +efectos. +\begin{itemize} +\item Se elimina la versión actual del fichero del directorio de +trabajo. +\item Mercurial deja de hacer seguimiento a los cambios del fichero + desde la próxima consignación. +\end{itemize} +Al eliminar un fichero \emph{no} se altera de ninguna manera el +\emph{historial} del mismo. + +Si actualiza su directorio de trabajo a un conjunto de cambios en el +cual el fichero que eliminó aún era tenido en cuenta, éste reaparecerá en +el directorio de trabajo, con los contenidos que este tenía cuando se +consignó tal conjunto de cambios. Si usted actualiza el directorio de +trabajo a un conjunto de cambios posterior en el cual el fichero había +sido eliminado, Mercurial lo eliminará de nuevo del directorio de +trabajo. + +\subsection{Ficheros perdidos} + +Mercurial considera como \emph{perdido} un fichero que usted borró, +pero para el que no se usó \hgcmd{remove}. Los ficheros perdidos se +representan con ``\texttt{!}'' al visualizar \hgcmd{status}. +Las órdenes de Mercurial generalmente no harán nada con los ficheros +perdidos. +\interaction{daily.files.missing} + +Si su repositorio contiene un fichero que \hgcmd{status} reporta como +perdido, y desea que el mismo se vaya, se puede usar +\hgcmdargs{remove}{\hgopt{remove}{--after}} posteriormente para +indicarle a Mercurial que usted deseaba borrar tal fichero. +\interaction{daily.files.remove-after} + +Por otro lado, si borró un fichero perdido por accidente, puede usar +\hgcmdargs{revert}{\emph{nombre de fichero}} para recuperar el +fichero. Reaparecerá, sin modificaciones. +\interaction{daily.files.recover-missing} + +\subsection{Nota al margen: ¿Por qué decirle explícitamente a Mercurial + que elimine un fichero?} + +Es posible que se haya preguntado por qué Mercurial exige que usted le +indique explícitamente que está borrando un fichero. Al principio del +desarrollo de Mercurial, este permitía que usted borrara el fichero +sin más; Mercurial se daría cuenta de la ausencia del fichero +automáticamente después de la ejecución de \hgcmd{commit}, y dejaría de +hacer seguimiento al fichero. En la práctica, resultaba muy sencillo +borrar un fichero accidentalmente sin darse cuenta. + +\subsection{Atajo útil---agregar y eliminar ficheros en un solo paso} + +Mercurial ofrece una orden combinada, \hgcmd{addremove}, que agrega +los ficheros que no tienen seguimiento y marca los ficheros faltantes +como eliminados. +\interaction{daily.files.addremove} +La orden \hgcmd{commit} se puede usar con la opción \hgopt{commit}{-A} +que aplica el mismo agregar-eliminar, seguido inmediatamente de una +consignación. +\interaction{daily.files.commit-addremove} + +\section{Copiar ficheros} + +Mercurial ofrece la orden \hgcmd{copy} para hacer una copia nueva de +un fichero. Cuando se copia un fichero con esta orden, Mercurial +lleva un registro indicando que el nuevo fichero es una copia del +fichero original. Los ficheros copiados se tratan de forma especial cuando +usted hace una fusión con el trabajo de alguien más. + +\subsection{Resultados de copiar un fichero durante una fusión} + +Durante una fusión los cambios ``siguen'' una copia. Para ilustrar +lo que esto significa, haremos un ejemplo. Comenzaremos con el mini +repositorio usual que contiene un solo fichero +\interaction{daily.copy.init} +Debemos hacer algo de trabajo en paralelo, de forma que tengamos algo para +fusionar. Aquí clonamos el repositorio. +\interaction{daily.copy.clone} +De vuelta en el repositorio inicial, usemos la orden \hgcmd{copy} para hacer +una copia del primer fichero que creamos. +\interaction{daily.copy.copy} + +Si vemos la salida de la orden \hgcmd{status}, el fichero copiado luce +tal como un fichero que se ha añadido normalmente. +\interaction{daily.copy.status} +Pero si usamos la opción \hgopt{status}{-C} de la orden +\hgcmd{status}, se imprimirá otra línea: el fichero \emph{desde} el +cual fue copiado nuestro fichero recién añadido. +\interaction{daily.copy.status-copy} + +Ahora, en el repositorio que clonamos, hagamos un cambio en +paralelo. Adicionaremos una línea de contenido al fichero original que +creamos. +\interaction{daily.copy.other} +Hemos modificado el fichero \filename{file} en este +repositorio. Cuando jalemos los cambios del primer repositorio y +fusionemos las dos cabezas, Mercurial propagará los cambios que hemos +hecho localmente en \filename{file} a su copia, \filename{new-file}. +\interaction{daily.copy.merge} + +\subsection{¿Por qué los cambios se reflejan en las copias?} +\label{sec:daily:why-copy} + +Este comportamiento de cambios en ficheros que se propagan a las +copias de los ficheros parecería esotérico, pero en la mayoría de +casos es absolutamente deseable. +Es indispensable recordar que esta propagación \emph{solamente} sucede +cuando fusionamos. Por lo tanto si sobre un fichero se usa +\hgcmd{copy}, y se modifica el fichero original durante el curso +normal de su trabajo, nada pasará. + +Lo segundo a tener en cuenta es que las modificaciones solamente se +propagarán en las copias únicamente si los repositorios de los cuales +está jalando los cambios \emph{no saben} de la copia. + +Explicaremos a continuación la razón de este comportamiento de +Mercurial. Digamos que yo he aplicado un arreglo de un fallo importante a un +fichero fuente y consigné los cambios. Por otro lado, usted decidió hacer +\hgcmd{copy} sobre el fichero en su repositorio, sin saber acerca del +fallo o sin ver el arreglo, y ha comenzado a trabajar sobre su copia +del fichero. + +Si jala y fusiona mis cambios y Mercurial \emph{no hubiera} propagado +los cambios en las copias, su fichero fuente tendría el fallo, a menos +que usted haya recordado propagar el arreglo del fallo a mano, el +mismo \emph{permanecería} en su copia del fichero. + +Mercurial previene esta clase de problemas, gracias a la propagación +automática del cambio que arregló el fallo del fichero original. Hasta +donde sé, Mercurial es el \emph{único} sistema de control de +revisiones que propaga los cambios en las copias de esta forma. + +Cuando su historial de cambios tiene un registro de la copia y la +subsecuente fusión, usualmente no es necesario propagar los cambios el +fichero original a las copias del mismo, y por esta razón Mercurial +propaga únicamente los cambios en las copias hasta este punto y no más +allá. + + +\subsection{Cómo hacer que los cambios \emph{no} sigan a la copia?} + +Si por algún motivo usted decide que esta característica de +propagación automática de cambios en las copias no es para usted, +simplemente use +la orden usual de su sistema para copiar ficheros (en sistemas tipo +Unix, es \command{cp}), y posteriormente use \hgcmd{add} sobre la nueva +copia hecha a mano. Antes de hacerlo, de todas maneras, relea la +sección~\ref{sec:daily:why-copy}, y tome una decisión asegurándose que +este comportamiento no es el apropiado para su caso específico. + +\subsection{Comportamiento de la orden \hgcmd{copy}} + +Cuando usa la orden \hgcmd{copy}, Mercurial hace una copia de cada +fichero fuente tal como se encuentra en el directorio actual. Esto +significa que si usted hace +modificaciones a un fichero, y le aplica \hgcmd{copy} sin haber +consignado primero los cambios, la nueva copia contendrá también las +modificaciones que haya hecho hasta ese punto. (Este comportamiento me +parece poco intuitivo, y por tal motivo lo menciono.) + +La orden \hgcmd{copy} actúa de forma parecida a la orden \command{cp} +de Unix (puede usar el alias \hgcmd{cp} si le es más cómodo). El +último argumento es el \emph{destino}, y todos los argumentos previos +son las \emph{fuentes}. Si solamente indica un fichero como la +fuente, y el destino no existe, se crea un fichero nuevo con ese nombre. +\interaction{daily.copy.simple} +Si el destino es un directorio, Mercurial copia las fuentes en éste. +\interaction{daily.copy.dir-dest} +La copia de un directorio es recursiva, y preserva la estructura del +directorio fuente. +\interaction{daily.copy.dir-src} +Si tanto la fuente como el destino son directorios, la estructura de +la fuente se recrea en el directorio destino. +\interaction{daily.copy.dir-src-dest} + +De la misma forma que la orden \hgcmd{rename}, si copia un fichero +manualmente y desea que Mercurial sepa que ha copiado un fichero, +basta con aplicar la opción \hgopt{copy}{--after} a la orden +\hgcmd{copy}. +\interaction{daily.copy.after} + +\section{Renombrar ficheros} + +La necesidad de renombrar un fichero es más común que hacer una copia +del mismo. La razón por la cual discutí la orden \hgcmd{copy} antes +de hablar acerca de cambiar el nombre de los ficheros, es que +Mercurial trata el renombrar un fichero de la misma forma que una +copia. Por lo tanto, saber lo que hace Mercurial cuando usted copia +un fichero le indica qué esperar cuando renombra un fichero. + +Cuando usa la orden \hgcmd{rename}, Mercurial hace una copia de cada +fichero fuente, lo borra y lo marca como fichero eliminado. +\interaction{daily.rename.rename} +La orden \hgcmd{status} muestra la nueva copia del fichero como +añadida y el fichero inicial de la copia, como eliminado. +\interaction{daily.rename.status} +De la misma forma en que se usa la orden \hgcmd{copy}, debemos usar la +opción \hgopt{status}{-C} de la orden \hgcmd{status} para verificar +que el fichero añadido realmente comienza a ser seguido por Mercurial +como una copia del fichero original, ahora eliminado. +\interaction{daily.rename.status-copy} + +Igual que con \hgcmd{remove} y \hgcmd{copy}, puede indicársele a +Mercurial acerca de un renombramiento inmediato con la opción +\hgopt{rename}{--after}. El comportamiento de la orden +\hgcmd{rename} y las opciones que acepta, son similares a la orden +\hgcmd{copy} en casi todo. + +\subsection{Renombrar ficheros y fusionar cambios} + +Dado que el renombrado de Mercurial se implementa como un +copiar-y-eliminar, la misma propagación de cambios ocurre cuando usted +fusiona después de renombrar como después de hacer una copia. + +Si yo modifico un fichero y usted lo renombra a un nuevo fichero, y +posteriormente fusionamos nuestros respectivos cambios, mi +modificación al fichero bajo su nombre original se propagará en el +fichero con el nuevo nombre. (Es lo que se esperaría que ``simplemente +funcione,'' +pero, no todos los sistemas de control de revisiones hacen esto.) + +Aunque el hecho de que los cambios sigan la copia es una característica +respecto a la cual usted puede estar de acuerdo y decir ``si, puede +ser útil,'' debería ser claro +que el seguimiento de cambios de un renombramiento es definitivamente +importante. Sin esto, sería muy sencillo que los cambios se +quedaran atrás cuando los ficheros se renombran. + +\subsection{Cambios de nombre divergentes y fusión} + +El caso de renombramiento con nombres divergentes ocurre cuando dos +desarrolladores comienzan con un fichero---llamémoslo +\filename{foo}---en sus repositorios respectivos. + +\interaction{rename.divergent.clone} +%TODO we must either change the example's names, and these names, or +%use the original names. as of now, we keep the old names +Anne renombra el fichero a \filename{bar}. +\interaction{rename.divergent.rename.anne} +Mientras que Bob lo renombra como \filename{quux}. +\interaction{rename.divergent.rename.bob} + +Veo esto como un conflicto porque cada desarrollador ha expresado +intenciones diferentes acerca de cómo considera debería haberse +nombrado el fichero. + +¿Qué cree que debería pasar cuando fusionen su trabajo? +El comportamiento de Mercurial es que siempre preserva \emph{ambos} +nombres cuando fusiona los conjuntos de cambios que contienen nombres +divergentes. +%TODO traducir texto interaccion +\interaction{rename.divergent.merge} + +Tenga en cuenta que Mercurial le advierte acerca de nombres +divergentes, pero deja que usted decida qué hacer con la divergencia +después de la fusión. + +\subsection{Cambios de nombre convergentes y fusión} + +Otra clase de conflicto al cambiar el nombre de ficheros ocurre cuando dos +personas eligen renombrar diferentes ficheros \emph{fuente} al mismo +\emph{destino}. En este caso Mercurial aplica su maquinaria de fusión +usual, y le permite a usted guiar la situación a una resolución adecuada. + +\subsection{Otros casos límite relacionados con renombramientos} + +Mercurial tiene un fallo de mucho tiempo en el cual no es capaz de +fusionar cuando por un lado hay un fichero con un nombre dado, +mientras que en otro hay un directorio con el mismo nombre. Esto está +documentado como~\bug{29}. +\interaction{issue29.go} + +\section{Recuperarse de equivocaciones} + +Mercurial tiene unas órdenes poderosas que le ayudarán a recuperarse +de equivocaciones comunes. + +La orden \hgcmd{revert} le permite deshacer cambios que haya hecho a +su directorio de trabajo. Por ejemplo, si aplicó \hgcmd{add} a un +fichero por accidente, ejecute \hgcmd{revert} con el nombre del +fichero que añadió, y en tanto que el fichero no haya sido tocado de +forma alguna, no será adicionado, ni seguido por Mercurial. También +puede usar \hgcmd{revert} para deshacerse de cambios erróneos a un +fichero. + +Tenga en cuenta que la orden \hgcmd{revert} se usa para cambios que no +han sido consignados. Cuando haya consignado un cambio, si decide que +era un error, puede hacer algo todavía, pero sus opciones pueden estar +más limitadas. + +Para obtener información acerca de la orden \hgcmd{revert} y detalles +de cómo tratar con cambios consignados, vea el capítulo~\ref{chap:undo}. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/examples/backout --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/backout Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/bisect --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/bisect Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/branch-named --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/branch-named Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/branch-repo --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/branch-repo Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/branching --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/branching Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/cmdref --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/cmdref Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/daily.copy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/daily.copy Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/daily.files --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/daily.files Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/daily.rename --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/daily.rename Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/daily.revert --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/daily.revert Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/data/check_whitespace.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/data/check_whitespace.py Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/data/netplug-1.2.5.tar.bz2 Binary file es/examples/data/netplug-1.2.5.tar.bz2 has changed diff -r 73b094b764ec -r 51b5d56744c5 es/examples/data/netplug-1.2.8.tar.bz2 Binary file es/examples/data/netplug-1.2.8.tar.bz2 has changed diff -r 73b094b764ec -r 51b5d56744c5 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 Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/extdiff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/extdiff Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/filenames --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/filenames Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/hook.msglen --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/hook.msglen Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/hook.simple --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/hook.simple Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/hook.ws --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/hook.ws Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/issue29 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/issue29 Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/mq.dodiff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.dodiff Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/mq.guards --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.guards Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/mq.id --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.id Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/mq.qinit-help --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.qinit-help Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,7 @@ +#!/bin/bash + +echo '[extensions]' >> $HGRC +echo 'hgext.mq =' >> $HGRC + +#$ name: help +hg help qinit diff -r 73b094b764ec -r 51b5d56744c5 es/examples/mq.tarball --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.tarball Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/mq.tools --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.tools Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/mq.tutorial --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/mq.tutorial Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/rename.divergent --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/rename.divergent Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/rollback --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/rollback Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/run-example --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/run-example Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/svn-long.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/svn-long.txt Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/svn-short.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/svn-short.txt Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/svn.style --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/svn.style Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,2 @@ +header = '------------------------------------------------------------------------\n\n' +changeset = svn.template diff -r 73b094b764ec -r 51b5d56744c5 es/examples/svn.template --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/svn.template Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,5 @@ +r{rev} | {author|user} | {date|isodate} ({date|rfc822date}) + +{desc|strip|fill76} + +------------------------------------------------------------------------ diff -r 73b094b764ec -r 51b5d56744c5 es/examples/tag --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/tag Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/template.simple --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/template.simple Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/template.svnstyle --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/template.svnstyle Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/tour --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/tour Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/examples/tour-merge-conflict --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/examples/tour-merge-conflict Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/filelog.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/filelog.svg Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/filenames.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/filenames.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,344 @@ +\chapter{Nombres de ficheros y asociación de patrones} +\label{chap:names} + +Mercurial provee mecanismos que le permiten trabajar con nombres de +ficheros en una manera consistente y expresiva. + +\section{Nombrado de ficheros simple} + +% TODO traducción literal de "under the hood". revisar +Mercurial usa un mecanismo unificado ``bajo el capó'' para manejar +nombres de ficheros. Cada comando se comporta de manera uniforme con +respecto a los nombres de fichero. La manera en que los comandos +operan con nombres de fichero es la siguiente. + +Si usted especifica explícitamente nombres reales de ficheros en la +línea de comandos, Mercurial opera únicamente sobre dichos ficheros, +como usted esperaría. +\interaction{filenames.files} + +Cuando usted provee el nombre de un directorio, Mercurial interpreta +eso como ``opere en cada fichero en este directorio y sus +subdirectorios''. Mercurial va por todos los ficheros y subdirectorios +de un directorio en orden alfabético. Cuando encuentra un +subdirectorio, lo recorrerá antes de continuar con el directorio +actual. +\interaction{filenames.dirs} + +\section{Ejecución de comandos sin ningún nombre de fichero} + +Los comandos de Mercurial que trabajan con nombres de fichero tienen +comportamientos por defecto adecuados cuando son utilizados sin pasar +ningún patrón o nombre de fichero. El tipo de comportamiento depende +de lo que haga el comando. Aquí presento unas cuantas reglas generales +que usted puede usar para que es lo que probablemente hará un comando +si usted no le pasa ningún nombre de fichero con el cual trabajar. +\begin{itemize} +\item Muchos comandos operarán sobre el directorio de trabajo + completo. Por ejemplo, esto es lo que hace el comando + \hgcmd{add}, +\item Si el comando tiene efectos difíciles o incluso imposibles de + revertir, se le obligará a usted a proveer explícitamente al menos + % TODO revisar ese "lo proteje a usted" + un nombre o patrón (ver más abajo). Esto lo proteje a usted de, + por ejemplo, borrar ficheros accidentalmente al ejecutar + \hgcmd{remove} sin ningún argumento. +\end{itemize} + + +Es fácil evitar este comportamiento por defecto, si no es el adecuado +para usted. Si un comando opera normalmente en todo el directorio de +trabajo, usted puede llamarlo para que trabaje sólo en el directorio +actual y sus subdirectorio pasándole el nombre ``\dirname{.}''. +\interaction{filenames.wdir-subdir} + +Siguiendo la misma línea, algunos comandos normalmente imprimen las +rutas de ficheros con respecto a la raíz del repositorio, aún si usted +los llama dentro de un subdirectorio. Dichos comandos imprimirán las +rutas de los ficheros respecto al directorio en que usted se encuentra +si se les pasan nombres explícitos. Vamos a ejecutar el comando +\hgcmd{status} desde un subdirectorio, y a hacer que opere en el +directorio de trabajo completo, a la vez que todas las rutas de +ficheros se imprimen respecto a nuestro subdirectorio, pasándole la +salida del comando \hgcmd{root}. +\interaction{filenames.wdir-relname} + +\section{Reportar que está pasando} + +El ejemplo con el comando \hgcmd{add} en la sección anterior ilustra +algo más que es útil acerca de los comandos de Mercurial. Si un +comando opera en un fichero que usted no pasó explícitamente en la +línea de comandos, usualmente se imprimirá el nombre del fichero, para +que usted no sea sorprendido por lo que sucede. + +Esto es el principio de \emph{mínima sorpresa}. Si usted se ha +referido explícitamente a un fichero en la línea de comandos, no tiene +mucho sentido repetir esto de vuelta a usted. Si Mercurial está +actuando en un fichero \emph{implícitamente}, porque usted no pasó +nombres, ni directorios, ni patrones (ver más abajo), lo más seguro es +decirle a usted qué se está haciendo. + +Usted puede silenciar a los comandos que se comportan de esta manera +usando la opción \hggopt{-q}. También puede hacer que impriman el +nombre de cada fichero, aún aquellos que usted indicó explícitamente, +usando la opción \hggopt{-v}. + +\section{Uso de patrones para identificar ficheros} + +Además de trabajar con nombres de ficheros y directorios, Mercurial le +permite usar \emph{patrones} para identificar ficheros. El manejo de +patrones de Mercurial es expresivo. + +En sistemas tipo Unix (Linux, MacOS, etc.), el trabajo de asociar +patrones con nombres de ficheros recae sobre el intérprete de comandos. +En estos sistemas, usted debe indicarle explícitamente a Mercurial que +el nombre que se le pasa es un patrón. En Windows, el intérprete no +expande los patrones, así que Mercurial identificará automáticamente +los nombres que son patrones, y hará la expansión necesaria. + +Para pasar un patrón en vez de un nombre normal en la línea de +comandos, el mecanismo es simple: +\begin{codesample2} + syntax:patternbody +\end{codesample2} +Un patrón es identificado por una cadena de texto corta que indica qué +tipo de patrón es, seguido por un dos puntos, seguido por el patrón en +sí. + +Mercurial soporta dos tipos de sintaxis para patrones. La que se usa +con más frecuencia se denomina \texttt{glob}\ndt{Grupo, colección, +aglomeración.}; es el mismo tipo de asociación de patrones usado por +el intérprete de Unix, y también debería ser familiar para los +usuarios de la línea de comandos de Windows. + +Cuando Mercurial hace asociación automática de patrones en Windows, +usa la sintaxis \texttt{glob}. Por esto, usted puede omitir el +prefijo ``\texttt{glob:}'' en Windows, pero también es seguro usarlo. + +La sintaxis \texttt{re}\ndt{Expresiones regulares.} es más poderosa; +le permite especificar patrones usando expresiones regulares, también +conocidas como regexps. + +A propósito, en los ejemplos siguientes, por favor note que yo tengo +el cuidado de rodear todos mis patrones con comillas sencillas, para +que no sean expandidos por el intérprete antes de que Mercurial pueda +verlos. + +\subsection{Patrones \texttt{glob} estilo intérprete} + +Este es un vistazo general de los tipos de patrones que usted puede +usar cuando está usando asociación con patrone glob. + +La secuencia ``\texttt{*}'' se asocia con cualquier cadena, dentro de +un único directorio. +\interaction{filenames.glob.star} + +La secuencia ``\texttt{**}'' se asocia con cualquier cadena, y cruza los +% TODO token +límites de los directorios. No es una elemento estándar de los tokens +de glob de Unix, pero es aceptado por varios intérpretes Unix +populares, y es muy útil. +\interaction{filenames.glob.starstar} + +La secuencia ``\texttt{?}'' se asocia con cualquier caracter sencillo. +\interaction{filenames.glob.question} + +El caracter ``\texttt{[}'' marca el inicio de una \emph{clase de +caracteres}. Ella se asocia con cualquier caracter sencillo dentro de +la clase. La clase se finaliza con un caracter ``\texttt{]}''. Una +clase puede contener múltiples \emph{rango}s de la forma +``\texttt{a-f}'', que en este caso es una abreviación para +``\texttt{abcdef}''. +\interaction{filenames.glob.range} +Si el primer caracter en aparecer después de ``\texttt{[}'' en la +clase de caracteres es un ``\texttt{!}'', se \emph{niega} la clase, +haciendo que se asocie con cualquier caracter sencillo que no se +encuentre en la clase. + +Un ``\texttt{\{}'' marca el inicio de un grupo de subpatrones, en +donde todo el grupo es asociado si cualquier subpatrón en el grupo +puede ser asociado. El caracter ``\texttt{,}'' separa los subpatrones, +y el ``\texttt{\}}'' finaliza el grupo. +\interaction{filenames.glob.group} + +\subsubsection{Cuidado!} + +No olvide que si usted desea asocia un patrón con cualquier +directorio, no debería usar el elemento para asociar con cualquier +cadena ``\texttt{*}'', ya que éste sólo generará asociaciones dentro +de un solo directorio. En vez de eso, use el caracter para asociar con +cualquier cadena ``\texttt{**}''. Este pequeño ejemplo ilustra la +diferencia entre los dos. +\interaction{filenames.glob.star-starstar} + +\subsection{Asociación con patrones de expresiones regulares \texttt{re}} + +Mercurial acepta la misma sintaxis para expresiones regulares del +lenguaje de programación Python (internamente se usa el motor de +expresiones regulares de Python). Esta sintaxis está basada en la +misma del lenguaje Perl, que es el dialecto más popular en uso +(por ejemplo, también se usa en Java). + +No discutiré el dialecto de expresiones regulares de Mercurial en +detalle aquí, ya que las mismas no son usadas frecuentemente. Las +expresiones regulares al estilo Perl se encuentran documentadas +exhaustivamente en una multitud de sitios web, y en muchos libros. +En vez de eso, me enfocaré en unas cuantas cosas que usted debería +conocer si tiene la necesidad de usar expresiones regulares en +Mercurial. + +Una expresión regular es comparada contra un nombre de fichero +completo, relativo a la raíz del repositorio. En otras palabras, aún +si usted se encuentra en un subdirectorio \dirname{foo}, si desea +asociar ficheros en este directorio, su patrón debe empezar con +``\texttt{foo/}''. + +Un detalle a tener en cuenta es que, si le son familiares las +expresiones regulares al estilo Perl, las de Mercurial están +\emph{enraízadas}. Esto es, que la asociación de una expresión se hace +desde el inicio de la cadena; no se buscan coincidencias dentro de la +cadena. Para buscar coincidencias en cualquier sitio dentro de una +cadena, empiece su patrón con un ``\texttt{.*}''. + +\section{Filtrado de ficheros} + +Mercurial no sólo le provee una variedad de formas para especificar +ficheros; le permite limitar aún más dichos ficheros mediante el uso +de \emph{filtros}. Los comandos que operan con nombres de fichero +aceptan dos opciones de filtrado. +\begin{itemize} +\item \hggopt{-I}, o \hggopt{--include}, le permite especificar un + patrón con el que deben coincidir los ficheros para ser + procesados. +\item \hggopt{-X}, o \hggopt{--exclude}, le brinda una manera de + \emph{evitar} procesar ficheros, si coinciden con este patrón. +\end{itemize} +Usted puede pasar múltiples veces las opciones \hggopt{-I} y +\hggopt{-X} en la línea de comandos, e intercalarlos como desee. +Por defecto, Mercurial interpreta los patrones que usted pase usando +la sintaxis glob (pero usted puede usar expresiones regulares si lo +necesita). + +El filtro \hggopt{-I} puede verse como un ``procese todos los ficheros +que coincidan con este filtro''. +\interaction{filenames.filter.include} +El filtro \hggopt{-X} puede verse como ``procese únicamente los +ficheros que no coincidan con este patrón''. +\interaction{filenames.filter.exclude} + +\section{Ignorar ficheros y directorios no deseados} + +XXX. + +\section{Sensibilidad a mayúsculas} +\label{sec:names:case} + +Si usted está trabajando en un ambiente de desarrollo mixto que +contiene tanto sistemas Linux (u otro Unix) y sistemas Mac o Windows, +debería tener en mente el hecho de que ellos tratan +%TODO FIXME seguir desde aqui, no tengo idea de como traducir case +%sensitivity +case (``N'' versus ``n'') of file names in incompatible ways. This is +not very likely to affect you, and it's easy to deal with if it does, +but it could surprise you if you don't know about it. + +Operating systems and filesystems differ in the way they handle the +\emph{case} of characters in file and directory names. There are +three common ways to handle case in names. +\begin{itemize} +\item Completely case insensitive. Uppercase and lowercase versions + of a letter are treated as identical, both when creating a file and + during subsequent accesses. This is common on older DOS-based + systems. +\item Case preserving, but insensitive. When a file or directory is + created, the case of its name is stored, and can be retrieved and + displayed by the operating system. When an existing file is being + looked up, its case is ignored. This is the standard arrangement on + Windows and MacOS. The names \filename{foo} and \filename{FoO} + identify the same file. This treatment of uppercase and lowercase + letters as interchangeable is also referred to as \emph{case + folding}. +\item Case sensitive. The case of a name is significant at all times. + The names \filename{foo} and {FoO} identify different files. This + is the way Linux and Unix systems normally work. +\end{itemize} + +On Unix-like systems, it is possible to have any or all of the above +ways of handling case in action at once. For example, if you use a +USB thumb drive formatted with a FAT32 filesystem on a Linux system, +Linux will handle names on that filesystem in a case preserving, but +insensitive, way. + +\subsection{Almacenamiento portable y seguro de repositorios} + +El mecanismo de almacenamiento de los repositorios en Mercurial es +\emph{robusto frente a sensibilidad/insensibilidad a mayúsculas}. Los nombres de +fichero son traducidos para que puedan ser almacenados de manera +segura tanto en sistemas sensibles como insensibles a mayúsculas. Esto +significa que usted puede usar herramientas normales de copia de +ficheros para transferir un repositorio Mercurial a, por ejemplo, una +memoria USB, y trasladar de manera segura la memoria y el repositorio +de ida y vuelta entre un Mac, un PC ejecutando Windows, y un sistema +Linux + +\subsection{Detección de conflictos de mayúsculas/minúsculas} + +Al operar en el directorio de trabajo, Mercurial respeta la política +de nombrado del sistema de ficheros en que se encuentre el directorio +de trabajo. Si el sistema de ficheros conserva las diferencias entre +mayúsculas, pero no es sensible a ellas, Mercurial tratará los nombres +que sólo difieren en mayúsculas como uno solo y el mismo. + +Un aspecto importante de este enfoque es que es posible consignar un +conjunto de cambios en un sistema de ficheros sensible a mayúsculas +(típicamente Linux o Unix) que terminará causando problemas para +usuarios en sistemas insensibles a mayúsculas (usualmente en Windows o +MacOS). Si un usuario de Linux consigna cambios a dos ficheros, uno de +ellos llamado \filename{myfile.c} y el otro llamado \filename{MyFile.C}, +ambos serán almacenados correctamente en el repositorio. Y serán +representados correctamente como ficheros separados, en los +directorios de trabajo de otros usuarios de Linux. + +Si un usuario de Windows o Mac jalan este cambio, no tendrán problemas +inicialmente, porque el mecanismo de almacenamiento de Mercurial es +seguro frente a sensibilidad/insensibilidad a mayúsculas. Sin embargo, +una vez que ellos traten de actualizar (\hgcmd{update}) el directorio +de trabajo con ese conjunto de cambios, o hagan fusión (\hgcmd{merge}) +con ese conjunto de cambios, Mercurial verá el conflicto entre los dos +nombres de fichero que el sistema de ficheros trataría como el mismo, +e impedirá que ocurra la actualización o fusión. + +\subsection{Arreglar un conflicto de mayúsculas/minúsculas} + +Si usted está usando Windows o Mac en un entorno mixto donde algunos +de sus colaboradores están usando Linux o Unix, y Mercurial reporta un +conflicto de mayúsculas/minúsculas cuando usted trata de actualizar +(\hgcmd{update}) o fusionar (\hgcmd{merge}), el procedimiento para +arreglar el problema es simple. + +Sólo busque un sistema Linux o Unix cercano, clone el repositorio +problema allí, y use el comando \hgcmd{rename} de Mercurial para +cambiar los nombres de cualquiera de los ficheros o directorios +problemáticos para que no causen más conflictos. Consigne este cambio, +y jálelo (\hgcmd{pull}) o empújelo (\hgcmd{push}) a su sistema Windows +o MacOS, y actualícelo (\hgcmd{update}) a la revisión con los nombres +que ya no generan conflictos. + +El conjunto de cambios con los nombres con conflictos de +mayúsculas/minúsculas permanecerá en el historial de su proyecto, y +usted no podrá actualizar (\hgcmd{update}) su directorio de trabajo a +dicho conjunto de cambios en un sistema Windows o MacOS, pero puede +continuar el desarrollo sin impedimentos. + +\begin{note} + Antes de la versión~0.9.3, Mercurial no usaba un mecanismos seguro + frente a sensibilidad/insensibilidad a mayúsculas o minúsculas, y no + detectaba los conflictos con nombres de ficheros. Si usted está + usando una versión más antigua de Mercurial en Windows o MacOS, le + recomiendo enérgicamente que se actualice. +\end{note} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/fixhtml.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/fixhtml.py Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,1 @@ +../en/fixhtml.py \ No newline at end of file diff -r 73b094b764ec -r 51b5d56744c5 es/fixsvg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/fixsvg Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,1 @@ +../en/fixsvg \ No newline at end of file diff -r 73b094b764ec -r 51b5d56744c5 es/hgbook.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/hgbook.css Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,1 @@ +../en/hgbook.css \ No newline at end of file diff -r 73b094b764ec -r 51b5d56744c5 es/hgext.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/hgext.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,441 @@ +\chapter{Añadir funcionalidad con extensiones} +\label{chap:hgext} + +A pesar de que el corazón de Mercurial es muy completo desde el punto +de vista de funcionalidad, carece de características rimbombantes +deliberadamente. Esta aproximación de preservar la simplicidad +mantiene el programa sencillo tanto para mantenedores como para +usuarios. + +Si embargo Mercurial no le cierra las posibilidades a un conjunto +inflexible de órdenes: usted puede añadir características como +\emph{extensiones} (aveces llamadas \emph{añadidos}\ndt{plugins}). Ya +hemos discutido algunas de estas extensiones en capítulos anteriores: +\begin{itemize} +\item La sección~\ref{sec:tour-merge:fetch} cubre la extensión + \hgext{fetch}; que combina jalar cambios y fusionarlos con los + cambios locales en una sola orden: \hgxcmd{fetch}{fetch}. +\item En el capítulo~\ref{chap:hook}, cubrimos muchas extensiones que + son útiles en funcionalidades relacionadas con ganchos: Los + \hgext{acl} añaden listas de control de acceso; \hgext{bugzilla} + añade integración con el sistema de seguimiento de fallos Bugzilla; y + \hgext{notify} envía notificaciones por correo de nuevos cambios. +\item La extensión de administración de parches MQ es tan invaluable + que amerita dos capítulos y un apéndice por sí misma. + El capítulo~\ref{chap:mq} cubre lo básico; el + capítulo~\ref{chap:mq-collab} discute temas avanzados; y el + apéndice~\ref{chap:mqref} muestra en detalle cada orden. +\end{itemize} + +En este capítulo cubriremos algunas extensiones adicionales +disponibles para Mercurial, y daremos un vistazo a la maquinaria que +necesita conocer en caso de que desee escribir una extensión. +\begin{itemize} +\item En la sección~\ref{sec:hgext:inotify}, discutiremos la + posibilidad de mejorar el desempeño \emph{en gran medida} con la extensión + \hgext{inotify}. +\end{itemize} + +\section{Mejorar el desempeño con la extensión \hgext{inotify}} +\label{sec:hgext:inotify} + +¿Desea lograr que las operaciones más comunmente usadas de Mercurial se +ejecuten centenas de veces más rápido? ¡A leer! + +Mercurial tiene gran desempeño bajo circunstancias normales. Por +ejemplo, cuando ejecuta la orden \hgcmd{status}, Mercurial tiene que +revisar casi todos los ficheros y directorios en su repositorio de +forma que pueda desplegar el estado de los ficheros. Muchas otras +órdenes tienen que hacer tal trabajo tras bambalinas; por ejemplo la +orden \hgcmd{diff} usa la maquinaria de estado para evitar hacer +operaciones de comparación costosas en ficheros que obviamente no han +cambiado. + +Dado que obtener el estado de los ficheros es crucial para obtener +buen desempeño, los autores de Mercurial han optimizado este código en +la medida de lo posible. Sin embargo, no puede obviarse el hecho de +que cuando ejecuta \hgcmd{status}, Mercurial tendrá que hacer por lo +menos una costosa llamada al sistema por cada fichero administrado +para determinar si ha cambiado desde la última vez que se consignó. +Para un repositorio suficientemente grande, puede tardar bastante +tiempo. + +Para mostrar en números la magnitud de este efect, creé un repositorio +que contenía 150.000 ficheros administrador. Tardó diez segundos para +ejecutar \hgcmd{status}, a pesar de que \emph{ninguno} de los ficheros +había sido modificado. + +Muchos sistemas operativos modernos contienen una facilidad de +notificación de ficheros. Si un programa se registra con un servicio +apropiado, el sistema operativo le notificará siempre que un fichero +de interés haya sido creado, modificado o borrado. En sistemas Linux, +el componente del núcleo que lo hace se llama \texttt{inotify}. + +La extensión \hgext{inotify} habla con el componente \texttt{inotify} +del núcleo para optimizar las órdenes de \hgcmd{status}. La extensión +tiene dos componentes. Un daemonio está en el fondo recibiendo +notificaciones del subsistema \texttt{inotify}. También escucha +conexiones de una orden regular de Mercurial. La extensión modifica +el comportamiento de Mercurial de tal forma que, en lugar de revisar +el sistema de ficheros, le pregunta al daemonio. Dado que el daemonio +tiene información perfecta acerca del estado del repositorio, puede +responder instantáneamente con el resultado, evitando la necesidad de +revisar cada directorio y fichero del repositorio. + +Retomando los diez segundos que medí al ejecutar la orden +\hgcmd{status} de Mercurial sobre un repositorio de 150.000 +ficheros. Con la extensión \hgext{inotify} habilitada, el tiempo se +disipó a 0.1~seconds, un factor \emph{cien veces} más rápido. + +Antes de continuar, tenga en cuenta algunos detalles: +\begin{itemize} +\item La extensión \hgext{inotify} es específica de Linux. Porque se + enlaza directamente con el subsistema \texttt{inotify} del núcleo + Linux, no funciona en otros sistemas operativos. +\item Debería funcionar en cualquier distribución Linux a partir de + comienzos del 2005. Las distribuciones más antiguas deben tener un + kernel sin \texttt{inotify}, o una versión de \texttt{glibc} que no + tiene necesariamente el soporte para la interfaz. +\item No todos los sistemas de ficheros pueden usarse con la extensión + \hgext{inotify}. Los sistemas de ficheros tales como NFS no lo + soportan, por ejemplo, si está corriendo Mercurial en vaios + sistemas, montados todos sobre el mismo sistema de ficheros en red. + El sistema \texttt{inotify} del kernel no tiene forma de saber + acerca de los cambios hechos en otro sistema. La mayoría de + sistemas de ficheros locales (p.e.~ext3, XFS, ReiserFS) deberían + funcionar bien. +\end{itemize} + +Hacia mayo de 2007 la extensión \hgext{inotify} no venía de forma +predeterminada en Mercurial\ndt{Desde el 2008 para kernels 2.6 viene + en Mercurial, pero no está activada de forma predeterminada}, y es +un poco más compleja de activar que otras extensiones. Pero la mejora +en el desempeño bien vale la pena! + +La extensión venía en dos partes: un conjunto de parches al código +fuente de Mercurial, y una librería de interfaces de Python hacia el +subsistema \texttt{inotify}. +\begin{note} + Hay \emph{dos} librerías de enlace de Python hacia \texttt{inotify}. + Una de ellas se llama \texttt{pyinotify}, y en algunas + distribuciones de Linux se encuentra como \texttt{python-inotify}. + Esta es la que \emph{no} necesita, puesto que tiene muchos fallos, + y es ineficiente para ser práctica. +\end{note} +Para comenzar, es mejor tener una copia de Mercurial funcional +instalada: +\begin{note} + Si sigue las instrucciones a continuación, estará + \emph{reemplazando} y sobreescribiendo cualquier instalación previa + de Mercurial que pudiera tener, con el código de Mercurial ``más + reciente y peligrosa''. No diga que no se le advirtio! +\end{note} +\begin{enumerate} +\item Clone el repositorio de interfaz entre Python e + \texttt{inotify}. Ármelo e instálelo: + \begin{codesample4} + hg clone http://hg.kublai.com/python/inotify + cd inotify + python setup.py build --force + sudo python setup.py install --skip-build + \end{codesample4} +\item Clone el repositorio \dirname{crew} de Mercurial. Clone el + repositorio de parches de \hgext{inotify} de forma tal que las colas + de Mercurial puedan aplicar los parches sobre el repositorio \dirname{crew}. + \begin{codesample4} + hg clone http://hg.intevation.org/mercurial/crew + hg clone crew inotify + hg clone http://hg.kublai.com/mercurial/patches/inotify inotify/.hg/patches + \end{codesample4} +\item Asegúrese de instalar la extensión Colas de Mercurial + \hgext{mq} y que estén habilitadas. Si nunca ha usado MQ, lea la + sección~\ref{sec:mq:start} para poder comenzar rápidamente. +\item Vaya al repositorio de \dirname{inotify} y aplique todos los + parches de \hgext{inotify} con la opción \hgxopt{mq}{qpush}{-a} de + la orden \hgxcmd{mq}{qpush}. + \begin{codesample4} + cd inotify + hg qpush -a + \end{codesample4} + Si obtiene un mensaje de error de \hgxcmd{mq}{qpush}, no debería + continuar. Mejor pida ayuda. +\item Arme e instale la versión parchada de Mercurial. + \begin{codesample4} + python setup.py build --force + sudo python setup.py install --skip-build + \end{codesample4} +\end{enumerate} +Una vez que haya armado una versión funcional parchada de Mercurial, +todo lo que necesita es habilitar la extensión \hgext{inotify} +colocando una entrada en su \hgrc. +\begin{codesample2} + [extensions] + inotify = +\end{codesample2} +Cuando la extensión \hgext{inotify} esté habilitada, Mercurial +iniciará transparente y automáticamente el daemonio de estado la +primera vez que ejecute un comando que requiera estado del +repositorio. Ejecuta un daemonio de estado por repositorio. + +El daemonio de estado se inicia silenciosamente y se ejecuta en el +fondo. Si mira a la lista de procesos en ejecución después de +habilitar la extensión \hgext{inotify} y ejecuta unos pocos comandos +en diferentes repositorios, verá que hay algunos procesos de +\texttt{hg} por ahí, esperando actualizaciones del kernel y +solicitudes de Mercurial. + +La primera vez que ejecuta un comando de Mercurial en un repositorio +cuando tiene la extensión \hgext{inotify} habilitada, correrá casi con +el mismo desempeño que una orden usual de Mercurial. Esto es debido a +que el estado del daemonio necesita aplicar una búsqueda normal sobre +el estado para poder tener una línea de partida frente a la cual +aplicar posteriormente actualizaciones del núcleo. De todas formas, +\emph{todo} comando posterior que haga cualquier clase de revisión del +estado debería ser notablemente más rápido en repositorios con incluso +un tamaño modesto. Aún mejor, a medida que su repositorio sea más +grande, mejor desempeño verá. El daemonio \hgext{inotify} hace +operaciones de estado de forma casi instantánea en repositorios de +todos los tamaños! + +Si lo desea, puede iniciar manualmente un daemonio de estado con la orden +\hgxcmd{inotify}{inserve}. Esto le da un control un poco más fino +acerca de cómo debería ejecutarse el daemonio. Esta orden solamente +estará disponible cuando haya habilitado la extensión \hgext{inotify}. + +Cuando esté usando la extensión \hgext{inotify}, +\emph{no debería ver diferencia} en el comportamiento de Mercurial, +con la única excepción de que los comandos relacionados con el estado +deberían ejectuarse mucho más rápido que como solían hacerlo. Debería +esperar específicamente que las órdenes no deberían ofrecer salidas +distintas; ni ofrecer resultados diferentes. Si alguna de estas +situaciones ocurre, por favor reporte el fallo. + +\section{Soporte flexible de diff con la extensión \hgext{extdiff}} +\label{sec:hgext:extdiff} + +La orden predeterminada \hgcmd{diff} de Mercurial despliega diffs en +texto plano unificadas. +\interaction{extdiff.diff} +Si dese emplear una herramienta externa para desplegar las +modificaciones, querrá usar la extensión \hgext{extdiff}. Esta le +permitirá usar por ejemplo una herramienta gráfica de diff. + +La extensión \hgext{extdiff} viene con Mercurial, y es fácil +configurar. En la sección \rcsection{extensions} de su \hgrc, +basta con añadir una entrada de una línea para habilitar la extensión. +\begin{codesample2} + [extensions] + extdiff = +\end{codesample2} +Esto introduce una orden llamada \hgxcmd{extdiff}{extdiff}, que de +forma predeterminada usa su orden del sistema \command{diff} para +generar un diff unificado de la misma forma que lo hace el comando +predeterminado \hgcmd{diff}. +\interaction{extdiff.extdiff} +El resultado no será exactamente el mismo que con la orden interna +\hgcmd{diff}, puesto que la salida de \command{diff} varía de un +sistema a otro, incluso pasando las mismas opciones. + +Como lo indican las líneas``\texttt{making snapshot}'', la orden +\hgxcmd{extdiff}{extdiff} funciona creando dos instantáneas de su +árbol de fuentes. La primera instantánea es la revisión fuente; la +segunda es la revisión objetivo del directorio de trabajo. La orden +\hgxcmd{extdiff}{extdiff} genera estas instantáneas en un directorio +temporal, pasa el nombre de cada directorio a un visor de diffs +temporal y borra los directorios temporales. Por cuestiones de +eficiencia solamente genera instantáneas de los directorios y ficheros +que han cambiado entre dos revisiones. + +Los nombres de los directorios de instantáneas tienen los mismos +nombres base de su repositorio. Si su repositorio tiene por ruta +\dirname{/quux/bar/foo}, \dirname{foo} será el nombre de cada +instantánea de directorio. Cada instantánea de directorio tiene sus +identificadores de conjuntos de cambios al final del nombre en caso de +que sea apropiado. Si una instantánea viene de la revisión +\texttt{a631aca1083f}, el directorio se llamará +\dirname{foo.a631aca1083f}. Una instantánea del directorio de trabajo +no tendrá el identificador del conjunto de cambios, y por lo tanto +será solamente \dirname{foo} en este ejemplo. Para ver cómo luce en +la práctica, veamos de nuevo el ejemplo \hgxcmd{extdiff}{extdiff} +antes mencionado. Tenga en cuenta que los diffs tienen los nombres de +las instantáneas de directorio dentro de su encabezado. + +La orden \hgxcmd{extdiff}{extdiff} acepta dos opciones importantes. +La opción \hgxopt{extdiff}{extdiff}{-p} le permite elegir un programa +para ver las diferencias, en lugar de \command{diff}. Con la opción +\hgxopt{extdiff}{extdiff}{-o} puede cambiar las opciones que +\hgxcmd{extdiff}{extdiff} pasa a tal programa (de forma predeterminada +las opciones son``\texttt{-Npru}'', que tienen sentido únicamente si +está usando \command{diff}). En otros aspectos, la orden +\hgxcmd{extdiff}{extdiff} actúa de forma similar a como lo hace la +orden \hgcmd{diff} de Mercurial: usted usa los mismos nombres de +opciones, sintaxis y argumentos para especificar las revisiones y los +ficheros que quiere, y así sucesivamente. + +Por ejemplo, para ejecutar la orden usual del sistema \command{diff}, +para lograr que se generen diferencias de contexto (con la opción +\cmdopt{diff}{-c}) en lugar de diferencias unificadas, y cinco líneas +de contexto en lugar de las tres predeterminadas (pasando \texttt{5} +como argumento a la opción \cmdopt{diff}{-C}). +\interaction{extdiff.extdiff-ctx} + +Es sencillo lanzar unas herramienta usual de diferencias. Para lanzar +el visor \command{kdiff3}: +\begin{codesample2} + hg extdiff -p kdiff3 -o '' +\end{codesample2} + +Si su orden para visualizar diferencias no puede tratar con +directorios, puede usar un poco de scripting para lograrlo. Un +ejemplo de un script con la extensión \hgext{mq} junto con la orden +\command{interdiff} está en la sección~\ref{mq-collab:tips:interdiff}. + +\subsection{Definición de alias de comandos} + +Acordarse de todas las opciones de las órdenes +\hgxcmd{extdiff}{extdiff} y el visor de diferencias de su preferencia +puede ser dispendioso, y por lo tanto la extensión \hgext{extdiff} le +permite definir \emph{nuevas} órdenes que invocarán su visor de +diferencias con las opciones exactas. + +Basta con editar su fichero \hgrc, y añadir una sección llamada +\rcsection{extdiff}. Dentro de esta sección puede definir varias +órdenes. Mostraremos como añadir la orden \texttt{kdiff3}. Después de +definido, puede teclear ``\texttt{hg kdiff3}'' y la extensión a +\hgext{extdiff} ejecutará la orden \command{kdiff3}. +\begin{codesample2} + [extdiff] + cmd.kdiff3 = +\end{codesample2} +Si deja vacía la porción derecha de la definición, como en el ejemplo, +la extensión \hgext{extdiff} usa el nombre de la orden se definirá +como el nombre del programa externo a ejecutar. Pero tales nombres no +tienen por qué ser iguales. Definimos ahora la orden llamada + ``\texttt{hg wibble}'', que ejecuta \command{kdiff3}. +\begin{codesample2} + [extdiff] + cmd.wibble = kdiff3 +\end{codesample2} + +También puede especificar las opciones predeterminadas con las cuales +desea invocar el visor de diferencias. Se usa el prefijo ``\texttt{opts.}'', +seguido por el nombre de la orden a la cual se aplican las opciones. +En este ejemplos se define la orden ``\texttt{hg vimdiff}'' que +ejecuta la extensión \texttt{DirDiff} del editor \command{vim}. +\begin{codesample2} + [extdiff] + cmd.vimdiff = vim + opts.vimdiff = -f '+next' '+execute "DirDiff" argv(0) argv(1)' +\end{codesample2} + +\section{Uso de la extensión \hgext{transplant} para seleccionar} +\label{sec:hgext:transplant} + +Need to have a long chat with Brendan about this. + +\section{Enviar cambios vía correo electrónico con la extensión \hgext{patchbomb}} +\label{sec:hgext:patchbomb} + +Varios proyectos tienen la cultura de ``revisión de cambios'', en la +cual la gente envía sus modificaciones a una lista de correo para que +otros las lean y comenten antes de consignar la versión final a un +repositorio compartido. Algunos proyectos tienen personas que actúan +como cancerberos; ellos aplican los cambios de otras personas a un +repositorio para aquellos que no tienen acceso. + +Mercurial facilita enviar cambios por correo para revisión o +aplicación gracias a su extensión \hgext{patchbomb}. La extensión es +tan popular porque los cambios se formatean como parches y es usual +que se envía un conjunto de cambios por cada correo. Enviar una gran +cantidad de cambios por correos se llama por tanto ``bombardear'' el +buzón de entrada del destinatario, de ahí su nombre ``bombardeo de +parches''. + +Como es usual, la configuración básica de la extensión +\hgext{patchbomb} consta de una o dos líneas en su \hgrc. +\begin{codesample2} + [extensions] + patchbomb = +\end{codesample2} +Cuando haya habilitado la extensión, dispondrá de una nueva orden, +llamada \hgxcmd{patchbomb}{email}. + +La forma mejor y más segura para invocar la orden +\hgxcmd{patchbomb}{email} es ejecutarla \emph{siempre} con la opción +\hgxopt{patchbomb}{email}{-n}; que le mostrará lo que la orden +\emph{enviaría}, sin enviar nada. Una vez que haya dado un vistazo a +los cambios y verificado que está enviando los correctos, puede volver +a ejecutar la misma orden, sin la opción \hgxopt{patchbomb}{email}{-n}. + +La orden \hgxcmd{patchbomb}{email} acepta la misma clase de sintaxis +de revisiones como cualquier otra orden de Mercurial. Por ejemplo, +enviará todas las revisiones entre la 7 y la \texttt{punta}, inclusive. +\begin{codesample2} + hg email -n 7:tip +\end{codesample2} +También puede especificar un \emph{repositorio} para comparar. Si +indica un repositoro sin revisiones, la orden \hgxcmd{patchbomb}{email} +enviará todas las revisiones en el repositorio local que no están +presentes en el repositorio remoto. Si especifica revisiones +adicionalmente o el nombre de una rama (la última con la opción +\hgxopt{patchbomb}{email}{-b}), respetará las revisiones enviadas. + +Ejecutar la orden \hgxcmd{patchbomb}{email} sin los nombres de +aquellas personas a las cuales desea enviar el correo es completamente +seguro: si lo hace, solicitará tales valores de forma interactiva. +(Si está usando Linux o un sistema tipo Unix, tendrá capacidades +estilo--\texttt{readline} aumentadas cuando ingrese tales encabezados, +lo cual es sumamente útil.) + +Cuando envíe una sola revisión, la orden \hgxcmd{patchbomb}{email} +de forma predeterminada usará la primera línea de descripción del +conjunto de cambios como el tema del único mensaje que se enviará. + +Si envía varias revisiones, la orden \hgxcmd{patchbomb}{email} enviará +normalmente un mensaje por conjunto de cambios. Colocará como +prefacio un mensaje introductorio en el cual usted debería describir +el propósito de la serie de cambios que está enviando. + +\subsection{Cambiar el comportamiento de las bombas de parches} + +Cada proyecto tiene sus propias convenciones para enviar cambios en un +correo electrónico; la extensión \hgext{patchbomb} intenta acomodarse +a diferentes variaciones gracias a las opciones de la línea de órdenes: +\begin{itemize} +\item Puede escribir un tema para el mensaje introductorio en la línea + de órdenes con la opciń \hgxopt{patchbomb}{email}{-s}. Toma un + argumento: el tema del mensaje a usar. +\item Para cambiar el correo electrónico del campo del cual se + origina, use la opción \hgxopt{patchbomb}{email}{-f}. Toma un + argumento, el correo electrónico a usar. +\item El comportamiento predeterminado es enviar diferencias + unificadas (consulte la sección~\ref{sec:mq:patch} si desea una + descripción del formato), una por mensaje. Puede enviar un conjunto + binario\ndt{binary bundle} con la opción \hgxopt{patchbomb}{email}{-b}. +\item Las diferencias unificadas están precedidas por un encabezado de + metadatos. Puede omitirlo, y enviar diferencias sin adornos con la + opción \hgxopt{patchbomb}{email}{--plain}. +\item Las diferencias usualmente se envían ``en línea'', como parte + del cuerpo del mensaje con la descripción del parche. Que facilita a + a la mayor cantidad de lectores citar y responder partes de un diff, + dado que algunos clientes de correo solamente citarán la primera + parte MIME del cuerpo de un mensaje. Si prefiere enviar la + descripción y el diff en partes separadas del cuerpo, use la opción + \hgxopt{patchbomb}{email}{-a}. +\item En lugar de enviar mensajes de correo puede escribirlos a un + fichero con formato-\texttt{mbox}- con la opción + \hgxopt{patchbomb}{email}{-m}. La opción recibe un argumento, el + nombre del fichero en el cual escribir. +\item Si desea añadir un resumen con formato-\command{diffstat} en + cada parche, y uno como mensaje introductorio, use la opción + \hgxopt{patchbomb}{email}{-d}. La orden \command{diffstat} + despliega una tabla que contiene el nombre de cada fichero parchado, + el número de líneas afectadas, y un historgrama mostrando cuánto ha + sido modificado cada fichero. Lo cual ofrece a los lectores una + mirada cuantitativa de cuan complejo es el parche. +\end{itemize} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/hook.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/hook.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,1568 @@ +\chapter{Manejo de eventos en repositorios mediante ganchos} +\label{chap:hook} + +Mercurial ofrece un poderoso mecanismo para permitirle a usted +automatizar la ejecución de acciones en respuesta a eventos que +ocurran en un repositorio. En algunos casos, usted puede controlar +incluso la respuesta de Mercurial a dichos eventos. + +Mercurial usa el término \emph{gancho} para identificar estas +acciones. Los ganchos son conocidos como ``disparadores'' en algunos +sistemas de control de revisiones, pero los dos nombres se refieren al +mismo concepto. + +\section{Vistazo general de ganchos en Mercurial} + +A continuación se encuentra una breve lista de los ganchos que +Mercurial soporta. Volveremos a cada uno de estos ganchos con más +detalle después, en la sección~\ref{sec:hook:ref}. + +\begin{itemize} +\item[\small\hook{changegroup}] Es ejecutado luego de que un grupo de + conjuntos de cambios ha sido traído al repositorio desde algún + otro sitio. +\item[\small\hook{commit}] Es ejecutado después de la creación de + un conjunto de cambios en el repositorio local. +\item[\small\hook{incoming}] Es ejecutado una vez por cada conjunto de + cambios traído al repositorio desde otra ubicación. Note la + diferencia respecto al gancho \hook{changegroup}, que es ejecutado + una vez por cada \emph{grupo} de conjuntos de cambios que se + traiga. +\item[\small\hook{outgoing}] Es ejecutado luego de que un grupo de + conjuntos de cambios ha sido transmitido desde el repositorio. +\item[\small\hook{prechangegroup}] Es ejecutado antes de iniciar la + recepción de un grupo de conjuntos de cambios en el repositorio. +\item[\small\hook{precommit}] De control. Es ejecutado antes de + iniciar una consignación. +\item[\small\hook{preoutgoing}] De control. Es ejecutado antes de + iniciar la transmisión de un grupo de conjuntos de cambios desde + el repositorio. +\item[\small\hook{pretag}] De control. Es ejecutado antes de crear una + etiqueta. +\item[\small\hook{pretxnchangegroup}] De control. Es ejecutado después + de haber recibido un grupo de conjuntos de cambios en el + repositorio local, pero antes de que la transacción se complete y + los cambios sean permanentes dentro del repositorio. +\item[\small\hook{pretxncommit}] De control. Es ejecutado luego de la + creación de un conjunto de cambios en el repositorio local, pero + antes de que la transacción que hace permanente el cambio sea + completada. +\item[\small\hook{preupdate}] De control. Es ejecutado antes de + iniciar una actualización o fusión en el directorio de trabajo. +\item[\small\hook{tag}] Es ejecutado después de la creación de una + etiqueta. +\item[\small\hook{update}] Es ejecutado después de que termina una + actualización o una fusión. +\end{itemize} +Cada uno de los ganchos cuya descripción empieza con la frase +``de control'' tiene la facultad de determinar si una actividad puede +continuar. Si el gancho se ejecuta con éxito, la actividad puede +continuar; si falla, o bien la actividad no es permitida, o se +deshacen los cambios que se puedan haber llevado a cabo, dependiendo +del gancho involucrado. + +\section{Ganchos y seguridad} + +\subsection{Los ganchos se ejecutan con sus privilegios de usuario} + +Cuando usted ejecuta un comando de Mercurial en un repositorio, y el +comando causa la ejecución de un gancho, dicho gancho se ejecuta en +\emph{su} sistema, en \emph{su} cuenta de usuario, con \emph{sus} +privilegios. Ya que los ganchos son elementos arbitrarios de código +ejecutable, usted debería tratarlos con un nivel adecuado de +desconfianza. No instale un gancho a menos en que confíe en quien lo +creó y en lo que el gancho hace. + +En algunos casos, usted puede estar expuesto a ganchos que usted no +%TODO acá introduzco algo de texto por mi cuenta, por claridad +instaló. Si usted usa Mercurial en un sistema extraño, tenga en cuenta +que Mercurial ejecutará los ganchos definidos en el fichero \hgrc. + +Si está trabajando con un repositorio propiedad de otro usuario, +Mercurial podrá ejecutar los ganchos definidos en el repositorio de +dicho usuario, pero los ejecutará como ``usted''. Por ejemplo, si +usted jala (\hgcmd{pull}) desde ese repositorio, y el +\sfilename{.hg/hgrc} define un gancho saliente (\hook{outgoing}), +dicho gancho se ejecuta bajo su cuenta de usuario, aun cuando usted no +es el propietario del repositorio. + +\begin{note} + Esto sólo aplica si usted está jalando desde un repositorio en un + sistema de ficheros local o de red. Si está jalando a través de http + o ssh, cualquier gancho saliente (\hook{outgoing}) se ejecutará bajo + la cuenta que está ejecutando el proceso servidor, en el servidor. +\end{note} + +XXX Para ver qué ganchos han sido definidos en un repositorio, use el +comando \hgcmdargs{config}{hooks}. Si usted está trabajando en un +repositorio, pero comunicándose con otro que no le pertenece +(por ejemplo, usando \hgcmd{pull} o \hgcmd{incoming}), recuerde que +los ganchos que debe considerar son los del otro repositorio, no los +del suyo. + +\subsection{Los ganchos no se propagan} + +En Mercurial, no se hace control de revisiones de los ganchos, y no se +propagan cuando usted clona, o jala de, un repositorio. El motivo para +esto es simple: un gancho es código ejecutable arbitrario. Se ejecuta +bajo su identidad, con su nivel de privilegios, en su máquina. + +Sería extremadamente descuidado de parte de cualquier sistema +distribuido de control de revisiones el implementar control de +revisiones para ganchos, ya que esto ofrecería maneras fácilmente +%TODO subvertir +aprovechables de subvertir las cuentas de los usuarios del sistema de +control de revisiones. + +Ya que Mercurial no propaga los ganchos, si usted está colaborando con +otras personas en un proyecto común, no debería asumir que ellos están +usando los mismos ganchos para Mercurial que usted usa, o que los de +ellos están configurado correctamente. Usted debería documentar los +ganchos que usted espera que la gente use. + +En una intranet corporativa, esto es algo más fácil de manejar, ya que +usted puede, por ejemplo, proveer una instalación ``estándar'' de +Mercurial en un sistema de ficheros NFS, y usar un fichero \hgrc\ +global para definir los ganchos que verán todos los usuarios. Sin +embargo, este enfoque tiene sus límites; vea más abajo. + +\subsection{Es posible hacer caso omiso de los ganchos} + +Mercurial le permite hacer caso omiso de la deficinión de un gancho, +a través de la redefinición del mismo. Usted puede deshabilitar el +gancho fijando su valor como una cadena vacía, o cambiar su +comportamiento como desee. + +Si usted instala un fichero \hgrc\ a nivel de sistema o sitio completo +que define algunos ganchos, debe entender que sus usuarios pueden +deshabilitar o hacer caso omiso de los mismos. + +\subsection{Asegurarse de que ganchos críticos sean ejecutados} + +Algunas veces usted puede querer hacer respetar una política, y no +permitir que los demás sean capaces de evitarla. Por ejemplo, usted +puede tener como requerimiento que cada conjunto de cambios debe pasar +un riguroso conjunto de pruebas. Definir este requerimientos a través +de un gancho en un fichero \hgrc\ global no servirá con usuarios +remotos en computadoras portátiles, y por supuesto que los usuarios +locales pueden evitar esto a voluntad haciendo caso omiso del gancho. + +En vez de eso, usted puede definir las políticas para usar Mercurial +de tal forma que se espere que los usuarios propaguen los cambios a +través de un servidor ``canónico'' bien conocido que usted ha +asegurado y configurado apropiadamente. + +Una manera de hacer esto es a través de una combinación de ingeniería +social y tecnología. Cree una cuenta de acceso restringido; los +usuarios pueden empujar cambios a través de la red a los repositorios +administrados por esta cuenta, pero no podrán ingresar a dicha cuenta +para ejecutar órdenes en el intérprete de comandos. En este escenario, +un usuario puede enviar un conjunto de cambios que contenga la +porquería que él desee. + +Cuando alguien empuja un conjunto de cambios al servidor del que todos +jalan, el servidor probará el conjunto de cambios antes de aceptarlo +como permanente, y lo rechazará si no logra pasar el conjunto de +pruebas. Si la gente sólo jala cambios desde este servidor de filtro, +servirá para asegurarse de que todos los cambios que la gente jala han +sido examinados automáticamente + +\section{Precauciones con ganchos \texttt{pretxn} en un repositorio de +acceso compartido} + +Si usted desea usar ganchos para llevar a cabo automáticamente algún +trabajo en un repositorio al que varias personas tienen acceso +compartido, debe tener cuidado con la forma de hacerlo. + +Mercurial sólo bloquea un repositorio cuando está escribiendo al +mismo, y sólo las partes de Mercurial que escriben al repositorio le +prestan atención a los bloqueos. Los bloqueos de escritura son +necesarios para evitar que múltiples escritores simultáneos +interfieran entre sí, corrompiendo el repositorio. + +Ya que Mercurial tiene cuidado con el orden en que lee y escribe +datos, no necesita adquirir un bloqueo cuando desea leer datos del +repositorio. Las partes de Mercurial que leen del repositorio nunca le +prestan atención a los bloqueos. Este esquema de lectura libre de +bloqueos incremententa en gran medida el desempeño y la concurrencia. + +Sin embargo, para tener un gran desempeño es necesario hacer +sacrificios, uno de los cuales tiene el potencial de causarle +problemas a menos de que usted esté consciente de él. Describirlo +requiere algo de detalle respecto a cómo Mercurial añade conjuntos de +cambios al repositorio y cómo lee esos cambios de vuelta. + +Cuando Mercurial \emph{escribe} metadatos, los escribe directamente en +el fichero de destino. Primero escribe los datos del fichero, luego +los datos del manifiesto (que contienen punteros a los nuevos datos +del fichero), luego datos de la bitácora de cambios (que contienen +punteros a los nuevos datos del manifiesto). Antes de la primera +escritura a cada fichero, se guarda un registro de dónde estaba el +final de fichero en su registro de transacciones. Si la transacción +debe ser deshecha, Mercurial simplemente trunca cada fichero de vuelta +al tamaño que tenía antes de que empezara la transacción. + +Cuando Mercurial \emph{lee} metadatos, lee la bitácora de cambios +primero, y luego todo lo demás. Como un lector sólo accederá a las +partes del manifiesto o de los metadatos de fichero que él puede ver +en la bitácora de cambios, nunca puede ver datos parcialmente +escritos. + +Algunos ganchos de control (\hook{pretxncommit} y +\hook{pretxnchangegroup}) se ejecutan cuando una transacción está casi +completa. Todos los metadatos han sido escritos, pero Mercurial aún +puede deshacer la transacción y hacer que los datos recién escritos +desaparezcan. + +Si alguno de estos ganchos permanece en ejecución por mucho tiempo, +abre una ventana de tiempo en la que un lector puede ver los metadatos +de conjuntos de cambios que aún no son permanentes y que no debería +considerarse que estén ``realmante ahí''. Entre más tiempo tome la +ejecución del gancho, más tiempo estará abierta esta ventana. + +\subsection{Ilustración del problema} + +En principio, un buen uso del gancho \hook{pretxnchangegroup} sería +ensamblar y probar automáticamente todos los cambios entrantes antes +de que sean aceptados en un repositorio central. Esto le permitiría a +usted garantizar que nadie pueda empujar cambios que ``rompan el +ensamblaje''. Pero si un cliente puede jalar cambios mientras están +siendo probados, la utilidad de esta prueba es nula; alguien confiado +puede jalar cambios sin probar, lo que potencialmente podría romper su +proceso de ensamblaje. + +La respuesta técnica más segura frente a este retos es montar dicho +repositorio ``guardián'' como \emph{unidireccional}. Permita que +reciba cambios desde el exterior, pero no permita que nadie jale +cambios de él (use el gancho \hook{preoutgoing} para bloquear esto). +Configure un gancho \hook{changegroup} para que si el ensamblaje o +prueba tiene éxito, el gancho empuje los nuevos cambios a otro +repositorio del que la gente \emph{pueda} jalar. + +En la práctica, montar un cuello de botella centralizado como éste a +menudo no es una buena idea, y la visibilidad de las transacciones no +tiene nada que ver con el problema. A medida que el tamaño de un +proyecto---y el tiempo que toma ensamblarlo y probarlo---crece, usted +se acerca rápidamente a un límite con este enfoque ``pruebe antes de +comprar'', en el que tiene más conjuntos de cambios a probar que +tiempo para ocuparse de ellos. El resultado inevitable es frustración +para todos los que estén involucrados. + +Una aproximación que permite manejar mejor el crecimiento es hacer que +la gente ensamble y pruebe antes de empujar, y ejecutar el ensamble y +pruebas automáticas centralmente \emph{después} de empujar, para +asegurarse de que todo esté bien. La ventaja de este enfoque es que no +impone un límite a la rata en la que un repositorio puede aceptar +cambios. + +\section{Tutorial corto de uso de ganchos} +\label{sec:hook:simple} + +Escribir un gancho para Mercurial es fácil. Empecemos con un gancho +que se ejecute cuando usted termine un \hgcmd{commit}, y simplemente +muestre el hash del conjunto de cambios que usted acaba de crear. El +gancho se llamará \hook{commit}. + +\begin{figure}[ht] + \interaction{hook.simple.init} + \caption{Un gancho simple que se ejecuta al hacer la consignación de + un conjunto de cambios} + \label{ex:hook:init} +\end{figure} + +Todos los ganchos siguen el patrón del ejemplo~\ref{ex:hook:init}. +Usted puede añadir una entrada a la sección \rcsection{hooks} de su +fichero \hgrc. A la izquierda está el nombre del evento respecto al +cual dispararse; a la derecha está la acción a llevar a cabo. Como +puede ver, es posible ejecutar cualquier orden de la línea de comandos +en un gancho. Mercurial le pasa información extra al gancho usando +variables de entorno (busque \envar{HG\_NODE} en el ejemplo). + +\subsection{Llevar a cabo varias acciones por evento} + +A menudo, usted querrá definir más de un gancho para un tipo de evento +particular, como se muestra en el ejemplo~\ref{ex:hook:ext}. +Mercurial le permite hacer esto añadiendo una \emph{extensión} al +final del nombre de un gancho. Usted extiende el nombre del gancho +%TODO Yuk, no me gusta ese "parada completa" +poniendo el nombre del gancho, seguido por una parada completa (el +caracter ``\texttt{.}''), seguido de algo más de texto de su elección. +Por ejemplo, Mercurial ejecutará tanto \texttt{commit.foo} como +\texttt{commit.bar} cuando ocurra el evento \texttt{commit}. + +\begin{figure}[ht] + \interaction{hook.simple.ext} + \caption{Definición de un segundo gancho \hook{commit}} + \label{ex:hook:ext} +\end{figure} + +Para dar un orden bien definido de ejecución cuando hay múltiples +ganchos definidos para un evento, Mercurial ordena los ganchos de +acuerdo a su extensión, y los ejecuta en dicho orden. En el ejemplo de +arribam \texttt{commit.bar} se ejecutará antes que +\texttt{commit.foo}, y \texttt{commit} se ejecutará antes de ambos. + +Es una buena idea usar una extensión descriptiva cuando usted define +un gancho. Esto le ayudará a recordar para qué se usa el gancho. Si el +gancho falla, usted recibirá un mensaje de error que contiene el +nombre y la extensión del gancho, así que usar una extensión +descriptiva le dará una pista inmediata de porqué el gancho falló (vea +un ejemplo en la sección~\ref{sec:hook:perm}). + +\subsection{Controlar cuándo puede llevarse a cabo una actividad} +\label{sec:hook:perm} + +En los ejemplos anteriores, usamos el gancho \hook{commit}, que es +ejecutado después de que se ha completado una consignación. Este es +uno de los varios ganchos que Mercurial ejecuta luego de que una +actividad termina. Tales ganchos no tienen forma de influenciar la +actividad como tal. + +Mercurial define un número de eventos que ocurren antes de que una +actividad empiece; o luego de que empiece, pero antes de que termine. +Los ganchos que se disparan con estos eventos tienen la capacidad +adicional de elegir si la actividad puede continuar, o si su ejecución +es abortada. + +El gancho \hook{pretxncommit} se ejecuta justo antes de que una +consignación se ejecute. En otras palabras, los metadatos que +representan el conjunto de cambios han sido escritos al disco, pero no +se ha terminado la transacción. El gancho \hook{pretxncommit} tiene la +capacidad de decidir si una transacción se completa, o debe +deshacerse. + +Si el gancho \hook{pretxncommit} termina con un código de salida de +cero, se permite que la transacción se complete; la consignación +termina; y el gancho \hook{commit} es ejecutado. Si el gancho +\hook{pretxncommit} termina con un código de salida diferente de cero, +la transacción es revertida; los metadatos representando el conjunto +de cambios son borrados; y el gancho \hook{commit} no es ejecutado. + +\begin{figure}[ht] + \interaction{hook.simple.pretxncommit} + \caption{Uso del gancho \hook{pretxncommit} para controlar consignaciones} + \label{ex:hook:pretxncommit} +\end{figure} + +El gancho en el ejemplo~\ref{ex:hook:pretxncommit} revisa si el +mensaje de consignación contiene el ID de algún fallo. Si lo contiene, +la consignación puede continuar. Si no, la consignación es cancelada. + +\section{Escribir sus propios ganchos} + +Cuando usted escriba un gancho, puede encontrar útil el ejecutar +Mercurial o bien pasándole la opción \hggopt{-v}, o con el valor de +configuración \rcitem{ui}{verbose} fijado en ``true'' (verdadero). +Cuando lo haga, Mercurial imprimirá un mensaje antes de llamar cada +gancho. + +\subsection{Escoger cómo debe ejecutarse su gancho} +\label{sec:hook:lang} + +Usted puede escribir un gancho que funcione como un programa normal +---típicamente un guión de línea de comandos---o como una función de +Python que se ejecuta dentro del proceso Mercurial. + +Escribir un gancho como un programa externo tiene la ventaja de que no +requiere ningún conocimiento del funcionamiento interno de Mercurial. +Usted puede ejecutar comandos Mercurial normales para obtener la +informción extra que pueda necesitar. La contraparte de esto es que +los ganchos externos son más lentos que los ganchos internos +ejecutados dentro del proceso. + +Un gancho Python interno tiene acceso completo a la API de Mercurial, +y no se ``externaliza'' a otro proceso, así que es inherentemente más +rápido que un gancho externo. Adicionalmente es más fácil obtener la +mayoría de la información que un gancho requiere a través de llamadas +directas a la API de Mercurial que hacerlo ejecutando comandos +Mercurial. + +Si se siente a gusto con Python, o requiere un alto desempeño, +escribir sus ganchos en Python puede ser una buena elección. Sin +embargo, cuando usted tiene un gancho bastante directo por escribir y +no le importa el desempeño (el caso de la mayoría de los ganchos), es +perfectamente admisible un guión de línea de comandos. + +\subsection{Parámetros para ganchos} +\label{sec:hook:param} + +Mercurial llama cada gancho con un conjunto de paŕametros bien +definidos. En Python, un parámetro se pasa como argumento de palabra +clave a su función de gancho. Para un programa externo, los parámetros +son pasados como variables de entornos. + +Sin importar si su gancho está escrito en Python o como guión de línea +de comandos, los nombres y valores de los parámetros específicos de +los ganchos serán los mismos. Un parámetro booleano será representado +como un valor booleano en Python, pero como el número 1 (para +``verdadero'') o 0 (para falso) en una variable de entorno para un +gancho externo. Si un parámetro se llama \texttt{foo}, el argumento de +palabra clave para un gancho en Python también se llamará +\texttt{foo}, mientras que la variable de entorno para un gancho +externo se llamará \texttt{HG\_FOO}. + +\subsection{Valores de retorno de ganchos y control de actividades} + +Un gancho que se ejecuta exitosamente debe terminar con un código de +salida de cero, si es externo, o retornar el valor booleano +``falso'', si es interno. Un fallo se indica con un código de salida +diferente de cero desde un gancho externo, o un valor de retorno +booleano ``verdadero''. Si un gancho interno genera una excepción, se +considera que el gancho ha fallado. + +Para los ganchos que controlan si una actividad puede continuar o no, +cero/falso quiere decir ``permitir'', mientras que +% TODO me suena mejor "no permitir" que "denegar" +no-cero/verdadero/excepción quiere decir ``no permitir''. + +\subsection{Escribir un gancho externo} + +Cuando usted define un gancho externo en su fichero \hgrc\ y el mismo +es ejecutado, dicha definición pasa a su intérprete de comandos, que +hace la interpretación correspondiente. Esto significa que usted puede +usar elementos normales del intérprete en el cuerpo del gancho. + +Un gancho ejecutable siempre es ejecutado con su directorio actual +fijado al directorio raíz del repositorio. + +Cada parámetro para el gancho es pasado como una variable de entorno; +el nombre está en mayúsculas, y tiene como prefijo la cadena +``\texttt{HG\_}''. + +Con la excepción de los parámetros para los ganchos, Mercurial no +define o modifica ninguna variable de entorno al ejecutar un gancho. +Es útil recordar esto al escribir un gancho global que podría ser +ejecutado por varios usuarios con distintas variables de entorno +fijadas. En situaciones con múltiples usuarios, usted no debería +asumir la existencia de ninguna variable de entorno, ni que sus +valores sean los mismos que tenían cuando usted probó el gancho en su +ambiente de trabajo. + +\subsection{Indicar a Mercurial que use un gancho interno} + +La sintaxis para definir un gancho interno en el fichero \hgrc\ es +ligeramente diferente de la usada para un gancho externo. El valor del +gancho debe comenzar con el texto ``\texttt{python:}'', y continuar +con el nombre completamente cualificado de un objeto invocable que se +usará como el valor del gancho. + +El módulo en que vive un gancho es importado automáticamente cuando se +ejecuta un gancho. Siempre que usted tenga el nombre del módulo y la +variable de entorno \envar{PYTHONPATH} ajustada adecuadamente, todo +debería funcionar sin problemas. + +El siguiente fragmento de ejemplo de un fichero \hgrc\ ilustra la +sintaxis y significado de los conceptos que acabamos de describir. +\begin{codesample2} + [hooks] + commit.example = python:mymodule.submodule.myhook +\end{codesample2} +Cuando Mercurial ejecuta el gancho \texttt{commit.example}, importa +\texttt{mymodule.submodule}, busca el objeto invocable llamado +\texttt{myhook}, y lo invoca (llama). + +\subsection{Escribir un gancho interno} + +El gancho interno más sencillo no hace nada, pero ilustra la +estructura básica de la API\ndt{\emph{Application Progamming +Interface}, Interfaz para Programación de Aplicaciones} para ganchos: +\begin{codesample2} + def myhook(ui, repo, **kwargs): + pass +\end{codesample2} +El primer argumento para un gancho Python siempre es un objeto +\pymodclass{mercurial.ui}{ui}. El segundo es un objeto repositorio; +de momento, siempre es una instancia de +\pymodclass{mercurial.localrepo}{localrepository}. Después de estos +dos argumentos están los argumentos de palabra clave. Los argumentos +que se pasen dependerán del tipo de gancho que se esté llamando, pero +un gancho siempre puede ignorar los argumentos que no le interesen, +relegándolos a un diccionario de argumentos por palabras clave, como se +hizo arriba con \texttt{**kwargs}. + +\section{Ejemplos de ganchos} + +\subsection{Escribir mensajes de consignación significativos} + +Es difícil de imaginar un mensaje de consignación útil y al mismo +tiempo muy corto. El simple gancho \hook{pretxncommit} de la +figura~\ref{ex:hook:msglen.go} evitará que usted consigne un conjunto +de cambios con un mensaje de menos de 10 bytes de longitud. + +\begin{figure}[ht] + \interaction{hook.msglen.go} + \caption{Un gancho que prohíbe mensajes de consignación demasiado + cortos} + \label{ex:hook:msglen.go} +\end{figure} + +\subsection{Comprobar espacios en blanco finales} + +Un uso interesante para ganchos relacionados con consignaciones es +ayudarle a escribir código más limpio. Un ejemplo simple de +%TODO dictum => regla +``código más limpio'' es la regla de que un cambio no debe añadir +líneas de texto que contengan ``espacios en blanco finales''. El +espacio en blanco final es una serie de caracteres de espacio y +tabulación que se encuentran al final de una línea de texto. En la +mayoría de los casos, el espacio en blanco final es innecesario, ruido +invisible, pero ocasionalmente es problemático, y la gente en general +prefiere deshacerse de él. + +Usted puede usar cualquiera de los ganchos \hook{precommit} o +\hook{pretxncommit} para revisar si tiene el problema de los espacios +en blanco finales. Si usa el gancho \hook{precommit}, el gancho no +sabrá qué ficheros se están consignando, por lo que se tendrá que +revisar cada fichero modificado en el repositorio para ver si tiene +espacios en blanco finales. Si usted sólo quiere consignar un cambio +al fichero \filename{foo}, y el fichero \filename{bar} contiene +espacios en blanco finales, hacer la revisión en el gancho +\hook{precommit} evitará que usted haga la consignación de +\filename{foo} debido al problem en \filename{bar}. Este no parece el +enfoque adeucado. + +Si usted escogiera el gancho \hook{pretxncommit}, la revisión no +ocurriría sino hasta justo antes de que la transacción para la +consignación se complete. Esto le permitirá comprobar por posibles +problemas sólo en los ficheros que serán consignados. Sin embargo, si +usted ingresó el mensaje de consignación de manera interactiva y el +%TODO roll-back +gancho falla, la transacción será deshecha; usted tendrá que +reingresar el mensaje de consignación luego de que corrija el problema +con los espacios en blanco finales y ejecute \hgcmd{commit} de nuevo. + +\begin{figure}[ht] + \interaction{hook.ws.simple} + \caption{Un gancho simple que revisa si hay espacios en blanco + finales} + \label{ex:hook:ws.simple} +\end{figure} + +La figura~\ref{ex:hook:ws.simple} presenta un gancho +\hook{pretxncommit} simple que comprueba la existencia de espacios en +blanco finales. Este gancho es corto, pero no brinda mucha ayuda. +Termina con un código de salida de error si un cambio añade una línea +con espacio en blanco final a cualquier fichero, pero no muestra +ninguna información que pueda ser útil para identificar el fichero o +la línea de texto origen del problema. También tiene la agradable +propiedad de no prestar atención a las líneas que no sufrieron +modificaciones; sólo las líneas que introducen nuevos espacios en +blanco finales causan problemas. + +\begin{figure}[ht] + \interaction{hook.ws.better} + \caption{Un mejor gancho para espacios en blanco finales} + \label{ex:hook:ws.better} +\end{figure} + +El ejemplo de la figura~\ref{ex:hook:ws.better} es mucho más complejo, +pero también más útil. El gancho procesa un diff unificado para +revisar si alguna línea añade espacios en blanco finales, e imprime el +nombre del fichero y el número de línea de cada ocurrencia. Aún mejor, +si el cambio añade espacios en blanco finales, este gancho guarda el +mensaje de consignación e imprime el nombre del fichero en el que el +mensaje fue guardado, antes de terminar e indicarle a Mercurial que +deshaga la transacción, para que uste pueda usar +\hgcmdargs{commit}{\hgopt{commit}{-l}~\emph{nombre\_fichero}} para +reutilizar el mensaje de consignación guardado anteriormente, una vez +usted haya corregido el problema. + +Como anotación final, note en la figura~\ref{ex:hook:ws.better} el +%TODO on-site => in-situ ? +uso de la característica de edición \emph{in-situ} de \command{perl} +para eliminar los espacios en blanco finales en un fichero. Esto es +lo suficientemente conciso y poderoso para que lo presente aquí. +% TODO corregí el backslash, y comprobé por mi cuenta un archivo +% aparte, y el comando hace lo que debe hacer. Favor copiar del pdf el +% comando perl y comprobar con un archivo con espacios en blanco +% finales, y si todo está bien (que debería), borrar esta nota +\begin{codesample2} + perl -pi -e 's,\textbackslash{}s+\$,,' nombre\_fichero +\end{codesample2} + +\section{Ganchos adicionales} + +Mercurial se instala con varios ganchos adicionales. Usted puede +encontrarlos en el directorio \dirname{hgext} del árbol de ficheros +fuente de Mercurial. Si usted está usando un paquete binario de +Mercurial, los ganchos estarán ubicados en el directorio +\dirname{hgext} en donde su instalador de paquetes haya puesto a +Mercurial. + +\subsection{\hgext{acl}---control de acceso a partes de un repositorio} + +La extensión \hgext{acl} le permite controlar a qué usuarios remotos +les está permitido empujar conjuntos de cambios a un servidor en red. +Usted puede proteger cualquier porción de un repositorio (incluyendo +el repositorio completo), de tal manera que un usuario remoto +específico pueda empujar cambios que no afecten la porción protegida. + +Esta extensión implementa control de acceso basado en la identidad del +usuario que empuja los conjuntos de cambios, \emph{no} en la identidad +de quien hizo la consignación de los mismos. Usar este gancho tiene +sentido sólo si se tiene un servidor adecuadamente asegurado que +autentique a los usuarios remotos, y si usted desea segurarse de que +sólo se le permita a ciertos usuarios empujar cambios a dicho +servidor. + +\subsubsection{Configuración del gancho \hook{acl}} + +Para administrar los conjuntos de cambios entrantes, se debe usar el +gancho \hgext{acl} como un gancho de tipo \hook{pretxnchangegroup}. +Esto le permite ver qué ficheros son modificados por cada conjunto de +%TODO rollback => "deshacer el efecto" +cambios entrante, y deshacer el efecto de un grupo de conjuntos de +cambios si alguno de ellos modifica algún fichero ``prohibido''. +Ejemplo: +\begin{codesample2} + [hooks] + pretxnchangegroup.acl = python:hgext.acl.hook +\end{codesample2} + +La extensión \hgext{acl} es configurada mediante tres secciones. + +La sección \rcsection{acl} sólo tiene una entrada, +\rcitem{acl}{sources}\ndt{Fuentes.}, que lista las fuentes de los +conjuntos de cambios entrantes a las que el gancho debe prestar +atención. Usualmente usted no necesita configurar esta sección. +\begin{itemize} + \item[\rcitem{acl}{serve}] Controlar conjuntos de + cambios entrantes que están llegando desde un repositorio a + través de http o ssh. Este es el valor por defecto de + \rcitem{acl}{sources}, y usualmente es el único valor de + configuración que necesitará para este ítem. +\item[\rcitem{acl}{pull}] Controlar conjuntos de cambios entrantes que + lleguen vía un pull (jalado) desde un repositorio local. +\item[\rcitem{acl}{push}] Controlar conjuntos de cambios entrantes que + lleguen vía un push (empuje) desde un repositorio local. +\item[\rcitem{acl}{bundle}] Controlar conjuntos de cambios entrantes + %TODO bundle + que lleguen desde otro repositorio a través de un paquete. +\end{itemize} + +La sección \rcsection{acl.allow} controla los usuarios a los que les +está permitido añadir conjuntos de cambios al repositorio. Si esta +sección no está presente, se le permite acceso a todos los usuarios +excepto a los que se les haya negado explícitamente el acceso. Si +esta sección no está presente, se niega el acceso a todos los usuarios +excepto a todos a los que se les haya permitido de manera explícita +(así que una sección vacía implica que se niega el acceso a todos los +usuarios). + +La sección \rcsection{acl.deny} determina a qué usuarios no se les +permite añadir conjuntos de cambios al repositorio. Si esta sección no +está presente o está vacía, no se niega el acceso a ningún usuario. + +La sintaxis para los ficheros \rcsection{acl.allow} y +\rcsection{acl.deny} es idéntica. A la izquierda de cada entrada se +encuentra un patrón glob que asocia ficheros o directorios, respecto a +la raíz del repositorio; a la derecha, un nombre usuario. + +En el siguiente ejemplo, el usuario \texttt{escritordoc} sólo puede +empujar cambios al directorio \dirname{docs} del repositorio, mientras +que \texttt{practicante} puede enviar cambios a cualquier fichero o +directorio excepto \dirname{fuentes/sensitivo}. +\begin{codesample2} + [acl.allow] + docs/** = escritordoc + + [acl.deny] + fuentes/sensitivo/** = practicante +\end{codesample2} + +\subsubsection{Pruebas y resolución de problemas} + +Si usted desea probar el gancho \hgext{acl}, ejecútelo habilitando la +opción de salida de depuración habilitada. Ya que usted probablemente +lo estará ejecutando en un servidor donde no es conveniente (o incluso +posible) pasar la opción \hggopt{--debug}, no olvide que usted puede +habilitar la salida de depuración en su \hgrc: +\begin{codesample2} + [ui] + debug = true +\end{codesample2} +Con esto habilitado, el gancho \hgext{acl} imprimirá suficiente +información para permitirle saber porqué está permitiendo o denegando +la operación de empujar a usuarios específicos. + +\subsection{\hgext{bugzilla}---integración con Bugzilla} + +La extensión \hgext{bugzilla} añade un comentario a un fallo Bugzilla +siempre que encuentre una referencia al ID de dicho fallo en un +mensaje de consignación. Usted puede instalar este gancho en un +servidor compartido, para que cada vez que un usuario remoto empuje +cambios al servidor, el gancho sea ejecutado. + +Se añade un comentario al fallo que se ve así (usted puede configurar +los contenidos del comentario---vea más abajo): +%TODO traducir? +\begin{codesample2} + Changeset aad8b264143a, made by Joe User in + the frobnitz repository, refers to this bug. + + For complete details, see + http://hg.domain.com/frobnitz?cmd=changeset;node=aad8b264143a + + Changeset description: + Fix bug 10483 by guarding against some NULL pointers +\end{codesample2} +El valor de este gancho se encuentra en que automatiza el proceso de +actualizar un fallo cuando un conjunto de cambios se refiera a él. Si +usted configura este gancho adecuadamente, hará fácil para la gente +navegar directamente desde un fallo Bugzilla a un conjunto de cambios +que se refiere a ese fallo. + +Usted puede usar el código de este gancho como un punto de partida +para otras recetas de integración con Bugzilla aún más exóticas. Acá +hay algunas posibilidades: +\begin{itemize} +\item Requerir que cada conjunto de cambios tenga un ID de fallo en su + mensaje de consignación. En este caso, usted querrá configurar el + gancho como uno de tipo \hook{pretxncommit}. Esto le permitirá al + gancho rechazar cambios que no contiene IDs de fallos. +\item Permitir a los conjuntos de cambios entrantes modificar + automáticamente el \emph{estado} de un fallo, así como simplemente + añadir un comentario. Por ejemplo, el gancho podría reconocer la + cadena ``corregido fallo 31337'' como la señal de que debería + actualizar el estado del fallo 31337 a ``requiere pruebas''. +\end{itemize} + +\subsubsection{Configuración del gancho \hook{bugzilla}} +\label{sec:hook:bugzilla:config} + +Usted debería configurar este gancho en el \hgrc\ de su servidor como +un gancho \hook{incoming}\ndt{Entrante.}, por ejemplo como sigue: +\begin{codesample2} + [hooks] + incoming.bugzilla = python:hgext.bugzilla.hook +\end{codesample2} + +Debido a la naturaleza especializada de este gancho, y porque Bugzilla +no fue escrito con este tipo de integración en mente, configurar este +% TODO involved => complejo ? no intarwebs here :( +gancho es un proceso algo complejo. + +Antes de empezar, usted debe instalar la interfaz de Python para MySQL +en los sistemas en los que se vaya a ejecutar el gancho. Si no está +disponible como paquete binario para su sistema, usted puede descargar +el paquete desde~\cite{web:mysql-python}. + +La información para configurar este gancho se ubica en la sección +\rcsection{bugzilla} de su \hgrc. +\begin{itemize} +\item[\rcitem{bugzilla}{version}] La versión de Bugzilla instalada en + el servidor. El esquema de base de datos que Bugzilla usa cambia + ocasionalmente, así que este gancho debe saber exactamente qué + esquema usar. A la fecha, la única versión soportada es la + \texttt{2.16}. +\item[\rcitem{bugzilla}{host}] El nombre de máquina (\emph{hostname}) + del servidor MySQL que almacena sus datos Bugzilla. La base de datos + debe ser configurada para permitir conexiones desde las máquinas en + las que usted ejecute el gancho \hook{bugzilla}. +\item[\rcitem{bugzilla}{user}] El nombre de usuario que se usará para + conectarse al servidor MySQL. La base de datos debe ser configurada + para permitir a dicho usuario conectarse desde cualquiera de las + máquinas en las que se ejecute el gancho \hook{bugzilla}. Este + usuario debe tener acceso y poder modificar las tablas de Bugzilla. + El valor por defecto para este ítem es \texttt{bugs}, que es el + nombre estándar del usuario para Bugzilla en una base de datos + MySQL. +\item[\rcitem{bugzilla}{password}] La contraseña MySQL para el usuario + configurado anteriormente. Ésta es almacenada como texto plano, así + que usted deberá asegurarse de que los usuarios no autorizados no + puedan leer el fichero \hgrc\ en donde usted guarda esta + información. +\item[\rcitem{bugzilla}{db}] El nombre de la base de datos Bugzilla en + el servidor MySQL. El nombre por defecto para este ítem es + \texttt{bugs}, que es el nombre estándar de la base de datos MySQL + en donde Bugzilla almacena sus datos. +\item[\rcitem{bugzilla}{notify}] Si usted desea que Bugzilla envíe un + %TODO suBscriptores? + correo de notificación a los suscriptores después de que el gancho + haya añadido un comentario a un fallo, necesitará que este gancho + ejecute un comando siempre que actualice la base de datos. El + comando que se ejecute depende de en dónde haya sido instalado + Bugzilla, pero típicamente se verá así, si usted ha instalado + Bugzilla en \dirname{/var/www/html/bugzilla}: + \begin{codesample4} + cd /var/www/html/bugzilla && ./processmail %s nobody@nowhere.com + \end{codesample4} + El programa \texttt{processmail} de Bugzilla espera recibir un ID de + fallo (el gancho reemplaza ``\texttt{\%s}'' por el ID del fallo) y + una dirección de correo. También espera poder escribir a ciertos + ficheros en el directorio en que se ejecuta. Si Bugzilla y éste + gancho no están instalados en la misma máquina, usted deberá + encontrar una manera de ejecutar \texttt{processmail} en el servidor + donde está instalado Bugzilla. +\end{itemize} + +\subsubsection{Asociar nombres de consignadores a nombres de usuario +Bugzilla} + +Por defecto, el gancho \hgext{bugzilla} trata de usar la dirección de +correo electrónico de la persona que hizo la consignación del conjunto +de cambios como el nombre de usuario Bugzilla con el cual debe +actualizar el fallo. Si esto no se ajusta a sus necesidades, es +posible asociar direcciones de correo a nombres de usuario Bugzilla +usando una sección \rcsection{usermap}. + +Cada ítem en la sección \rcsection{usermap} contiene una dirección de +correo electrónico a la izquierda, y un nombre de usuario Bugzilla a +la derecha. +\begin{codesample2} + [usermap] + jane.user@example.com = jane +\end{codesample2} +Usted puede mantener los datos de \rcsection{usermap} en un fichero +\hgrc, o decirle al gancho \hgext{bugzilla} que lea la información +desde un fichero \filename{usermap} externo. En este caso, usted +puede almacenar los datos de \filename{usermap} en (por ejemplo) un +repositorio modificable por los usuarios. Esto hace posible para sus +usuarios mantener sus propias entradas \rcitem{bugzilla}{usermap}. El +fichero \hgrc\ principal se vería así: +\begin{codesample2} + # fichero hgrc normal se refiere a un fichero usermap externo + [bugzilla] + usermap = /home/hg/repos/userdata/bugzilla-usermap.conf +\end{codesample2} +Mientras que el fichero \filename{usermap} al que se hace referencia +se vería así: +\begin{codesample2} + # bugzilla-usermap.conf - dentro de un repositorio hg + [usermap] + stephanie@example.com = steph +\end{codesample2} + +\subsubsection{Configurar el texto que se añade a un fallo} + +Usted puede configurar el texto que este gancho añade como comentario; +usted los especifica como una plantilla Mercurial. Varias entradas +\hgrc\ (aún en la sección \rcsection{bugzilla}) controlan este +comportamiento. +\begin{itemize} +\item[\texttt{strip}] La cantidad de elementos iniciales de ruta a + remover de un nombre de ruta del repositorio para construir una ruta + parcial para una URL. Por ejemplo, si los repositorios en su + servidor se ubican en \dirname{/home/hg/repos}, y usted tiene un + repositorio cuya ruta es \dirname{/home/hg/repos/app/tests}, + entonces fijar \texttt{strip} a \texttt{4} resultará en una ruta + parcial de \dirname{app/tests}. El gancho hará disponible esta ruta + parcial cuando expanda una plantilla, como \texttt{webroot}. +\item[\texttt{template}] El texto de la plantilla a usar. En adición a + las variables usuales relacionadas con conjuntos de cambios, esta + plantilla puede usar \texttt{hgweb} (el valor del ítem de + configuración \texttt{hgweb} de arriba) y \texttt{webroot} (la ruta + construida usando \texttt{strip} arriba). +\end{itemize} + +Adicionalmente, usted puede añadir un ítem \rcitem{web}{baseurl} a la +sección \rcsection{web} de su \hgrc. El gancho \hgext{bugzilla} +publicará esto cuando expanda una plantilla, como la cadena base a +usar cuando se construya una URL que le permita a los usuarios navegar +desde un comentario de Bugzilla a la vista de un conjunto de cambios. +Ejemplo: +\begin{codesample2} + [web] + baseurl = http://hg.domain.com/ +\end{codesample2} + +A continuación se presenta un ejemplo completo de configuración para +el gancho \hgext{bugzilla}. +%TODO traducir? +\begin{codesample2} + [bugzilla] + host = bugzilla.example.com + password = mypassword + version = 2.16 + # server-side repos live in /home/hg/repos, so strip 4 leading + # separators + strip = 4 + hgweb = http://hg.example.com/ + usermap = /home/hg/repos/notify/bugzilla.conf + template = Changeset \{node|short\}, made by \{author\} in the \{webroot\} + repo, refers to this bug.\\nFor complete details, see + \{hgweb\}\{webroot\}?cmd=changeset;node=\{node|short\}\\nChangeset + description:\\n\\t\{desc|tabindent\} +\end{codesample2} + +\subsubsection{Pruebas y resolución de problemas} + +Los problemas más comunes que aparecen en la configuración del gancho +\hgext{bugzilla} suelen estar relacionados con la ejecución del guión +de Bugzilla \filename{processmail} y la asociación de nombres de +consignadores a nombres de usuario. + +Recuerde que en la sección~\ref{sec:hook:bugzilla:config} arriba el +usuario que ejecuta el proceso Mercurial en el servidor es también +el usuario que ejecutará el guión \filename{processmail}. El guión +\filename{processmail} algunas veces hace que Bugzilla escriba en +ficheros en su directorio de configuración, y los ficheros de +configuración de Bugzilla usualmente son propiedad del usuario bajo el +cual se ejecuta el servidor web. + +Usted puede hacer que \filename{processmail} sea ejecutado con la +identidad del usuario adecuado usando el comando \command{sudo}. A +continuación se presenta una entrada de ejemplo para un fichero +\filename{sudoers}. +\begin{codesample2} + hg_user = (httpd_user) NOPASSWD: /var/www/html/bugzilla/processmail-wrapper %s +\end{codesample2} +Esto permite que el usuario \texttt{hg\_user} ejecute el programa +\filename{processmail-wrapper} con la identidad del usuario +\texttt{httpd\_user}. + +Esta indirección a través de un guión envoltorio es necesaria, porque +\filename{processmail} espera que al ser ejecutado su directorio +actual sea aquel en el cual se instaló Bugzilla; usted no puede +especificar ese tipo de condición en un fichero \filename{sudoers}. +Los contenidos del giuón envoltorio son simples: +\begin{codesample2} + #!/bin/sh + cd `dirname $0` && ./processmail "$1" nobody@example.com +\end{codesample2} +No parece importar qué dirección de correo se le pase a +\filename{processmail}. + +Si su \rcsection{usermap} no es configurada correctamente, los +usuarios verán un mensaje de error del gancho \hgext{bugzilla} cuando +empujen cambios al servidor. El mensaje de error se verá así: +\begin{codesample2} + cannot find bugzilla user id for john.q.public@example.com +\end{codesample2} +Lo que esto quiere decir es que la dirección del consignador, +\texttt{john.q.public@example.com}, no es un nombre de usuario +Bugzilla válido, ni tiene una entrada en su \rcsection{usermap} que lo +asocie con un nombre de usuario válido Bugzilla. + +\subsection{\hgext{notify}---enviar notificaciones de correo +electrónico} + +%TODO feeds => notificaciones: lo más fácil es mirar en wikipedia +Aunque el servidor web embebido de Mercurial provee notificaciones de +cambios en cada repositorio, muchas personas prefieren recibir las +notificaciones de cambios vía correo electrónico. El gancho +\hgext{notify}\ndt{Notificación.} le permite a usted enviar +notificaciones a un conjunto de direcciones de correo cuando lleguen +conjuntos de cambios en los que los subscriptores estén interesados. + +De la misma forma que con el gancho \hgext{bugzilla}, el gancho +\hgext{notify} está orientado a plantillas, así que usted puede +personalizar los contenidos del mensaje de notificación que se envía. + +Por defecto, el gancho \hgext{notify} incluye un diff de cada conjunto +%TODO que se envía? revisar, pienso que es ``que se recibe'' +de cambios que se envía; usted puede limitar el tamaño del diff, o +desactivar completamente esta característica. Es útil para permitir a +los subscriptores revisar los cambios inmediatamente, en vez de tener +que hacer clic para visitar una URL. + +\subsubsection{Configuración del gancho \hgext{notify}} + +Usted puede configurar el gancho \hgext{notify} para enviar un mensaje +de correo por conjunto de cambios entrante, o uno por grupo entrante +de conjuntos de cambios (todos los que llegaron en un único empuje o +jalado). +\begin{codesample2} + [hooks] + # enviar un correo por grupo de cambios + changegroup.notify = python:hgext.notify.hook + # enviar un correo por cambio + incoming.notify = python:hgext.notify.hook +\end{codesample2} + +La información para configurar este gancho se ubica en la sección +\rcsection{notify} de un fichero \hgrc. +\begin{itemize} +\item[\rcitem{notify}{test}] Por defecto, este gancho no envía correos + en absoluto; en vez de eso, imprime el mensaje que se + \emph{enviaría}. Fije este ítem en \texttt{false} para permitir el + envío de correos. El motivo por el que el envío de correos está + desactivado es que hacen falta varios intentos para configurar esta + extensión exactamente como usted desea, y sería maleducado enviar a + los subscriptores una cantidad de notificaciones ``rotas'' mientras + usted depura su configuración. +\item[\rcitem{notify}{config}] La ruta a un fichero de configuración + que contiene información de subscripción. Esto se mantiene separado + del \hgrc\ principal para que usted pueda mantenerlo en un + repositorio. La gente puede clonar ese repositorio, actualizar sus + subscripciones, y empujar los cambios de vuelta a su servidor. +\item[\rcitem{notify}{strip}] La cantidad de caracteres iniciales de + separación de ruta a remover de la ruta del repositorio, al decidir + si un repositorio tiene subscriptores. Por ejemplo, si los + repositorios en su servidor están en \dirname{/home/hg/repos}, y + \hgext{notify} está trabajando con un repositorio llamado + \dirname{/home/hg/repos/shared/test}, fijar \rcitem{notify}{strip} a + \texttt{4} hará que \hgext{notify} elimine las partes iniciales de + la ruta hasta \dirname{shared/test}, y asociará los subscriptores + frente a dicha ruta. +\item[\rcitem{notify}{template}] El texto de plantilla a usar cuando + se envíen mensajes. Especifica los contenidos de la cabecera del + mensaje y el cuerpo del mismo. +\item[\rcitem{notify}{maxdiff}] El número máximo de líneas de datos de + diff a añadir al final de un mensaje. Si la longitud de un diff es + mayor a eso, se trunca. Por defecto, está fijado en 300. Fije esto a + \texttt{0} para omitir los diffs en los correos de notificación. +\item[\rcitem{notify}{sources}] Una lista de fuentes de conjuntos de + cambios a considerar. Esto le permite a usted indicar a + \hgext{notify} para que sólo envíe correos acerca de cambios que + usuarios remotos hayan empujado al repositorio vía un servidor, por + ejemplo. Vea la sección~\ref{sec:hook:sources} para las fuentes que + usted puede especificar aquí. +\end{itemize} + +Si usted fija el ítem \rcitem{web}{baseurl} en la sección +\rcsection{web}, usted lo puede usar en una plantilla; estará +disponible como \texttt{webroot}. + +A continuación se presenta un ejemplo completo de configuración para +el gancho \hgext{notify}. +\begin{codesample2} + [notify] + # enviar correo + test = false + # datos de subscriptores están en el repositorio notify + config = /home/hg/repos/notify/notify.conf + # repos están en /home/hg/repos on server, así que elimine 4 + # caracteres"/" + strip = 4 + template = X-Hg-Repo: \{webroot\} + Subject: \{webroot\}: \{desc|firstline|strip\} + From: \{author\} + + changeset \{node|short\} in \{root\} + details: \{baseurl\}\{webroot\}?cmd=changeset;node=\{node|short\} + description: + \{desc|tabindent|strip\} + + [web] + baseurl = http://hg.example.com/ +\end{codesample2} + +Esto producirá un mensaje que se verá como el siguiente: +\begin{codesample2} + X-Hg-Repo: tests/slave + Subject: tests/slave: Handle error case when slave has no buffers + Date: Wed, 2 Aug 2006 15:25:46 -0700 (PDT) + + changeset 3cba9bfe74b5 in /home/hg/repos/tests/slave + details: http://hg.example.com/tests/slave?cmd=changeset;node=3cba9bfe74b5 + description: + Handle error case when slave has no buffers + diffs (54 lines): + + diff -r 9d95df7cf2ad -r 3cba9bfe74b5 include/tests.h + --- a/include/tests.h Wed Aug 02 15:19:52 2006 -0700 + +++ b/include/tests.h Wed Aug 02 15:25:26 2006 -0700 + @@ -212,6 +212,15 @@ static __inline__ void test_headers(void *h) + [...snip...] +\end{codesample2} + +\subsubsection{Pruebas y resolución de problemas} + +No olvide que por defecto, la extensión \hgext{notify} \emph{no +enviará ningún correo electrónico} hasta que usted la configure +explícitamente para hacerlo, fijando el valor de \rcitem{notify}{test} +a \texttt{false}. Hasta que usted haga eso, simplemente se imprimirá +el mensaje que se \emph{enviaría}. + +\section{Información para escritores de ganchos} +\label{sec:hook:ref} + +\subsection{Ejecución de ganchos internos} + +Un gancho interno es llamado con argumentos de la siguiente forma: +\begin{codesample2} + def myhook(ui, repo, **kwargs): + pass +\end{codesample2} +El parámetro \texttt{ui} es un objeto \pymodclass{mercurial.ui}{ui}. +El parámetro \texttt{repo} es un objeto +\pymodclass{mercurial.localrepo}{localrepository}. Los nombres y +valores de los parámetros en \texttt{**kwargs} dependen del gancho que +se invoque, con las siguientes características en común: +\begin{itemize} +\item Si hay un parámetro llamado \texttt{node} o + \texttt{parent\emph{N}}, contendrá un ID hexadecimal de un conjunto + de cambios. La cadena vacía es usada para representar un + ``ID de conjunto de cambios nulo'' en vez de una cadena de ceros. +\item Si hay un parámetro llamado \texttt{url}, contendrá la URL de un + repositorio remoto, si puede ser determinada. +\item Los parámetros booleanos son representados como objetos + \texttt{bool} de Python. +\end{itemize} + +Un gancho interno es ejecutado sin cambiar el directorio de trabajo +del proceso (a diferencia de los ganchos externos, que son ejecutados +desde la raíz del repositorio). El gancho no debe cambiar el +directorio de trabajo del proceso, porque esto haría que falle +cualquier llamada que se haga a la API de Mercurial. + +Si un gancho retorna el valor booleano ``false''\ndt{Falso.}, se +considera que éste tuvo éxito. Si retorna +``true''\ndt{Verdadero.} o genera una excepción, se considera que +ha fallado. Una manera útil de pensar en esta convención de llamado es +``dígame si usted falló''. + +Note que los IDs de conjuntos de cambios son pasados a los ganchos de +Python como cadenas hexadecimales, no como los hashes binarios que la +API de Mercurial usa normalmente. Para convertir un hash de +hexadecimal a binario, use la función \pymodfunc{mercurial.node}{bin}. + +\subsection{Ejecución de ganchos externos} + +Un gancho externo es pasado al intérprete de comandos del usuario bajo +el cual se ejecuta Mercurial. Las características del intérprete, como +sustitución de variables y redirección de comandos, están disponibles. +El gancho es ejecutado desde el directorio raíz del repositorio +(a diferencia de los ganchos internos, que se ejecutan desde el mismo +directorio en que Mercurial fue ejecutado). + +Los parámetros para el gancho se pasan como variables de entorno. El +nombre de cada variable de entorno se pasa a mayúsculas y se le añade +el prefijo ``\texttt{HG\_}''. Por ejemplo, si el nombre de un +parámetro es ``\texttt{node}'', el nombre de la variable de entorno +que almacena el parámetro se llamará ``\texttt{HG\_NODE}''. + +Un parámetro booleano se representa con la cadena ``\texttt{1}'' para +``true'', ``\texttt{0}'' para ``false''. Si una variable se llama +\envar{HG\_NODE}, \envar{HG\_PARENT1} o \envar{HG\_PARENT2}, +contendrá un ID de conjunto de cambios representado como una cadena +hexadecimal. La cadena vacía es usada para representar un ``ID de +conjunto de cambios nulo'' en vez de una cadena de ceros. Si una +variable de entorno se llama \envar{HG\_URL}, contendrá la URL de un +repositorio remoto, si puede ser determinada. + +Si un gancho termina con un código de salida de cero, se considera que +tuvo éxito. Si termina con un código de salida diferente de cero, se +considera que falló. + +\subsection{Averiguar de dónde vienen los conjuntos de cambios} +%TODO los trae la cigüeña. De París. Y quedan debajo de una col. + +Un gancho que involucra la transferencia de conjuntos de cambios entre +un repositorio local y otro puede ser capaz de averiguar información +acerca de ``el otro lado''. Mercurial sabe \emph{cómo} son +transferidos los conjuntos de cambios, y en muchos casos también desde +o hacia donde están siendo transferidos. + +\subsubsection{Fuentes de conjuntos de cambios} +\label{sec:hook:sources} + +Mercurial le indicará a un gancho cuáles son, o fueron, los medios +usados para transferir los conjuntos de cambios entre repositorios. +Esta información es provista por Mercurial en un parámetro Python +llamado \texttt{source}\ndt{Fuente.}, o una variable de entorno +llamada \envar{HG\_SOURCE}. + +\begin{itemize} +\item[\texttt{serve}] Los conjuntos de cambios son transferidos desde + o hacia un repositorio remoto a través de http o ssh. +\item[\texttt{pull}] Los conjuntos de cambios son transferidos vía una + operación de jalado de un repositorio a otro. +\item[\texttt{push}] Los conjuntos de cambios son transferidos vía un + empuje de un repositorio a otro. +\item[\texttt{bundle}] Los conjuntos de cambios son transferidos desde + %TODO bundle + o hacia un paquete. +\end{itemize} + +\subsubsection{A dónde van los cambios---URLs de repositorios remotos} +\label{sec:hook:url} +%TODO al cielo? no, ésos son los perros + +Cuando es posible, Mercurial le indicará a los ganchos la ubicación de +``el otro lado'' de una actividad que transfiera datos de conjuntos de +cambios entre repositorios. Esto es provisto por Mercurial en un +parámetro Python llamado \texttt{url}, o en una variable de entorno +llamada \envar{HG\_URL}. + +No siempre esta información está disponible. Si un gancho es invocado +un repositorio que es servido a través de http o ssh, Mercurial no +puede averiguar dónde está el repositorio remoto, pero puede saber +desde dónde se conecta el cliente. En esos casos, la URL tendrá una de +las siguientes formas: +\begin{itemize} +\item \texttt{remote:ssh:\emph{ip-address}}---cliente ssh remoto, en + la dirección IP dada. +\item \texttt{remote:http:\emph{ip-address}}---cliente remoto http, en + la dirección IP dada. Si el cliente está usando SSL, tendrá la forma + \texttt{remote:https:\emph{ip-address}}. +\item Vacío---no se pudo descubrir información acerca del cliente + remoto. +\end{itemize} + +\section{Referencia de ganchos} + +\subsection{\hook{changegroup}---luego de añadir conjuntos de cambios +remotos} +\label{sec:hook:changegroup} + +Este gancho es ejecutado luego de que un grupo de conjuntos de cambios +preexistentes ha sido añadido al repositorio, por ejemplo vía un +\hgcmd{pull} o \hgcmd{unbundle}. Este gancho es ejecutado una vez por +cada operación que añade uno o más conjuntos de cambios. Este gancho +se diferencia del gancho \hook{incoming}, que es ejecutado una vez por +cada conjunto de cambios, sin importar si los cambios llegan en grupo. + +Algunos usos posibles para este gancho includen el probar o ensamblar +los conjuntos de cambios añadidos, actualizar una base de datos de +fallos, o notificar a subscriptores de que el repositorio contiene +nuevos cambios. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{node}] Un ID de conjunto de cambios. El ID del primer conjunto + de cambios que fue añadido en el grupo. Todos los conjuntos de + cambios entre éste y la punta + %TODO mirar qué hacer con el índice + \index{tags!\texttt{tip}}(\texttt{tip}), inclusive, fueron añadidos + %TODO unbundle + por un único jalado (\hgcmd{pull}), empuje (\hgcmd{push}) o \hgcmd{unbundle}. +\item[\texttt{source}] Una cadena. La fuente de estos cambios. Vea la + sección~\ref{sec:hook:sources} para más detalles. +\item[\texttt{url}] Una URL. La ubicación del repositorio remoto, si + es conocida. Vea la sección~\ref{sec:hook:url} para más información. +\end{itemize} + +Veta también: \hook{incoming} (sección~\ref{sec:hook:incoming}), +\hook{prechangegroup} (sección~\ref{sec:hook:prechangegroup}), +\hook{pretxnchangegroup} (sección~\ref{sec:hook:pretxnchangegroup}) + +\subsection{\hook{commit}---luego de la creación de un nuevo conjunto +de cambios} +\label{sec:hook:commit} + +Este gancho es ejecutado luego de la creación de un nuevo conjunto de +cambios. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{node}] Un ID de conjunto de cambios. El ID de conjunto + de cambios del conjunto de cambios que acabó de ser consignado. +\item[\texttt{parent1}] Un ID de conjunto de cambios. El ID de + conjunto de cambios del primer padre del conjunto de cambios que + acaba de ser consignado. +\item[\texttt{parent2}] Un ID de conjunto de cambios. El ID de + conjunto de cambios del segundo padre del conjunto de cambios que + acaba de ser consignado. +\end{itemize} + +Vea también: \hook{precommit} (sección~\ref{sec:hook:precommit}), +\hook{pretxncommit} (sección~\ref{sec:hook:pretxncommit}) + +\subsection{\hook{incoming}---luego de que un conjunto de cambios +remoto es añadido} +\label{sec:hook:incoming} + +Este gancho es ejecutado luego de que un conjunto de cambios +preexistente ha sido añadido al repositorio, por ejemplo, vía un +\hgcmd{push}. Si un grupo de conjuntos de cambios fue añadido en una +sola operación, este gancho es ejecutado una vez por cada conjunto de +cambios añadido. + +Usted puede usar este gancho para los mismos fines que el gancho +\hook{changegroup} (sección~\ref{sec:hook:changegroup}); simplemente +algunas veces es más conveniente ejecutar un gancho una vez por cada +grupo de conjuntos de cambios, mientras que otras es más útil correrlo +por cada conjunto de cambios. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{node}] Un ID de conjunto de cambios. El ID del conjunto + de cambios recién añadido. +\item[\texttt{source}] Una cadena. La fuente de estos cambios. Vea la + sección~\ref{sec:hook:sources} para más detalles. +\item[\texttt{url}] Una URL. La ubicación del repositorio remoto, si + es conocida. Vea la sección~\ref{sec:hook:url} para más información. +\end{itemize} + +Vea también: \hook{changegroup} (sección~\ref{sec:hook:changegroup}) +\hook{prechangegroup} (sección~\ref{sec:hook:prechangegroup}), +\hook{pretxnchangegroup} (sección~\ref{sec:hook:pretxnchangegroup}) + +\subsection{\hook{outgoing}---luego de la propagación de los conjuntos +de cambios} +\label{sec:hook:outgoing} + +Este gancho es ejecutado luego de que un grupo de conjuntos de cambios +ha sido propagado fuera de éste repositorio, por ejemplo por un +comando \hgcmd{push} o \hgcmd{bundle}. + +Un uso posible para este gancho es notificar a los administradores que +los cambios han sido jalados. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{node}] Un ID de conjunto de cambios. El ID del primer conjunto + de cambios del grupo que fue enviado. +\item[\texttt{source}] Una cadena. La fuente de la operación (vea la + sección~\ref{sec:hook:sources}). Si un cliente remoto jaló cambios + de este repositorio, \texttt{source} será \texttt{serve}. Si el + cliente que obtuvo los cambios desde este repositorio era local, + \texttt{source} será \texttt{bundle}, \texttt{pull}, o + \texttt{push}, dependiendo de la operación que llevó a cabo el + cliente. +\item[\texttt{url}] Una URL. La ubicación del repositorio remoto, si + es conocida. Vea la sección~\ref{sec:hook:url} para más información. +\end{itemize} + +Vea también: \hook{preoutgoing} (sección~\ref{sec:hook:preoutgoing}) + +\subsection{\hook{prechangegroup}---antes de empezar la adición de +conjuntos de cambios remotos} +\label{sec:hook:prechangegroup} + +Este gancho de control es ejecutado antes de que Mercurial empiece a +añadir un grupo de conjuntos de cambios de otro repositorio. + +Este gancho no tiene ninguna información acerca de los conjuntos de +cambios que van a ser añadidos, porque es ejecutado antes de que se +permita que empiece la transmisión de dichos conjuntos de cambios. Si +este gancho falla, los conjuntos de cambios no serán transmitidos. + +Un uso para este gancho es prevenir que se añadan cambios externos a un +repositorio. Por ejemplo, usted podría usarlo para ``congelar'' +temporal o permanentemente una rama ubicada en un servidor para que +los usuarios no puedan empujar cambios a ella, y permitiendo al mismo +tiempo modificaciones al repositorio por parte de un administrador +local. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{source}] Una cadena. La fuente de estos cambios. Vea la + sección~\ref{sec:hook:sources} para más detalles. +\item[\texttt{url}] Una URL. La ubicación del repositorio remoto, si + es conocida. Vea la sección~\ref{sec:hook:url} para más información. +\end{itemize} + +Vea también: \hook{changegroup} (sección~\ref{sec:hook:changegroup}), +\hook{incoming} (sección~\ref{sec:hook:incoming}), , +\hook{pretxnchangegroup} (sección~\ref{sec:hook:pretxnchangegroup}) + +\subsection{\hook{precommit}---antes de iniciar la consignación de un +conjunto de cambios} +\label{sec:hook:precommit} + +Este gancho es ejecutado antes de que Mercurial inicie la consignación +de un nuevo conjunto de cambios. Es ejecutado antes de que Mercurial +tenga cualquier de los metadatos para la consignación, como los +ficheros a ser consignados, el mensaje de consignación, o la fecha de +consignación. + +Un uso para este gancho es deshabilitar la capacidad de consignar +nuevos conjuntos de cambios, pero permitiendo conjuntos de cambios +entrantes. Otro es ejecutar un proceso de ensamble/compilación o +prueba, y permitir la consignación sólo si el ensamble/compilación o +prueba tiene éxito. + +Parámetros para este gancho: +\begin{itemize} + \item[\texttt{parent1}] Un ID de conjunto de cambios. El ID de + conjunto de cambios del primer padre del directorio de trabajo. +\item[\texttt{parent2}] Un ID de conjunto de cambios. El ID de + conjunto de cambios del segundo padre del directorio de trabajo. +\end{itemize} +Si la consignación continúa, los padres del directorio de trabajo se +convertirán en los padres del nuevo conjunto de cambios. + +Vea también: \hook{commit} (sección~\ref{sec:hook:commit}), +\hook{pretxncommit} (sección~\ref{sec:hook:pretxncommit}) + +\subsection{\hook{preoutgoing}---antes de empezar la propagación de +conjuntos de cambios} +\label{sec:hook:preoutgoing} + +Este gancho es ejecutado antes de que Mercurial conozca las +identidades de los conjuntos de cambios que deben ser transmitidos. + +Un uso para este gancho es evitar que los cambios sean transmitidos a +otro repositorio. + +Parámetros para este gancho: +\begin{itemize} + \item[\texttt{source}] Una cadena. La fuente la operación que está + tratando de obtener cambios de éste repositorio (vea + la sección~\ref{sec:hook:sources}). Revise la documentación para + el parámetro \texttt{source} del gancho \hook{outgoing}, en la + sección~\ref{sec:hook:outgoing}, para ver los posibles valores de + este parámetro. +\item[\texttt{url}] Una URL. La ubicación del repositorio remoto, si + es conocida. Vea la sección~\ref{sec:hook:url} para más información. +\end{itemize} + +Vea también: \hook{outgoing} (sección~\ref{sec:hook:outgoing}) + +\subsection{\hook{pretag}---antes de etiquetar un conjunto de cambios} +\label{sec:hook:pretag} + +Este gancho de control es ejecutado antes de la creación de una +etiqueta. Si el gancho termina exitosamente, la creación de la +etiqueta continúa. Si el gancho falla, no se crea la etiqueta. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{local}] Un booleano. Indica si la etiqueta es local a + ésta instancia del repositorio (p.e.~almacenado en + \sfilename{.hg/localtags}) o administrado por Mercurial (almacenado + en \sfilename{.hgtags}). +\item[\texttt{node}] Un ID de conjunto de cambios. El ID del conjunto + de cambios a etiquetar. +\item[\texttt{tag}] Una cadena. El nombre de la etiqueta por crear. +\end{itemize} + +Si la etiqueta que se va a crear se encuentra bajo control de +revisiones, los ganchos \hook{precommit} y \hook{pretxncommit} +(secciones~\ref{sec:hook:commit} y~\ref{sec:hook:pretxncommit}) +también serán ejecutados. + +Vea también: \hook{tag} (sección~\ref{sec:hook:tag}) + +\subsection{\hook{pretxnchangegroup}---antes de completar la adición +de conjuntos de cambios remotos} +\label{sec:hook:pretxnchangegroup} + +Este gancho de control es ejecutado antes de una transacción---la que +maneja la adición de un grupo de conjuntos de cambios nuevos desde +fuera del repositorio---se complete. Si el gancho tiene éxito, la +transacción se completa, y todos los conjuntos de cambios se vuelven +permanentes dentro de este repositorio. Si el gancho falla, la +transacción es deshecha, y los datos para los conjuntos de cambios son +eliminados. + +Este gancho puede acceder a los metadatos asociados con los conjuntos +de cambios casi añadidos, pero no debe hacer nada permanente con estos +datos. Tampoco debe modificar el directorio de trabajo. + +Mientras este gancho está corriendo, si otro proceso Mercurial accesa +el repositorio, podrá ver los conjuntos de cambios casi añadidos como +si fueran permanentes. Esto puede llevar a condiciones de carrera si +usted no toma precauciones para evitarlas. + +Este gancho puede ser usado para examinar automáticamente un grupo de +conjuntos de cambios. Si el gancho falla, todos los conjuntos de +cambios son ``rechazados'' cuando la transacción se deshace. + +Parámetros para este gancho: +\begin{itemize} + \item[\texttt{node}] Un ID de conjunto de cambios. El ID del primer + conjunto de cambios que fue añadido en el grupo. Todos los + conjuntos de cambios entre éste y el + \index{tags!\texttt{tip}}\texttt{tip}, inclusive, fueron añadidos + por un único \hgcmd{pull}, \hgcmd{push} o \hgcmd{unbundle}. +\item[\texttt{source}] Una cadena. La fuente de estos cambios. Vea la + sección~\ref{sec:hook:sources} para más detalles. +\item[\texttt{url}] Una URL. La ubicación del repositorio remoto, si + es conocida. Vea la sección~\ref{sec:hook:url} para más información. +\end{itemize} + +Vea también: \hook{changegroup} (sección~\ref{sec:hook:changegroup}), +\hook{incoming} (sección~\ref{sec:hook:incoming}), +\hook{prechangegroup} (sección~\ref{sec:hook:prechangegroup}) + +\subsection{\hook{pretxncommit}---antes de completar la consignación +de un nuevo conjunto de cambios} +\label{sec:hook:pretxncommit} + +Este gancho de control es ejecutado antes de que una transacción---que +maneja una nueva consignación---se complete. Si el gancho tiene éxito, +la transacción se completa y el conjunto de cambios se hace permanente +dentro de éste repositorio. Si el gancho falla, la transacción es +deshecha, y los datos de consignación son borrados. + +Este gancho tiene acceso a los metadatos asociados con el +prácticamente nuevo conjunto de cambios, pero no debería hacer nada +permanente con estos datos. Tampoco debe modificar el directorio de +trabajo. + +Mientras este gancho está corriendo, si otro proceso Mercurial accesa +éste repositorio, podrá ver el prácticamente nuevo conjunto de cambios +como si fuera permanente. Esto puede llevar a condiciones de carrera si +usted no toma precauciones para evitarlas. + +Parámetros para este gancho: +\begin{itemize} + \item[\texttt{node}] Un ID de conjunto de cambios. El ID del + conjunto de cambios recién consignado. +\item[\texttt{parent1}] Un ID de conjunto de cambios. El ID de + conjunto de cambios del primer padre del conjunto de cambios que + acaba de ser consignado. +\item[\texttt{parent2}] Un ID de conjunto de cambios. El ID de + conjunto de cambios del segundo padre del conjunto de cambios que + acaba de ser consignado. +\end{itemize} + +Vea también: \hook{precommit} (sección~\ref{sec:hook:precommit}) + +\subsection{\hook{preupdate}---antes de actualizar o fusionar el +directorio de trabajo} +\label{sec:hook:preupdate} + +Este gancho de control es ejecutado antes de actualizar o fusionar el +directorio de trabajo. Es ejecutado sólo si las revisiones usuales de +Mercurial antes de las actualizaciones determinan que la actualización +o fusión pueden proceder. Si el gancho termina exitosamente, la +actualización o fusión pueden proceder.; si falla, la actualización o +fusión no empiezan. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{parent1}] Un ID de conjunto de cambios. El ID del + padre al que el directorio de trabajo será actualizado. Si se está + fusionando el directorio de trabajo, no cambiará este padre. +\item[\texttt{parent2}] Un ID de conjunto de cambios. Sólo está + definido si se está fusionando el directorio de trabajo. El ID de la + revisión con la cual está siendo fusionado el directorio de trabajo. +\end{itemize} + +Vea también: \hook{update} (sección~\ref{sec:hook:update}) + +\subsection{\hook{tag}---luego de etiquetar un conjunto de cambios} +\label{sec:hook:tag} + +Este gancho es ejecutado luego de la creación de una etiqueta. + +Parámetros para este gancho: +\begin{itemize} +\item[\texttt{local}] Un booleano. Indica si la etiqueta es local a + ésta instancia del repositorio (p.e.~almacenado en + \sfilename{.hg/localtags}) o administrado por Mercurial (almacenado + en \sfilename{.hgtags}). +\item[\texttt{node}] Un ID de conjunto de cambios. El ID del + conjunto de cambios que fue etiquetado. +\item[\texttt{tag}] Una cadena. El nombre de la etiqueta que fue + creada. +\end{itemize} + +Si la etiqueta creada está bajo control de revisiones, el gancho +\hook{commit} (sección~\ref{sec:hook:commit}) es ejecutado antes de +este gancho. + +Vea también: \hook{pretag} (sección~\ref{sec:hook:pretag}) + +\subsection{\hook{update}---luego de actualizar o fusionar el +directorio de trabajo} +\label{sec:hook:update} + +Este gancho es ejecutado después de una actualización o fusión en el +directorio de trabajo. Ya que una fusión puede fallar (si el comando +externo \command{hgmerge} no puede resolver los conflictos en un +fichero), este gancho indica si la actualización o fusión fueron +completados adecuadamente. + +\begin{itemize} +\item[\texttt{error}] Un booleano. Indica si la actualización o fusión + fue completada exitosamente. +\item[\texttt{parent1}] Un ID de conjunto de cambios. El ID del padre + al cual fue actualizado el directorio de trabajo. Si se fusionó el + directorio de trabajo, no se habrá cambiado este padre. +\item[\texttt{parent2}] Un ID de conjunto de cambios. Sólo está + definido si se fusionó el directorio de trabajo. El ID de la + revisión con la que fue fusionado el directorio de trabajo. +\end{itemize} + +Vea también: \hook{preupdate} (sección~\ref{sec:hook:preupdate}) + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/htlatex.book --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/htlatex.book Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,1 @@ +../en/htlatex.book \ No newline at end of file diff -r 73b094b764ec -r 51b5d56744c5 es/intro.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/intro.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,621 @@ +\chapter{Introducción} +\label{chap:intro} + +\section{Acerca del control de revisiones} + +El control de revisiones es el proceso de administrar diferentes +versiones de una pieza de información. En su forma más simple es algo +que la mayoría de gente hace a mano: cada vez que usted modifica un +fichero, lo graba con un nuevo nombre que contiene un número, cada uno +mayor que el anterior. + +Administrar manualmente muchas versiones de incluso sólo un fichero es una tarea +propensa a errores, a pesar de que hace bastante tiempo hay +herramientas que ayudan en este proceso. Las primeras herramientas +para automatizar el control de revisiones fueron pensadas para que un +usuario administrara un solo fichero. En las décadas pasadas, el +alcance de las herramientas de control de revisiones ha ido aumentando +considerablemente; ahora manejan muchos ficheros y facilitan el +trabajo en conjunto de varias personas. Las mejores herramientas de +control de revisiones de la actualidad no tienen problema con miles de +personas trabajando en proyectos que consisten de cientos de miles de +ficheros. + +\subsection{¿Por qué usar control de revisiones?} + +Hay muchas razones por las cuales usted o su equipo desearía usar una +herramienta automática de control de revisiones para un proyecto. +\begin{itemize} +\item Llevar registro del historial y la evolución de su proyecto, para + evitar hacer la tarea manualmente. Por cada cambio, tendrá una + bitácora de \emph{quién} lo hizo; \emph{por qué} se hizo; + \emph{cuándo} se hizo; y de \emph{qué} se trataba el cambio. +\item Cuando trabaja con más personas, los programas de control de + revisiones facilitan la colaboración. Por ejemplo, cuando varias + personas hacen cambios potencialmente incompatibles de forma casi + simultánea, el programa le ayudará a identificar y resolver tales + conflictos. +\item Puede ayudarle a recuperarse de equivocaciones. Si aplica un + cambio que posteriormente se evidencia como un error, puede + revertirlo a una versión previa a uno o muchos ficheros. De hecho, + una herramienta \emph{realmente} buena, incluso puede ayudarle + efectivamente a darse cuenta exactamente cuándo se introdujo el + error (para más detalles ver la sección~\ref{sec:undo:bisect}). +\item Le ayudará a trabajar simultáneamente, y a manejar las diferencias + entre múltiples versiones de su proyecto. +\end{itemize} +La mayoría de estas razones son igualmente válidas ---por lo menos en +teoría--- así esté trabajando en un proyecto solo usted, o con mucha gente. + +Algo fundamental acerca de lo práctico de un sistema de control de +revisiones en estas dos escalas (``un hacker solitario'' y ``un equipo +gigantesco'') es cómo se comparan los \emph{beneficios} con los +\emph{costos}. Una herramienta de control de revisiones que sea +difícil de entender o usar impondrá un costo alto. + +Un proyecto de quinientas personas es muy propenso a colapsar +solamente con su peso inmediatamente sin una herramienta y un proceso +de control de versiones. En este caso, el costo de usar control de +revisiones ni siquiera se tiene en cuenta, puesto que \emph{sin} él, +el fracaso está casi garantizado. + +Por otra parte, un ``arreglo rápido'' de una sola persona, excluiría +la necesidad de usar una herramienta de control de revisiones, porque +casi seguramente, el costo de usar una estaría cerca del costo del +proyecto. ¿No es así? + +Mercurial soporta \emph{ambas} escalas de de desarrollo de manera +única. Puede aprender lo básico en pocos minutos, y dado su bajo +sobrecosto, puede aplicar el control de revisiones al proyecto más +pequeño con facilidad. Su simplicidad significa que no tendrá que +preocuparse por conceptos obtusos o secuencias de órdenes compitiendo +por espacio mental con lo que sea que \emph{realmente} esté tratando +de hacer. Al mismo tiempo, Mercurial tiene alto desempeño y su +%TODO distribuida? en vez de p2p +naturaleza peer-to-peer le permite escalar indoloramente para manejar +grandes proyectos. + +Ninguna herramienta de control de revisiones puede salvar un +proyecto mal administrado, pero la elección de herramientas puede +hacer una gran diferencia en la fluidez con la cual usted puede +trabajar en un proyecto. + +\subsection{La cantidad de nombres del control de revisiones} + +El control de revisiones es un campo amplio, tan amplio que no hay un +acrónimo o nombre único. A continuación presentamos un listado de +nombres comunes y acrónimos que se podrían encontrar: +\begin{itemize} +\item Control de revisiones (RCS) +\item Manejo de Configuraciones de Programas (SCM), o administracón de + configuraciones +\item Administración de código fuente +\item Control de Código Fuente, o Control de Fuentes +\item Control de Versiones (VCS) +\end{itemize} +Algunas personas aducen que estos términos tienen significados +diversos, pero en la práctica se sobreponen tanto que no hay una +forma acordada o incluso adecuada de separarlos. + +\section{Historia resumida del control de revisiones} + +La herramienta de control de revisiones más antigua conocida es SCCS +(Sistema de Control de Código), escrito por Marc Rochkind en Bell +Labs, a comienzos de los setentas (1970s). SCCS operaba sobre ficheros +individuales, y requería que cada persona que trabajara en el proyecto +tuviera acceso a un espacio compartido en un solo sistema. Solamente +una persona podía modificar un fichero en un momento dado; el +arbitramiento del acceso a los ficheros se hacía con candados. Era +común que la gente pusiera los candados a los ficheros, y que +posteriormente olvidara quitarlos, impidiendo que otro pudiera +modificar los ficheros en cuestión sin la intervención del +administrador. + +Walter Tichy desarrolló una alternativa gratuita a SCCS a comienzos +de los ochentas (1980s); llamó a su programa RCS (Sistema de Control de +Revisiones). Al igual que SCCS, RCS requería que los desarrolladores +trabajaran en un único espacio compartido y colocaran candados a los +ficheros para evitar que varias personas los modificaran +simultáneamente. + +Después en los ochenta, Dick Grune usó RCS como un bloque de +construcción para un conjunto de guiones de línea de comando, que +inicialmente llamó cmt, pero que renombró a CVS (Sistema Concurrente de +Versiones). La gran innovación de CVS era que permitía a los +desarrolladores trabajar simultáneamente de una forma más o menos +independiente en sus propios espacios de trabajo. Los espacios de +trabajo personales impedían que los desarrolladores se pisaran las +mangueras todo el tiempo, situación común con SCCS y RCS. Cada +desarrollador tenía una copia de todos los ficheros del proyecto y podía +modificar sus copias independientemente, Tenían que fusionar sus +ediciones antes de consignar los cambios al repositorio central. + +Brian Berliner tomó los scripts originales de Grune y los reescribió +en~C, publicando en 1989 el código sobre el cual se ha +desarrollado la versión moderna de CVS. CVS adquirió posteriormente +la habilidad de operar sobre una conexión de red, dotándolo de una +arquitectura, cliente/servidor. La arquitectura de CVS es +centralizada; el historial del proyecto está únicamente en el +repositorio central. Los espacios de trabajo de los clientes +contienen únicamente copias recientes de las versiones de los +ficheros, y pocos metadatos para indicar dónde está el servidor. CVS +ha tenido un éxito enorme; Es probablemente el sistema de control de +revisiones más extendido del planeta. + +A comienzos de los noventa~(1990s), Sun MicroSystems desarrollo un +temprano sistema distribuido de control de revisiones llamado +TeamWare. +Un espacio de trabajo TeamWare contiene una copia completa del +historial del proyecto. TeamWare no tiene la noción de repositorio +central. (CVS se basaba en RCS para el almacenamiento de su historial; +TeamWare usaba SCCS.) + +A medida que avanzaba la decada de los noventa, se empezó a +evidenciar los problemas de CVS. Almacena cambios simultáneos a muchos +ficheros de forma individual, en lugar de agruparlos como una +operación única y atómica lógicamente. No maneja bien su jerarquía de +ficheros; es fácil desordenar un repositorio al renombrar ficheros +y directorios. Peor aún, su código fuente es difícil de leer y +mantener, lo que hizo que su ``umbral de dolor'' para arreglar sus +problemas arquitecturales fuera algo prohibitivo. + +En 2001, Jim Blandy y Karl Fogel, dos desarrolladores que habían +trabajado en CVS, comenzaron un proyecto para reemplazarlo con una +herramienta con mejor arquitectura y código más limpio. El resultado, +Subversion, no se separó del modelo centralizado cliente/servidor de +CVS, pero añadió consignaciones atómicas de varios ficheros, mejor +manejo de espacios de nombres , y otras características que lo hacen +mejor que CVS. Desde su versión inicial, ha ido creciendo en +popularidad rápidamente. + +Más o menos en forma simultánea Graydon Hoare comenzó a trabajar en un +ambicioso sistema distribuido de control de versiones que llamó +Monotone. Mientras que Monotone se enfocaba a evitar algunas fallas de +diseño de CVS con una arquitectura peer-to-peer, fue mucho más +allá de las herramientas anteriores (y posteriores) de +control de revisiones en varios aspectos innovadores. Usa hashes +criptográficos como identificadores, y tiene una noción integral de +``confianza'' para código de diversas fuentes. + +Mercurial nació en el 2005. Algunos de sus aspectos de de diseño +fueron influenciados por Monotone, pero Mercurial se enfoca en la +facilidad de uso, gran rendimiento y escalabilidad para proyectos muy +grandes. + +\section{Tendencias en el control de revisiones} + +Ha habido una tendencia inconfundible en el desarrollo y uso de las herramientas +de control de revisiones en las cuatro décadas pasadas, mientras la +gente se ha hecho familiar con las capacidades de sus herramientas y +se ha visto restringida por sus limitaciones. + +La primera generación comenzó administrando ficheros individuales en +computadores por persona. A pesar de que tales herramientas +representaron un avance importante frente al control de revisiones +manual, su modelo de candados y la dependencia a un sólo computador +los limitó a equipos de trabajo pequeños y acoplados. + +La segunda generación dejó atrás esas limitaciones moviéndose a +arquitecturas centradas en redes, y administrando proyectos completos +a la vez. A medida que los proyectos crecían, nacieron nuevos +problemas. Con la necesidad de comunicación frecuente con los +servidores, escalar estas máquinas se convirtió en un problema en +proyectos realmente grandes. Las redes con poca estabilidad podrían +impedir que usuarios remotos se conectaran al servidor. A medida que +los +proyectos de código abierto comenzaron a ofrecer acceso de sólo lectura +de forma anónima a cualquiera, la gente sin permiso para consignar +vio que no podían usar tales herramientas para interactuar en un +proyecto de forma natural, puesto que no podían guardar sus cambios. + +La generación actual de herramientas de control de revisiones es +peer-to-peer por naturaleza. Todos estos sistemas han eliminado la +dependencia de un único servidor central, y han permitido que la +gente distribuya sus datos de control de revisiones donde realmente se +necesita. La colaboración a través de Internet ha cambiado las +limitantes tecnológicas por la cuestión de elección y consenso. Las +herramientas modernas pueden operar sin conexión indefinidamente y +autónomamente, necesitando una conexión de red solamente para +sincronizar los cambios con otro repositorio. + +\section{Algunas ventajas del control distribuido de revisiones} + +A pesar de que las herramientas para el control distribuido de +revisiones lleva varios años siendo tan robustas y usables como la +generación previa de sus contrapartes, algunas personas que usan las +herramientas más antiguas no se han percatado de sus ventajas. Hay +gran cantidad +de situaciones en las cuales las herramientas distribuidas brillan +frente a las centralizadas. + +Para un desarrollador individual, las herramientas distribuidas casi +siempre son más rápidas que las centralizadas. Por una razón sencilla: +Una herramienta centralizada necesita comunicarse por red para las +operaciones más usuales, debido a que los metadatos se almacenan en +una sola copia en el servidor central. Una herramienta distribuida +almacena todos sus metadatos localmente. Con todo lo demás de la +misma forma, comunicarse por red tiene un sobrecosto en una +herramienta centralizada. No subestime el valor de una herramienta de +respuesta rápida: Usted empleará mucho tiempo interactuando con su +programa de control de revisiones. + +Las herramientas distribuidas son indiferentes a los caprichos de su +infraestructura de servidores, de nuevo, debido a la replicación de +metadatos en tantos lugares. Si usa un sistema centralizado y su +servidor explota, ojalá los medios físicos de su copia de seguridad +sean confiables, y que su última copia sea reciente y además +funcione. Con una herramienta distribuida tiene tantas copias de +seguridad disponibles como computadores de contribuidores. + +La confiabilidad de su red afectará las herramientas distribuidas de +una forma mucho menor que a las herramientas centralizadas. Usted no puede +siquiera usar una herramienta centralizada sin conexión de red, +excepto por algunas órdenes muy limitadas. Con herramientas +distribuidas, si sus conexión cae mientras usted está trabajando, +podría nisiquiera darse cuenta. Lo único que que no podrá hacer es +comunicarse con repositorios en otros computadores, algo que es +relativamente raro comparado con las operaciones locales. Si tiene +colaboradores remotos en su equipo, puede ser importante. + +\subsection{Ventajas para proyectos de código abierto} + +Si descubre un proyecto de código abierto y decide que desea comenzar +a trabajar en él, y ese proyecto usa una herramienta de control +distribuido de revisiones, usted es de inmediato un par con la gente que se +considera el ``alma'' del proyecto. Si ellos publican sus +repositorios, usted puede copiar inmediatamente el historial del proyecto, +hacer cambios y guardar su trabajo, usando las mismas herramientas de +la misma forma que ellos. En contraste, con una herramienta +centralizada, usted debe usar el programa en un modo ``sólo lectura'' a +menos que alguien le otorgue permisos para consignar cambios en el +repositorio central. Hasta entonces, no podrá almacenar sus cambios y +sus modificaciones locales correrán el riesgo de dañarse cuando trate +de actualizar su vista del repositorio. + +\subsubsection{Las bifurcaciones (forks) no son un problema} + +Se ha mencionado que las herramientas de control distribuido de +versiones albergan un riesgo a los proyectos de código abierto, puesto +que se vuelve muy sencillo hacer una ``bifurcación''\ndt{fork.} del +desarrollo del proyecto. Una bifurcación sucede cuando hay diferencias +de opinión o actitud entre grupos de desarrolladores que desemboca en +la decisión de la imposibilidad de continuar trabajando juntos. Cada +parte toma una copia más o menos completa del código fuente del +proyecto y toma su propio rumbo. + +En algunas ocasiones los líderes de las bifurcaciones reconcilian sus +diferencias. Con un sistema centralizado de control de revisiones, el +proceso \emph{técnico} de reconciliarse es doloroso, y se hace de +forma muy manual. Usted tiene que decidir qué historial de revisiones va a +``ganar'', e injertar los cambios del otro equipo en el árbol de alguna +manera. Con esto usualmente se pierde algo o todo del historial de la +revisión de alguna de las partes. + +Lo que las herramientas distribuidas hacen con respecto a las +bifurcaciones, es que las bifurcaciones son la \emph{única} forma de +desarrollar un proyecto. Cada cambio que usted hace es potencialmente +un punto de bifurcación. La gran fortaleza de esta aproximación es que +las herramientas distribuidas de control de revisiones tiene que ser +bueno al \emph{fusionar} las bifurcaciones, porque las bifurcaciones +son absolutamente fundamentales: pasan todo el tiempo. + +Si todas las porciones de trabajo que todos hacen, todo el tiempo, se +enmarcan en términos de bifurcaciones y fusiones, entonces a aquello a +lo que se refiere en el mundo del código abierto a una ``bifurcación'' +se convierte \emph{puramente} en una cuestión social. Lo que hacen las +herramientas distribuidas es \emph{disminuir} la posibilidad de una +bifurcación porque: +\begin{itemize} +\item Eliminan la distinción social que imponen las herramientas + centralizadas: aquélla entre miembros (personas con permiso de + consignar) y forasteros (los que no tienen el permiso). +\item Facilitan la reconciliación después de una bifurcación social, + porque todo lo que concierne al programa de control de revisiones es + una fusión. +\end{itemize} + +Algunas personas se resisten a las herramientas distribuidas porque +desean mantener control completo sobre sus proyectos, y creen que las +herramientas centralizadas les dan tal control. En todo caso, si este +es su parecer, y usted publica sus repositorios de CVS o Subversion, hay +muchas herramientas disponibles que pueden obtener el historial +completo (aunque sea lentamente) y recrearlo en otro sitio que usted no +controla. Siendo así un control ilusorio, puesto que está impidiendo +la fluidez de colaboración en lugar de prevenir que alguien se sienta +impulsado a obtener una copia y hacer una bifurcación con su historial. + +\subsection{Ventajas para proyectos comerciales} + +Muchos proyectos comerciales tienen grupos de trabajo distribuidos +alrededor del globo. Quienes contribuyen y están lejos de un +repositorio central verán una ejecución más lenta de los comandos y tal +vez menos confiabilidad. Los sistemas de control de revisión +comerciales intentan amortiguar estos problemas con adiciones de +replicación remota que usualmente son muy costosos y complicados de +administrar. Un sistema distribuido no padece estos problemas. Mejor +aún, puede colocar varios servidores autorizados, por ejemplo, uno por +sitio, de tal forma que no haya comunicación redundante entre +repositorios sobre enlaces de conexión costosos. + +Los sistemas de control de revisiones distribuidos tienden a ser poco +escalables. No es inusual que costosos sistemas centralizados caigan +ante la carga combinada de unas cuantas docenas de usuarios +concurrentes. De nuevo, las respuestas típicas de replicación tienden +a ser costosas y complejas de instalar y administrar. Dado que la +carga en un servidor central---si es que tiene uno---es muchas veces +menor con una herramienta distribuida (debido a que los datos están +replicados en todas partes), un solo servidor económico puede tratar +las necesidades de equipos mucho más grandes, y la replicación para +balancear la carga se vuelve cosa de guiones. + +Si tiene un empleado en el campo, se beneficiará grandemente de un +sistema distribuido de control de versiones al resolver problemas en +el sitio del cliente. La herramienta le permitirá generar +construcciones a la medida, probar diferentes arreglos de forma +independiente y buscar de forma eficiente las fuentes de fallos en el +historial y regresiones en los ambientes de los clientes, todo sin +necesidad de conectarse al servidor de su compañía. + +\section{¿Por qué elegir Mercurial?} + +Mercurial cuenta con un conjunto único de propiedades que lo hacen +una elección particularmente buena como sistema de control de +revisiones, puesto que: +\begin{itemize} +\item Es fácil de aprender y usar. +\item Es liviano. +\item Escala de forma excelente. +\item Es fácil de acondicionar. +\end{itemize} + +Si los sistemas de control de revisiones le son familiares, debería +estar listo para usar Mercurial en menos de cinco minutos. Si no, sólo va a +tomar unos pocos minutos más. Las órdenes de Mercurial y su conjunto +de características son uniformes y consistentes generalmente, y basta +con que siga unas pocas reglas generales en lugar de un montón de +excepciones. + +En un proyecto pequeño, usted puede comenzar a trabajar con Mercurial en +pocos momentos. Crear nuevos cambios y ramas, transferir cambios (localmente +o por la red); y las operaciones relacionadas con el estado y el +historial son rápidas. Mercurial buscar ser ligero y no incomodar en su +camino combinando poca sobrecarga cognitiva con operaciones +asombrosamente rápidas. + +La utilidad de Mercurial no se limita a proyectos pequeños: está +siendo usado por proyectos con centenas de miles de contribuyentes, +cada uno conteniendo decenas de miles de ficheros y centenas de +megabytes de código fuente + +Si la funcionalidad básica de Mercurial no es suficiente para usted, +es muy fácil extenderlo. Mercurial se comporta muy bien con tareas de +scripting y su limpieza interna junto con su implementación en Python +permiten añadir características fácilmente en forma de extensiones. +Hay un buen número de extensiones útiles y populares en este momento, +desde ayudar a identificar fallos hasta mejorar su desempeño. + +\section{Comparación de Mercurial con otras herramientas} + +Antes de leer, por favor tenga en cuenta que esta sección +necesariamente refleja mis propias experiencias, intereses y (tengo que +decirlo) mis preferencias. He usado cada una de las herramientas de +control de versiones listadas a continuación, y en muchos casos por +varios años. + + +\subsection{Subversion} + +Subversion es una herramienta de control de revisiones muy popular, +desarrollada para reemplazar a CVS. Tiene una arquitectura +centralizada tipo cliente/servidor. + +Subversion y Mercurial tienen comandos con nombres similares para hacer +las mismas operaciones, por lo que si le son familiares en una, será +sencillo aprender a usar la otra. Ambas herramientas son portables en +todos los sistemas operativos populares. + +Antes de la versión 1.5, Subversion no tenía soporte para fusiones. En +el momento de la escritura, sus capcidades para llevar cuenta de las +funciones son nuevas, +\href{http://svnbook.red-bean.com/nightly/en/svn.branchmerge.advanced.html#svn.branchmerge.advanced.finalword}{complicadas + y poco estables\ndt{buggy}}. + +Mercurial tiene una ventaja considerable en desempeño sobre +Subversion en cualquier operación de control de revisiones que yo haya +medido. He medido sus ventajas con factores desde dos hasta seis veces +comparando con almacenamiento de ficheros \emph{ra\_local} +Subversion~1.4.3, el cual es el método de acceso más rápido. En los +escenarios más realistas incluyendo almacenamiento con la red de por +medio, Subversion se encuentra en desventaja aún mayor. Dado que casi +todas las órdenes de Subversion deben tratar con el servidor y +Subversion no tiene utilidades de replicación adecuadas, la capacidad +del servidor y el ancho de banda se convierten en cuellos de botella +para proyectos modestamente grandes. + +Adicionalmente, Subversion tiene un sobrecosto considerable en +almacenamiento para evitar transacciones por red en algunas +operaciones, +tales como encontrar ficheros modificados (\texttt{status}) y desplegar +información frente a la revisión actual (\texttt{diff}). Como +resultado, la copia de trabajo de Subversion termina siendo del mismo +tamaño o más grande que un repositorio de Mercurial y el directorio de +trabajo, a pesar de que el repositorio de Mercurial contiene el +historial completo del proyecto. + +Subversion tiene soporte amplio de otras herramientas. Mercurial por +ahora está bastante atrás en este aspecto. Esta diferencia está +disminuyendo, y algunas de las herramientas GUI\ndt{Interfaz de + Usuario Gráfica}, eclipsan sus equivalentes de Subversion. Al igual +que Mercurial, Subversion tiene un excelente manual de usuario. + +Dado que Subversion no almacena el historial de revisiones en el +cliente, es muy bueno para administrar proyectos que tienen muchos +ficheros binarios grandes y opacos. Si consigna cincuenta revisiones +de un fichero de 10MB que no es comprimible, el esapacio en el cliente +de Subversion se mantendrá constante mientras que el espacio usado por +cualquier Sistema Distribuido de Control de Revisiones crecerá +rápidamente en proporción con el número de revisiones, debido a que +las diferencias entre cada revisión es grande. + +Adicionalmente, generalmente es difícil o más bien, imposible mezclar +diferentes versiones de un fichero binario. La habilidad de Subversion +para permitirle al usuario poner una cerradura a un fichero, de modo +que tenga un permiso exclusivo para consignar cambios, puede ser una +ventaja significativa en un proyecto donde los ficheros binarios sean +usados ampliamente. + +Mercurial puede importar el historial de revisiones de un repositorio +de Subversion. También puede exportar el historial de revisiones a un +repositorio de Subversion. De esta forma es sencillo ``dar un +vistazo'' y usar Mercurial y Subversion en paralelo antes de decidirse +a dar el paso. La conversión del historial es incremental, de modo +que puede aplicar una conversión inicial, y después conversiones +pequeñas y adicionales posteriormente para traer nuevos cambios. + +\subsection{Git} + +Git es una herramienta distribuida de control de revisiones +desarrollada para administrar el arbol del kernel de Linux. Al igual +que Mercurial los principios de su diseño fueron influenciados por +Monotone. + +Git tiene un conjunto de órdenes muy grande; en la versión~1.5.0 +ofrece~139 órdenes individuales. Tiene cierta reputación de ser +difícil de aprender. Comparado con Git, Mercurial tiene un fuerte +enfoque hacia la facilidad. + +En términos de rendimiento, Git es extremadamente rápido. En muchos +casos, es más rápido que Mercurial, por lo menos en Linux, mientras +que Mercurial se comporta mejor en otras operaciones. De todas +maneras en Windows, el desempeño y el nivel general de soporte que +ofrece Git, al momento de la escritura, está bastante atrás de +Mercurial. + +Mientras que el repositorio de Mercurial no requiere mantenimiento, el +repositorio de Git requiere frecuentes ``reempaquetados'' de sus metadatos. +Sin estos, el desempeño se degrada y el uso de espacio crece rápidamente. Un +servidor que contenga repositorios de Git que no sean reempacados +rigurosa y frecuentemente requerirá trabajo intenso de disco durante +las copias de seguridad, y ha habido situaciones en copias de +seguridad diaria que toman más de~24 horas como resultado. Un +repositorio recién reempacado de Git es un poco más pequeño que un +repositorio de Mercurial, pero un repositorio sin reempacar es varios +órdenes de magnitud más grande. + +El corazón de Git está escrito en C. Muchas órdenes de Git están +implementadas como guiones de línea de comandos o de Perl y la calidad de esos +guiones varía ampliamente. He encontrado muchas situaciones en las +cuales los guiones no tuvieron en cuenta la presencia de errores que +podrían haber sido fatales. + +Mercurial puede importar el historial de revisiones de un repositorio +de Git. + +\subsection{CVS} + +CVS es probablemente la herramienta de control de revisiones más +ampliamente usada en el planeta. Debido a su edad y su poca pulcritud +interna, ha sido ligeramente mantenida en muchos años. + +Tiene una arquitectura centralizada cliente/servidor. No agrupa +cambios relacionados en consignaciones atómicas, pemitiendo que con +facilidad la gente ``rompa la construcción'': una persona puede +consignar exitósamente parte del cambio y estar bloqueada por la +necesidad de una mezcla, forzando a otras personas a ver solamente una +porción del trabajo que estaban buscando hacer. Esto afecta también +la forma como usted trabaja con el historial del proyecto. Si quiere +ver todas las modificaciones que alguien hizo como parte de una tarea, +necesitará inspeccionar manualmente las descripciones y las marcas de +tiempo de cambio de cada fichero involucrado (esto, si usted saber +cuáles eran tales ficheros). + +CVS tiene una noción confusa de etiquetas y ramas que yo no trataría +incluso de describir. No soporta renombramiento de ficheros o +directorios adecuadamente, facilitando el corromper un +repositorio. Casi no tiene chequeo de consistencia interna, por lo +tanto es casi imposible identificar por que o cómo se corrompió un +repositorio. Yo no recomendaría un repositorio de CVS para proyecto +alguno, ni existente ni nuevo. + +Mercurial puede importar el historial de revisiones de CVS. De todas +maneras hay ciertas precauciones que aplican; las cuales también son +necesarias para cualquier herramienta importadora de historial de +CVS. Debido a la falta de atomicidad de cambios y el no versionamiento +de la jerarquía del sistema de ficheros, es imposible reconstruir +completamente el historial de CVS con precisión; hay cierto trabajo de +conjetura involucrado y los renombramientos tampoco se +mostrarán. Debido a que gran parte de la administración avanzada de +CVS tiene que hacerse manualmente y por lo tanto es proclive al error, +es común que los importadores de CVS encuentren muchos problemas con +repositorios corruptos (marcas de tiempo totalmente desubicadas y +ficheros que han permanecido con candados por más de una década son +dos de los problemas menos interesantes de los que puedo retomar de mi +experiencia personal). + +Mercurial puede importar el historial de revisiones de un repositorio +CVS. + +\subsection{Herramientas comerciales} + +Perforce tiene una arquitectura centralizada cliente/servidor sin +almacenamiento de dato alguno de caché en el lado del cliente. A diferencia de +las herramientas modernas de control de revisiones, Perforce requiere +que un usuario ejecute un comando para informar al servidor acerca de +todo fichero que se vaya a editar. + +El rendimiento de Perforce es muy bueno para equipos pequeños, pero se +degrada rápidamente cuando el número de usuarios va más allá de pocas +docenas. Instalaciones modestamente grandes de Perforce requiere la +organización de proxies para soportar la carga que sus usuarios generan. + +\subsection{Elegir una herramienta de control de revisiones} + +Con la excepción de CVS, toda las herramientas que se han listado +anteriormente tienen fortalezas únicas que las hacen valiosas de acuerdo al +tipo de trabajo. No hay una única herramienta de control de revisiones +que sea la mejor en todas las situaciones. + +Por ejemplo, Subversion es una buena elección para trabajar con +edición frecuente de ficheros binarios, debido a su naturaleza +centralizada y soporte para poner candados a ficheros. + +Personalmente encuentro las propiedades de simplicidad, desempeño, y +buen soporte de fusiones de Mercurial una combinación llamativa que ha +dado buenos frutos por varios años. + + +\section{Migrar de otra herramienta hacia Mercurial} + +Mercurial viene con una extensión llamada \hgext{convert}, que puede +importar historiales de revisiones de forma incremental desde varias +herramientas de control de revisiones. Por ``incremental'', quiero +decir que puede migrar toda el historial de un proyecto en una primera +instancia y después volver a ejecutar la migración posteriormente para +obtener los nuevos cambios que han sucedido después de la migración +inicial. + +A continuación presentamos las herramientas de revisiones que soporta +el comando \hgext{convert}: +\begin{itemize} +\item Subversion +\item CVS +\item Git +\item Darcs +\end{itemize} + +Adicionalmente, \hgext{convert} puede exportar cambios de Mercurial +hacia Subversion. Lo que hace posible probar Subversion y Mercurial +en paralelo antes de lanzarse a un migración total, sin arriesgarse a +perder trabajo alguno. + +El comando \hgxcmd{conver}{convert} es sencillo de usar. Basta con +apuntarlo hacia la ruta o el URL del repositorio fuente, opcionalmente +darle el nombre del nombre del repositorio destino y comenzará a hacer +su trabajo. Después de la conversión inicial, basta con invocar de +nuevo el comando para importar cambios nuevos. + + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/kdiff3.png Binary file es/kdiff3.png has changed diff -r 73b094b764ec -r 51b5d56744c5 es/license.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/license.tex Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/metadata.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/metadata.svg Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,337 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bitácora de cambios + Manifiesto + Bitácora de archivos + + diff -r 73b094b764ec -r 51b5d56744c5 es/mq-collab.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/mq-collab.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,428 @@ +\chapter{Usos avanzados de las Colas de Mercurial} +\label{chap:mq-collab} + +Auunque es fácil aprender los usos más directos de las Colas de +Mercurial, tener algo de disciplina junto con algunas de las +capacidadees menos usadas de MQ hace posible trabajar en entornos de +desarrollo complejos. + +En este capítulo, usaré como ejemplo una técnica que he usado para +administrar el desarrollo de un controlador de dispositivo Infiniband +para el kernel de Linux. El controlador en cuestión es grande +(al menos en lo que se refiere a controladores), con 25,000 líneas de +código esparcidas en 35 ficheros fuente. Es mantenido por un equipo +pequeño de desarrolladores. + +Aunque mucho del material en este capítulo es específico de Linux, los +mismos principios aplican a cualquier base de código de la que usted +no sea el propietario principal, y sobre la que usted necesita hacer +un montón de desarrollo. + +\section{El problema de múltiples objetivos} + +El kernel de Linux cambia con rapidez, y nunca ha sido estable +internamente; los desarrolladores hacen cambios drásticos entre +%TODO no encontré una traducción adecuada para "release". Por eso el +%cambio +versiones frecuentemente. Esto significa que una versión del +controlador que funciona bien con una versión particular del kernel ni +siquiera \emph{compilará} correctamente contra, típicamente, cualquier +otra versión. + +Para mantener un controlador, debemos tener en cuenta una buena +cantidad de versiones de Linux en mente. +\begin{itemize} +\item Un objetivo es el árbol de desarrollo principal del kernel de + Linux. En este caso el mantenimiento del código es compartido + parcialmente por otros desarrolladores en la comunidad del kernel, + %TODO drive-by. + quienes hacen modificaciones ``de-afán'' al controlador a medida que + desarrollan y refinan subsistemas en el kernel. + %TODO backport +\item También mantenemos algunos ``backports'' para versiones antiguas + del kernel de Linux, para dar soporte a las necesidades de los + clientes que están corriendo versiones antiguas de Linux que no + incorporan nuestros controladores. (Hacer el \emph{backport} de un + pedazo de código es modificarlo para que trabaje en una versión + de su entorno objetivo anterior a aquella para la cual fue escrito.) +\item Finalmente, nosotros liberamos nuestro software de acuerdo a un + cronograma que no necesariamente está alineado con el que usan los + distribuidores de Linux y los desarrolladores del kernel, así que + podemos entregar nuevas características a los clientes sin forzarlos + a actualizar kernels completos o distribuciones. +\end{itemize} + +\subsection{Aproximaciones tentadoras que no funcionan adecuadamente} + +Hay dos maneras estándar de mantener una porción de software que debe +funcionar en muchos entornos diferentes. + +La primera es mantener varias ramas, cada una pensada para un único +entorno. El problema de esta aproximación es que usted debe tener una +disciplina férrea con el flujo de cambios entre repositorios. Una +nueva característica o un arreglo de fallo deben empezar su vida en un +repositorio ``prístino'', y luego propagarse a cada repositorio de +backport. Los cambios para backports están más limitados respecto a +las ramas a las que deberían propagarse; un cambio para backport que +es aplicado a una rama en la que no corresponde probablemente hará que +el controlador no compile. + +La segunda es mantener un único árbol de código fuente lleno de +declaraciones que activen o desactiven secciones de código dependiendo +del entorno objetivo. Ya que estos ``ifdefs'' no están permitidos en +el árbol del kernel de Linux, debe seguirse algún proceso manual o +automático para eliminarlos y producir un árbol limpio. Una base de +código mantenida de esta manera se convierte rápidamente en un nido de +ratas de bloques condicionales que son difíciles de entender y +mantener. + +%TODO canónica? +Ninguno de estos enfoques es adecuado para situaciones en las que +usted no es ``dueño'' de la copia canónica de un árbol de fuentes. En +el caso de un controlador de Linux que es distribuido con el kernel +estándar, el árbol de Linux contiene la copia del código que será +considerada por el mundo como la canónica. La versión oficial de +``mi'' controlador puede ser modificada por gente que no conozco, sin +que yo siquiera me entere de ello hasta después de que los cambios +aparecen en el árbol de Linus. + +Estos enfoques tienen la debilidad adicional de dificultar la +%TODO upstream. no no es río arriba +generación de parches bien formados para enviarlos a la versión +oficial. + +En principio, las Colas de Mercurial parecen ser un buen candidato +para administrar un escenario de desarrollo como el de arriba. Aunque +este es de hecho el caso, MQ tiene unas cuantas características +adicionales que hacen el trabajo más agradable. + +\section{Aplicar parches condicionalmente mediante guardias} + +Tal vez la mejor manera de conservar la cordura con tantos entornos +objetivo es poder escoger parches específicos para aplicar para cada +situación. MQ provee una característica llamada ``guardias'' +(que se origina del comando \texttt{guards} de Quilt) que hace +precisamente ésto. Para empezar, creemos un repositorio sencillo para +experimentar. +\interaction{mq.guards.init} +Esto nos brinda un pequeño repositorio que contiene dos parches que no +tienen ninguna dependencia respecto al otro, porque tocan ficheros +diferentes. + +La idea detrás de la aplicación condicional es que usted puede +``etiquetar'' un parche con un \emph{guardia}, que simplemente es una +cadena de texto de su elección, y luego decirle a MQ que seleccione +guardias específicos para usar cuando aplique parches. MQ entonces +aplicará, u omitirá, un parche vigilado, dependiendo de los guardias +que usted haya seleccionado. + +Un parche puede tener una cantidad arbitraria de guardias; cada uno es +\emph{positivo} (``aplique el parche si este guardia es +seleccionado'') o \emph{negativo} (``omita este parche si este guardia +es seleccionado''). Un parche sin guardias siempre es aplicado. + +\section{Controlar los guardias de un parche} + +%TODO tal vez no decir determinar, sino definir? +El comando \hgxcmd{mq}{qguard} le permite determinar qué guardias +deben aplicarse a un parche, o mostrar los guardias que están en +efecto. Sin ningún argumento, el comando muestra los guardias del +parche actual de la parte más alta de la pila. +\interaction{mq.guards.qguard} +Para poner un guardia positivo en un parche, prefije el nombre del +guardia con un ``\texttt{+}''. +\interaction{mq.guards.qguard.pos} +Para poner un guardia negativo en un parche, prefije el nombre del +guardia con un ``\texttt{-}''. +\interaction{mq.guards.qguard.neg} + +\begin{note} + El comando \hgxcmd{mq}{qguard} \emph{pone} los guardias en un + parche; no los \emph{modifica}. Esto significa que si usted ejecuta + \hgcmdargs{qguard}{+a +b} sobre un parche, y luego + \hgcmdargs{qguard}{+c} en el mismo parche, el único guardia sobre el + parche después del comando será \texttt{+c}. +\end{note} + +Mercurial almacena los guardias en el fichero \sfilename{series}; la +forma en que son almacenados es fácil tanto de entender como de editar +a mano. (En otras palabras, usted no tiene que usar el comando +\hgxcmd{mq}{qguard} si no lo desea; está bien simplemente editar el +fichero \sfilename{series}) +\interaction{mq.guards.series} + +\section{Selecccionar los guardias a usar} + +%TODO tal vez no decir determinar, sino definir? +El comando \hgxcmd{mq}{qselect} determina qué guardias están activos +en cualquier momento. El efecto de esto es determinar qué parches +aplicará MQ la próxima vez que usted ejecute \hgxcmd{mq}{qpush}. No +tiene ningún otro efecto; en particular, no hace nada a los parches +que ya han sido aplicados. + +Sin argumentos, el comando \hgxcmd{mq}{qselect} lista los guardias en +efecto actualmente, uno por cada línea de salida. Cada argumento es +tratado como el nombre de un guardia a aplicar. +\interaction{mq.guards.qselect.foo} +Si está interesado, los guardias seleccionados actualmente están +almacenados en el fichero \sfilename{guards}. +\interaction{mq.guards.qselect.cat} +Podemos ver el efecto que tienen los guardias seleccionados cuando +ejecutamos \hgxcmd{mq}{qpush}. +\interaction{mq.guards.qselect.qpush} + +Un guardia no puede empezar con un caracter ``\texttt{+}'' o +``\texttt{-}''. El nombre del guardia no debe contener espacios en +blanco, pero muchos otros caracteres son aceptables. Si usted trata de +usar un guardia con un nombre inválido, MQ se quejará: +\interaction{mq.guards.qselect.error} +Cambiar los guardias seleccionados cambia los parches que son +aplicados. +\interaction{mq.guards.qselect.quux} +Usted puede ver en el ejemplo de abajo que los guardias negativos +tienen precedencia sobre los guardias positivos. +\interaction{mq.guards.qselect.foobar} + +\section{Reglas de MQ para aplicar parches} + +Las reglas que MQ usa para decidir si debe aplicar un parche son las +siguientes. +\begin{itemize} +\item Un parche sin guardias es aplicado siempre. +\item Si el parche tiene algún guardia negativo que corresponda con + cualquiera de los guardias seleccionados, se salta el parche. +\item Si el parche tiene algún guardia positivo que corresponda con + cualquiera de los guardias seleccionados, se aplica el parche. +\item Si el parche tiene guardias positivos o negativos, pero ninguno + corresponde con cualquiera de los guardias seleccionados, se salta + el parche. +\end{itemize} + +\section{Podar el entorno de trabajo} + +En el trabajo del controlador de dispositivo que mencioné +anteriormente, yo no aplico los parches a un árbol normal del kernel +de Linux. En cambio, uso un repositorio que sólo contiene una +instantánea de los ficheros fuente y de cabecera que son relevantes +para el desarrollo de Infiniband. Este repositorio tiene un~1\% del +tamaño del repositorio del kernel, por lo que es más fácil trabajar +con él. + +Luego escojo una versión ``base'' sobre la cual son aplicados los +parches. Es una instantánea del árbol del kernel de Linux en una +revisión de mi elección. Cuando tomo la instantánea, almaceno el ID de +conjunto de cambios en el mensaje de consignación. Ya que la +instantánea preserva la ``forma'' y el contenido de las partes +relevantes del árbol del kernel, puedo aplicar mis parches sobre mi +pequeño repositorio o sobre un árbol normal del kernel. + +Normalmente, el árbol base sobre el que se aplican los parches debería +ser una instantánea de un árbol de desarrollo muy reciente. Esto +facilita mucho el desarrollo de parches que puedan ser enviados al +árbol oficial con pocas o ninguna modificación. + +\section{Dividir el fichero \sfilename{series}} + +Yo categorizo los parches en el fichero \sfilename{series} en una +serie de grupos lógicos. Cada sección de parches similares empieza con +un bloque de comentarios que describen el propósito de los parches que +le siguen. + +La secuencia de grupos de parches que mantengo se muestra a +continuación. El orden de los grupos es importante; explicaré porqué +luego de que presente los grupos. +\begin{itemize} +\item El grupo ``aceptado''. Son parches que el equipo de desarrollo + ha enviado al mantenedor del subsistema Infiniband, y que él ha + aceptado, pero que no están presentes en la instantánea en la cual + está basada el repositorio pequeño. Estos son parches de + ``sólo lectura'', presentes únicamente para transformar el árbol en + un estado similar al del repositorio del mantenedor oficial. +\item El grupo ``revisar''. Parches que yo he enviado, pero sobre los + que que el mantenedor oficial ha solicitado modificaciones antes de + aceptarlos. +\item El grupo ``pendiente''. Parches que no he enviado al mantenedor + oficial, pero que ya están terminados. Estos parches serán de + ``sólo lectura'' por un buen tiempo. Si el mantenedor oficial los + acepta cuando los envíe, los moveré al final del grupo ``aceptado''. + Si él solicita que modificaciones en alguno de ellos, los moveré al + principio del grupo ``revisar''. +\item El grupo ``en proceso''. Parches que están siendo activamente + desarrollados, y no deberían ser enviados a ninguna parte aún. +\item El grupo ``backport''. Parches que adaptan el árbol de fuentes a + versiones antiguas del árbol del kernel. +\item El grupo ``no enviar''. Parches que por alguna razón nunca deben + ser enviados al mantenedor oficial del kernel. Por ejemplo, alguno + de esos parches podría cambiar las cadenas de identificación + embebidas del controlador para hacer más fácil la distinción, en + pruebas de campo, entre una versión del controlador de + salida-del-árbol y una versión entregada por un vendedor de alguna + distribución. +\end{itemize} + +Ahora volvemos a las razones para ordenar los grupos de parches en +esta manera. Quisiéramos que los parches del fondo de la pila sean tan +estables como sea posible, para no tener que revisar parches más +arriba debido a cambios de contexto. Poner los parches que nunca +cambiarán en el primer lugar del fichero \sfilename{series} sirve a +este propósito. + +También desearíamos que los parches que sabemos que debemos modificar +sean aplicados sobre un árbol de fuentes que se parezca al oficial +tanto como sea posible. Es por esto que mantenemos los parches +aceptados disponibles por una buena cantidad de tiempo. + +Los parches ``backport'' y ``no enviar'' flotan al final del fichero +\sfilename{series}. Los parches de backport deben ser aplicados encima +de todos los otros parches, y los parches ``no enviar'' pueden +perfectamente quedarse fuera del camino. + +\section{Mantener la serie de parches} + +En mi trabajo, uso varios guardias para controlar qué parches deben +ser aplicados. + +\begin{itemize} +\item Los parches ``aceptados'' son vigilados con + \texttt{accepted}. Yo habilito este guardia la mayoría de las veces. + Cuando aplico los parches sobre un árbol donde los parches ya están + %TODO no será ``desactivar este guardia''? si sí, corregir versión + %en inglés también + presentes, puedo desactivar este parche, y los parches que lo siguen + se aplicarán sin problemas. +\item Los parches que están ``terminados'', pero no han sido enviados, + no tienen guardias. Si estoy aplicando la pila de parches a una + copia del árbol oficial, no necesito habilitar ningún guardia para + obtener un árbol de fuentes razonablemente seguro. +\item Los parches que necesitan revisión antes de ser reenviados + tienen el guardia \texttt{rework}. +\item Para aquellos parches que aún están bajo desarrollo, uso + \texttt{devel}. +\item Un parche de backport puede tener varios guardias, uno para cada + versión del kernel a la que aplica. Por ejemplo, un parche que hace + backport de un segmento de código a~2.6.9 tendrá un guardia~\texttt{2.6.9}. +\end{itemize} +La variedad de guardias me brinda una flexibilidad considerable para +determinar qué tipo de árbol de fuentes acabaré por obtener. En la +mayoría de las situaciones, la selección de guardias apropiados es +automatizada durante el proceso de compilación, pero puedo ajustar +manualmente los guardias a usar para circunstancias poco comunes. + +\subsection{El arte de escribir parches de backport} + +Al usar MQ, escribir un parche de backport es un proceso simple. Todo +lo que dicho parche debe hacer es modificar una sección de código que +usa una característica del kernel que no está presente en la versión +anterior del kernel, para que el controlador siga funcionando +correctamente en esa versión anterior. + +Una meta útil al escribir un buen parche de backport es hacer parecer +que el código hubiera sido escrito para la versión vieja del kernel +que usted tiene como objetivo. Entre menos intrusivo el parche, más +fácil será entenderlo y mantenerlo. Si usted está escribiendo una +colección de parches de backport para evitar el efecto de ``nido de +ratas'' de tener muchos \texttt{\#ifdef}s (secciones de código fuente +que sólo son usados condicionalmente) en su código, no introduzca +\texttt{\#ifdef}s dependientes de versiones específicas en los +parches. En vez de eso, escriba varios parches, cada uno de ellos +haciendo cambios incondicionales, y controle su aplicación usando +guardias. + +Hay dos razones para ubicar los parches de backport en un grupo +diferente, aparte de los parches ``regulares'' cuyos efectos son +modificados por ellos. La primera es que mezclar los dos hace más +difícil usar herramientas como la extensión \hgext{patchbomb} para +automatizar el proceso de enviar los parches a un mantenedor oficial. +La segunda es que un parche de backport puede perturbar el contexto en +el que se aplica un parche regular subsecuente, haciendo imposible +aplicar el parche normal limpiamente \emph{sin} que el parche de +backport sea aplicado antes. + +\section{Consejos útiles para hacer desarrollo con MQ} + +\subsection{Organizar parches en directorios} + +Si está trabajando en un proyecto grande con MQ, no es difícil +acumular un gran número de parches. Por ejemplo, tengo un repositorio +de parches que contiene más de 250 parches. + +Si usted puede agrupar estos parches en categorías lógicas separadas, +usted puede almacenarlos en diferentes directorios si lo desea; MQ no +tiene problemas manejando nombres de parches que contienen separadores +de ruta. + +\subsection{Ver el historial de un parche} +\label{mq-collab:tips:interdiff} + +Si usted está desarrollando un conjunto de parches en un período de +tiempo grande, es una buena idea mantenerlos en un repositorio, como +se discutió en la sección~\ref{sec:mq:repo}. Si lo hace, notará +rápidamente que usar el comando \hgcmd{diff} para mirar el historial +del repositorio no es viable. Esto es debido en parte a que usted está +mirando la segunda derivada del código real (el diff de un diff), pero +también porque MQ añade ruido al proceso al modificar las marcas de +tiempo y los nombres de directorio cuando actualiza un parche. + +Sin embargo, usted puede usar la extensión \hgext{extdiff}, que es +provisto junto con Mercurial, para convertir un diff de dos versiones +de un parche en algo legible. Para hacer esto, usted necesitará un +paquete de un tercero llamado +\package{patchutils}~\cite{web:patchutils}. Éste paquete provee un +comando llamado \command{interdiff}, que muestra las diferencias entre +dos diffs como un diff. Al usarlo en dos versiones del mismo diff, +genera un diff que representa el diff de la primera a la segunda +versión. + +Usted puede habilitar la extensión \hgext{extdiff} de la manera usual, +añadiendo una línea a la sección \rcsection{extensions} de su \hgrc. +\begin{codesample2} + [extensions] + extdiff = +\end{codesample2} +El comando \command{interdiff} espera recibir los nombres de dos +ficheros, pero la extensión \hgext{extdiff} le pasa un par de +directorios al programa que ejecuta, cada uno de los cuales puede +contener una cantidad arbitraria de ficheros. Por esto necesitamos un +programa pequeño que ejecute \command{interdiff} en cada par de +ficheros de estos dos directorios. Este programa está disponible como +\sfilename{hg-interdiff} en el directorio \dirname{examples} del +repositorio de código fuente que acompaña a este libro. +\excode{hg-interdiff} + +Con el programa \sfilename{hg-interdiff} en la ruta de búsqueda de su +intérprete de comandos, puede ejecutarlo como sigue, desde dentro de +un directorio de parches MQ: +\begin{codesample2} + hg extdiff -p hg-interdiff -r A:B my-change.patch +\end{codesample2} +Ya que usted seguramente querrá usar este comando tan largo a menudo, +puede hacer que \hgext{hgext} lo haga disponible como un comando +normal de Mercurial, editando de nuevo su \hgrc. +\begin{codesample2} + [extdiff] + cmd.interdiff = hg-interdiff +\end{codesample2} +Esto le indica a \hgext{hgext} que ponga a disposición un comando +\texttt{interdiff}, con lo que usted puede abreviar la invocación +anterior de \hgxcmd{extdiff}{extdiff} a algo un poco más manejable. +\begin{codesample2} + hg interdiff -r A:B my-change.patch +\end{codesample2} + +\begin{note} + %TODO revisar redacción + El comando \command{interdiff} trabaja bien sólo si los ficheros + contra los cuales son generadas las versiones de un parche se + mantienen iguales. Si usted crea un parche, modifica los ficheros + subyacentes, y luego regenera el parche, \command{interdiff} podría + no producir ningún resultado útil. +\end{note} + +La extensión \hgext{extdiff} es útil para más que solamente mejorar la +presentación de los parches~MQ. Para leer más acerca de esto, vaya a +la sección~\ref{sec:hgext:extdiff}. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/mq-ref.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/mq-ref.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,378 @@ +\chapter{Referencia de las Colas de Mercurial} +\label{chap:mqref} + +\section{Referencia de órdenes MQ} +\label{sec:mqref:cmdref} + +Si desea dar un vistazo a las órdenes que ofrece MQ, use la orden +\hgcmdargs{help}{mq}. + +\subsection{\hgxcmd{mq}{qapplied}---imprimir los parches aplicados} + +La orden \hgxcmd{mq}{qapplied} imprime la pila actual de parches +aplicados. Los parches se imprimen en orden de antigüedad, primero +los más antiguos y después los más recientes, por lo tanto el último +parche de la lista es el que está en el ``tope''. + +\subsection{\hgxcmd{mq}{qcommit}---consignar cambios en la cola del repositorio} + +La orden \hgxcmd{mq}{qcommit} consigna cualquier cambio sobresaliente +en el repositorio \sdirname{.hg/patches}. Esta orden solamente +funciona si el directorio \sdirname{.hg/patches} es un repositorio, +p.e.~usted creó el directorio con +\hgcmdargs{qinit}{\hgxopt{mq}{qinit}{-c}} o ejecutó +\hgcmd{init} en el directorio después de correr \hgxcmd{mq}{qinit}. + +Esta orden es un atajo para \hgcmdargs{commit}{--cwd .hg/patches}. + +\subsection{\hgxcmd{mq}{qdelete}---eliminar un parche del fichero + \sfilename{series}} + +La orden \hgxcmd{mq}{qdelete} elimina la entrada del fichero +\sfilename{series} para el parche en el directorio +\sdirname{.hg/patches}. No sca el parche si ha sido aplicado. De +forma predeterminada no borra el fichero del parche; use la opción +\hgxopt{mq}{qdel}{-f} para hacerlo. + +Opciones: +\begin{itemize} +\item[\hgxopt{mq}{qdel}{-f}] Elimina el fichero del parche. +\end{itemize} + +\subsection{\hgxcmd{mq}{qdiff}---imprimir la diferencia del último + parche aplicado} + +La orden \hgxcmd{mq}{qdiff} imprime un diff del parche más +recientemente aplicado. Es equivalente a \hgcmdargs{diff}{-r-2:-1}. + +\subsection{\hgxcmd{mq}{qfold}---fusionar (``integrar'') varios parches en + uno solo} + +La orden \hgxcmd{mq}{qfold} fusiona muchos parches en el último parche +aplicado, de tal forma que el último parche aplicado es la unión de +todos los cambios de los parches en cuestión. + +Los parches a fusionar no deben haber sido aplicados; +\hgxcmd{mq}{qfold} saldrá indicando un error si alguno ha sido +aplicado. El orden en el cual los parches se pliegan es +significativo; \hgcmdargs{qfold}{a b} significa ``aplique el parche +más reciente, seguido de \texttt{a}, y seguido de \texttt{b}''. + +Los comentarios de los parches integrados se colocan al final de los +comentarios del parche destino, con cada bloque de comentarios +separado con tres asteriscos (``\texttt{*}''). Se usa la opción +\hgxopt{mq}{qfold}{-e} para editar el mensaje de consignación para el +conjunto de cambios/parches después de completarse el pliegue. + +Opciones: +\begin{itemize} +\item[\hgxopt{mq}{qfold}{-e}] Edita el mensaje de consignación y la + descripción del parche del parche que se ha integrado. +\item[\hgxopt{mq}{qfold}{-l}] Usa los contenidos del fichero dado como + el nuevo mensaje de consignación y descripción del parche para el + parche a integrar. +\item[\hgxopt{mq}{qfold}{-m}] Usa el texto dado como el mensaje de + consignación y descripción del parche para el parche integrado. +\end{itemize} + +\subsection{\hgxcmd{mq}{qheader}---desplegar el encabezado/descripción + de un parche} + +La orden \hgxcmd{mq}{qheader} imprime el encabezado o descripción de +un parche. De forma predeterminada, imprime el encabezado del último +parche aplicado. Si se da un argumento, imprime el encabezado del +parche referenciado. + +\subsection{\hgxcmd{mq}{qimport}---importar el parche de un tercero en + la cola} + +La orden \hgxcmd{mq}{qimport} añade una entrada de un parche externo +al fichero \sfilename{series} y copia el parche en el directorio +\sdirname{.hg/patches}. Añade la entrada inmediatamente después del +último parche aplicado, pero no introduce el parche. + +Si el directorio \sdirname{.hg/patches} es un repositorio, +\hgxcmd{mq}{qimport} automáticamente hace un \hgcmd{add} del parche +importado. + +\subsection{\hgxcmd{mq}{qinit}---preparar un repositorio para trabajar + con MQ} + +La orden \hgxcmd{mq}{qinit} prepara un repositorio para trabajar con +MQ. Crea un directorio llamado \sdirname{.hg/patches}. + +Opciones: +\begin{itemize} +\item[\hgxopt{mq}{qinit}{-c}] Crea \sdirname{.hg/patches} como un + repositorio por sí mismo. También crea un fichero + \sfilename{.hgignore} que ignorará el fichero \sfilename{status}. +\end{itemize} + +Cuando el directorio \sdirname{.hg/patches} es un repositorio, las órdenes +\hgxcmd{mq}{qimport} y \hgxcmd{mq}{qnew} hacen \hgcmd{add} +automáticamente a los parches nuevos. + +\subsection{\hgxcmd{mq}{qnew}---crear un parche nuevo} + +La orden \hgxcmd{mq}{qnew} crea un parche nuevo. Exige un argumento, +el nombre que se usará para tal parche. El parche recién creado está +vacío inicialmente. Se añade al fichero \sfilename{series} después +del último parche aplicado, y se introduce en el tope de ese parche. + +Si \hgxcmd{mq}{qnew} encuentra ficheros modificados en el directorio +de trabajo, rehusará crear un parche nuevo a meos que se emplee +\hgxopt{mq}{qnew}{-f} la opción (ver más adelante). Este +comportamiento le permite hacer \hgxcmd{mq}{qrefresh} al último parche +aplicado antes de aplicar un parche nuevo encima de este. + +Opciones: +\begin{itemize} +\item[\hgxopt{mq}{qnew}{-f}] Crea un parche nuevo si los contenidos + del directorio actual han sido modificados. Cualquier modificación + significativa se añade al parche recientemente creado, de tal forma + que al finalizar la orden, el directorio de trabajo no lucirá + modificado. +\item[\hgxopt{mq}{qnew}{-m}] Usa el texto dado como el mensaje de + consignación. Este texto se almacenará al principio del fichero del + parche, antes de los datos del parche. +\end{itemize} + +\subsection{\hgxcmd{mq}{qnext}---imprimir el nombre del próximo parche} + +La orden \hgxcmd{mq}{qnext} imprime el nombre del siguiente parche en +el fichero \sfilename{series} a continuación del último parche +aplicado. Este parche sería el próximo parche a aplicar si se +ejecutara la orden \hgxcmd{mq}{qpush}. + +\subsection{\hgxcmd{mq}{qpop}---sustraer parches de la pila} + +La orden \hgxcmd{mq}{qpop} elimina los parches aplicados del tope de +la pila de parches aplicados. De forma predeterminada solamente +remueve un parche. + +Esta orden elimina los conjuntos de cambios que representan los +parches sustraídos del repositorio, y actualiza el directorio de +trabajo para deshacer los efectos de los parches. + +Esta orden toma un argumento opcional, que usa como el nombre o el +índice del parche que desea sustraer. Si se da el nombre, sustraerá +los parches hasta que el parche nombrado sea el último parche +aplicado. Si se da un número, \hgxcmd{mq}{qpop} lo trata como un +índice dentro del fichero \sfilename{series}, contando desde +cero (no cuenta las líneas vacías o aquellas que sean únicamente +comentarios). Sustrae los parches hasta que el parche identificado +por el índice sea el último parche aplicado. + +La orden \hgxcmd{mq}{qpop} no lee o escribe parches en el fichero +\sfilename{series}. \hgxcmd{mq}{qpop} se constituye por tanto en una +forma segura de sustraer un parche del fichero \sfilename{series} o un +parche que ha eliminado o renombrado completamente. En los dos +últimos casos, use el nombre del parche tal como lo hizo cuando lo +aplicó. + +De forma predeterminada, la orden \hgxcmd{mq}{qpop} no sustraerá +parche alguno si el directorio de trabajo ha sido modificado. Puede +modificar este comportamiento con la opción \hgxopt{mq}{qpop}{-f}, que +revierte todas las modificaciones del directorio de trabajo. + +Opciones: +\begin{itemize} +\item[\hgxopt{mq}{qpop}{-a}] Sustrae todos los parches aplicados. + Restaura el repositorio al estado antes de haber aplicado parche alguno. +\item[\hgxopt{mq}{qpop}{-f}] Revertir forzadamente cualquier + modificación del directorio de trabajo cuando se hace sustracciones. +\item[\hgxopt{mq}{qpop}{-n}] Sustraer un parche de la cola dado un nombre. +\end{itemize} + +La orden \hgxcmd{mq}{qpop} elimina una línea del final del fichero +\sfilename{status} por cada parche que se sustrae. + +\subsection{\hgxcmd{mq}{qprev}---imprimir el nombre del parche anterior} + +La orden \hgxcmd{mq}{qprev} imprime el nombre del parche en el fichero +\sfilename{series} que está antes del último parche aplicado. Este +se volverá el último parche aplicado si ejecuta \hgxcmd{mq}{qpop}. + +\subsection{\hgxcmd{mq}{qpush}---introducir parches a la pila} +\label{sec:mqref:cmd:qpush} + +La orden \hgxcmd{mq}{qpush} añade parches a la pila. De forma +predeterminada añade solamente un parche. + +Esta orden crea un conjunto de cambios que representa cada parche +aplicado y actualiza el directorio de trabajo aplicando los efectos de +los parches. + +Los datos predeterminados cuando se crea un conjunto de cambios +corresponde a: +\begin{itemize} +\item La fecha de consignación y zona horaria corresponden a la hora + actual de la zona. Dado que tales datos se usan para computar la + identidad de un conjunto de cambios, significa que si hace + \hgxcmd{mq}{qpop} a un parche y \hgxcmd{mq}{qpush} de nuevo, el + conjunto de cambios que introduzca tendrá una identidad distinta a + la del conjunto de cambios que sustrajo. +\item El autor es el mismo que el predeterminado usado por la orden + \hgcmd{commit}. +\item El mensaje de consignación es cualquier texto del fichero del + parche que viene antes del primer encabezado del diff. Si no hay + tal texto, un mensaje predeterminado se sua para identificar el + nombre del parche. +\end{itemize} +Su un parche contiene un encabezado de parche de Mercurial (XXX add +link), la información en el encabezado del parche tiene precedencia +sobre el predeterminado. + +Opciones: +\begin{itemize} +\item[\hgxopt{mq}{qpush}{-a}] Introduce todos los parches que no han + sido aplicados del fichero \sfilename{series} hasta que no haya nada + más para introducir. +\item[\hgxopt{mq}{qpush}{-l}] Añade el nombre del parche al final del + mensaje de consignación +\item[\hgxopt{mq}{qpush}{-m}] Si un parche no se aplica limpiamente, + usa la entrada para un parche en otra cola almacenada para computar + los parámetros en una fusión de tres, y aplica una fusión de tres + fuentes usando la maquinaria usual de Mercurial. Usa la resolución + de la fusión como el contenido del parche nuevo. +\item[\hgxopt{mq}{qpush}{-n}] Usa la cola mencionada si se está + fusionando en la introducción. +\end{itemize} + +La orden \hgxcmd{mq}{qpush} lee, pero no modifica el fichero +\sfilename{series}. Añade al final del fichero \hgcmd{status} una +línea por cada parche que se introduce. + +\subsection{\hgxcmd{mq}{qrefresh}---actualiza el último parche aplicado} + +La orden \hgxcmd{mq}{qrefresh} actualiza el último parche aplicado. +Modifica el parche, elimina el último conjunto de cambios que +representó el parche, y crea un nuevo conjunto de cambios para +representar el parche modificado. + +La orden \hgxcmd{mq}{qrefresh} busca las siguientes modificaciones: +\begin{itemize} +\item Los cambios al mensaje de consignación, p.e.~el texto antes del + primer encabezado de diff en el fichero del parche, se replejan en + el nuevo conjunto de cambios que representa el parche. +\item Las modificaciones a los ficheros a los que se les da + seguimiento en el directorio de trabajo se añade al parche. +\item Los cambios a los ficheros a los que se les da seguimiento con + \hgcmd{add}, \hgcmd{copy}, \hgcmd{remove}, o \hgcmd{rename}. Se + añaden al parche los ficheros añadidos, copiados y renombrados, + mientras que los ficheros eliminados y las fuentes renombradas se + eliminan. +\end{itemize} + +Incluso si \hgxcmd{mq}{qrefresh} no detecta cambios, de todas maneras +recrea el conjunto de cambios que representa el cambio. Esto causa +que la identidad del conjunto de cambios difiera del conjunto de +cambios previo que identificó al parche. + +Opciones: +\begin{itemize} +\item[\hgxopt{mq}{qrefresh}{-e}] Modificar la descripción de la + consignación y el parche con el editor de texto preferido. +\item[\hgxopt{mq}{qrefresh}{-m}] Modificar el mensaje de consignación + y la descripción del parche con el texto dado. +\item[\hgxopt{mq}{qrefresh}{-l}] Modificar el mensaje de consignación + y la descripción del parche con el texto del fichero dado. +\end{itemize} + +\subsection{\hgxcmd{mq}{qrename}---renombrar un parche} + +La orden \hgxcmd{mq}{qrename} renombra un parche y cambia la entrada +del parche en el fichero \sfilename{series}. + +Con un argumento sencillo, \hgxcmd{mq}{qrename} renombra el último +parche aplicado. Con dos argumentos, renombra el primer argumento con +el segundo. + +\subsection{\hgxcmd{mq}{qrestore}---restaurar el estado almacenado de + la cola} + +XXX No idea what this does. + +\subsection{\hgxcmd{mq}{qsave}---almacena el estado actual de la cola} + +XXX Likewise. + +\subsection{\hgxcmd{mq}{qseries}---imprime la serie completa de parches} + +La orden \hgxcmd{mq}{qseries} imprime la serie completa de parches del +fichero \sfilename{series}. Imprime solamente los nombres de los +parches sin las líneas en blanco o comentarios. Imprime primero el +primero y de último, el último aplicado. + +\subsection{\hgxcmd{mq}{qtop}---imprime el nombre del parche actual} + +\hgxcmd{mq}{qtop} imprime el nombre del último parche aplicado. + +\subsection{\hgxcmd{mq}{qunapplied}---imprimir los parches que aún no + se han aplicado} + +La orden \hgxcmd{mq}{qunapplied} imprime los nombres de los parches +del fichero \sfilename{series} que todavía no han sido aplicados. Los +imprime de acuerdo al orden en el cual serían introducidos. + +\subsection{\hgcmd{strip}---remover una revisión y sus descendientes} + +La orden \hgcmd{strip} remueve una revisión, y todos sus descendientes +del repositorio. Deshace los efectos de las revisiones removidas del +repositorio, y actualiza el directorio de trabajo hasta el primer +padre de la revisión removida. + +La orden \hgcmd{strip} almacena una copia de segurida de los conjuntos +de cambios en un agrupamiento, de forma tal que puedan ser reaplicados +en caso de que se hayan removido por equivocación. + +Opciones: +\begin{itemize} +\item[\hgopt{strip}{-b}] Almacenar conjuntos de cambios no + relacionados que se han mezclado con los conjuntos de cambios que + están en franjas con el agrupamiento de copia de seguridad. +\item[\hgopt{strip}{-f}] Si una rama tiene varias ramas principales + remueve todos los frentes. XXX This should be renamed, y usa + \texttt{-f} para desagrupar revisiones cuando hay cambios pendientes. +\item[\hgopt{strip}{-n}] No almacene la copia de seguridad agrupada. +\end{itemize} + +\section{Referencia de ficheros de MQ} + +\subsection{El fichero \sfilename{series}} + +El fichero \sfilename{series} contiene una lista de los nombres de +todos los parches que MQ puede aplicar. Se representa como una lista +de nombres, uno por línea. Se ignora el espacio en blanco al +principio y al final. + +Las líneas pueden contener comentario. Un comentario comienza con el +caracter ``\texttt{\#}'', y va hasta el final de la línea. Se ignoran +las líneas vacías y las que solamente contengan comentarios. + +En algún momento podría editar el fichero \sfilename{series} a mano, +por tal motivo se admiten comentarios y líneas en blanco como se +menciono anteriormente. Por ejemplo, puede poner en comentario un +parche temporalmente y \hgxcmd{mq}{qpush} omitirá tal parche cuando +los aplique. También puede cambiar el orden en el cual se aplican los +parches, reordenando las entradas en el fichero \sfilename{series}. + +También es posible colocar el fichero \sfilename{series} bajo control +de revisiones; también es favorable colocar todos los parches que refiera +bajo control de revisiones. Si crea un directorio de parches con la +opción \hgxopt{mq}{qinit}{-c} de \hgxcmd{mq}{qinit}, esto se hará +automáticamente. + +\subsection{El fichero \sfilename{status}} + +El fichero \sfilename{status} contiene los nombres y los hashes de los +conjuntos de cambios de todos los parches que MQ ha aplicado. A +diferencia del fichero \sfilename{series}, este NO ha sido diseñado +para ser editado. No debería colocar este fichero bajo el control de +revisiones o modificarlo de forma alguna. MQ lo usa estrictamente +para administración interna. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/mq-stack.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/mq-stack.svg Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/mq.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/mq.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,1103 @@ +\chapter{Administración de cambios con Colas de Mercurial} +\label{chap:mq} + +\section{El problema de la administración de parches} +\label{sec:mq:patch-mgmt} + +Un escenario frecuente: usted necesita instalar un paquete de software +desde las fuentes, pero encuentra un fallo que debe arreglar antes de +poder comenzar a usarlo. Hace sus cambios, y se olvida del paquete +por un tiempo, unos meses después necesita actualizar a una nueva +versión del paquete. Si la nueva versión del paquete todavía tiene el +fallo, debe extraer su arreglo del árbol de fuentes anteriores y +aplicarlo a la nueva versión. Una tarea tediosa en la cual es fácil +equivocarse. + +Este es un caso simple del problema del ``manejo de parches''. Usted +tiene un árbol de fuentes del ``mantenedor principal'' que no puede +cambiar: necesita hacer algunos cambios locales sobre el árbol +principal; y desearía poder mantener tales cambios separados, de forma +tal que pueda aplicarlos a versiones más nuevas del árbol principal. + +El problema de administración de parches surge en muchas situaciones. +Probablemente la más visible es cuando un usuario de un proyecto de +software de fuentes abiertas contribuye con un arreglo de un fallo o +una nueva característica a los mantenedores del proyecto en la forma +de un parche. + +Aquellos que distribuyen sistemas operativos que incluyen programas +abiertos usualmente requieren hacer cambios en los paquetes que +distribuyen de tal forma que se armen apropiadamente en sus ambientes. + +Cuando hay pocos cambios por mantener, es muy sencillo administrar un +solo parche con los programas estándar \command{diff} y +\command{patch} (ver la sección~\ref{sec:mq:patch} para ver cómo +emplear tales herramientas). Cuando la cantidad de cambios comienza a +crecer, tiene sentido mantener parches como ``porciones de trabajo'' +individual, de forma que cada cambio contiene solamente un arreglo de +un fallo (el parche puede modificar varios ficheros, pero está +``haciendo una sola cosa''), y puede tener cierta cantidad de tales +parches para diferentes fallos y cambios locales. En esta situación, +si envía un parche que arregla un fallo a los mantenedores principales +de un paquete y ellos incluyen su arreglo en una publicación +posterior, puede deshacerse de tal parche cuando se actualice a la +nueva versión. + +Mantener un solo parche frente a un árbol principal es algo tedioso y +es fácil equivocarse, pero no es difícil. Aunque, la complejidad del +problema crece rápidamente a medida que la cantidad de parches que +tiene que mantener crece. Con más que una pequeña cantidad de +cambios, entender cuáles ha aplicado se convierte de algo desordenado +a algo avasallante. + +Afortunadamente Mercurial provee una extensión poderos: Colas de +Mercurial (o simplemente ``MQ''), que simplifica en gran medida el +problema de administración de parches. + +\section{La prehistoria de las Colas de Mercurial} +\label{sec:mq:history} + +A finales de los 90s, muchos desarrolladores del núcleo de Linux +comenzaron a mantener ``series de parches'' que modificaron el +comportamiento del núcleo de Linux. Algunos se enfocaban en +estabilidad, otros en aumentar las características, y otros un poco +más especulativos. + +Los tamaños de las series de parches crecieron rápidamente. En el +2002, Andrew Morton publicó algunos guiones de línea de órdenes que +estuvo usando para automatizar la tarea de administrar su cola de +parches. Andrew usó exitósamente tales guiones para administrar +centenas (a veces millares) de parches en el núcleo de Linux. + +\subsection{Trabajar parches con quilt} +\label{sec:mq:quilt} + +A comienzos del 2003, Andreas Gruenbacher y Martin Quinson tomaron la +aproximación de los guiones de Andrew y publicaron una herramienta +llamada +``patchwork quilt''~\cite{web:quilt}, o simplemente ``quilt'' +(ver~\cite{gruenbacher:2005} el paper que lo describe). Dado que +quilt automatizaba sustancialmente la administración de parches, fue +adoptado en gran medida por desarrolladores de programas abiertos. + +Quilt maneja una \emph{pila de parches} sobre un árbol de directorios. +Para comenzar, usted le indica a quilt que administre un árbol de +directorios, le indica qué ficheros manejar; Este almacena los nombres +y los contenidos de estos ficheros. Para arreglar un fallo, usted +crea un nuevo parche (con una sola orden), edita los ficheros que está +arreglando y ``refresca'' el parche. + +El paso de refresco hace que quilt revise el árbol de directorios; +actualiza el parche con todos los cambios que usted haya hecho. Puede +crear otro parche sobre el primero, que hará seguimiento de los +cambios requeridos para modificar el árbol desde ``el árbol con un +parch aplicado'' a un ``árbol con dos parches aplicados''. + +Usted puede \emph{elegir} qué cambios desea aplicar al árbol. Si +``pop''\ndt{saca} un parche, los cambios hechos por tal parchve +desapareceŕan del árbol de directorios. Quilt recuerda qué parches ha +sacado, para que pueda ``introducirlos''\ndt{push} posteriormente, así el +árbol de directorios se restaurará con las modificaciones que vienen +del parche. Lo más importante es que puede ejecutar la orden +``refresh'' en cualquier momento, y el último parche será +actualizado. Esto significa que puede, en cualquier momento, cambiar +qué parches serán aplicados y qué modificaciones hacen ellos. + +Quilt no tiene nada que ver con herramientas de control de versiones, +y puede trabajar bien sobre un conjunto de fuentes que viene de un +fichero comprimido y empaquetado o una copia de trabajo de Subversion. + +\subsection{Pasar de trabajo con parches con Quilt hacia Colas de Mercurial} +\label{sec:mq:quilt-mq} + +A mediados de 2005, Chris Mason tomó las características de quilt y +escribió una extensión que llamó Colas de Mercurial\ndt{Mercurial +Queues}, que proporcionó un comportamiento a Mercurial al estilo +quilt. + +La diferencia clave entre quilt y MQ es que quilt no sabe nada acerca +del sistema de control de revisiones, mientras que MQ está +\emph{integrado} con Mercurial. Cada parche que usted introduce se +representa como un conjunto de cambios en Mercurial. Si sustrae un +parche, el conjunto de cambios desaparece.\ndt{introduce originalmente es +push y pop es sustraer en este contexto, usaremos el original en inglés +cuando encontremos que facilita la comprensión} + +Dado que quilt no se preocupa por las herramientas de control de +revisiones, continúa siendo una porción de software tremendamente útil +para aquellas situaciones en las cuales no puede usar Mercurial y MQ. + +\section{La gran ventaja de MQ} + +No puedo sobreestimar el valor que MQ ofrece en la unificación de +parches y el control de revisiones. + +La principal razón por la cual los parches han persistido en el mundo +del software libre y de fuentes abiertas--a pesar de la creciente +disponibilidad de herramientas poderosas de control de revisiones-- es +la \emph{agilidad} que ofrecen. + +Las herramientas tradicionales de control de revisiones llevan un +registro permanente e irreversible de todo lo que usted hace. A pesar +de que esto tiene gran valor, también es bastante sutil. Si requiere +realizar un experimento ((((wild-eyed)))), debe ser cuidadoso en cómo +lo hace, o puede dejar trazas innecesarias--o peor aún, +desconcertantes o desestabilizantes--- de los pasos y errores en el +registro de revisiones de forma permanente. + +En contraste, con la cohesión de MQ con el control de revisiones +distribuidos y los parches, resulta más sencillo aislar su trabajo. +Sus parches viven encima del historial de revisiones normales, y +puede hacer que ellos desaparezcan o reaparezcan cuando lo desee. Si +no le gusta un parche, puede desecharlo. Si un parche no satisface +todo lo que usted desea, puede arreglarlo---tantas veces como lo +requiera, hasta que lo haya refinado lo suficiente hacia sus +expectativas. + +Por ejemplo, la integración de parches con el control de revisiones +hace que el entender los parches y revisar sus efectos---y sus +interacciones con el código en el cuál están enlazados--- sea +\emph{mucho} más sencillo. Dado que todo parche que se aplique tiene +un conjunto de cambios asociado, puede usar +\hgcmdargs{log}{\emph{filename}} para ver qué conjuntos de cambios y +parches afectaron un fichero. Puede usar la orden \hgext{bisect} para +hacer una búsqueda binaria sobre todos los conjuntos de cambios y +parches aplicados para ver dónde se introdujo un fallo o dónde fue +arreglado. Puede usar la orden \hgcmd{annotate} para ver qué +conjuntos de cambios o parches modificaron una línea particular de un +fichero fuente. Y mucho más. + +\section{Entender los parches} +\label{sec:mq:patch} + +Dado que MQ no esconde su naturaleza parche-céntrica, es muy útil para +entender de qué se tratan los parches, y un poco acerca de las +herramientas que trabajan con ellos. + +La orden de Unix tradicional \command{diff} compara dos ficheros, e +imprime una lista de diferencias de sus líneas. La orden +\command{patch} entiende esas diferencias como \emph{modificaciones} +para construir un fichero. Vea en la figura~\ref{ex:mq:diff} un +ejemplo sencillo de tales órdenes en acción. + +\begin{figure}[ht] + \interaction{mq.dodiff.diff} + \caption{Uso sencillo de las órdenes \command{diff} y \command{patch}} + \label{ex:mq:diff} +\end{figure} + +El tipo de fichero que \command{diff} genera (y que \command{patch} +toma como entrada) se llama un ``parche'' o un ``diff''; no hay +diferencia entre un parche y un diff. (Usaremos el término ``parche'', +dado que es el que más comunmente se usa.) + +Un parche puede comenzar con un texto arbitrario; la orden \command{patch} +ignora este texto, pero MQ lo usa como el mensaje de consignación +cuando se crean conjuntos de cambios. Para encontrar el inicio del +contenido de un parche, la orden \command{patch} busca la primera +línea que comience con la cadena ``\texttt{diff~-}''. + +MQ trabaja con diffs \emph{unificados} (\command{patch} acepta varios +formatos de diff adicionales, pero MQ no). Un diff unificado contiene +dos clases de encabezados. El \emph{encabezado de fichero} describe +el fichero que se está modificando; contiene el nombre del fichero a +modificar. Cuando \command{patch} ve un nuevo encabezado de fichero, +busca un fichero con ese nombre para modificarlo. + +Después del encabezaado vienen varios \emph{trozos}. Cada trozo +comienza con un encabezado; que identifica el rango de líneas del +fichero que el trozo debe modificar. Después del encabezado, un trozo +comienza y termina con unas pocas líneas (usualmente tres) de texto del +fichero que no han sido modificadas; las cuales llamamos el +\emph{contexto} del trozo. Si solamente hay una pequeña cantidad de +contexto entre trozos sucesivos, \command{diff} no imprime un nuevo +encabezado para el trozo, continua integrando los trozos, con unas +líneas de contexto entre las modificaciones. + +Cada línea de contexto comienza con un caracter de espacio. En el +trozo, si una línea comienza con ``\texttt{-}'' significa ``elimine +esta línea'', si la línea comienza con un ``\texttt{+}'' significa +``inserte esta línea''. Por ejemplo, una línea que se modifica se +representa con una línea eliminada y una línea insertada. + +Retomaremos aspectos más sutiles acerca de parches posteriormente (en +la sección~\ref{sec:mq:adv-patch}), pero en el momento usted ya +debería tener suficiente información para usar MQ. + +\section{Comenzar a usar Colas de Mercurial} +\label{sec:mq:start} + +Dado que MQ está implementado como una extensión, debe habilitarla +explícitamente antes de comenzar a usarla. (No necesita descargar +nada; MQ viene con la distribución estándar de Mercurial.) Para +habilitar MQ, edite su fichero \tildefile{.hgrc}, y añada las líneas +de la figura~\ref{ex:mq:config}. + +\begin{figure}[ht] + \begin{codesample4} + [extensions] + hgext.mq = + \end{codesample4} + \label{ex:mq:config} + \caption{Líneas a añadir en \tildefile{.hgrc} para habilitar la extensión MQ} +\end{figure} + +Cuando la extensión esté habilitada, aparecerán varios comandos. Para +verificar que la extensión está trabajando, puede usar \hgcmd{help} +para ver si la orden \hgxcmd{mq}{qinit} está disponible; vea un +ejemplo en la figura~\ref{ex:mq:enabled}. + +\begin{figure}[ht] + \interaction{mq.qinit-help.help} + \caption{Cómo verificar que MQ está habilitado} + \label{ex:mq:enabled} +\end{figure} + +Puede usar MQ en \emph{cualquier} repositorio de Mercurial, y sus +comandos solamente operarán con tal repositorio. Para comenzar, basta +con preparar el repositorio con la orden \hgxcmd{mq}{qinit} (ver la +figura~\ref{ex:mq:qinit}). Esta orden crea un directorio vacío +llamado \sdirname{.hg/patches}, donde MQ mantendrá sus metadatos. Como +otras ordenes de Mercurial, la orden \hgxcmd{mq}{qinit} no imprime +nada cuando es exitosa. + +\begin{figure}[ht] + \interaction{mq.tutorial.qinit} + \caption{Preparar un repositorio para usar MQ} + \label{ex:mq:qinit} +\end{figure} + +\begin{figure}[ht] + \interaction{mq.tutorial.qnew} + \caption{Crear un nuevo parche} + \label{ex:mq:qnew} +\end{figure} + +\subsection{Crear un nuevo parche} + +Para comenzar a trabajar en un nuevo parche use la orden +\hgxcmd{mq}{qnew}. Esta orden recibe un argumento, el nombre del +parche a crear. MQ lo usará como el nombre del fichero en el +directorio \sdirname{.hg/patches}, como puede apreciarlo en la +figura~\ref{ex:mq:qnew}. + +También hay otros dos nuevos ficheros en el directorio +\sdirname{.hg/patches}: \sfilename{series} y \sfilename{status}. El +fichero \sfilename{series} lista todos los parches de los cuales MQ +tiene noticia para este repositorio, con un parche por línea. +Mercurial usa el fichero \sfilename{status} para mantener registros +interns; da seguimiento a todos los parches que MQ ha \emph{aplicado} +en el repositorio. + +\begin{note} + En ciertas ocasiones usted querrá editar el fichero + \sfilename{series} a mano; por ejemplo, cambiar el orden en que se + aplican ciertos parches. A pesar de esto, es una mala idea editar + manualmente el fichero \sfilename{status}, dado que es fácil + desorientar a MQ acerca de lo que está pasando. +\end{note} + +Una vez que haya creado un nuevo parche, puede editar los ficheros en +el directorio de trabajo, como lo haría usualmente. Toda las órdenes +que de a Mercurial, tales como \hgcmd{diff} y \hgcmd{annotate}, +trabajarán de la misma forma como lo han hecho antes. + +\subsection{Refrescar un parche} + +Cuando usted llega a un punto en el cual desea guardar su trabajo, use +la orden \hgxcmd{mq}{qrefresh} (figura~\ref{ex:mq:qnew}) para +actualizar el parche en el cual está trabajando. Esta orden almacena +los cambios que haya hecho al directorio actual de trabajo en su +parche, y almacena el conjunto de cambios correspondiente que contiene +los cambios. + +\begin{figure}[ht] + \interaction{mq.tutorial.qrefresh} + \caption{Refrescar un parche} + \label{ex:mq:qrefresh} +\end{figure} + +Puede ejecutar la orden \hgxcmd{mq}{qrefresh} tan seguido como quiera, +y es una buena forma de ``colocar marcas'' a su trabajo. Refresque su +parche en momentos oportunos; intente un experimento; si el +experimento no funciona, Use \hgcmd{revert} sobre sus modificaciones +para volver al refresco anterior. + +\begin{figure}[ht] + \interaction{mq.tutorial.qrefresh2} + \caption{Refrescar un parche muchas veces para acumular cambios} + \label{ex:mq:qrefresh2} +\end{figure} + +\subsection{Aplicar un parche tras otro y dar seguimiento} + +Cuando haya terminado de trabajar en un parche, o necesite trabajar en +otro, puede usar la orden \hgxcmd{mq}{qnew} para crear un nuevo +parche. Mercurial aplicará este parche sobre su parche anterior. +Para un ejemplo, ver la figura~\ref{ex:mq:qnew2}. Note que el parche +contiene los cambios en nuestro parche anterior como parte de su +contexto (lo verá más claramente en la salida de \hgcmd{annotate}). + +\begin{figure}[ht] + \interaction{mq.tutorial.qnew2} + \caption{Aplicar un parche después del primero} + \label{ex:mq:qnew2} +\end{figure} + +Hasta ahora, con excepción de \hgxcmd{mq}{qnew} y +\hgxcmd{mq}{qrefresh}, hemos sido cuidadosos para aplicar únicamente +órdenes usuaales de Mercurial. De todas maneras, MQ ofrece muchos +comandos que son más sencillos de usar cuando esté pensando acerca de +parches, como se puede ver en la figura~\ref{ex:mq:qseries}: + +\begin{itemize} +\item La orden \hgxcmd{mq}{qseries} lista cada parche del cual MQ + tiene noticia en este repositorio, desde el más antiguo hasta el más + nuevo (El último \emph{creado}). +\item La orden \hgxcmd{mq}{qapplied} lista cada parche que MQ haya + \emph{aplicado} en este repositorio, de nuevo, desde el más antiguo + hasta el más nuevo (El aplicado más recientemente). +\end{itemize} + +\begin{figure}[ht] + \interaction{mq.tutorial.qseries} + \caption{Entender la pila de parches con \hgxcmd{mq}{qseries} y + \hgxcmd{mq}{qapplied}} + \label{ex:mq:qseries} +\end{figure} + +\subsection{Manipular la pila de parches} + +La discusión previa indicó que debe haber una diferencia entre los +parches ``conocidos'' y ``aplicados'', y efectivamente la hay. MQ +puede manejar un parche sin que este haya sido aplicado al +repositorio. + +Un parche \emph{aplicado} tiene su correspondiente conjunto de cambios +en el repositorio, y los efectos del parche y el conjunto de cambios +son visibles en el directorio de trabajo. Puede deshacer la +aplicación de un parche con la orden \hgxcmd{mq}{qpop}. MQ +\emph{sabe acerca de}, o maneja un parche sustraído, pero el parche ya +no tendrá un conjunto de cambios correspondientes en el repositorio, y +el directorio de trabajo no contendrá los cambios hechos por el +parche. La figura~\ref{fig:mq:stack} ilustra la diferencia entre +parches aplicados y seguidos. + +\begin{figure}[ht] + \centering + \grafix{mq-stack} + \caption{Parches aplicados y no aplicados en la pila de parches de MQ} + \label{fig:mq:stack} +\end{figure} + +Puede reaplicar un parche no aplicado o sustraído con la orden +\hgxcmd{mq}{qpush}. Esto crea un nuevo conjunto de cambios +correspondiente al parche, y los cambios del parche estarán presentes +de nuevo en el directorio de trabajo. Vea ejemplos de +\hgxcmd{mq}{qpop} y \hgxcmd{mq}{qpush} en acción en la +figura~\ref{ex:mq:qpop}. Vea que hemos sustraído uno o dos parches, +la salida de\hgxcmd{mq}{qseries} continúa igual, mientras que +\hgxcmd{mq}{qapplied} ha cambiado. + +\begin{figure}[ht] + \interaction{mq.tutorial.qpop} + \caption{Modificar la pila de parches aplicados} + \label{ex:mq:qpop} +\end{figure} + +\subsection{Introducir y sustraer muchos parches} + +Mientras que \hgxcmd{mq}{qpush} y \hgxcmd{mq}{qpop} operan sobre un +único parche cada vez, puede introducir y sustraer varios parches de +una vez. La opción \hgxopt{mq}{qpush}{-a} de \hgxcmd{mq}{qpush} +introduce todos los cambios que no hayan sido aplicados, mientras que +la opción \hgxopt{mq}{qpop}{-a} de \hgxcmd{mq}{qpop} sustrae todos los +cambios aplicados. (Vea la sección~\ref{sec:mq:perf} más adelante +en la cual se explican otras formas de de introducir y sustraer varios +cambios.) + +\begin{figure}[ht] + \interaction{mq.tutorial.qpush-a} + \caption{Pushing all unapplied patches} + \label{ex:mq:qpush-a} +\end{figure} + +\subsection{Medidas de seguridad y cómo saltarlas} + +Muchas órdenes MQ revisan el directorio de trabajo antes de hacer +cualquier cosa, y fallan si encuentran alguna modificación. Lo hacen +para garantizar que usted no pierda cambio alguno de los que haya +hecho, pero que no hayan sido incorporados en algún parche. La +figura~\ref{ex:mq:add} ilusta esto; la orden \hgxcmd{mq}{qnew} no +creará un nuevo parche si hay cambios notorios, causados en este caso +por aplicado la orden \hgcmd{add} a \filename{file3}. + +\begin{figure}[ht] + \interaction{mq.tutorial.add} + \caption{Crear un parche a la fuerza} + \label{ex:mq:add} +\end{figure} + +Las órdenes que revisan el directorio actual cuentan con una opción +``Se lo que estoy haciendo'', que siempre está nombrada como +\option{-f}. El significado exacto de \option{-f} depende de la +orden. Por ejemplo, \hgcmdargs{qnew}{\hgxopt{mq}{qnew}{-f}} +incorporarán cualquier cambio notorio en el nuevo parche que crea pero +\hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-f}} revertirá las modificaciones a +cualquier fichero que haya sido afectado por el parche que está siendo +sustraído. ¡Asegúrese de leer la documentación de la opción \option{-f} +de cada comando antes de usarla! + +\subsection{Trabajar con varios parches a la vez} + +La orden \hgxcmd{mq}{qrefresh} siempre refresca el \emph{último} +parche aplicado. Esto significa que usted puede suspender su trabajo +en un parche (refrescándolo), sustraerlo o introducirlo para lograr +que otro parche esté de último y trabajar en \emph{ese} parche por un +rato. + +A continuación un ejemplo que ilustra cómo puede usar esta habilidad. +Digamos que está desarrollando una nueva característica en dos +parches. El primero es un cambio en la parte fundamental de su +programa, y el segundo--sobre el primero---cambia la interfaz de +usuario para usar el código que ha añadido a la parte fundamental. Si +ve que hay un fallo en la parte fundamental mientras está trabajando +en el parche de UI\ndt{Interfaz de Usuario, User Interface en inglés}, es fácil arreglar la parte fundamental. +Simplemente use \hgxcmd{mq}{qrefresh} sobre el parche de la UI para +guardar los cambios de su trabajo en progreso, y use \hgxcmd{mq}{qpop} +para sacar sustraer el parche de la parte fundamental. Arregla el +fallo sobre la parte fundamental, aplique \hgxcmd{mq}{qrefresh} sobre +el parche fundamental, y aplique \hgxcmd{mq}{qpush} sobre el parche de +UI para continuar donde había quedado. + +\section{Más acerca de parches} +\label{sec:mq:adv-patch} + +MQ usa la orden GNU \command{patch} para aplicar los parches, por lo +tanto es útil conocer ciertos detalles de cómo trabaja +\command{patch}, y también acerca de los parches. + +\subsection{La cantidad de franjas} + +Si ve el encabezado de un parche, notará que la ruta al fichero tiene +un componente adicional al principio, que no está presente en la +ruta. Esta es una traza de cómo generaba anteriormente los parches la +gente (algunos aún lo hacen, pero es raro con las herramientas de +control de revisiones del actuales). + +Alicia desempaquetaría un comprimido, editaría sus ficheros, y querría +crear un parche. Por lo tanto ella renombraría su directorio de +trabajo, desempacaría el comprimido de nuevo (para lo cual necesitó el +renombramiento), y usaría las opciones \cmdopt{diff}{-r} y +\cmdopt{diff}{-N} de \command{diff} para generar recursivamente un +parche entre el directorio original y el modificado. El resultado +sería que el nombre del directorio original estaría al principio de +toda ruta en cada encabezado de fichero, y el nombre del directorio +modificado estaría al frente de la porción derecha de la ruta del +fichero. + +Como alguien que reciba un parche de Alicia en la red podría obtener +dos directorios, uno original y el otro modificado con exactamente los +mismos nombres, la orden \command{patch} tiene la opción +\cmdopt{patch}{-p} que indica la cantidad de componentes de la ruta +a eliminar cuando se vaya a aplicar el parche. Este número se +llama la \emph{cantidad de eliminaciones}. + +La opción con ``\texttt{-p1}'' significa ``elimine uno''. Si +\command{patch} ve un nombre de fichero \filename{foo/bar/baz} en el +encabezado del fichero, eliminará \filename{foo} y tratará de parchar +un fichero llamado \filename{bar/baz}. (Hablando estrictamente, la +cantidad de eliminaciones se refiere a la cantidad de \emph{separadores de + ruta} (y los componentes que vayan con ellos) a eliminar. Si el +contador es uno volverá \filename{foo/bar} en \filename{bar}, pero +\filename{/foo/bar} (note la barra extra) en \filename{foo/bar}.) + +La cantidad a eliminar``estándar'' para parches es uno; casi todos los +parches contienen un componente inicial de la ruta que necesita ser +eliminado. La orden \hgcmd{diff} de Mercurial genera nombres de ruta +de esta forma, y la orden \hgcmd{import} y MQ esperan parches que +tengan a uno como cuenta de eliminaciones. + +Si recibe un parche de alguien de quien desea adicionar adicionar a su +cola de parches, y el parche necesita una cuenta de eliminación que no +sea uno, no podrá aplicar \hgxcmd{mq}{qimport} en primera medida, +porque \hgxcmd{mq}{qimport} no tiene todavía una opción \texttt{-p} +option (ver~\bug{311}). Lo mejor que puede hacer es aplicar +\hgxcmd{mq}{qnew} por su cuenta, y después usar \cmdargs{patch}{-p\emph{N}} +para aplicar tal parche, seguido de \hgcmd{addremove} para tener en +cuenta cualquier fichero adicionado o eliminado por el parche, seguido +de \hgxcmd{mq}{qrefresh}. Esta complejidad puede ser innecesaria; +consulte~\bug{311} para más información. + +\subsection{Estrategias para aplicar parches} + +Cuando \command{patch} aplica un trozo, intenta varias estrategias +sucesivas que decrecen en precisión para intentar aplicarlo. Esta +técnica de pruebas y error aveces permite que un parche que fue +generado contra una versión anterior de un fichero, sea aplicada sobre +una versión más nueva del mismo. + +Primero \command{patch} intenta una correspondencia perfecta donde los +números de línea, el contexto y el texto a modificar deben coincidir +perfectamente. Si no lo logra, intenta encontrar una correspondencia +exacta del contexto, sin tener en cuenta el número de línea. Si es +exitoso, imprime una línea indicando que el trozo fue aplicado, pero a +un \emph{corrimiento} del número de línea original. + +Si falla la correspondencia por contexto, \command{patch} elimina la +primera y la última línea del contexto, e intenta una correspondencia +\emph{reducida} del contexto. Si el trozo con contexto reducido es +exitoso, imprime un mensaje indicando que aplicó el trozo con un +\emph{factor difuso} (el número después del factor difuso indica +cuántas líneas de contexto \command{patch} tuvo que eliminar antes de +aplicar el parche). + +Cuando ninguna de estas técnicas funciona, \command{patch} imprime un +mensaje indicando que el trozo en cuestión se desechó. Almacena los +trozos desechados (también llamados ``descartados'') en un fichero con +el mismo nombre, y la extensión \sfilename{.rej} añadida. También +almacena una copia igual al fichero original con la extensión +\sfilename{.orig}; la copia del fichero sin extensión contendrá +cualquier cambio hecho por los trozos que \emph{sí} se aplicaron sin +problema. Si usted tiene un parche que modifica \filename{foo} con +seis trozos, y uno de ellos falla al aplicarse, tendrá : un fichero +original \filename{foo.orig}, un fichero \filename{foo.rej} que +contiene el trozo, y \filename{foo}, que contiene los cambios que se +aplicaron por los cinco trozos exitosos. + +\subsection{Algunos detalles de la representación de parches} + +Hay ciertas cosas útiles por saber acerca de cómo trabaja +\command{patch} con los ficheros: +\begin{itemize} +\item Debería ser obvio que \command{patch} no puede manipular + ficheros binarios. +\item No se preocupa por el bit ejecutable; crea ficheros nuevos en + modo lectura, pero no ejecutable. +\item \command{patch} intenta eliminar un fichero como una diferencia + entre el fichero a eliminar y un fichero vacío. Y por lo tanto su + idea de ``Borré este fichero'' debería pensarse como ``toda línea de + este fichero fue eliminada'' en un parche. +\item Trata la adición de un fichero como un diff entre un fichero + vacío y el fichero a ser adicionado. Por lo tanto en un parche su + idea de ``Añadí este fichero'' se vería como ``toda línea de este + fichero fue añadida''. +\item Trata el renombramiento de un fichero como la eliminación del + nombre anterior y la adición del nuevo nombre. Esto significa que + los ficheros renombrados dejan un rastro grande en los parches. + (Tenga en cuenta que Mercurial no trata de inferir cuando los + ficheros han sido renombrados o copiados en un parche en este + momento.) +\item \command{patch} no puede representar ficheros vacíos, por lo + tanto no puede usar un parche para representar la noción ``Añadí + este fichero vacío al árbol''. +\end{itemize} +\subsection{Cuidado con los difusos} + +Cuando aplique un trozo con un corrimiento, o con un factor difuso, +aveces será taotalmente exitoso, tales técnicas inexactas dejan +claramente la posibilidad de corromper el fichero parchado. Los casos +más típicos involucran aplicar un parche dos veces o en un sitio +incorrecto del fichero. Si \command{patch} o \hgxcmd{mq}{qpush} llegan +a mencionar un corrimiento o un factor difuso, debería asegurarse que +los ficheros modificados estén correctos después del suceso. + +Casi siempre es buena idea refrescar un parche que fue aplicado con un +corrimiento o un factor difuso; refrescar el parche genera nueva +información de contexto que permitirá aplicarlo limpiamente. Digo +``casi siempre,'' no ``siempre'', puesto que en ciertas ocasiones +refrescar un parche lo hará fallar frente a una revisión diferente del +fichero. En algunos casos, como por ejemplo, cuando usted está +manteniendo un parche que debe estar encima de múltiples revisiones de +un árbol de fuentes, es aceptable tener un parche aplicado algo +difuso, siempre que haya verificado los resultados del proceso de +parchar. + +\subsection{Manejo de descartes} + +Si \hgxcmd{mq}{qpush} falla al aplicar un parche, mostrará un texto de +error y saldrá. Si ha dejado ficheros \sfilename{.rej}, es mejor +arreglar los trozos descartados antes de introducir parches +adicionales o hacer cualquier otra cosa. + +Si su parche \emph{solía} aplicarse limpiamente, y ya no lo hace +porque ha cambiado código subyacente en el cual se basa su parche, las +Colas de Mercurial pueden ayudar; consulte la sección~\ref{sec:mq:merge}. + +Desafortunadamente, no hay grandes técnicas para tratar los trozos +descartados. Casi siempre deberá consultar el fichero +\sfilename{.rej} y editar el fichero objetivo, aplicando los trozos +descartados a mano. + +Si es aventurero, Neil Brown, un hacker del núcleo Linux, escribió una +herramienta llamada \command{wiggle}~\cite{web:wiggle}, que es más +vigorosa que \command{patch} en su intento de hacer que se aplique un +parche. + +Otro hacker del nucleo Linux, Chris Mason (el autor de las Colas de +Mercurial), escribió una herramienta similar llamada +\command{mpatch}~\cite{web:mpatch}, que sigue una aproximación +sencilla para automatizar la aplicación de trozos descartados por +\command{patch}. La orden \command{mpatch} puede ayudar con cuatro +razones comunes por las cuales un parche ha sido descartado: + +\begin{itemize} +\item El contexto en la mitad de un trozo ha cambiado. +\item Un trozo ha perdido cierto contexto al principio o al final. +\item Un trozo largo podría aplicarse mejor---por completo o una + parte---si estaba cortado en trozos más pequeños. +\item Un trozo remueve líneas con contenido ligeramente diferente que + aquellas que están presentes en el fichero. +\end{itemize} + +Si usted usa \command{wiggle} o \command{mpatch}, debería ser +doblemente cuidadoso al revisar sus resultados cuando haya terminado. +De hecho, \command{mpatch} refuerza este método de revisar por partida +doble su salida, dejándolo a usted en un programa de fusión cuando la +herramienta haya terminado su trabajo, de tal forma que usted pueda +verificar lo que ha hecho y pueda terminar de aplicar cualquier fusión +faltante. + +\section{maximizar el rendimiento de MQ} +\label{sec:mq:perf} + +MQ es muy eficiente al tratar con una gran cantidad de parches. Corrí +unos experimentos de desempeño a mediados del 2006 para una charla que +dí en la conferencia EuroPython 2006~\cite{web:europython}. Empleé la +serie de parches para el núcleo Linux 2.6.17-mm1, que contaba con 1.738 +parches. Los apliqué sobre un repositorio del núcleo de Linux con +todas las 27.472 revisiones entre 2.6.12-rc2 y 2.6.17. + +En mi portátil antiguo y lento, logré aplicar +\hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-a}} a los 1.738 parches en 3.5 +minutos, y \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} en 30 segundos. +(En un portátil más nuevo, el tiempo para introducir todos los +parches, se logró en menos de dos minutos.) Apliqué +\hgxcmd{mq}{qrefresh} sobre uno de los parches más grandes (que hizo +22.779 líneas de cambios en 287 ficheros) en 6,6 segundos. + +Claramente, MQ funciona adecuadamente en árboles grandes, y además hay +unos trucos que pueden emplearse para obtener el máximo desempeño. + +En primer lugar, trate de hacer ``en lote'' las operaciones. Cada vez +que ejecute \hgxcmd{mq}{qpush} o \hgxcmd{mq}{qpop}, tales órdenes +revisan el directorio de trabajo para asegurarse de que usted no ha +hecho cambios y ha olvidado ejecutar \hgxcmd{mq}{qrefresh}. En un +árbol pequeño, el tiempo de esta revisión puede ser mínimo, Pero en +un árbol mediano (con decenas de miles de ficheros), puede tomar un +segundo o más. + +Las órdenes \hgxcmd{mq}{qpush} y \hgxcmd{mq}{qpop} le permiten +introducir o sustraer varios parches en una operación. Puede +identificar el ``parche destino'' que desee. Cuando aplique +\hgxcmd{mq}{qpush} con un destino, introducirá tantos parches como sea +necesario hasta que el especificado esté en el tope de la pila. +Cuando emplee \hgxcmd{mq}{qpop} con un destino, MQ sustraerá parches +hasta que el parche destino esté en el tope. + +Puede identificar un parche destino con el nombre del parche o con el +número. Si se refiere al número, los parches se contarán desde cero; +esto significa que el primer parche es cero, el segundo es uno y así +sucesivamente. + +\section{Actualiar los parches cuando el código cambia} +\label{sec:mq:merge} + +Es común contar con una pila de parches sobre un repositorio que usted +no modifica directamente. Si está trabajando en cambios de código de +otros, o en una característica que tarda bastante en desarrollarse +comparada con la tasa de cambio del código sobre la cual se está +trabajando, necesitará sincronizarse con el código, y ajustar +cualquier trozo en sus parches que ya no estén al día. A esto se le +llama hacer \emph{rebase} a su serie de parches. + +La vía más sencilla de hacerlo es con \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} +sobre sus parches, después hacer \hgcmd{pull} de los cambios en el +repositorio, y finalmente hacer +\hgcmdargs{qpush}{\hgxopt{mq}{qpop}{-a}} con sus parches de nuevo. MQ +dejará de de introducir parches siempre que llegue a un parche que no se pueda +aplicar debido a un conflicto, permitiéndole a usted arreglarlo, +aplicar \hgxcmd{mq}{qrefresh} al parche afectado y continuar +introduciendo hasta que haya arreglado la pila completa. + +Esta aproximación es sencilla y funciona bien si no espera cambios en +el código original que afecte en gran medida los parches que usted +esté aplicando. Si su pila de parches toca código que es modificado +frecuentemente o de forma invasiva sobre el código subyacente, +arreglar trozos manualmente se vuelve desgastante. + +Es posible automatizar de forma parcial el proceso de rebase. Si sus +parches se aplican limpiamente sobre algunas revisiones del +repositorio subyacente, MQ puede usar esta información para ayudarle a +a resolver conflictos entre sus parches y una revisión distinta. + +El proceso resulta un poco complejo: +\begin{enumerate} +\item Para comenzar, haga \hgcmdargs{qpush}{-a} sobre todos los + parches que usted sepa se aplican limpiamente. +\item Guarde una copia de seguridad de su directorio de parches con + \hgcmdargs{qsave}{\hgxopt{mq}{qsave}{-e} \hgxopt{mq}{qsave}{-c}}. + Esto imprime el nombre del directorio en el cual se han guardado los + parches. Guardará los parches en un directorio llamado + \sdirname{.hg/patches.\emph{N}}, donde \texttt{\emph{N}} es un + entero pequeño. También consigna un ``conjunto de cambios de + seguridad'' sobre sus parches aplicados; esto es para mantener el + histórico, y guarda los estados de los ficheros \sfilename{series} + y \sfilename{status}. +\item Use \hgcmd{pull} para traer los nuevos cambios en el repositorio + subyacente. (No ejecute \hgcmdargs{pull}{-u}; vea más adelante por qué.) +\item Actualice a la nueva revisión punta con + \hgcmdargs{update}{\hgopt{update}{-C}} para sobreescribir los + parches que haya introducido. +\item Fusione todos los parches con \hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-m} + \hgxopt{mq}{qpush}{-a}}. La opción \hgxopt{mq}{qpush}{-m} de \hgxcmd{mq}{qpush} + le indica a MQ que haga una fusión que involucra tres fuentes si el + parche falla al aplicarse. +\end{enumerate} + +Durante el \hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-m}}, cada parche en +el fichero \sfilename{series} se aplica normalmente. Si un parche se +aplica difusamente o se niea a aplicarse, MQ consulta la cola que +usted guardó con \hgxcmd{mq}{qsave}, y aplica una fusión de tres con +el correspondiente conjunto de cambios. Esta fusión usa la maquinaria +de Mercurial, por lo tanto puede mostrar una herramienta de fusión GUI +para ayudarle a resolver los problemas. + +Cuando termine de resolver los efectos de un parche, MQ refrescará su +parche basado en el resultado de la fusión. + +Al final de este proceso, su repositorio tendrá una cabeza extra de la +antigua cola de parches, y una copia de la cola de parches anterio +estará en \sdirname{.hg/patches.\emph{N}}. Puede eliminar la cabeza +extra con \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a} \hgxopt{mq}{qpop}{-n} patches.\emph{N}} +o \hgcmd{strip}. Puede eliminar \sdirname{.hg/patches.\emph{N}} una +vez que esté seguro de que no lo necesita más como copia de seguridad. + +\section{Identificar parches} + +Las órdenes de MQ le permiten trabajar refiriéndose al nombre del +parche o al número. Es obvio hacerlo por el nombre; por ejemplo se +pasa el nombre \filename{foo.patch} a \hgxcmd{mq}{qpush}, que +introducirá los parches hasta que \filename{foo.patch} se aplique. + +Para hacerlo más corto, puede referirse a un parche con un nombre y un +corrimiento de número; por ejemplo, \texttt{foo.patch-2} significa +``dos parches antes de \texttt{foo.patch}'', mientras que +\texttt{bar.patch+4} significa ``cuatro parches después de \texttt{bar.patch}''. + +Referirse a un parche por su índice no es muy diferente. El primer +parche que se imprime en la salida de \hgxcmd{mq}{qseries} es el +parche cero (si, es el primero en los sistemas que comienzan su conteo +en cero); el segundo parche es uno y así sucesivamente. + +MQ facilita el trabajo cuando está usando órdenes normales de +Mercurial. Cada comando que acepte Identificadores de conjuntos de +cambios también aceptará el nombre de un parche aplicado. MQ aumenta +las etiquetas normalmente en el repositorio con un distintivo para cada +parche aplicado. Adicionalmente, las etiquetas especiales \index{tags!special tag + names!\texttt{qbase}}\texttt{qbase} y \index{tags!special tag + names!\texttt{qtip}}\texttt{qtip} identifican los parches +``primero'' y último, respectivamente. + +Junto con las capacidades de Mercurial para etiquetar, estas adiciones +hacen que trabajar con parches sea muy sencillo. +\begin{itemize} +\item ¿Desea enviar una bomba de parches a una lista de correo con los + últimos cambios que ha hecho? + \begin{codesample4} + hg email qbase:qtip + \end{codesample4} + (¿No sabe qué es una ``bomba de parches''? Consulte la + sección~\ref{sec:hgext:patchbomb}.) +\item ¿Desea ver todos los parches desde que se aplicó + \texttt{foo.patch} sobre los ficheros de un subdirectorio en su + árbol? + \begin{codesample4} + hg log -r foo.patch:qtip \emph{subdir} + \end{codesample4} +\end{itemize} + +Dado que MQ nombra los parches disponibles al resto de Mercurial con +su maquinaria de etiquetas interna, usted no necesita teclear el +nombre completo de un parche cuando desea identificarlo por su nombre. + +\begin{figure}[ht] + \interaction{mq.id.output} + \caption{Uso de las características de etiquetamiento al trabajar + con MQ} + \label{ex:mq:id} +\end{figure} + +Otra consecuencia deseable al representar los nombres de parches como +etiquetas es que cuando ejecute la orden \hgcmd{log}, desplegará el +nombre del parche como una etiqueta, usualmente con la salida normal. +Esto facilita distinguir visualmente los parches aplicados de las +revisiones ``normales''. La figura~\ref{ex:mq:id} muestra algunos +comandos usuales de Mercurial al trabajar con parches. + +\section{Otra información útil} + +Hay una cantidad de aspectos que hacen que el uso de MQ no representen +secciones en sí mismas, pero de los cuales es bueno estar +enterado. Los presentamos en aquí: + +\begin{itemize} +\item Usualmente cuando hace \hgxcmd{mq}{qpop} a un parche y vuelve a + hacerle \hgxcmd{mq}{qpush}, el conjunto de cambios que representa el + parche después de introducir/sustraer tendrá una \emph{identidad + distinta} que aquella que representaba el conjunto de cambios + anteriormente. Consulte la secctión~\ref{sec:mqref:cmd:qpush} para + obtener información del por qué de esto. +\item No es una buena idea aplicar \hgcmd{merge} de cambios de otra + rama con un conjunto de cambios de parches, por lo menos si desea + mantener la ``información de parches'' de ese conjunto de cambios y + los conjuntos de cambios que se encuentran por debajo en la pila de + parches. Si intenta hacerlo, parecerá que ha sido exitoso, pero MQ + se confundirá. +\end{itemize} + +\section{Administrar parches en un repositorio} +\label{sec:mq:repo} + +Dado que el directorio \sdirname{.hg/patches} de MQ reside fuera del +repositorio de trabajo de Mercurial, el repositorio ``subyacente'' de +Mercurial no sabe nada acerca de la administración o presencia de +parches. + +Esto presenta la interesante posibilidad de administrar los contenidos +del directorio de parches como un repositorio de Mercurial por su +cuenta. Puede ser una forma útil de trabajar. Por ejemplo, puede +trabajar en un parche por un rato, hacerle \hgxcmd{mq}{qrefresh} y +después hacer \hgcmd{commit} al estado actual del parche. Esto le +permite ``devolverse'' a esa versión del parche posteriormente. + +Puede también compartir diferentes versiones de la misma pila de +parches entre varios repositorios subyacentes. Uso esto cuando estoy +desarrollando una característica del núcleo Linux. Tengo una copia +original de las fuentes del núcleo para varias arquitecturas, y cloné +un rpositorio en cada una que contiene los parches en los cuales +estoy trabajando. Cuando quiero probar un cambio en una arquitectura +diferente, introduzco mis parches actuales al repositorio de parches +asociado con el árbol del kernel, sustraigo e introduzco todos mis +parches, armo y pruebo el núcleo. + +Llevar los parches en un repositorio permite que varios +desarrolladores puedan trabajar en la misma serie de parches sin +sobreponerse, todo sobre la fuente base subyacente que pueden o no +controlar. + +\subsection{Soporte de MQ para repositorios de parches} + +MQ le ayuda a trabajar con el directorio \sdirname{.hg/patches} como +un repositorio; cuando usted prepara un repositorio para trabajar con +parches usando \hgxcmd{mq}{qinit}, puede pasarle la opción +\hgxopt{mq}{qinit}{-c} para que se cree el directorio +\sdirname{.hg/patches} como un repositorio de Mercurial. + +\begin{note} + Si olvida usar la opción \hgxopt{mq}{qinit}{-c} option, puede ir al + directorio \sdirname{.hg/patches} en cualquier momento y ejecutar + \hgcmd{init}. No olvide añadir una entrada en el fichero + \sfilename{status} del fichero \sfilename{.hgignore}, a pesar de que + (\hgcmdargs{qinit}{\hgxopt{mq}{qinit}{-c}} hace estodo de forma + automática para usted); usted \emph{seguro} no quiere administrar el + fichero \sfilename{status}. +\end{note} + +MQ nota convenientemente que el directorio \dirname{.hg/patches} +es un repositorio, hará \hgcmd{add} automáticamente a cada parche que +usted cree e importe. + +MQ provee una orden corta, \hgxcmd{mq}{qcommit}, que ejecuta +\hgcmd{commit} en el directorio \sdirname{.hg/patches}. Lo que ahorra +tecleo recurrente. + +Finalmente, para administrar convenientemente el directorio de +parches, puede definir el alias \command{mq} en sistemas Unix. Por +ejemplo, en sistemas Linux con el intérprete \command{bash}, puede +incluir el siguiente recorte de código\ndt{snippet} en su fichero +\tildefile{.bashrc}. + +\begin{codesample2} + alias mq=`hg -R \$(hg root)/.hg/patches' +\end{codesample2} + +Puede aplicar las órdenes de la forma \cmdargs{mq}{pull} al +repositorio principal. + +\subsection{Detalles a tener en cuenta} + +El soporte de MQ para trabajar con un repositorio de parches es +limitado en ciertos aspectos: + +MQ no puede detectar automáticamente los cambios que haga al +directorio de parches. Si aplica \hgcmd{pull}, edita manualmente, o +hace \hgcmd{update} a los parches o el fichero \sfilename{series}, +tendrá que aplicar \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} y después +\hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-a}} en el repositorio subyacente +para que los cambios se reflejen allí. Si olvida hacerlo, puede +confundir a MQ en cuanto a qué parches se han aplicado. + +\section{Otras herramientas para trabajar con parches} +\label{sec:mq:tools} + +Cuando haya trabajado por cierto tiempo con parches, deseará +herramientas que le ayuden a entender y manipular los parches con los +que esté tratando. + +La orden \command{diffstat}~\cite{web:diffstat} genera un histograma +de modificaciones hechas a cada fichero en un parche. Provee una +interesante forma de ``dar un vistazo'' al parche---qué ficheros +afecta, y cuántos cambios introduce a cada fichero y en total. (Me ha +parecido interesante usar la opción \cmdopt{diffstat}{-p} de +\command{diffstat}, puesto que de otra forma intentará hacer cosas +inteligentes con prefijos de ficheros que terminan confundiéndome.) + +\begin{figure}[ht] + \interaction{mq.tools.tools} + \caption{Las órdenes \command{diffstat}, \command{filterdiff}, y \command{lsdiff}} + \label{ex:mq:tools} +\end{figure} + +El paquete \package{patchutils}~\cite{web:patchutils} es +invaluable. Provee un conjunto de pequeñas utilidades que siguen la +``filosofía Unix''; cada una hace una cosa muy bien hecha a un +parche. La orden \package{patchutils} que más uso es +\command{filterdiff}, que extrae subconjuntos de un fichero de +parche. Por ejemplo, dado un parche que modifica centenas de ficheros +en docenas de directorios, una única invocación de +\command{filterdiff} puede generear un parche más pequeño que +solamente toca aquellos ficheros con un patrón. Puede ver otro +ejemplo en la sección~\ref{mq-collab:tips:interdiff}. + +\section{Buenas prácticas de trabajo con parches} + +En caso de que esté trabajando en una serie de parches para enviar a +un proyecto de software libre o de fuentes abiertas, o en una serie +que desea tratar como un conjunto de cambios regular, cuando haya +terminado, puede usar técnicas sencillas para mantener su trabajo bien +organizado. + +De nombres descriptivos a sus parches. Un buen nombre para un parche +podría ser \filename{rework-device-alloc.patch}, porque da de forma +inmediata una pista del propósito del parche. Los nombres largos no +deben ser un problema; no los estará tecleando repetidamente, pero +\emph{estará} ejecutando regularmente órdenes como +\hgxcmd{mq}{qapplied} y \hgxcmd{mq}{qtop}. Los nombres adecuados son +especialmente importantes cuando tiene bastantes parches con los +cuales trabajar, o si está trabajando en diferentes tareas y sus +parches toman solamente una porción de su atención. + +Tenga en cuenta en qué parche está trabajando. Use la orden \hgxcmd{mq}{qtop} +para dar un vistazo al texto de sus parches frecuentemente---por +ejemplo, use \hgcmdargs{tip}{\hgopt{tip}{-p}})---para asegurarse en +dónde está ubicado. En distintas oportunidades me sucedió que apliqué +\hgxcmd{mq}{qrefresh} a un parche distinto al que deseaba hacerlo, y +usualmente es complejo migrar los cambios al parche correcto después +de haberlo hecho mal. + +Por este motivo, vale la pena invertir ese poco tiempo para aprender +cómo usar otras herramientas que describí en la +sección~\ref{sec:mq:tools}, particularmente \command{diffstat} y +\command{filterdiff}. La primera le dará una idea de qué cambios está +haciendo su parche, mientras que la segunda permite seleccionar trozos +de un parche para colocarlos en otro. + +\section{Recetas de MQ} + +\subsection{Administrar parches ``triviales''} + +Puesto que colocar ficheros en un repositorio de Mercurial es tan +sencillo, tiene bastante sentido administrar parches de esta forma +incluso si desea hacer algunos cambios al paquete de ficheros que +descargó. + +Para comenzar a descargar y desempaqueter un paquete de ficheros, y +volverlo en un repositorio de Mercurial: +\interaction{mq.tarball.download} + +Continue creando una pila de parches y haga sus cambios. +\interaction{mq.tarball.qinit} + +Digamos que pasan unas semanas o meses, y el autor del paquete libera +una nueva versión. Primero se traen sus cambios al repositorio. +\interaction{mq.tarball.newsource} +La porción que comienza con \hgcmd{locate} mostrada más arriba, borra +todos los ficheros en el directorio de trabajo, así que la opción +\hgopt{commit}{--addremove} de \hgcmd{commit} puede indicar qué +ficheros se han eliminado en la nueva versión del árbol de fuentes. + +Finalmente, puede aplicar sus parches encima del nuevo árbol de fuentes +\interaction{mq.tarball.repush} + +\subsection{Combinar parches completos} +\label{sec:mq:combine} + +MQ provee la orden \hgxcmd{mq}{qfold} que le permite combinar parches +enteros. Se ``integran''\ndt{fold} los parches que usted nombre, en +el orden que especifique, en el último parche aplicado, y concatena +sus descripciones al final de su descripción. Deberá sustraer los +cambios para poder integrarlos. + +El orden en el que integre los parches importa. Si el parche +últimamente aplicado es \texttt{foo}, e integra \hgxcmd{mq}{qfold} \texttt{bar} y +\texttt{quux} en él, terminará con un parche que tiene el mismo efecto +que si hubiera aplicado primero \texttt{foo}, y después \texttt{bar}, +seguido de \texttt{quux}. + +\subsection{Fusionar una porción de un parche dentro de otro} + +Fusionar \emph{partes} de un parche dentro de otro es más complejo que +combinar completamente dos parches. + +Si desea mover cambios de ficheros completos, puede usar las opciones +\command{filterdiff}'s \cmdopt{filterdiff}{-i} y +\cmdopt{filterdiff}{-x} para elegir las modificaciones remover de un +parche, concatenar su salida al final del parche donde desea +fusionarlo. Usualmente no necesitará modificar el parche del cuál ha +fusionado los cambios. En cambio, MQ reportará que hay unos trozos +que se han desechado cuando usted aplique \hgxcmd{mq}{qpush} (de los +trozos que haya movido al otro parche), y puede sencillamente aplicar +\hgxcmd{mq}{qrefresh} para eliminar los trozos replicados. + +Si tiene un parche que tiene varios trozos que modifican un fichero, y +desea mover solamente unos de ellos, el trabajo es un poco más +enredado, pero puede automatizarlo parcialmente. Use +\cmdargs{lsdiff}{-nvv} para imprimir algunos metadatos del parche. +\interaction{mq.tools.lsdiff} + +Esta orden imprime tres clases diferentes de números: +\begin{itemize} +\item (en la primera columna) un \emph{número de fichero} para + identificar cada fichero modificado en el parche; +\item (En la siguiente línea, indentado) el número de línea dentro de + un fichero modificado donde comienza el trozo; y +\item (en la misma línea) un \emph{número de trozo} que identifica el + trozo. +\end{itemize} + +Tendrá que hacer una inspección visual, y leer el parche para +identificar los números de fichero y trozo que desea, pero puede pasar +posteriormente a las opciones \cmdopt{filterdiff}{--files} y +\cmdopt{filterdiff}{--hunks} de \command{filterdiff}, para seleccionar +exactamente el fichero y el trozo que desea extraer. + +Cuando tenga el trozo, puede concatenarlo al final de su parche +objetivo y continuar como en la sección~\ref{sec:mq:combine}. + +\section{Diferencias entre quilt y MQ} + +Si le es familiar quilt, MQ provee un conjunto similar de órdenes. Hay +algunas diferencias en cómo funcionan. + +Debe haber notado que la mayoría de comandos de quilt tienen su +contraparte en MQ, que simplemente comienzan con ``\texttt{q}''. Las +excepciones son las órdenes \texttt{add} y \texttt{remove} de quilt, +que realmente son las órdenes \hgcmd{add} y \hgcmd{remove} de +Mercurial. No hay un equivalente en MQ para la orden +\texttt{edit} de quilt. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/note.png Binary file es/note.png has changed diff -r 73b094b764ec -r 51b5d56744c5 es/preface.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/preface.tex Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/revlog.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/revlog.svg Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/snapshot.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/snapshot.svg Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/srcinstall.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/srcinstall.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,55 @@ +\chapter{Instalar Mercurial desde las fuentes} +\label{chap:srcinstall} + +\section{En un sistema tipo Unix} +\label{sec:srcinstall:unixlike} + +Si usa un sistema tipo Unix que tiene una versión suficientemente +reciente de Python (2.3~o superior) disponible, es fácil instalar +Mercurial desde las fuentes. +\begin{enumerate} +\item Descargue un paquete fuente reciente de + \url{http://www.selenic.com/mercurial/download}. +\item Descomprímalo: + \begin{codesample4} + gzip -dc mercurial-\emph{version}.tar.gz | tar xf - + \end{codesample4} +\item Vaya al directorio fuente y ejecute el guión de instalación. + Esto armará Mercurial y lo instalará en su directorio casa: + \begin{codesample4} + cd mercurial-\emph{version} + python setup.py install --force --home=\$HOME + \end{codesample4} +\end{enumerate} +Cuando termine la instalación, Mercurial estará en el subdirectorio +\texttt{bin} de su directorio casa. No olvide asegurarse de que este +directorio esté presente en el camino de búsqueda de su intérprete de +órdenes. + +Probablemente necesitará establecer la variable de ambiente +\envar{PYTHONPATH} de tal forma que los ejecutables de Mercurial +puedan encontrar el resto de los paquetes de Mercurial. Por ejemplo, +en mi portátil, la establecía a \texttt{/home/bos/lib/python}. La +ruta exacta que usted use dependerá de como ha sido construído Python +en su sistema, pero debería ser fácil deducirla. Si no está seguro, +mire lo que haya mostrado el script en el paso anterior, y vea dónde +se instalaron los contenidos del directorio \texttt{mercurial} se +instalaron. + +\section{En Windows} + +Armar e instalar Mercurial en Windows requiere una variedad de +herramientas, cierta suficiencia técnica y paciencia considerable. +Personalmente, \emph{no le recomiendo} hacerlo si es un ``usuario +casual''. A menos que intente hacer hacks a Mercurial, le recomiendo +que mejor use un paquete binario. + +Si está decidido a construir Mercurial desde las fuentes en Windows, +siga el ``camino difícil'' indicado en el wiki de Mercurial en +\url{http://www.selenic.com/mercurial/wiki/index.cgi/WindowsInstall}, +y espere que el proceso sea realmente un trabajo duro. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/template.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/template.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,497 @@ +\chapter{Personalizar los mensajes de Mercurial} +\label{chap:template} + +Mercurial provee un poderoso mecanismo que permite controlar como +despliega la información. El mecanismo se basa en plantillas. Puede +usar plantillas para generar salida específica para una orden +particular o para especificar la visualización completa de la interfaz +web embebida. + +\section{Usar estilos que vienen con Mercurial} +\label{sec:style} + +Hay ciertos estilos listos que vienen con Mercurial. Un estilo es +simplemente una plantilla predeterminada que alguien escribió e +instaló en un sitio en el cual Mercurial puede encontrarla. + +Antes de dar un vistazo a los estilos que trae Mercurial, revisemos su +salida usual. + +\interaction{template.simple.normal} + +Es en cierta medida informativa, pero ocupa mucho espacio---cinco +líneas de salida por cada conjunto de cambios. El estilo +\texttt{compact} lo reduce a tres líneas, presentadas de forma +suscinta. + +\interaction{template.simple.compact} + +El estilo de la \texttt{bitácora de cambios} vislumbra el poder +expresivo del sistema de plantillas de Mercurial. Este estilo busca +seguir los estándares de bitácora de cambios del proyecto +GNU\cite{web:changelog}. + +\interaction{template.simple.changelog} + +No es una sorpresa que el estilo predeterminado de Mercurial se llame +\texttt{default}\ndt{predeterminado}. + +\subsection{Especificar un estilo predeterminado} + +Puede modificar el estilo de presentación que Mercurial usará para +toda orden vía el fichero \hgrc\, indicando el estilo que prefiere +usar. + +\begin{codesample2} + [ui] + style = compact +\end{codesample2} + +Si escribe un estilo, puede usarlo bien sea proveyendo la ruta a su +fichero de estilo o copiando su fichero de estilo a un lugar en el +cual Mercurial pueda encontrarlo (típicamente el subdirectorio +\texttt{templates} de su directorio de instalación de Mercurial). + +\section{Órdenes que soportan estilos y plantillas} + +Todas las órdenes de Mercurial``relacionadas con \texttt{log}'' le +permiten usar estilos y plantillas: \hgcmd{incoming}, \hgcmd{log}, +\hgcmd{outgoing} y \hgcmd{tip}. + +Al momento de la escritura del manual estas son las únicas órdenes que +soportan estilos y plantillas. Dado que son las órdenes más +importantes que necesitan personalización, no ha habido muchas +solicitudes desde la comunidad de usuarios de Mercurial para añadir +soporte de plantillas y estilos a otras órdenes. + +\section{Cuestiones básicas de plantillas} + +Una plantilla de Mercurial es sencillamente una pieza de texto. +Cierta porción nunca cambia, otras partes se \emph{expanden}, o +reemplazan con texto nuevo cuando es necesario. + +Antes de continuar, veamos de nuevo un ejemplo sencillo de la salida +usual de Mercurial: + +\interaction{template.simple.normal} + +Ahora, ejecutemos la misma orden, pero usemos una plantilla para +modificar su salida: + +\interaction{template.simple.simplest} + +El ejemplo anterior ilustra la plantilla más sencilla posible; es +solamente una porción estática de código que se imprime una vez por +cada conjunto de cambios. La opción \hgopt{log}{--template} de la +orden \hgcmd{log} indica a Mercurial usar el texto dado como la +plantilla cuando se imprime cada conjunto de cambios. + +Observe que la cadena de plantilla anterior termina con el texto +``\Verb+\n+''. Es una \emph{secuencia de control}, que le indica a +Mercurial imprimira una nueva línea al final de cada objeto de la +plantilla. Si omite esta nueva línea, Mercurial colocará cada pieza +de salida seguida. Si desea más detalles acerca de secuencias de +control, vea la sección~\ref{sec:template:escape}. + +Una plantilla que imprime una cadena fija de texto siempre no es muy +útil; intentemos algo un poco más complejo. + +\interaction{template.simple.simplesub} + +Como puede ver, la cadena ``\Verb+{desc}+'' en la plantilla ha sido +reemplazada en la salida con la descricipción de cada conjunto de +cambios. Cada vez que Mercurial encuentra texto encerrado entre +corchetes (``\texttt{\{}'' y ``\texttt{\}}''), intentará reemplazar los +corchetes y el texto con la expansión de lo que sea está adentro. +Para imprimir un corchete de forma literal, debe escaparlo, como se +describe en la sección~\ref{sec:template:escape}. + +\section{Palabras claves más comunes en las plantillas} +\label{sec:template:keyword} + +Puede empezar a escribir plantillas sencillas rápidamente con las +palabras claves descritas a continuación: + +\begin{itemize} +\item[\tplkword{author}] Cadena. El autor NO modificado del conjunto + de cambios. +\item[\tplkword{branches}] Cadena. El nombre de la rama en la cual se + consignó el conjunto de cambios. Será vacía si el nombre de la rama es + \texttt{default}. +\item[\tplkword{date}] Información de fecha. La fecha en la cual se + consignó el conjunto de cambios. \emph{No} es legible por un + humano, debe pasarla por un firltro que la desplegará + apropiadamente. En la sección~\ref{sec:template:filter} hay más + detalles acerca de filtros. La fecha se expresa como un par de + números. El primer número corresponde a una marca de tiempo UNIX + UTC (segundos desde el primero de enero de 1970); la segunda es el + corrimiento horario de la zona horaria del UTC en la cual se encontraba + quien hizo la consignación, en segundos. +\item[\tplkword{desc}] Cadena. La descripción en texto del conjunto + de cambios. +\item[\tplkword{files}] Lista de cadenas. Todos los ficheros + modificados, adicionados o eliminados por este conjunto de cambios. +\item[\tplkword{file\_adds}] Lista de cadenas. Ficheros adicionados + por este conjunto de cambios. +\item[\tplkword{file\_dels}] Lista de cadenas. Ficheros eliminados + por este conjunto de cambios. +\item[\tplkword{node}] Cadena. El hash de identificación de este + conjunto de cambios como una cadena hexadecimal de 40 caracteres. +\item[\tplkword{parents}] Lista de cadenas. Los ancestros del + conjunto de cambios. +\item[\tplkword{rev}] Entero. El número de revisión del repositorio + local. +\item[\tplkword{tags}] Lista de cadenas. Todas las etiquetas + asociadas al conjunto de cambios. +\end{itemize} + +Unos experimentos sencillos nos mostrarán qué esperar cuando usamos +estas palabras claves; puede ver los resultados en la +figura~\ref{fig:template:keywords}. + +\begin{figure} + \interaction{template.simple.keywords} + \caption{Template keywords in use} + \label{fig:template:keywords} +\end{figure} + +Como mencionamos anteriormente, la palabra clave de fecha no produce +salida legible por un humano, debemos tratarla de forma especial. +Esto involucra usar un \emph{filtro}, acerca de lo cual hay más en la +sección~\ref{sec:template:filter}. + +\interaction{template.simple.datekeyword} + +\section{Secuencias de Control} +\label{sec:template:escape} + +El motor de plantillas de Mercurial reconoce las secuencias de control +más comunmente usadas dentro de las cadenas. Cuando ve un backslash +(``\Verb+\+''), ve el caracter siguiente y sustituye los dos +caracteres con un reemplazo sencillo, como se describe a continuación: + +\begin{itemize} +\item[\Verb+\textbackslash\textbackslash+] Backslash, ``\Verb+\+'', + ASCII~134. +\item[\Verb+\textbackslash n+] Nueva línea, ASCII~12. +\item[\Verb+\textbackslash r+] Cambio de línea, ASCII~15. +\item[\Verb+\textbackslash t+] Tab, ASCII~11. +\item[\Verb+\textbackslash v+] Tab Vertical, ASCII~13. +\item[\Verb+\textbackslash \{+] Corchete abierto, ``\Verb+{+'', ASCII~173. +\item[\Verb+\textbackslash \}+] Corchete cerrado, ``\Verb+}+'', ASCII~175. +\end{itemize} + +Como se indicó arriba, si desea que la expansión en una plantilla +contenga un caracter literal ``\Verb+\+'', ``\Verb+{+'', o + ``\Verb+{+'', debe escaparlo. + +\section{Uso de filtros con palabras claves} +\label{sec:template:filter} + +Algunos de los resultados de la expansión de la plantilla no son +fáciles de usar de inmediato. Mercurial le permite especificar una +cadena de \emph{filtros} opcionales para modificar el resultado de +expandir una palabra clave. Ya ha visto el filtro usual +\tplkwfilt{date}{isodate} en acción con anterioridad para hacer +legible la fecha. + +A continuación hay una lista de los filtros de Mercurial más +comunmente usados. Ciertos filtros pueden aplicarse a cualquier +texto, otros pueden usarse únicamente en circunstancias específicas. +El nombre de cada filtro está seguido de la indicación de dónde puede +ser usado y una descripción de su efecto. + +\begin{itemize} +\item[\tplfilter{addbreaks}] Cualquier texto. Añade una etiqueta XHTML + ``\Verb+
+'' antes del final de cada línea excepto en la final. + Por ejemplo, ``\Verb+foo\nbar+'' se convierte en ``\Verb+foo
\nbar+''. +\item[\tplkwfilt{date}{age}] palabra clave \tplkword{date}. Muestra + la edad de la fecha, relativa al tiempo actual. Ofrece una cadena como + ``\Verb+10 minutes+''. +\item[\tplfilter{basename}] Cualquier texto, pero de utilidad sobre + todo en palabras claves relativas a \tplkword{ficheros}. Trata el + texto como una ruta, retornando el nombre base. Por ejemplo, + ``\Verb+foo/bar/baz+'', se convierte en ``\Verb+baz+''. +\item[\tplkwfilt{date}{date}] \tplkword{date} palabra clave. Mostrar + la fecha en un formato similar a la orden \tplkword{date} de + in a similar format to the Unix, pero con la zona horaria incluída. + Una cadena como ``\Verb+Mon Sep 04 15:13:13 2006 -0700+''. +\item[\tplkwfilt{author}{domain}] Cualquier texto, pero de mayor + utilidad para la palabra clave \tplkword{author}. Encuentra la + primera cadena que luce como una dirección de correo electrónico, y + extrae solamente el componente del dominio. Por ejemplo, de + ``\Verb+Bryan O'Sullivan +'' se extrae + ``\Verb+serpentine.com+''. +\item[\tplkwfilt{author}{email}] Cualquier texto, pero de mayor + utilidad para la palabra clave \tplkword{author}. Extrae la primera + cadena que luce como una dirección de correo. Por ejemplo, de + ``\Verb+Bryan O'Sullivan +'' extrae + ``\Verb+bos@serpentine.com+''. +\item[\tplfilter{escape}] Cualquier texto. Reemplaza los caracteres + especiales de XML/XHTML: ``\Verb+&+'', ``\Verb+<+'' y ``\Verb+>+'' + con las entidades XML. +\item[\tplfilter{fill68}] Cualquier texto. Lograr que el texto ocupe + las primeras 68 columnas. Es útil emplearlo antes de pasar el texto + por el filtro \tplfilter{tabindent}, y queremos que aún quepa en una + ventana de fuente fija y 80 columnas. +\item[\tplfilter{fill76}] Cualquier texto. Lograr que el texto quepa + en 76 columnas. +\item[\tplfilter{firstline}] Cualquier texto. Mostrar la primera + línea de texto sin saltos de línea. +\item[\tplkwfilt{date}{hgdate}] \tplkword{date} palabra clave. + Mostrar la fecha como un par de números legibles. Muestra una + cadena como ``\Verb+1157407993 25200+''. +\item[\tplkwfilt{date}{isodate}] \tplkword{date} palabra clave. + Mostrar la fecha como una cadena de texto en el formato. Muestra + una cadena como ``\Verb+2006-09-04 15:13:13 -0700+''. +\item[\tplfilter{obfuscate}] Cualquier texto, pero de mayor utilidad + para la palabra clave \tplkword{author}. Muestra el campo de texto + como una secuencia de entidades XML. Esto ayuda a eliminar ciertos + robots estúpidos de adquisición de correo. +\item[\tplkwfilt{author}{person}] Cualquier texto, útil sobre todo + para la palabra clave \tplkword{author}. Muestra el texto que hay + antes de la dirección de correo electrónico. Por ejemplo, + ``\Verb+Bryan O'Sullivan +'' mostraría + ``\Verb+Bryan O'Sullivan+''. +\item[\tplkwfilt{date}{rfc822date}] \tplkword{date} palabra clave. + Muestra una fecha con el mismo formato que se usa en los encabezados + de correo. Mostraría una cadena como + ``\Verb+Mon, 04 Sep 2006 15:13:13 -0700+''. +\item[\tplkwfilt{node}{short}] Hash del conjunto de cambios. Muestra + la forma corta de un hash de conjunto de cambios, + of a changeset hash, p.e.~una cadena hexadecimal de 12 bytes. +\item[\tplkwfilt{date}{shortdate}] \tplkword{date} palabra clave. + Mostrar año, mes y día de una fecha. Muestrauna cadena como + ``\Verb+2006-09-04+''. +\item[\tplfilter{strip}] Cualquier texto. Elimina todos los espacios + en blanco al principio y al final de la cadena. +\item[\tplfilter{tabindent}] Cualquier texto. Muestra el texto con + todas las líneas excepto la primera que comience con el caracter tab. +\item[\tplfilter{urlescape}] Cualquier texto. Escapa todos los + caracteres que se consideren como ``especiales'' por los parsers de + URL. Por ejemplo, \Verb+foo bar+ se convierte en \Verb+foo%20bar+. +\item[\tplkwfilt{author}{user}] Cualquier texto, útil sobre todo para + la palabra clave \tplkword{author}. Retorna el ``usuario'' de una + dirección de correo. Por ejemplo, + ``\Verb+Bryan O'Sullivan +'' se convierte en + ``\Verb+bos+''. +\end{itemize} + +\begin{figure} + \interaction{template.simple.manyfilters} + \caption{Filtros de plantilla en acción} + \label{fig:template:filters} +\end{figure} + +\begin{note} + Si trata de aplicar un filtro a una porción de datos que no puede + procesarse, Mercurial fallará e imprimirá una excepción de Python. + Por ejemplo, el tratar de usar la salida de la palabra clave + \tplkword{desc} con el filtro \tplkwfilt{date}{isodate} no resultará + algo útil. +\end{note} + +\subsection{Combinar filtros} + +Combinar filtros es para generar una salida en la forma como usted lo +desea es muy sencillo. La cadena de filtros siguientes arman una +descripción, después aseguran que cabe limpiamente en 68 columnas, y +las indenta con 8~caracteres (por lo menos en sistemas tipo Unix, en +los que el tab por convención se extiende en 8~caracteres). + +\interaction{template.simple.combine} + +Observe el uso de ``\Verb+\t+'' (un caracter tab) en la plantilla para +forzar que la primera línea se indente; esto es necesario para lograr +que la primera línea luzca indentada; es necesario debido a que +\tplkword{tabindent} indenta todas las líneas \emph{excepto} la primera. + +Tenga en cuenta que el orden de los filtros importa. El primer filtro +se aplica primero al resultado de la palabra clave; el segundo al +resultado de la aplicación del primer filtro y así sucesivamente. Por +ejemplo, usar \Verb+fill68|tabindent+ es muy distinto al resultado de +usar \Verb+tabindent|fill68+. + + +\section{De plantillas a estilos} + +Una plantilla provee una forma rápida y sencilla para dar formato a +una salida. Las plantillas pueden volvers verbosas, y es útil poder +darle un nombre a una plantilla. Un fichero de estilo es una +plantilla con un nombre, almacenado en un fichero. + +Más aún, al usar un fichero de estilo se dispara el poder del motor de +plantillas en un nivel imposible de alcanzar usando las opción +\hgopt{log}{--template} desde la línea de órdenes. + + +\subsection{Los ficheros de estilo más sencillos} + +Nuestro fichero sencillo de estilo contiene una sola línea: + +\interaction{template.simple.rev} + +Se le indica a Mercurial, ``si está imprimiendo un conjunto de +cambios, use el texto de la derecha como la plantilla''. + +\subsection{Sintaxis de ficheros de estilo} + +Las reglas de sintaxis para un fichero de estilo son sencillas: + +\begin{itemize} +\item El fichero se procesa línea por línea. + +\item Se ignoran el espacio en blanco circundante. + +\item Se omiten las líneas en blanco. + +\item Si una línea comienza con los caracteres ``\texttt{\#}'' o + ``\texttt{;}'', la línea completa se trata como un comentario, y se + omite como si fuera vacía. + +\item Una línea comienza con una palabra clave. Esta debe comenzar + con una caracter alfabético o una raya al piso, y puede contener + subsecuentemente cualquier caracter alfanumérico o una raya al + piso. (En notación de expresiones regulares debe coincidir con + \Verb+[A-Za-z_][A-Za-z0-9_]*+.) + +\item El próximo elemento debe ser un caracter ``\texttt{=}'', que + puede estar precedido o seguido por una cantidad arbitraria de + espacio. + +\item Si el resto de la línea comienza y termina con caracteres + encerrados entre caracteres de comillas (bien sea sencillas o + dobles), se trata como cuerpo de la plantilla. + +\item Si el resto de la línea \emph{no} comienza con una comilla, se + trata como el nombre de un fichero; los contenidos de este fichero + se leerán y se usarán como cuerpo de la plantilla. +\end{itemize} + +\section{Ejemplos de ficheros de estilos} + +Para ilustrar la creación de un fichero de estilo, construiremos +algunos ejemplos. En lugar de ofrecer un fichero completo de estilo y +analizarlo, replicaremos el proceso usual de desarrollo de un fichero +de estilo comenzando con algo muy sencillo, y avanzando por una serie +de ejemplos sucesivos más completos. + +\subsection{Identificar equivocaciones en ficheros de estilo} + +Si Mercurial encuentra un problema en un fichero de estilo en el cual +usted está trabajando, imprime un mensaje de error suscinto, cuando +usted identifique lo que significa, resulta muy útil. + +\interaction{template.svnstyle.syntax.input} + +Tenga en cuenta que \filename{broken.style} trata de definir la +palabra clave \texttt{changeset}, pero omite dar un contenido para esta. +Cuando se le indica a Mercurial que use este fichero de estilo, se +queja inmediatamente. + +\interaction{template.svnstyle.syntax.error} + +Este mensaje de error luce intimidante, pero no es muy difícil de +seguir: + +\begin{itemize} +\item El primer componente es la forma como Mercurial dice ``me rindo''. + \begin{codesample4} + \textbf{abort:} broken.style:1: parse error + \end{codesample4} + +\item A continuación viene el nombre del fichero que contiene el error. + \begin{codesample4} + abort: \textbf{broken.style}:1: parse error + \end{codesample4} + +\item Siguendo el nombre del fichero viene el número de línea en la + que se encontró el error. + \begin{codesample4} + abort: broken.style:\textbf{1}: parse error + \end{codesample4} + +\item Finalmente, la descripción de lo que falló. + \begin{codesample4} + abort: broken.style:1: \textbf{parse error} + \end{codesample4} + La descripción del problema no siempre es clara (como en este caso), + pero aunque sea críptica, casi siempre es trivial la inspección + visual de la línea en el fichero de estilo y encontrar lo que está + mal. +\end{itemize} + +\subsection{Identificar de forma única un repositorio} + +Si desea identificar un repositorio de Mercurial ``de forma única'' +con una cadena corta como identificador, puede usar la primera +revisión en el repositorio. +\interaction{template.svnstyle.id} +No es garantía de unicidad, pero no es útill en ciertos casos: +many cases. +\begin{itemize} +\item No funcionará en un repositorio completamente vacío, porque un + repositorio así no tiene una revisión~zero. +\item Tampoco funcionará en caso (muy raro) cuando el repositorio sea + una fusión de dos repositorios independientes y tiene los dos + directorios por ahí. +\end{itemize} +Hay ciertos casos en los cuales podría colocar el identificador: +\begin{itemize} +\item Como una llave en la tabla de una base de datos que administra + repositorios en un servidor. +\item Como una parte del par \{\emph{ID~repositorio}, \emph{ID~revisión}\}. + Almacene esta información de forma independiente cuando ejecute + construcciones automatizadas u otras actividades, de forma que pueda + ``reconstruir'' posteriormente en caso de ser necesario. +\end{itemize} + +\subsection{Mostrando salida parecida a Subversion} + +Intentemos emular la salida usual que usa otro sistema de control de +revisiones, Subversion. +\interaction{template.svnstyle.short} + +Dado que la salida de Subversion es sencilla, es fácil copiar y pegar +una porción de su salida en un fichero, y reemplazar el texto +producido previamente por Subversion con valores base que quisiéramos +ver expandidos. +\interaction{template.svnstyle.template} + +Esta plantilla difiere en algunos detalles de la salida producida por +Subversion: +\begin{itemize} +\item Subversion imprime una fecha ``legible'' (el ``\texttt{Wed, 27 Sep + 2006}'' en el ejemplo de salida anterior) en paréntesis. El motor + de plantillas de Mercurial no ofrece una forma sencilla de desplegar + una fecha en este formato sin imprimir también la hora y la zona horaria. +\item Emulamos las líneas de ``separación'' de subversion con caracteres + ``\texttt{-}'' en una línea. Usamos la palabra clave + \tplkword{header} del motor de plantillas para imprimir una línea de + separación como la primera línea de salida (ver más abajo), para + lograr una salida similara a la de Subversion. +\item La salida de subversion incluye un conteo en el encabezado del + número de líneas en el mensaje de consinación. No podemos + replicarlo en Mercurial; el motor de plantilla no ofrece en la + actualidad un filtro que cuente la cantidad de objetos que se le + pasen. +\end{itemize} +No me tomó más de un minuto o dos de trabajo para reemplazar texto +literal de un ejemplo de salida de la salida de Subversion con ciertas +palabras claves y filtros para ofrecer la plantilla anterior. El +fichero de estilo se refiere sencillamente a la plantilla. +\interaction{template.svnstyle.style} + +Podríamos haber incluído el texto del fichero plantilla directamente +en el fichero de estilo encerrando entre comillas y reemplazando las +nuevas líneas con secuencias ``\verb!\n!'', pero haría muy difícil de +leer el fichero de estilos. La facilidad para leer es importante +cuando está decidiendo si un texto pertenece a un fichero de estilo o +a un fichero de plantilla incluído en el estilo. Si el fichero de +estilo luce muy grande o complicado, si inserta una pieza de texto +literal, mejor colóquelo en una plantilla. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/tour-basic.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-basic.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,690 @@ +\chapter{Una gira de Mercurial: lo básico} +\label{chap:tour-basic} + +\section{Instalar Mercurial en su sistema} +\label{sec:tour:install} +Hay paquetes binarios precompilados de Mercurial disponibles para cada +sistema operativo popular. Esto hace fácil empezar a usar Mercurial +en su computador inmediatamente. + +\subsection{Linux} + +Dado que cada distribución de Linux tiene sus propias herramientas de +manejo de paquetes, políticas, y ritmos de desarrollo, es difícil dar +un conjunto exhaustivo de instrucciones sobre cómo instalar el paquete +de Mercurial. La versión de Mercurial que usted tenga a disposición +puede variar dependiendo de qué tan activa sea la persona que mantiene +el paquete para su distribución. + +Para mantener las cosas simples, me enfocaré en instalar Mercurial +desde la línea de comandos en las distribuciones de Linux más +populares. La mayoría de estas distribuciones proveen administradores +de paquetes gráficos que le permitirán instalar Mercurial con un solo +clic; el nombre de paquete a buscar es \texttt{mercurial}. + +\begin{itemize} +\item[Debian] + \begin{codesample4} + apt-get install mercurial + \end{codesample4} + +\item[Fedora Core] + \begin{codesample4} + yum install mercurial + \end{codesample4} + +\item[Gentoo] + \begin{codesample4} + emerge mercurial + \end{codesample4} + +\item[OpenSUSE] + \begin{codesample4} + yum install mercurial + \end{codesample4} + +\item[Ubuntu] El paquete de Mercurial de Ubuntu está basado en el de + Debian. Para instalarlo, ejecute el siguiente comando. + \begin{codesample4} + apt-get install mercurial + \end{codesample4} + El paquete de Mercurial para Ubuntu tiende a atrasarse con respecto + a la versión de Debian por un margen de tiempo considerable + (al momento de escribir esto, 7 meses), lo que en algunos casos + significará que usted puede encontrarse con problemas que ya habrán + sido resueltos en el paquete de Debian. +\end{itemize} + +\subsection{Solaris} + +SunFreeWare, en \url{http://www.sunfreeware.com}, es una buena fuente +para un gran número de paquetes compilados para Solaris para las +arquitecturas Intel y Sparc de 32 y 64 bits, incluyendo versiones +actuales de Mercurial. + +\subsection{Mac OS X} + +Lee Cantey publica un instalador de Mercurial para Mac OS~X en +\url{http://mercurial.berkwood.com}. Este paquete funciona en tanto +en Macs basados en Intel como basados en PowerPC. Antes de que pueda +usarlo, usted debe instalar una versión compatible de Universal +MacPython~\cite{web:macpython}. Esto es fácil de hacer; simplemente +siga las instrucciones del sitio de Lee. + +También es posible instalar Mercurial usando Fink o MacPorts, dos +administradores de paquetes gratuitos y populares para Mac OS X. Si +usted tiene Fink, use \command{sudo apt-get install mercurial-py25}. +Si usa MacPorts, \command{sudo port install mercurial}. + +\subsection{Windows} + +Lee Cantey publica un instalador de Mercurial para Windows en +\url{http://mercurial.berkwood.com}. Este paquete no tiene +% TODO traducción de it just works. Agreed? +dependencias externas; ``simplemente funciona''. + +\begin{note} + La versión de Windows de Mercurial no convierte automáticamente + los fines de línea entre estilos Windows y Unix. Si usted desea + compartir trabajo con usuarios de Unix, deberá hacer un trabajo + adicional de configuración. XXX Terminar esto. +\end{note} + +\section{Arrancando} + +Para empezar, usaremos el comando \hgcmd{version} para revisar si +Mercurial está instalado adecuadamente. La información de la versión +que es impresa no es tan importante; lo que nos importa es si imprime +algo en absoluto. + +\interaction{tour.version} + +% TODO builtin-> integrado? +\subsection{Ayuda integrada} + +Mercurial provee un sistema de ayuda integrada. Esto es invaluable +para ésas ocasiones en la que usted está atorado tratando de recordar +cómo ejecutar un comando. Si está completamente atorado, simplemente +ejecute \hgcmd{help}; esto imprimirá una breve lista de comandos, +junto con una descripción de qué hace cada uno. Si usted solicita +ayuda sobre un comando específico (como abajo), se imprime información +más detallada. +\interaction{tour.help} +Para un nivel más impresionante de detalle (que usted no va a +necesitar usualmente) ejecute \hgcmdargs{help}{\hggopt{-v}}. La opción +\hggopt{-v} es la abreviación para \hggopt{--verbose}, y le indica a +Mercurial que imprima más información de lo que haría usualmente. + +\section{Trabajar con un repositorio} + +En Mercurial, todo sucede dentro de un \emph{repositorio}. El +repositorio para un proyecto contiene todos los ficheros que +``pertenecen a'' ése proyecto, junto con un registro histórico de los +ficheros de ese proyecto. + +No hay nada particularmente mágico acerca de un repositorio; es +simplemente un árbol de directorios en su sistema de ficheros que +Mercurial trata como especial. Usted puede renombrar o borrar un +repositorio en el momento que lo desee, usando bien sea la línea de +comandos o su explorador de ficheros. + +\subsection{Hacer una copia local de un repositorio} + +\emph{Copiar} un repositorio es sólo ligeramente especial. Aunque +usted podría usar un programa normal de copia de ficheros para hacer +una copia del repositorio, es mejor usar el comando integrado que +Mercurial ofrece. Este comando se llama \hgcmd{clone}\ndt{Del término +``clonar'' en inglés.}, porque crea una copia idéntica de un +repositorio existente. +\interaction{tour.clone} +Si nuestro clonado tiene éxito, deberíamos tener un directorio local +llamado \dirname{hello}. Este directorio contendrá algunos ficheros. +\interaction{tour.ls} +Estos ficheros tienen el mismo contenido e historial en nuestro +repositorio y en el repositorio que clonamos. + +Cada repositorio Mercurial está completo, es autocontenido e +independiente. Contiene su propia copia de los ficheros y el historial +de un proyecto. Un repositorio clonado recuerda la ubicación de la que +fue clonado, pero no se comunica con ese repositorio, ni con ningún +otro, a menos que usted le indique que lo haga. + +Lo que esto significa por ahora es que somos libres de experimentar +con nuestro repositorio, con la tranquilidad de saber que es una +% TODO figure out what to say instead of sandbox +``caja de arena'' privada que no afectará a nadie más. + +\subsection{Qué hay en un repositorio?} + +Cuando miramos en detalle dentro de un repositorio, podemos ver que +contiene un directorio llamado \dirname{.hg}. Aquí es donde Mercurial +mantiene todos los metadatos del repositorio. +\interaction{tour.ls-a} + +Los contenidos del directorio \dirname{.hg} y sus subdirectorios son +exclusivos de Mercurial. Usted es libre de hacer lo que desee con +cualquier otro fichero o directorio en el repositorio. + +Para introducir algo de terminología, el directorio \dirname{.hg} es +el repositorio ``real'', y todos los ficheros y directorios que +coexisten con él están en el \emph{directorio de trabajo}. Una forma +sencilla de recordar esta distinción es que el \emph{repositorio} +contiene el \emph{historial} de su proyecto, mientras que el +\emph{directorio de trabajo} contiene una \emph{instantánea} de su +proyecto en un punto particular del historial. + +\section{Vistazo rápido al historial} + +Una de las primeras cosas que se desea hacer con un repositorio nuevo, +poco conocido, es conocer su historial. El comando \hgcmd{log} nos +permite ver el mismo. +\interaction{tour.log} +Por defecto este programa imprime un párrafo breve por cada cambio al +proyecto que haya sido grabado. Dentro de la terminología de +Mercurial, cada uno de estos eventos es llamado \emph{conjunto de +cambios}, porque pueden contener un registro de cambios a varios +ficheros. + +Los campos de la salida de \hgcmd{log} son los siguientes. +\begin{itemize} + \item[\texttt{changeset}]\hspace{-0.5em}\ndt{Conjunto de cambios.} Este campo + tiene un número, seguido por un + % TODO digo mejor seguido por un dos puntos ? string => + % cadena? + \texttt{:}, seguido por una cadena hexadecimal. Ambos son + \emph{identificadores} para el conjunto de cambios. Hay dos + identificadores porque el número es más corto y más fácil de + recordar que la cadena hexadecimal. + +\item[\texttt{user}]\hspace{-0.5em}\ndt{Usuario.} La identidad de la + persona que creó el conjunto de cambios. Este es un campo en el + que se puede almacenar cualquier valor, pero en la mayoría de los + casos contiene el nombre de una persona y su dirección de correo + electrónico. + +\item[\texttt{date}]\hspace{-0.5em}\ndt{Fecha.} La fecha y hora en la + que el conjunto de cambios fue creado, y la zona horaria en la que + fue creado. (La fecha y hora son locales a dicha zona horaria; + ambos muestran la fecha y hora para la persona que creó el + conjunto de cambios). + +\item[\texttt{summary}]\hspace{-0.5em}\ndt{Sumario.} + La primera línea del texto que usó la persona que creó el conjunto + de cambios para describir el mismo. +\end{itemize} +El texto impreso por \hgcmd{log} es sólo un sumario; omite una gran +cantidad de detalles. + +La figura~\ref{fig:tour-basic:history} es una representación +gráfica del historial del repositorio \dirname{hello}, para hacer más +fácil ver en qué dirección está ``fluyendo'' el historial. Volveremos +a esto varias veces en este capítulo y en los siguientes. + +\begin{figure}[ht] + \centering + \grafix{tour-history} + \caption{Historial gráfico del repositorio \dirname{hello}} + \label{fig:tour-basic:history} +\end{figure} + +\subsection{Conjuntos de cambios, revisiones, y comunicándose con +otras personas} + +%TODO sloppy => desordenado ? TODO hablar del inglés? o de español? +Ya que el inglés es un lenguaje notablemente desordenado, y el área de +ciencias de la computación tiene una notable historia de confusión de +% TODO insertar ? al revés. no sé cómo en un teclado de estos. +términos (porqué usar sólo un término cuando cuatro pueden servir?), +el control de revisiones tiene una variedad de frases y palabras que +tienen el mismo significado. Si usted habla acerca del historial de +Mercurial con alguien, encontrará que la expresión ``conjunto de +cambios'' es abreviada a menudo como ``cambio'' o (por escrito) +``cset''\ndt{Abreviatura para la expresión ``changeset'' en inglés.}, +y algunas veces un se hace referencia a un conjunto de cambios como +una ``revisión'' o ``rev''\ndt{De nuevo, como abreviación para el +término en inglés para ``revisión'' (``revision'').}. + +Si bien no es relevante qué \emph{palabra} use usted para referirse al +concepto ``conjunto de cambios'', el \emph{identificador} que usted +use para referise a ``un \emph{conjunto de cambios} particular'' es +muy importante. Recuerde que el campo \texttt{changeset} en la salida +de \hgcmd{log} identifica un conjunto de cambios usando tanto un +número como una cadena hexadecimal. + +\begin{itemize} + \item El número de revisión \emph{sólo es válido dentro del + repositorio}. + \item Por otro lado, la cadena hexadecimal es el + \emph{identificador permanente e inmutable} que siempre + identificará ése conjunto de cambios en \emph{todas} las + copias del repositorio. +\end{itemize} +La diferencia es importante. Si usted le envía a alguien un correo +electrónico hablando acerca de la ``revisión~33'', hay una +probabilidad alta de que la revisión~33 de esa persona \emph{no sea la +misma suya}. Esto sucede porque el número de revisión depende del +orden en que llegan los cambios al repositorio, y no hay ninguna +garantía de que los mismos cambios llegarán en el mismo orden en +diferentes repositorios. Tres cambios dados $a,b,c$ pueden aparecer en +un repositorio como $0,1,2$, mientras que en otro aparecen como +$1,0,2$. + +Mercurial usa los números de revisión simplemente como una abreviación +conveniente. Si usted necesita hablar con alguien acerca de un +conjunto de cambios, o llevar el registro de un conjunto de cambios +por alguna otra razón (por ejemplo, en un reporte de fallo), use el +identificador hexadecimal. + +\subsection{Ver revisiones específicas} + +Para reducir la salida de \hgcmd{log} a una sola revisión, use la +opción \hgopt{log}{-r} (o \hgopt{log}{--rev}). Puede usar un número +de revisión o un identificador hexadecimal de conjunto de cambios, y +puede pasar tantas revisiones como desee. + +\interaction{tour.log-r} + +Si desea ver el historial de varias revisiones sin tener que mencionar +cada una de ellas, puede usar la \emph{notación de rango}; esto le +permite expresar el concepto ``quiero ver todas las revisiones entre +$a$ y $b$, inclusive''. +\interaction{tour.log.range} +Mercurial también respeta el orden en que usted especifica las +revisiones, así que \hgcmdargs{log}{-r 2:4} muestra $2,3,4$ mientras +que \hgcmdargs{log}{-r 4:2} muestra $4,3,2$. + +\subsection{Información más detallada} +Aunque la información presentada por \hgcmd{log} es útil si usted sabe +de antemano qué está buscando, puede que necesite ver una descripción +completa del cambio, o una lista de los ficheros que cambiaron, si +está tratando de averiguar si un conjunto de cambios dado es el que +usted está buscando. La opción \hggopt{-v} (or \hggopt{--verbose}) del +comando \hgcmd{log} le da este nivel extra de detalle. +\interaction{tour.log-v} + +Si desea ver tanto la descripción como el contenido de un cambio, +añada la opción \hgopt{log}{-p} (o \hgopt{log}{--patch}). Esto muestra +% TODO qué hacemos con diff unificado? convervarlo, por ser la +% acepción usual? +el contenido de un cambio como un \emph{diff unificado} (si usted +nunca ha visto un diff unificado antes, vea la +sección~\ref{sec:mq:patch} para un vistazo global). +\interaction{tour.log-vp} + +\section{Todo acerca de las opciones para comandos} + +Tomemos un breve descanso de la tarea de explorar los comandos de +Mercurial para hablar de un patrón en la manera en que trabajan; será +útil tener esto en mente a medida que avanza nuestra gira. + +Mercurial tiene un enfoque directo y consistente en el manejo de las +opciones que usted le puede pasar a los comandos. Se siguen las +convenciones para opciones que son comunes en sistemas Linux y Unix +modernos. +\begin{itemize} +\item Cada opción tiene un nombre largo. Por ejemplo, el comando + \hgcmd{log} acepta la opción \hgopt{log}{--rev}, como ya hemos + visto. +\item Muchas opciones tienen también un nombre corto. En vez de + \hgopt{log}{--rev}, podemos usar \hgopt{log}{-r}. (El motivo para + que algunas opciones no tengan nombres cortos es que dichas + opciones se usan rara vez.) +\item Las opciones largas empiezan con dos guiones (p.ej.~\hgopt{log}{--rev}), + mientras que las opciones cortas empiezan con uno (e.g.~\hgopt{log}{-r}). +\item El nombre y uso de las opciones es consistente en todos los + comandos. Por ejemplo, cada comando que le permite pasar un ID de + conjunto de cambios o un número de revisión acepta tanto la opción + \hgopt{log}{-r} como la \hgopt{log}{--rev}. +\end{itemize} +En los ejemplos en este libro, uso las opciones cortas en vez de las +largas. Esto sólo muestra mis preferencias, así que no le dé +significado especial a eso. + +Muchos de los comandos que generan salida de algún tipo mostrarán más +salida cuando se les pase la opción \hggopt{-v} (o +\hggopt{--verbose}\ndt{Prolijo.}), y menos cuando se les pase la opción \hggopt{-q} +(o \hggopt{--quiet}\ndt{Silencioso.}). + +\section{Hacer y repasar cambios} + +Ahora que tenemos una comprensión adecuada sobre cómo revisar el +historial en Mercurial, hagamos algunos cambios y veamos cómo +examinarlos. + +Lo primero que haremos será aislar nuestro experimento en un +repositorio propio. Usaremos el comando \hgcmd{clone}, pero no hace +falta clonar una copia del repositorio remoto. Como ya contamos con +una copia local del mismo, podemos clonar esa. Esto es mucho más +rápido que clonar a través de la red, y en la mayoría de los casos +clonar un repositorio local usa menos espacio en disco también. +\interaction{tour.reclone} +A manera de recomendación, es considerado buena práctica mantener una +copia ``prístina'' de un repositorio remoto a mano, del cual usted +puede hacer clones temporales para crear cajas de arena para cada +tarea en la que desee trabajar. Esto le permite trabajar en múltiples +tareas en paralelo, teniendo cada una de ellas aislada de las otras +hasta que estén completas y usted esté listo para integrar los cambios +de vuelta. Como los clones locales son tan baratos, clonar y destruir +repositorios no consume demasiados recursos, lo que facilita hacerlo +en cualquier momento. + +En nuestro repositorio \dirname{my-hello}, hay un fichero +\filename{hello.c} que contiene el clásico programa ``hello, +world''\ndt{Hola, mundo.}. Usaremos el clásico y venerado comando +\command{sed} para editar este fichero y hacer que imprima una segunda +línea de salida. (Estoy usando el comando \command{sed} para hacer +esto sólo porque es fácil escribir un ejemplo automatizado con él. +Dado que usted no tiene esta restricción, probablemente no querrá usar +\command{sed}; use su editor de texto preferido para hacer lo mismo). +\interaction{tour.sed} + +El comando \hgcmd{status} de Mercurial nos dice lo que Mercurial sabe +acerca de los ficheros en el repositorio. +\interaction{tour.status} +El comando \hgcmd{status} no imprime nada para algunos ficheros, sólo +una línea empezando con ``\texttt{M}'' para el fichero +\filename{hello.c}. A menos que usted lo indique explícitamente, +\hgcmd{status} no imprimirá nada respecto a los ficheros que no han +sido modificados. + +La ``\texttt{M}'' indica que Mercurial se dio cuenta de que nosotros +modificamos \filename{hello.c}. No tuvimos que \emph{decirle} a +Mercurial que íbamos a modificar ese fichero antes de hacerlo, o que +lo modificamos una vez terminamos de hacerlo; él fue capaz de darse +cuenta de esto por sí mismo. + +Es algo útil saber que hemos modificado el fichero \filename{hello.c}, +pero preferiríamos saber exactamente \emph{qué} cambios hicimos. +Para averiguar esto, usamos el comando \hgcmd{diff}. +\interaction{tour.diff} + +\section{Grabar cambios en un nuevo conjunto de cambios} + +Podemos modificar, compilar y probar nuestros cambios, y usar +\hgcmd{status} y \hgcmd{diff} para revisar los mismos, hasta que +estemos satisfechos con los resultados y lleguemos a un momento en el +que sea natural que querramos guardar nuestro trabajo en un nuevo +conjunto de cambios. + +El comando \hgcmd{commit} nos permite crear un nuevo conjunto de +cambios. Nos referiremos usualmente a esto como ``hacer una consigna'' +o consignar. + +\subsection{Definir un nombre de usuario} + +Cuando usted trata de ejecutar \hgcmd{commit}\ndt{Hacer una +consignación} por primera vez, no está garantizado que lo logre. +Mercurial registra su nombre y dirección en cada cambio que usted +consigna, para que más adelante otros puedan saber quién es el +responsable de cada cambio. Mercurial trata de encontrar un nombre de +% TODO consigna o consignación? +usuario adecuado con el cual registrar la consignación. Se intenta con +cada uno de los siguientes métodos, en el orden presentado. +\begin{enumerate} +\item Si usted pasa la opción \hgopt{commit}{-u} al comando \hgcmd{commit} + en la línea de comandos, seguido de un nombre de usuario, se le da a + esto la máxima precedencia. +\item A continuación se revisa si usted ha definido la variable de + entorno \envar{HGUSER}. +\item Si usted crea un fichero en su directorio personal llamado + \sfilename{.hgrc}, con una entrada \rcitem{ui}{username}, se usa + luego. Para revisar cómo debe verse este fichero, refiérase a la + sección~\ref{sec:tour-basic:username} más abajo. +\item Si usted ha definido la variable de entorno \envar{EMAIL}, será + usada a continuación. +\item Mercurial le pedirá a su sistema buscar su nombre de usuario + % TODO host => máquina + local, y el nombre de máquina, y construirá un nombre de usuario a + partir de estos componentes. Ya que esto generalmente termina + generando un nombre de usuario no muy útil, se imprimirá una + advertencia si es necesario hacerlo. +\end{enumerate} +Si todos estos procedimientos fallan, Mercurial fallará, e imprimirá +un mensaje de error. En este caso, no le permitirá hacer la +consignación hasta que usted defina un nombre de usuario. + +Trate de ver la variable de entorno \envar{HGUSER} y la opción +\hgopt{commit}{-u} del comando \hgcmd{commit} como formas de +\emph{hacer caso omiso} de la selección de nombre de usuario que +Mercurial hace normalmente. Para uso normal, la manera más simple y +sencilla de definir un nombre de usuario para usted es crear un +fichero \sfilename{.hgrc}; los detalles se encuentran más adelante. + +\subsubsection{Crear el fichero de configuración de Mercurial} +\label{sec:tour-basic:username} + +Para definir un nombre de usuario, use su editor de texto favorito +para crear un fichero llamado \sfilename{.hgrc} en su directorio +personal. Mercurial usará este fichero para obtener las +configuraciones personalizadas que usted haya hecho. El contenido +inicial de su fichero \sfilename{.hgrc} debería verse así. +\begin{codesample2} + # Este es un fichero de configuración de Mercurial. + [ui] + username = Primernombre Apellido +\end{codesample2} +La línea ``\texttt{[ui]}'' define una \emph{section} del fichero de +configuración, así que usted puede leer la línea ``\texttt{username = +...}'' como ``defina el valor del elemento \texttt{username} en la +sección \texttt{ui}''. +Una sección continua hasta que empieza otra nueva, o se llega al final +del fichero. Mercurial ignora las líneas vacías y considera cualquier +texto desde el caracter ``\texttt{\#}'' hasta el final de la línea +como un comentario. + +\subsubsection{Escoger un nombre de usuario} + +Usted puede usar el texto que desee como el valor del campo de +configuración \texttt{username}, ya que esta información será leída +por otras personas, e interpretada por Mercurial. La convención que +sigue la mayoría de la gente es usar su nombre y dirección de correo, +como en el ejemplo anterior. + +\begin{note} + % TODO web + El servidor web integrado de Mercurial ofusca las direcciones de + correo, para dificultar la tarea de las herramientas de + recolección de direcciones de correo que usan los + spammers\ndt{Personas que envían correo no solicitado, también + conocido como correo basura}. Esto reduce la probabilidad de que + usted empiece a recibir más correo basura si publica un + repositorio en la red. +\end{note} + +\subsection{Escribir un mensaje de consignación} + +Cuando consignamos un cambio, Mercurial nos ubica dentro de un editor +de texto, para ingresar un mensaje que describa las modificaciones que +hemos introducido en este conjunto de cambios. Esto es conocido como +un \emph{mensaje de consignación}. Será un registro de lo que hicimos +y porqué lo hicimos, y será impreso por \hgcmd{log} una vez hayamos +hecho la consignación. +\interaction{tour.commit} + +El editor en que \hgcmd{commit} nos ubica contendrá una línea vacía, +seguida de varias líneas que empiezan con la cadena ``\texttt{HG:}''. +\begin{codesample2} + \emph{línea vacía} + HG: changed hello.c +\end{codesample2} +Mercurial ignora las líneas que empiezan con ``\texttt{HG:}''; sólo +las usa para indicarnos para cuáles ficheros está registrando los +cambios. Modificar o borrar estas líneas no tiene ningún efecto. + +\subsection{Escribir un buen mensaje de consignación} + +Ya que por defecto \hgcmd{log} sólo muestra la primera línea de un +mensaje de consignación, lo mejor es escribir un mensaje cuya primera +línea tenga significado por sí misma. A continuación se encuentra un +ejemplo de un mensaje de consignación que \emph{no} sigue esta +pauta, y debido a ello tiene un sumario que no es legible. +\begin{codesample2} + changeset: 73:584af0e231be + user: Persona Censurada + date: Tue Sep 26 21:37:07 2006 -0700 + summary: se incluye buildmeister/commondefs. Añade un módulo +\end{codesample2} + +Con respecto al resto del contenido del mensaje de consignación, no +hay reglas estrictas-y-rápidas. Mercurial no interpreta ni le da +importancia a los contenidos del mensaje de consignación, aunque es +posible que su proyecto tenga políticas que definan una manera +particular de escribirlo. + +Mi preferencia personal es usar mensajes de consignación cortos pero +informativos, que me digan algo que no puedo inferir con una mirada +rápida a la salida de \hgcmdargs{log}{--patch}. + +\subsection{Cancelar una consignación} + +Si usted decide que no desea hacer la consignación mientras está +editando el mensaje de la misma, simplemente cierre su editor sin +guardar los cambios al fichero que está editando. Esto hará que no +pase nada ni en el repositorio ni en el directorio de trabajo. + +Si ejecutamos el comando \hgcmd{commit} sin ningún argumento, se +registran todos los cambios que hemos hecho, como lo indican +\hgcmd{status} y \hgcmd{diff}. + +\subsection{Admirar nuestro trabajo} + +Una vez hemos terminado la consignación, podemos usar el comando +\hgcmd{tip}\ndt{Punta.} para mostrar el conjunto de cambios que acabamos de crear. +La salida de este comando es idéntica a la de \hgcmd{log}, pero sólo +muestra la revisión más reciente en el repositorio. +\interaction{tour.tip} +Nos referimos a la revisión más reciente en el repositorio como la +revisión de punta, o simplemente la punta. + +\section{Compartir cambios} + +Anteriormente mencionamos que los repositorios en Mercurial están auto +contenidos. Esto quiere decir que el conjunto de cambios que acabamos +de crear sólo existe en nuestro repositorio \dirname{my-hello}. Veamos +unas cuantas formas de propagar este cambio a otros repositorios. + +\subsection{Jalar cambios desde otro repositorio} +\label{sec:tour:pull} + +Para empezar, clonemos nuestro repositorio \dirname{hello} original, +el cual no contiene el cambio que acabamos de consignar. Llamaremos a +este repositorio temporal \dirname{hello-pull}. +\interaction{tour.clone-pull} + +Usaremos el comando \hgcmd{pull} para traer los cambios de +\dirname{my-hello} y ponerlos en \dirname{hello-pull}. Sin embargo, +traer cambios desconocidos y aplicarlos en un repositorio es una +perspectiva que asusta al menos un poco. Mercurial cuenta con el +comando \hgcmd{incoming}\ndt{Entrante, o cambios entrantes.} para +decirnos qué cambios \emph{jalaría} el comando \hgcmd{pull} al +repositorio, sin jalarlos. +\interaction{tour.incoming} +(Por supuesto, alguien podría enviar más conjuntos de cambios al +repositorio en el tiempo que pasa entre la ejecución de +\hgcmd{incoming} y la ejecución de \hgcmd{pull} para jalar los +cambios, así que es posible que terminemos jalando cambios que no +esperábamos.) + +Traer cambios al repositorio simplemente es cuestión de ejecutar el +comando \hgcmd{pull}, indicándole de qué repositorio debe jalarlos. +\interaction{tour.pull} +Como puede verse por las salidas antes-y-después de \hgcmd{tip}, hemos +jalado exitosamente los cambios en nuestro repositorio. Aún falta un +paso para que podamos ver estos cambios en nuestro directorio de +trabajo. + +\subsection{Actualizar el directorio de trabajo} + +Hasta ahora hemos pasado por alto la relación entre un repositorio y +su directorio de trabajo. El comando \hgcmd{pull} que ejecutamos en la +sección~\ref{sec:tour:pull} trajo los cambios al repositorio, pero si +revisamos, no hay rastro de esos cambios en el directorio de trabajo. +Esto pasa porque \hgcmd{pull} (por defecto) no modifica el directorio de +trabajo. En vez de eso, usamos el comando +\hgcmd{update}\ndt{Actualizar.} para hacerlo. +\interaction{tour.update} + +Puede parecer algo raro que \hgcmd{pull} no actualice el directorio de +trabajo automáticamente. De hecho, hay una buena razón para esto: +usted puede usar \hgcmd{update} para actualizar el directorio de +trabajo al estado en que se encontraba en \emph{cualquier revisión} +del historial del repositorio. Si usted hubiera actualizado el +directorio de trabajo a una revisión anterior---digamos, para buscar +el origen de un fallo---y hubiera corrido un \hgcmd{pull} que hubiera +actualizado el directorio de trabajo automáticamente a la nueva +revisión, puede que no estuviera particularmente contento. + +Sin embargo, como jalar-y-actualizar es una secuencia de operaciones +muy común, Mercurial le permite combinarlas al pasar la opción +\hgopt{pull}{-u} +a \hgcmd{pull}. +\begin{codesample2} + hg pull -u +\end{codesample2} +Si mira de vuelta la salida de \hgcmd{pull} en la +sección~\ref{sec:tour:pull} cuando lo ejecutamos sin la opción \hgopt{pull}{-u}, +verá que el comando imprimió un amable recordatorio de que tenemos que +encargarnos explícitamente de actualizar el directorio de trabajo: +\begin{codesample2} + (run 'hg update' to get a working copy) +\end{codesample2} + +Para averiguar en qué revisión se encuentra el directorio de trabajo, +use el comando \hgcmd{parents}. +\interaction{tour.parents} +Si mira de nuevo la figura~\ref{fig:tour-basic:history}, verá flechas +conectando cada conjunto de cambios. En cada caso, el nodo del que la flecha +\emph{sale} es un padre, y el nodo al que la flecha \emph{llega} es +su hijo. El directorio de trabajo tiene un padre exactamente de la +misma manera; ése es el conjunto de cambios que contiene actualmente +el directorio de trabajo. + +Para actualizar el conjunto de trabajo a una revisión particular, pase +un número de revisión o un ID de conjunto de cambios al comando +\hgcmd{update}. +\interaction{tour.older} +Si no indica explícitamente una revisión, \hgcmd{update} actualizará +hasta la revisión de punta, como se vio en la segunda llamada a +\hgcmd{update} en el ejemplo anterior. + +\subsection{Empujar cambios a otro repositorio} + +Mercurial nos permite empujar cambios a otro repositorio, desde el +% TODO cambié "visitando" por "usando" +repositorio que estemos usando actualmente. De la misma forma que en +el ejemplo de \hgcmd{pull} arriba, crearemos un repositorio temporal +para empujar allí nuestros cambios. +\interaction{tour.clone-push} +El comando \hgcmd{outgoing}\ndt{Saliente. Cambios salientes.} nos dice +qué cambios serían empujados en el otro repositorio. +\interaction{tour.outgoing} +Y el comando \hgcmd{push} se encarga de empujar dichos cambios. +\interaction{tour.push} +Al igual que \hgcmd{pull}, el comando \hgcmd{push} no actualiza el +directorio de trabajo del repositorio en el que estamos empujando los +cambios. (A diferencia de \hgcmd{pull}, \hgcmd{push} no ofrece la +opción \texttt{-u} para actualizar el directorio de trabajo del otro +repositorio.) + +% TODO poner interrogante de apertura +Qué pasa si tratamos de jalar o empujar cambios y el repositorio +receptor ya tiene esos cambios? Nada emocionante. +\interaction{tour.push.nothing} + +\subsection{Compartir cambios a través de una red} + +Los comandos que hemos presentando en las pocas secciones anteriores +no están limitados a trabajar con repositorios locales. Cada uno de +ellos funciona exactamente de la misma manera a través de una conexión +% TODO poner ndt para URL +de red. Simplemente pase una URL en vez de una ruta local. +\interaction{tour.outgoing.net} +En este ejemplo, podemos ver qué cambios empujaríamos al repositorio +remoto, aunque, de manera entendible, el repositorio remoto está +configurado para no permitir a usuarios anónimos empujar cambios a él. +\interaction{tour.push.net} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/tour-history.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-history.svg Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/tour-merge-conflict.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-merge-conflict.svg Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/tour-merge-merge.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-merge-merge.svg Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/tour-merge-pull.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-merge-pull.svg Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/tour-merge-sep-repos.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-merge-sep-repos.svg Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/tour-merge.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/tour-merge.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,308 @@ +\chapter{Una gira de Mercurial: fusionar trabajo} +\label{chap:tour-merge} + +Hasta ahora hemos cubierto cómo clonar un repositorio, hacer cambios, +y jalar o empujar dichos cambios de un repositorio a otro. Nuestro +siguiente paso es \emph{fusionar} cambios de repositorios separados. + +% TODO cambié streams por líneas. check please +\section{Fusionar líneas de trabajo} + +Fusionar es una parte fundamental de trabajar con una herramienta +de control distribuido de versiones. +\begin{itemize} +\item Alicia y Roberto tienen cada uno una copia personal del + repositorio de un proyecto en el que están trabajando. Alicia + arregla un fallo en su repositorio; Roberto añade una nueva + característica en el suyo. Ambos desean que el repositorio + compartido contenga el arreglo del fallo y la nueva + característica. +\item Frecuentemente trabajo en varias tareas diferentes en un mismo + proyecto al mismo tiempo, cada una aislada convenientemente de las + otras en su propio repositorio. Trabajar de esta manera significa + que a menudo debo fusionar una parte de mi propio trabajo con + otra. +\end{itemize} + +Como fusionar es una operación tan necesaria y común, Mercurial la +facilita. Revisemos el proceso. Empezaremos clonando (otro) +% TODO poner interrogante de apertura +repositorio (ve lo seguido que aparecen?) y haciendo un cambio en él. +\interaction{tour.merge.clone} +Ahora deberíamos tener dos copias de \filename{hello.c} con contenidos +diferentes. El historial de los dos repositorios diverge ahora, como +se ilustra en la figura~\ref{fig:tour-merge:sep-repos}. +\interaction{tour.merge.cat} + +\begin{figure}[ht] + \centering + \grafix{tour-merge-sep-repos} + \caption{Historial reciente divergente de los repositorios + \dirname{my-hello} y \dirname{my-new-hello}} + \label{fig:tour-merge:sep-repos} +\end{figure} + +Ya sabemos que jalar los cambios desde nuestro repositorio +\dirname{my-hello} no tendrá efecto en el directorio de trabajo. +\interaction{tour.merge.pull} +Sin embargo, el comando \hgcmd{pull} dice algo acerca de +``frentes''\ndt{El autor se refiere a \emph{heads} aquí.}. + +\subsection{Conjuntos de cambios de frentes} + +Un frente es un cambio que no tiene descendientes, o hijos, como +también se les conoce. La revisión de punta es, por tanto, un frente, +porque la revisión más reciente en un repositorio no tiene ningún +% TODO cambio en la redacción de la frase, pero espero que conserve el +% sentido. Querido human@, apruebe o corrija :D +hijo. Sin embargo, un repositorio puede contener más de un frente. + +\begin{figure}[ht] + \centering + \grafix{tour-merge-pull} + \caption{Contenidos del repositorio después de jalar + \dirname{my-hello} a \dirname{my-new-hello}} + \label{fig:tour-merge:pull} +\end{figure} + +En la figura~\ref{fig:tour-merge:pull} usted puede ver el efecto que +tiene jalar los cambios de \dirname{my-hello} a \dirname{my-new-hello}. +El historial que ya existía en \dirname{my-new-hello} se mantiene +intacto, pero fue añadida una nueva revisión. Refiriéndonos a la +figura~\ref{fig:tour-merge:sep-repos}, podemos ver que el \emph{ID del +conjunto de cambios} se mantiene igual en el nuevo repositorio, pero +el \emph{número de revisión} ha cambiado. (Incidentalmente, éste es un +buen ejemplo de porqué no es seguro usar números de revisión cuando se +habla de conjuntos de cambios). Podemos ver los frentes en un +repositorio usando el comando \hgcmd{heads}\ndt{Frentes.}. +\interaction{tour.merge.heads} + +\subsection{Hacer la fusión} + +% TODO poner interrogante de apertura +Qué pasa si tratamos de usar el comando usual, \hgcmd{update}, para +actualizar el nuevo frente? +\interaction{tour.merge.update} +Mercurial nos indica que el comando \hgcmd{update} no hará la fusión; +no actualizará el directorio de trabajo cuando considera que lo que +deseamos hacer es una fusión, a menos que lo obliguemos a hacerlo. +En vez de \hgcmd{update}, usamos el comando \hgcmd{merge} para hacer +la fusión entre los dos frentes. +\interaction{tour.merge.merge} + +\begin{figure}[ht] + \centering + \grafix{tour-merge-merge} + \caption{Directorio de trabajo y repositorio durante la fusión, y + consignación consecuente} + \label{fig:tour-merge:merge} +\end{figure} + +Esto actualiza el directorio de trabajo, de tal forma que contenga los +cambios de \emph{ambos} frentes, lo que se ve reflejado tanto en la +salida de \hgcmd{parents} como en los contenidos de \filename{hello.c}. +\interaction{tour.merge.parents} + +\subsection{Consignar los resultados de la fusión} + +Siempre que hacemos una fusión, \hgcmd{parents} mostrará dos padres +hasta que consignemos (\hgcmd{commit}) los resultados de la fusión. +\interaction{tour.merge.commit} +Ahora tenemos una nueva revisión de punta; note que tiene \emph{los +dos} frentes anteriores como sus padres. Estos son las mismas +revisiones que mostró previamente el comando \hgcmd{parents}. +\interaction{tour.merge.tip} +En la figura~\ref{fig:tour-merge:merge} usted puede apreciar una +representación de lo que pasa en el directorio de trabajo durante la +fusión cuando se hace la consignación. Durante la fusión, el +directorio de trabajo tiene dos conjuntos de cambios como sus padres, +y éstos se vuelven los padres del nuevo conjunto de cambios. + +\section{Fusionar cambios con conflictos} + +La mayoría de las fusiones son algo simple, pero a veces usted se +encontrará fusionando cambios donde más de uno de ellos afecta las +mismas secciones de los mismos ficheros. A menos que ambas +modificaciones sean idénticas, el resultado es un \emph{conflicto}, en +donde usted debe decidir cómo reconciliar ambos cambios y producir un +resultado coherente. + +\begin{figure}[ht] + \centering + \grafix{tour-merge-conflict} + \caption{Cambios con conflictos a un documento} + \label{fig:tour-merge:conflict} +\end{figure} + +La figura~\ref{fig:tour-merge:conflict} ilustra un ejemplo con dos +cambios generando conflictos en un documento. Empezamos con una sola +versión del fichero; luego hicimos algunos cambios; mientras tanto, +alguien más hizo cambios diferentes en el mismo texto. Lo que debemos +hacer para resolver el conflicto causado por ambos cambios es decidir +cómo debe quedar finalmente el fichero. + +Mercurial no tiene ninguna utilidad integrada para manejar conflictos. +En vez de eso, ejecuta un programa externo llamado \command{hgmerge}. +Es un guión de línea de comandos que es instalado junto con Mercurial; +usted puede modificarlo para que se comporte como usted lo desee. Por +defecto, lo que hace es tratar de encontrar una de varias herramientas +para fusionar que es probable que estén instaladas en su sistema. +Primero se intenta con unas herramientas para fusionar cambios +automáticamente; si esto no tiene éxito (porque la fusión demanda +una guía humana) o dichas herramientas no están presentes, el guión +intenta con herramientas gráficas para fusionar. + +También es posible hacer que Mercurial ejecute otro programa o guión +en vez de \command{hgmerge}, definiendo la variable de entorno +\envar{HGMERGE} con el nombre del programa de su preferencia. + +\subsection{Usar una herramienta gráfica para fusión} + +Mi herramienta favorita para hacer fusiones es \command{kdiff3}, y la +usaré para describir las características comunes de las herramientas +gráficas para hacer fusiones. Puede ver una captura de pantalla de +\command{kdiff3} ejecutándose, en la +figura~\ref{fig:tour-merge:kdiff3}. El tipo de fusión que la +herramienta hace se conoce como \emph{fusión de tres vías}, porque hay +tres versiones diferentes del fichero en que estamos interesados. +Debido a esto la herramienta divide la parte superior de la ventana en +tres paneles. +\begin{itemize} +\item A la izquierda está la revisión \emph{base} del fichero, p.ej.~la + versión más reciente de la que descienden las dos versiones que + estamos tratando de fusionar. +\item En la mitad está ``nuestra'' versión del fichero, con las + modificaciones que hemos hecho. +\item A la derecha está la versión del fichero de ``ellos'', la que + forma parte del conjunto de cambios que estamos tratando de + fusionar. +\end{itemize} +En el panel inferior se encuentra el \emph{resultado} actual de la +fusión. Nuestra tarea es reemplazar todo el texto rojo, que muestra +los conflictos sin resolver, con una fusión adecuada de ``nuestra'' +versión del fichero y la de ``ellos''. + +Los cuatro paneles están \emph{enlazados}; si avanzamos vertical o +horizontalmente en cualquiera de ellos, los otros son actualizados +para mostrar las secciones correspondientes del fichero que tengan +asociado. + +\begin{figure}[ht] + \centering + \grafix[width=\textwidth]{kdiff3} + \caption{Usando \command{kdiff3} para fusionar versiones de un + fichero} + \label{fig:tour-merge:kdiff3} +\end{figure} + +En cada conflicto del fichero podemos escoger resolverlo usando +cualquier combinación del texto de la revisión base, la nuestra, o la +de ellos. También podemos editar manualmente el fichero en que queda +la fusión, si es necesario hacer cambios adicionales. + +Hay \emph{muchas} herramientas para fusionar ficheros disponibles. Se +diferencian en las plataformas para las que están disponibles, y en +sus fortalezas y debilidades particulares. La mayoría están afinadas +para fusionar texto plano, mientras que otras están pensadas para +formatos de ficheros especializados (generalmente XML). + +% TODO traduje "worked" como "real" +\subsection{Un ejemplo real} + +En este ejemplo, reproduciremos el historial de modificaciones al +fichero de la figura~\ref{fig:tour-merge:conflict} mostrada +anteriormente. Empecemos creando un repositorio con la versión base +de nuestro documento. +\interaction{tour-merge-conflict.wife} +Clonaremos el repositorio y haremos un cambio al fichero. +\interaction{tour-merge-conflict.cousin} +Y haremos otro clon, para simular a alguien más haciendo un cambio al +mismo fichero. (Esto introduce la idea de que no es tan inusual hacer +fusiones consigo mismo, cuando usted aísla tareas en repositorios +separados, y de hecho encuentra conflictos al hacerlo.) +\interaction{tour-merge-conflict.son} +Ahora que tenemos dos versiones diferentes de nuestro fichero, +crearemos un entorno adecuado para hacer la fusión. +\interaction{tour-merge-conflict.pull} + +En este ejemplo, no usaré el comando normal de Mercurial para hacer la +fusión (\command{hgmerge}), porque lanzaría mi linda herramienta +automatizada para correr ejemplos dentro de una interfaz gráfica de +usuario. En vez de eso, definiré la variable de entorno +\envar{HGMERGE} para indicarle a Mercurial que use el comando +\command{merge}. Este comando forma parte de la instalación base de +muchos sistemas Unix y similares. Si usted está ejecutando este +ejemplo en su computador, no se moleste en definir \envar{HGMERGE}. +\interaction{tour-merge-conflict.merge} +Debido a que \command{merge} no puede resolver los conflictos que +aparecen, él deja \emph{marcadores de fusión} en el fichero con +conflictos, indicando si provienen de nuestra versión o de la de +ellos. + +Mercurial puede saber ---por el código de salida del comando +\command{merge}--- que no fue posible hacer la fusión exitosamente, +así que nos indica qué comandos debemos ejecutar si queremos rehacer +la fusión. Esto puede ser útil si, por ejemplo, estamos ejecutando una +herramienta gráfica de fusión y salimos de ella porque nos confundimos +o cometimos un error. + +Si la fusión ---automática o manual--- falla, no hay nada que nos +impida ``arreglar'' los ficheros afectados por nosotros mismos, y +consignar los resultados de nuestra fusión: +% TODO este mercurial no tiene el comando resolve. Revisar si sigue +% siendo necesario +\interaction{tour-merge-conflict.commit} + +\section{Simplificar el ciclo jalar-fusionar-consignar} +\label{sec:tour-merge:fetch} + +El proceso de fusionar cambios delineado anteriomente es directo, pero +requiere la ejecución de tres comandos en sucesión. +\begin{codesample2} + hg pull + hg merge + hg commit -m 'Fusionados cambios remotos' +\end{codesample2} +En la consignación final usted debe proveer un mensaje adecuado, que +casi siempre es un fragmento de texto ``de relleno'' carente de valor +particular. + +Sería agradable reducir la cantidad de pasos necesarios, si fuera +posible. De hecho, Mercurial es distribuido junto con una extensión +llamada \hgext{fetch}\ndt{Descargar, traer.} que hace precisamente +esto. + +Mercurial cuenta con un mecanismo de extensión flexible que le permite +% TODO lets people => permite a usuarios +a sus usuarios extender su funcionalidad, manteniendo el núcleo de +Mercurial pequeño y fácil de manejar. Algunas extensiones añaden +nuevos comandos que usted puede usar desde la línea de comandos, +mientras que otros funcionan ``tras bambalinas'', por ejemplo, +añadiendo funcionalidad al servidor. + +La extensión \hgext{fetch} añade un comando llamado, no +sorpresivamente, \hgcmd{fetch}. Esta extensión actúa como una +combinación de \hgcmd{pull}, \hgcmd{update} y \hgcmd{merge}. Empieza +jalando cambios de otro repositorio al repositorio actual. Si +encuentra que los cambios añaden un nuevo frente en el repositorio +actual, inicia una fusión, y luego consigna el resultado de la misma +con un mensaje generado automáticamente. Si no se añadieron nuevos +frentes, actualiza el directorio de trabajo con el nuevo conjunto de +cambios de punta. + +Activar la extensión \hgext{fetch} es fácil. Edite su +\sfilename{.hgrc}, y vaya a (o cree) la sección +\rcsection{extensions}. Luego añada una línea que diga simplemente +``\Verb+fetch +''. +\begin{codesample2} + [extensions] + fetch = +\end{codesample2} +(Normalmente, a la derecha del ``\texttt{=}'' debería aparecer la +ubicación de la extensión, pero como el comando \hgext{fetch} es parte +de la distribución estándar, Mercurial sabe dónde buscarla.) + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/undo-manual-merge.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/undo-manual-merge.dot Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/undo-manual.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/undo-manual.dot Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/undo-non-tip.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/undo-non-tip.dot Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/undo-simple.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/undo-simple.dot Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,4 @@ +digraph undo_simple { + "primer cambio" -> "segundo cambio"; + "segundo cambio" -> "reversar\nsegundo cambio"; +} diff -r 73b094b764ec -r 51b5d56744c5 es/undo.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/undo.tex Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,799 @@ +\chapter{Encontrar y arreglar sus equivocaciones} +\label{chap:undo} + +Errar es humano, pero tratar adecuadamente las consecuencias requiere +un sistema de control de revisiones de primera categoría. En este +capítulo, discutiremos algunas técnicas que puede usar cuando +encuentra que hay un problema enraizado en su proyecto. Mercurial +tiene unas características poderosas que le ayudarán a isolar las +fuentes de los problemas, y a dar cuenta de ellas apropiadamente. + +\section{Borrar el historial local} + +\subsection{La consignación accidental} + +Tengo el problema ocasional, pero persistente de teclear más rápido de +lo que pienso, que aveces resulta en consignar un conjunto de cambios +incompleto o simplemente malo. En mi caso, el conjunto de cambios +incompleto consiste en que creé un nuevo fichero fuente, pero olvidé +hacerle \hgcmd{add}. Un conjunto de cambios``simplemente malo'' no es +tan común, pero sí resulta muy molesto. + +\subsection{Hacer rollback una transacción} +\label{sec:undo:rollback} + +En la sección~\ref{sec:concepts:txn}, mencioné que Mercurial trata +modificación a un repositorio como una \emph{transacción}. Cada vez +que consigna un conjunto de cambios o lo jala de otro repositorio, +Mercurial recuerda lo que hizo. Puede deshacer, o hacer \emph{roll back}\ndt{El significado igual que en los + ambientes de sistemas manejadores de bases de datos se refiere a + la atomicidad e integridad al devolver un conjunto de acciones que + permitan dejar el repositorio en un estado consistente previo}, +exactamente una de tales acciones usando la orden \hgcmd{rollback}. +(Ver en la sección~\ref{sec:undo:rollback-after-push} una anotación +importante acerca del uso de esta orden.) + +A continuación una equivocación que me sucede frecuentemente: +consignar un cambio en el cual he creado un nuevo fichero, pero he +olvidado hacerle \hgcmd{add}. +\interaction{rollback.commit} +La salida de \hgcmd{status} después de la consignación confirma +inmediatamente este error. +\interaction{rollback.status} +La consignación capturó los cambios en el fichero \filename{a}, pero +no el nuevo fichero \filename{b}. Si yo publicara este conjunto de +cambios a un repositorio compartido con un colega, es bastante +probable que algo en \filename{a} se refiriera a \filename{b}, el cual +podría no estar presente cuando jalen mis cambios del repositorio. Me +convertiría el sujeto de cierta indignación. + +Como sea, la suerte me acompaña---Encontré mi error antes de publicar +el conjunto de cambios. Uso la orden \hgcmd{rollback}, y Mercurial +hace desaparecer el último conjunto de cambios. +\interaction{rollback.rollback} +El conjunto de cambios ya no está en el historial del repositorio, y el +directorio de trabajo cree que el fichero \filename{a} ha sido +modificado. La consignación y el roll back dejaron el directorio de +trabajo exactamente como estaba antes de la consignación; el conjunto +de cambios ha sido eliminado totlamente. Ahora puedo hacer \hgcmd{add} +al fichero \filename{b}, y hacer de nuevo la consignación. +\interaction{rollback.add} + +\subsection{Erroneamente jalado} + +Mantener ramas de desarrollo separadas de un proyecto en distintos +repositorios es una práctica común con Mercurial. Su equipo de +desarrollo puede tener un repositorio compartido para la versión ``0.9'' +y otra con cambios distintos para la versión ``1.0''. + +Con este escenario, puede imaginar las consecuencias si tuviera un +repositorio local ``0.9'', y jalara accidentalmente los cambios del +repositorio compartido de la versión ``1.0'' en este. En el peor de +los casos, por falta de atención, es posible que publique tales +cambios en el árbol compartido ``0.9'', confundiendo a todo su equipo +de trabajo (pero no se preocupe, volveremos a este terrorífico +escenario posteriormente). En todo caso, es muy probable que usted se +de cuenta inmediatamente, dado que Mercurial mostrará el URL de donde +está jalando, o que vea jalando una sospechosa gran cantidad de +cambios en el repositorio. + +La orden \hgcmd{rollback} excluirá eficientemente los conjuntos de +cambios que haya acabado de jalar. Mercurial agrupa todos los cambios +de un \hgcmd{pull} a una única transacción y bastará con un +\hgcmd{rollback} para deshacer esta equivocación. + +\subsection{Después de publicar, un roll back es futil} +\label{sec:undo:rollback-after-push} + +El valor de \hgcmd{rollback} se anula cuando ha publicado sus cambios +a otro repositorio. Un cambio desaparece totalmente al hacer roll back, +pero \emph{solamente} en el repositorio en el cual aplica +\hgcmd{rollback}. Debido a que un roll back elimina el historial, +no hay forma de que la desaparición de un cambio se propague entre +repositorios. + +Si ha publicado un cambio en otro repositorio---particularmente si es +un repositorio público---esencialmente está ``en terreno agreste,'' +y tendrá que reparar la equivocación de un modo distinto. Lo que +pasará si publica un conjunto de cambios en algún sitio, hacer +rollback y después volver a jalar del repositorio del cual había +publicado, es que el conjunto de cambios reaparecerá en su repositorio. + +(Si está absolutamente segruro de que el conjunto de cambios al que +desea hacer rollback es el cambio más reciente del repositorio en el +cual publicó, \emph{y} sabe que nadie más pudo haber jalado de tal +repositorio, puede hacer rollback del conjunto de cambios allí, pero +es mejor no confiar en una solución de este estilo. Si lo hace, tarde +o temprano un conjunto de cambios logrará colarse en un repositorio +que usted no controle directamente (o del cual se ha olvidado), y +volverá a hostigarle.) + +\subsection{Solamente hay un roll back} + +Mercurial almacena exactamente una transacción en su bitácora de +transacciones; tal transacción es la más reciente de las que haya +ocurrido en el repositorio. Esto significa que solamente puede hacer +roll back a una transacción. Si espera poder hacer roll back a una +transacción después al antecesor, observará que no es el +comportamiento que obtendrá. +\interaction{rollback.twice} +Una vez que haya aplicado un rollback en una transacción a un +repositorio, no podrá volver a hacer rollback hasta que haga una +consignación o haya jalado. + +\section{Revertir un cambio equivocado} + +Si modifica un fichero y se da cuenta que no quería realmente cambiar +tal fichero, y todavía no ha consignado los cambios, la orden +necesaria es \hgcmd{revert}. Observa el conjunto de cambios padre del +directorio y restaura los contenidos del fichero al estado de tal +conjunto de cambios. (Es una forma larga de decirlo, usualmente +deshace sus modificaciones.) + +Ilustremos como actúa la orden \hgcmd{revert} con un ejemplo +pequeño. Comenzaremos modificando un fichero al cual Mercurial ya está +siguiendo. +\interaction{daily.revert.modify} +Si no queremos ese cambio, podemos aplicar \hgcmd{revert} al fichero. +\interaction{daily.revert.unmodify} +La orden \hgcmd{revert} nos brinda un grado adicional de seguridad +guardando nuestro fichero modificado con la extensión \filename{.orig}. +\interaction{daily.revert.status} + +Este es un resumen de casos en los cuales la orden \hgcmd{revert} es +de utilidad. Describiremos cada uno de ellos con más detalle en la +sección siguiente. +\begin{itemize} +\item Si usted modifica un fichero, lo restaurará a su estado sin + modificación previo. +\item Si usted hace \hgcmd{add} a un fichero, revertirá el estado de + ``adicionado'' del fichero, pero no lo tocará +\item Si borra un fichero sin decirle a Mercurial, restaurará el + fichero con sus contenidos sin modificación. +\item Si usa la orden \hgcmd{remove} para eliminar un fichero, deshará + el estado ``removido'' del fichero, y lo restaurará con sus + contenidos sin modificación. +\end{itemize} + +\subsection{Errores al administrar ficheros} +\label{sec:undo:mgmt} + +La orden \hgcmd{revert} es útil para más que ficheros modificados. Le +permite reversar los resultados de todas las órdenes de administración +de ficheros que provee Mercurial---\hgcmd{add}, \hgcmd{remove}, y las +demás. + +Si usted hace \hgcmd{add} a un fichero, y no deseaba que Mercurial le +diera seguimiento, use \hgcmd{revert} para deshacer la adición. No se +preocupe; Mercurial no modificará de forma alguna el fichero. +Solamente lo ``desmarcará''. +\interaction{daily.revert.add} + +De forma similar, Si le solicita a Mercurial hacer \hgcmd{remove} a un +fichero, puede usar \hgcmd{revert} para restarurarlo a los contenidos +que tenía la revisión padre del directorio de trabajo. +\interaction{daily.revert.remove} +Funciona de la misma manera para un fichero que usted haya eliminado +manualmente, sin decirle a Mercurial (recuerde que en la terminología +de Mercurial esta clase de fichero se llama ``faltante''). +\interaction{daily.revert.missing} + +Si usted revierte un \hgcmd{copy}, el fichero a donde se copió +permanece en su directorio de trabajo, pero sin seguimiento. Dado que +una copia no afecta el fichero fuente de copiado de ninguna maner, +Mercurial no hace nada con este. +\interaction{daily.revert.copy} + +\subsubsection{Un caso ligeramente especial:revertir un renombramiento} + +Si hace \hgcmd{rename} a un fichero, hay un detalle que debe tener en +cuenta. Cuando aplica \hgcmd{revert} a un cambio de nombre, no es +suficiente proveer el nombre del fichero destino, como puede verlo en +el siguiente ejemplo. +\interaction{daily.revert.rename} +Como puede ver en la salida de \hgcmd{status}, el fichero con el nuevo +nombre no se identifica más como agregado, pero el fichero con el +nombre-\emph{inicial} se elimna! Esto es contra-intuitivo (por lo +menos para mí), pero por lo menos es fácil arreglarlo. +\interaction{daily.revert.rename-orig} +Por lo tanto, recuerde, para revertir un \hgcmd{rename}, debe proveer +\emph{ambos} nombres, la fuente y el destino. + +% TODO: the output doesn't look like it will be removed! + +(A propósito, si elimina un fichero, y modifica el fichero con el +nuevo nombre, al revertir ambos componentes del renombramiento, cuando +Mercurial restaure el fichero que fue eliminado como parte del +renombramiento, no será modificado. +Si necesita que las modificaciones en el fichero destino del +renombramiento se muestren, no olvide copiarlas encima.) + +Estos aspectos engorrosos al revertir un renombramiento se constituyen +discutiblemente en un fallo de Mercurial. + +\section{Tratar cambios consignados} + +Considere un caso en el que ha consignado el cambio $a$, y otro cambio +$b$ sobre este; se ha dado cuenta que el cambio $a$ era +incorrecto. Mercurial le permite ``retroceder'' un conjunto de cambios +completo automáticamente, y construir bloques que le permitan revertir +parte de un conjunto de cambios a mano. + +Antes de leer esta sección, hay algo para tener en cuenta: la orden +\hgcmd{backout} deshace cambios \emph{adicionando} al historial, sin +modificar o borrar. Es la herramienta correcta si está arreglando +fallos, pero no si está tratando de deshacer algún cambio que tiene +consecuencias catastróficas. Para tratar con esos, vea la sección~\ref{sec:undo:aaaiiieee}. + +\subsection{Retroceder un conjunto de cambios} + +La orden \hgcmd{backout} le permite ``deshacer'' los efectos de todo +un conjunto de cambios de forma automatizada. Dado que el historial de +Mercurial es inmutable, esta orden \emph{no} se deshace del conjunto +de cambios que usted desea deshacer. En cambio, crea un nuevo +conjunto de cambios que \emph{reversa} el conjunto de cambios que +usted indique. + +La operación de la orden \hgcmd{backout} es un poco intrincada, y lo +ilustraremos con algunos ejemplos. Primero crearemos un repositorio +con algunos cambios sencillos. +\interaction{backout.init} + +La orden \hgcmd{backout} toma un ID de conjunto de cambios como su +argumento; el conjunto de cambios a retroceder. Normalmente +\hgcmd{backout} le ofrecerá un editor de texto para escribir el +mensaje de la consignación, para dejar un registro de por qué está +retrocediendo. En este ejemplo, colocamos un mensaje en la +consignación usando la opción \hgopt{backout}{-m}. + +\subsection{Retroceder el conjunto de cambios punta} + +Comenzamos retrocediendo el último conjunto de cambios que consignamos. +\interaction{backout.simple} +Puede ver que la segunda línea de \filename{myfile} ya no está +presente. La salida de \hgcmd{log} nos da una idea de lo que la orden +\hgcmd{backout} ha hecho. +\interaction{backout.simple.log} +Vea que el nuevo conjunto de cambios que \hgcmd{backout} ha creado es +un hijo del conjunto de cambios que retrocedimos. Es más sencillo de +ver en la figura~\ref{fig:undo:backout}, que presenta una vista +gráfica del historial de cambios. Como puede ver, el historial es +bonito y lineal. + +\begin{figure}[htb] + \centering + \grafix{undo-simple} + \caption{Retroceso de un cambio con la orden \hgcmd{backout}} + \label{fig:undo:backout} +\end{figure} + +\subsection{Retroceso de un cambio que no es la punta} + +Si desea retrocede un cambio distinto al último que ha consignado, use +la opción \hgopt{backout}{--merge} a la orden \hgcmd{backout}. +\interaction{backout.non-tip.clone} +Que resulta en un retroceso de un conjunto de cambios ``en un sólo +tiro'', una operación que resulta normalmente rápida y sencilla. +\interaction{backout.non-tip.backout} + +Si ve los contenidos del fichero \filename{myfile} después de +finalizar el retroceso, verá que el primer y el tercer cambio están +presentes, pero no el segundo. +\interaction{backout.non-tip.cat} + +Como lo muestra el historial gráfico en la +figura~\ref{fig:undo:backout-non-tip}, Mercurial realmente consigna +\emph{dos} cambios en estas situaciones (los nodos encerrados en una +caja son aquellos que Mercurial consigna automaticamente). Antes de +que Mercurial comience el proceso de retroceso, primero recuerda cuál +es el padre del directorio de trabajo. Posteriormente hace un +retroceso al conjunto de cambios objetivo y lo consigna como un +conjunto de cambios. Finalmente, fusiona con el padre anterior del +directorio de trabajo, y consigna el resultado de la fusión. + +% TODO: to me it looks like mercurial doesn't commit the second merge automatically! + +\begin{figure}[htb] + \centering + \grafix{undo-non-tip} + \caption{Retroceso automatizado de un cambio a algo que no es la punta con la orden \hgcmd{backout}} + \label{fig:undo:backout-non-tip} +\end{figure} + +El resultado es que usted termina ``donde estaba'', solamente con un +poco de historial adicional que deshace el efecto de un conjunto de +cambios que usted quería evitar. + +\subsubsection{Use siempre la opción \hgopt{backout}{--merge}} + +De hecho, dado que la opción \hgopt{backout}{--merge} siempre hara lo +``correcto'' esté o no retrocediendo el conjunto de cambios punta +(p.e.~no tratará de fusionar si está retrocediendo la punta, dado que +no es necesario), usted debería usar \emph{siempre} esta opción cuando +ejecuta la orden \hgcmd{backout}. + +\subsection{Más control sobre el proceso de retroceso} + +A pesar de que recomiendo usar siempre la opción +\hgopt{backout}{--merge} cuando está retrocediendo un cambio, la orden +\hgcmd{backout} le permite decidir cómo mezclar un retroceso de un +conjunto de cambios. Es muy extraño que usted necestite tomar control +del proceso de retroceso de forma manual, pero puede ser útil entender +lo que la orden \hgcmd{backout} está haciendo automáticamente para +usted. Para ilustrarlo, clonemos nuestro primer repositorio, pero +omitamos el retroceso que contiene. + +\interaction{backout.manual.clone} +Como en el ejemplo anterior, consignaremos un tercer cambio, después +haremos retroceso de su padre, y veremos qué pasa. +\interaction{backout.manual.backout} +Nuestro nuevo conjunto de cambios es de nuevo un descendiente del +conjunto de cambio que retrocedimos; es por lo tanto una nueva cabeza, +\emph{no} un descendiente del conjunto de cambios que era la punta. La +orden \hgcmd{backout} fue muy explícita diciéndolo. +\interaction{backout.manual.log} + +De nuevo, es más sencillo lo que pasó viendo una gráfica del +historial de revisiones, en la figura~\ref{fig:undo:backout-manual}. +Esto nos aclara que cuando usamos \hgcmd{backout} para retroceder un +cambio a algo que no sea la punta, Mercurial añade una nueva cabeza al +repositorio (el cambio que consignó está encerrado en una caja). + +\begin{figure}[htb] + \centering + \grafix{undo-manual} + \caption{Retroceso usando la orden \hgcmd{backout}} + \label{fig:undo:backout-manual} +\end{figure} + +Después de que la orden \hgcmd{backout} ha terminado, deja un nuevo +conjunto de cambios de ``retroceso'' como el padre del directorio de trabajo. +\interaction{backout.manual.parents} +Ahora tenemos dos conjuntos de cambios aislados. +\interaction{backout.manual.heads} + +Reflexionemos acerca de lo que esperamos ver como contenidos de +\filename{myfile}. El primer cambio debería estar presente, porque +nunca le hicimos retroceso. El segundo cambio debió desaparecer, +puesto que es el que retrocedimos. Dado que la gráfica del historial +muestra que el tercer camlio es una cabeza separada, \emph{no} +esperamos ver el tercer cambio presente en \filename{myfile}. +\interaction{backout.manual.cat} +Para que el tercer cambio esté en el fichero, hacemos una fusión usual +de las dos cabezas. +\interaction{backout.manual.merge} +Después de eso, el historial gráfica de nuestro repositorio luce como +la figura~\ref{fig:undo:backout-manual-merge}. + +\begin{figure}[htb] + \centering + \grafix{undo-manual-merge} + \caption{Fusión manual de un retroceso} + \label{fig:undo:backout-manual-merge} +\end{figure} + +\subsection{Por qué \hgcmd{backout} hace lo que hace} + +Esta es una descripción corta de cómo trabaja la orden \hgcmd{backout}. +\begin{enumerate} +\item Se asegura de que el directorio de trabajo es ``limpio'', esto + es, que la salida de \hgcmd{status} debería ser vacía. +\item Recuerda el padre actual del directorio de trabajo. A este + conjunto de cambio lo llamaremos \texttt{orig} +\item Hace el equivalente de un \hgcmd{update} para sincronizar el + directorio de trabajo con el conjunto de cambios que usted quiere + retroceder. Lo llamaremos \texttt{backout} +\item Encuentra el padre del conjunto de cambios. Lo llamaremos + \texttt{parent}. +\item Para cada fichero del conjunto de cambios que el + \texttt{retroceso} afecte, hará el equivalente a + \hgcmdargs{revert}{-r parent} sobre ese fichero, para restaurarlo a + los contenidos que tenía antes de que el conjunto de cambios fuera + consignado. +\item Se consigna el resultado como un nuevo conjunto de cambios y + tiene a \texttt{backout} como su padre. +\item Si especifica \hgopt{backout}{--merge} en la línea de comandos, + se fusiona con \texttt{orig}, y se consigna el resultado de la + fusión. +\end{enumerate} + +Una vía alternativa de implementar la orden \hgcmd{backout} sería usar +\hgcmd{export} sobre el conjunto de cambios a retroceder como un diff +y después usar laa opción \cmdopt{patch}{--reverse} de la orden +\command{patch} para reversar el efecto del cambio sin molestar el +directorio de trabajo. Suena mucho más simple, pero no funcionaría +bien ni de cerca. + +La razón por la cual \hgcmd{backout} hace una actualización, una +consignación, una fusión y otra consignación es para dar a la +maquinaria de fusión la mayor oportunidad de hacer un buen trabajo +cuando se trata con todos los cambios \emph{entre} el cambio que está +retrocediendo y la punta actual. + +Si está retrocediendo un conjunto de cambios que está a unas ~100 +atrás en su historial del proyecto, las posibilidades de que una orden +\command{patch} sea capaz de ser aplicada a un diff reverso, +claramente no son altas, porque los cambios que intervienen podrían +``no coincidir con el contexto'' que \command{patch} usa para +determinar si puede aplicar un parche (si esto suena como cháchara, +vea una discusión de la orden \command{patch} en \ref{sec:mq:patch}). +Adicionalmente, la maquinaria de fusión de Mercurial manejará ficheros +y directorios renombrados, cambios de permisos, y modificaciones a +ficheros binarios, nada de lo cual la orden \command{patch} puede manejar. + +\section{Cambios que nunca debieron ocurrir} +\label{sec:undo:aaaiiieee} + +En la mayoría de los casos, la orden \hgcmd{backout} es exactamente lo +que necesita para deshacer los efectos de un cambio. Deja un registro +permanente y exacto de lo que usted hizo, cuando se consignó el +conjunto de cambios original y cuando se hizo la limpieza. + +En ocasiones particulares, puede haber consignado un cambio que no +debería estar de ninguna forma en el repositorio. Por ejemplo, sería +muy inusual, y considerado como una equivocación, consignar los +ficheros objeto junto con el código fuente. Los ficheros objeto no +tienen valor intrínseco y son \emph{grandes}, por lo tanto aumentan el +tamaño del repositorio y la cantidad de tiempo que se emplea al clonar +o jalar cambios. + +Antes de discutir las opciones que tiene si consignó cambio del tipo +``bolsa de papel deschable'' (el tipo que es tan malo que le gustaría +colocarse una bolsa de papel desechable en su cabeza), permítame +discutir primero unas aproximaciones que probablemente no funcionen. + +Dado que Mercurial trata de forma acumulativa al historial---cada +cambio se coloca encima de todos los cambios que le +preceden---usualmente usted no puede hacer que unos cambios desastrosos +desaparezcan. La única excepción es cuando usted ha acabado de +consignar un cambio y este no ha sido publicado o jalado en otro +repositorio. Ahí es cuando puede usar la orden \hgcmd{rollback} con +seguridad, como detallé en la sección~\ref{sec:undo:rollback}. + +Después de que usted haya publicado un cambio en otro repositorio, usted +\emph{podría} usar la orden \hgcmd{rollback} para hacer que en su copia +local desaparezca el cambio, pero no tendrá las consecuencias que +desea. El cambio estará presente en un repositorio remoto, y +reaparecerá en su repositorio local la próxima vez que jale + +Si una situación como esta se presenta, y usted sabe en qué +repositorios su mal cambio se ha propagado, puede \emph{intentar} +deshacerse del conjunto de cambios de \emph{todos} los repositorios en +los que se pueda encontrar. Esta por supuesto, no es una solución +satisfactoria: si usted deja de hacerlo en un solo repositorio, +mientras esté eliminándolo, el cambio todavía estará ``allí afuera'', +y podría propagarse más tarde. + +Si ha consignado uno o más cambios \emph{después} del cambio que desea +desaparecer, sus opciones son aún más reducidas. Mercurial no provee +una forma de ``cabar un hueco'' en el historial, dejando los conjuntos +de cambios intactos. + +%Dejamos de traducir lo que viene a continuación, porque será +%modificado por upstream... + +XXX This needs filling out. The \texttt{hg-replay} script in the +\texttt{examples} directory works, but doesn't handle merge +changesets. Kind of an important omission. + +\subsection{Cómo protegerse de cambios que han ``escapado''} + +Si ha consignado cambios a su repositorio local y estos han sido +publicados o jalados en cualquier otro sitio, no es necesariamente un +desastre. Puede protegerse de antemano de ciertas clases de conjuntos +de cambios malos. Esto es particularmente sencillo si su equipo de +trabajo jala cambios de un repositorio central. + +Al configurar algunos ganchos en el repositorio central para validar +conjuntos de cambios (ver capítulo~\ref{chap:hook}), puede prevenir la +publicación automáticamente de cierta clase de cambios malos. Con tal +configuración, cierta clase de conjuntos de cambios malos tenderán +naturalmente a``morir'' debido a que no pueden propagarse al +repositorio central. Esto sucederá sin necesidad de intervención +explícita. + +Por ejemplo, un gancho de cambios de entrada que verifique que un +conjunto de cambios compila, puede prevenir que la gente ``rompa +la compilación'' inadvertidamente. + +\section{Al encuentro de la fuente de un fallo} +\label{sec:undo:bisect} + +Aunque es muy bueno poder retroceder el conjunto de cambios que +originó un fallo, se requiere que usted sepa cual conjunto de cambios +retroceder. Mercurial brinda una orden invaluable, llamada +\hgcmd{bisect}, que ayuda a automatizar este proceso y a alcanzarlo +muy eficientemente. + +La idea tras la orden \hgcmd{bisect} es que el conjunto de cambios que +ha introducido un cambio de comportamiento pueda identificarse con una +prueba binaria sencilla. No tiene que saber qué pieza de código +introdujo el cambio, pero si requiere que sepa cómo probar la +existencia de un fallo. La orden \hgcmd{bisect} usa su prueba para +dirigir su búsqueda del conjunto de cambios que introdujo el código +causante del fallo. + +A continuación un conjunto de escenarios que puede ayudarle a entender +cómo puede aplicar esta orden. +\begin{itemize} +\item La versión más reciente de su programa tiene un fallo que usted + recuerda no estaba hace unas semanas, pero no sabe cuándo fue + introducido. En este caso, su prueba binaria busca la presencia de + tal fallo. +\item Usted arregló un fallo en un apurto, y es hora de dar por + cerrado el caso en la base de datos de fallos de su equipo de + trabajo. La base de datos de fallos requiere el ID del conjunto de + cambios que permita dar por cerrado el caso, pero usted no recuerda + qué conjunto de cambios arregló tal fallo. De nuevo la prueba + binaria revisa la presencia del fallo. +\item Su programa funciona correctamente, pero core ~15\% más lento + que la última vez que lo midió. Usted desea saber qué conjunto de + cambios introdujo esta disminución de desempeño. En este caso su + prueba binaria mide el desempeño de su programa, para ver dónde es + ``rápido'' y dónde es ``lento''. +\item Los tamaños de los componentes del proyecto que usted lleva se + expandieron recientemente, y sospecha que algo cambio en la forma en + que se construye su proyecto. +\end{itemize} + +Para estos ejemplos debería ser claro que la orden \hgcmd{bisect} +es útil no solamente para encontrar la fuente de los fallos. Puede +usarla para encontrar cualquier ``propiedad emergente'' de un +repositorio (Cualquier cosa que usted no pueda encontrar con una +búsqueda de texto sencilla sobre los ficheros en el árbol) para la +cual pueda escribir una prueba binaria. + +A continuación introduciremos algo terminología, para aclarar qué +partes del proceso de búsqueda son su responsabilidad y cuáles de +Mercurial. Una \emph{prueba} es algo que \emph{usted} ejecuta cuando +\hgcmd{bisect} elige un conjunto de cambios. Un \emph{sondeo} es lo que +\hgcmd{bisect} ejecuta para decidir si una revisión es buena. Finalmente, +usaremos la palabra ``biseccionar', en frases como ``buscar con la +orden \hgcmd{bisect}''. + +Una forma sencilla de automatizar el proceso de búsqueda sería probar +cada conjunto de cambios. Lo cual escala muy poco. Si le tomó diez +minutos hacer pruebas sobre un conjunto de cambios y tiene 10.000 +conjuntos de cambios en su repositorio, esta aproximación exhaustiva +tomaría en promedio~35 \emph{días} para encontrar el conjunto de +cambios que introdujo el fallo. Incluso si supiera que el fallo se +introdujo en un de los últimos 500 conjuntos de cambios y limitara la +búsqueda a ellos, estaría tomabdi más de 40 horas para encontrar al +conjunto de cambios culpable. + +La orden \hgcmd{bisect} usa su conocimiento de la ``forma'' del +historial de revisiones de su proyecto para hacer una búsqueda +proporcional al \emph{logaritmo} del número de conjunto de cambios a +revisar (el tipo de búsqueda que realiza se llama búsqueda +binaria). Con esta aproximación, el buscar entre 10.000 conjuntos de +cambios tomará menos de 3 horas, incluso a diez minutos por prueba (La +búsqueda requerirá cerca de 14 pruebas). Al limitar la búsqueda a la +última centena de conjuntos de cambios, tomará a lo sumo una +hora (Apenas unas 7 pruebas). + +La orden \hgcmd{bisect} tiene en cuenta la naturaleza ``ramificada'' +del historial de revisiones del proyecto con Mercurial, así que no +hay problemas al tratar con ramas, fusiones o cabezas múltiples en un +repositorio. Puede evitar ramas enteras de historial con un solo +sondeo. + +\subsection{Uso de la orden \hgcmd{bisect}} + +A continuación un ejemplo de \hgcmd{bisect} en acción. + +\begin{note} + En las versiones 0.9.5 y anteriores de Mercurial, \hgcmd{bisect} no + era una orden incluída en la distribución principal: se ofrecía como + una extensión de Mercurial. Esta sección describe la orden embebida + y no la extensión anterior. +\end{note} + +Creamos un repostorio para probar el comando \hgcmd{bisect} de forma +aislada +\interaction{bisect.init} +Simularemos de forma sencilla un proyecto con un fallo: haremos +cambios triviales en un ciclo, e indicaremos que un cambio específico +sea el ``fallo''. Este ciclo crea 35 conjuntos de cambios, cada uno +añade un único fichero al repositorio. Representaremos nuestro ``fallo'' +con un fichero que contiene el texto ``tengo un gub''. +\interaction{bisect.commits} + +A continuación observaremos cómo usar la orden \hgcmd{bisect}. Podemos +usar el mecanismo de ayuda embebida que trae Mercurial. +\interaction{bisect.help} + +La orden \hgcmd{bisect} trabaja en etapas, de la siguiente forma: +\begin{enumerate} +\item Usted ejecuta una prueba binaria. + \begin{itemize} + \item Si la prueba es exitosa, usted se lo indicará a \hgcmd{bisect} + ejecutando la orden \hgcmdargs{bisect}{good}. + \item Si falla, ejecutará la orden \hgcmdargs{bisect}{--bad}. + \end{itemize} +\item La orden usa su información para decidir qué conjuntos de + cambios deben probarse a continuación. +\item Actualiza el directorio de trabajo a tal conjunto de cambios y + el proceso se lleva a cabo de nuevo. +\end{enumerate} +El proceso termina cuando \hgcmd{bisect} identifica un único conjunto +de cambios que marca el punto donde se encontró la transición de +``exitoso'' a ``fallido''. + +Para comenzar la búsqueda, es indispensable ejecutar la orden +\hgcmdargs{bisect}{--reset}. +\interaction{bisect.search.init} + +En nuestro caso, la prueba binaria es sencilla: revisamos si el +fichero en el repositorio contiene la cadena ``tengo un gub''. Si la +tiene, este conjunto de cambios contiene aquel que ``causó el fallo''. +Por convención, un conjunto de cambios que tiene la propiedad que +estamos buscando es ``malo'', mientras que el otro que no la tiene es +``bueno''. + +En la mayoría de casos, la revisión del directorio actual (usualmente +la punta) exhibe el problema introducido por el cambio con el fallo, +por lo tanto la marcaremos como ``mala''. +\interaction{bisect.search.bad-init} + +Nuestra próxima tarea es nominar al conjunto de cambios que sabemos +\emph{no} tiene el fallo; la orden \hgcmd{bisect} ``acotará'' su +búsqueda entre el primer par de conjuntos de cambios buenos y malos. +En nuestro caso, sabemos que la revisión~10 no tenía el fallo. (Más +adelante diré un poco más acerca de la elección del conjunto de +cambios ``bueno''.) +\interaction{bisect.search.good-init} + +Note que esta orden mostró algo. +\begin{itemize} +\item Nos dijo cuántos conjuntos de cambios debe considerar antes de + que pueda identifica aquel que introdujo el fallo, y cuántas pruebas + se requerirán. +\item Actualizó el directorio de trabajo al siguiente conjunto de + cambios, y nos dijo qué conjunto de cambios está evaluando. +\end{itemize} + +Ahora ejecutamos nuestra prueba en el directorio de trabajo. Usamos la +orden \command{grep} para ver si nuestro fichero ``malo'' está +presente en el directorio de trabajo. Si lo está, esta revisión es +mala; si no esta revisión es buena. +\interaction{bisect.search.step1} + +Esta prueba luce como candidata perfecta para automatizarse, por lo +tanto la convertimos en una función de interfaz de comandos. +\interaction{bisect.search.mytest} +Ahora podemos ejecutar un paso entero de pruebas con un solo comando, +\texttt{mytest}. +\interaction{bisect.search.step2} +Unas invocaciones más de nuestra prueba, y hemos terminado. +\interaction{bisect.search.rest} + +Aunque teníamos unos~40 conjuntos de cambios en los cuales buscar, la +orden \hgcmd{bisect} nos permitió encontrar el conjunto de cambios que +introdujo el ``fallo'' con sólo cinco pruebas. Porque el número de +pruebas que la orden \hgcmd{bisect} ejecuta crece logarítmicamente con +la cantidad de conjuntos de cambios a buscar, la ventaja que esto +tiene frente a la búsqueda por``fuerza bruta'' crece con cada +conjunto de cambios que usted adicione. + +\subsection{Limpieza después de la búsqueda} + +Cuando haya terminado de usar la orden \hgcmd{bisect} en un +repositorio, puede usar la orden \hgcmdargs{bisect}{reset} para +deshacerse de la información que se estaba usando para lograr la +búsqueda. Lar orden no usa mucho espacio, así que no hay problema si +olvida ejecutar la orden. En todo caso, \hgcmd{bisect} no le +permitirá comenzar una nueva búsqueda sobre el repositorio hasta que +no aplique \hgcmdargs{bisect}{reset}. +\interaction{bisect.search.reset} + +\section{Consejos para encontrar fallos efectivamente} + +\subsection{Dar una entrada consistente} + +La orden \hgcmd{bisect} requiere que usted ofrezca un reporte correcto +del resultado de cada prueba que aplique. Si usted le dice que una +prueba falla cuando en realidad era acertada, \emph{podría} detectar +la inconsistencia. Si puede identificar una inconsistencia en sus +reportes, le dirá que un conjunto de cambios particular es a la vez +bueno y malo. Aunque puede no hacerlo; estaría tratando de reportar +un conjunto de cambios como el responsable de un fallo aunque no lo +sea. + +\subsection{Automatizar tanto como se pueda} + +Cuando comencé a usar la orden \hgcmd{bisect}, intenté ejecutar +algunas veces las pruebas a mano desde la línea de comandos. Es una +aproximación a la cual no esta acostumbrado. Después de algunos +intentos, me di cuenta que estaba cometiendo tantas equivocaciones que +tenía que comenzar de nuevo con mis búsquedas varias veces antes de +llegar a los resultados deseados. + +Mi problema inicial al dirigir a la orden \hgcmd{bisect} manualmente +ocurrieron incluso con búsquedas en repositorios pequeños; si el +problema que está buscando es más sutil, o el número de pruebas que +\hgcmd{bisect} debe aplicar, la posibilidad de errar es mucho más +alta. Una vez que comencé a automatizar mis pruebas, obtuve mejores +resultados. + +La clave para las pruebas automatizadas se puede resumir en: +\begin{itemize} +\item pruebe siempre buscando el mismo síntoma y +\item ofrezca siempre datos consistentes a la orden \hgcmd{bisect}. +\end{itemize} +En mi tutorial de ejemplo anterior, la orden \command{grep} busca el +síntoma, y la construcción \texttt{if} toma el resultado de esta +prueba y verifica que siempre alimentamos con los mismos datos a la +orden \hgcmd{bisect}. La función \texttt{mytest} los une de una forma +reproducible, logrando que cada prueba sea uniforme y consistente. + +\subsection{Verificar los resultados} + +Dado que la salida de la búsqueda de \hgcmd{bisect} es tan buena como +los datos ofrecidos por usted, no confíe en esto como si fuera la +verdad absoluta. Una forma sencilla de asegurarse es ejecutar +manualmente su prueba a cada uno de los siguientes conjuntos de +cambios: +\begin{itemize} +\item El conjunto de cambios que se reportó como la primera versión + erronea. Su prueba debería dar un reporte de fallo. +\item El conjunto de cambios padre (cada padre, si es una fusión). + Su prueba debería reportar este(os) conjunto(s) de cambios como + bueno(s). +\item Un hijo del conjunto de cambios. Su prueba debería reportar al + conjunto de cambios hijo como malo. +\end{itemize} + +\subsection{Tener en cuenta la interferencia entre fallos} + +Es posible que su búsqueda de un fallo pueda viciarse por la presencia +de otro. Por ejemplo, digamos que su programa se revienta en la +revisión 100, y que funcionó correctamente en la revisión 50. Sin su +conocimiento, alguien introdujo un fallo con consecuencias grandes en +la revisión 60, y lo arregló en la revisión 80. Sus resultados +estarían distorcionados de una o muchas formas. + +Es posible que este fallo ``enmascare'' completamente al suyo, y que +podría haberse revelado antes de que su propio fallo haya tenido +oportunidad de manifestarse. Si no puede saltar el otro fallo (por +ejemplo, este evita que su proyecto se arme o compile), y de esta +forma no se pueda revisar si su fallo esté presente en un conjunto +particular de cambios, la orden \hgcmd{bisect} no podrá ayudarle +directamente. En cambio, puede marcar este conjunto de cambios como +al ejecutar \hgcmdargs{bisect}{--skip}. + +Un problema distinto podría surgir si su prueba de la presencia de un +fallo no es suficientemente específica. Si usted busca ``mi programa +se revienta'', entonces tanto su fallo como el otro fallo sin relación +que terminan presentando síntomas distintos, podría terminar +confundiendo a \hgcmd{bisect}. + +Otra situación en la cual sería de mucha utilidad emplear a +\hgcmdargs{bisect}{--skip} surge cuando usted no puede probar una +revisión porque su proyecto estaba en una situación de rompimiento y +por lo tanto en un estado en el cual era imposible hacer la prueba en +esa revisión, tal vez porque alguien consignó un cambio que hacía +imposible la construcción del proyecto. + +\subsection{Acotar la búsqueda perezosamente} + +Elegir los primeros ``buenos'' y ``malos'' conjuntos de cambios que +marcarán los límites de su búsqueda en general es sencillo, pero vale +la pena discutirlo. Desde la perspectiva de \hgcmd{bisect}, el +conjunto de cambios ``más nuevo'' por convención es el ``malo'', y el +otro conjunto de cambios es el ``bueno''. + +Si no recuerda cuál podría ser el cambio ``bueno'', para informar a +\hgcmd{bisect}, podría hacer pruebas aleatorias en el peor de los +casos. Pero recuerde eliminar aquellos conjuntos de cambios que +podrían no exhibir el fallo (tal vez porque la característica donde se +presenta el fallo todavía no está presente) y aquellos en los cuales +otro fallo puede enmascararlo (como se discutió anteriormente). + +Incluso si termina ``muy atrás'' por miles de conjuntos de cambios o +meses de historial, solamente estaŕa adicionando unas pruebas contadas +para \hgcmd{bisect}, gracias al comportamiento logarítmico. + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r 73b094b764ec -r 51b5d56744c5 es/wdir-after-commit.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/wdir-after-commit.svg Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/wdir-branch.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/wdir-branch.svg Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/wdir-merge.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/wdir-merge.svg Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/wdir-pre-branch.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/wdir-pre-branch.svg Thu Jan 29 22:00:07 2009 -0800 @@ -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 73b094b764ec -r 51b5d56744c5 es/wdir.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/es/wdir.svg Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + e7639888bb2f + + 7b064d8bac5e + + + 000000000000 + + + Historia en el repositorio + + + + + e7639888bb2f + + + + 000000000000 + + Primer padre + Segundo padre + + Padres del directorio de trabajo + + + + diff -r 73b094b764ec -r 51b5d56744c5 html/index.es.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/html/index.es.html Thu Jan 29 22:00:07 2009 -0800 @@ -0,0 +1,53 @@ + + + + + + + Control Distribuido de Revisiones con Mercurial + + + +

Control Distribuido de Revisiones con Mercurial

+ +

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

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

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

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

+ +

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

+ +

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

+ + diff -r 73b094b764ec -r 51b5d56744c5 html/index.html.var --- a/html/index.html.var Thu Jan 15 10:13:51 2009 +0100 +++ b/html/index.html.var Thu Jan 29 22:00:07 2009 -0800 @@ -1,3 +1,7 @@ URI: index.en.html Content-Language: en Content-Type: text/html; charset=UTF-8 + +URI: index.es.html +Content-Language: es +Content-Type: text/html; charset=UTF-8