# HG changeset patch # User FUJIWARA Katsunori # Date 1249037356 -32400 # Node ID d6ca1334a19d4cedd82304f98520e8754e906e25 # Parent a24b370a16eee2aa368a5b76010841671472d017 Japanese translation on a24b370a16ee diff -r a24b370a16ee -r d6ca1334a19d .hgignore --- a/.hgignore Sun Jun 17 11:21:32 2007 -0700 +++ b/.hgignore Fri Jul 31 19:49:16 2009 +0900 @@ -24,6 +24,7 @@ *.orig */pdf/*.out *.pdf +*.bb *.png *.ps *.run diff -r a24b370a16ee -r d6ca1334a19d ja/00book.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/00book.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,58 @@ +\title{Mercurial による分散構成管理} +\author{Bryan O'Sullivan} +\date{Copyright \copyright\ 2006, 2007 Bryan O'Sullivan.\\ + This material may be distributed only subject to the terms and + conditions set forth in version 1.0 of the Open Publication License. + ライセンス条項に関する詳細は、付録~\ref{cha:opl}を参照してください。\\ + 本書はリビジョン + \href{http://hg.serpentine.com/mercurial/book/}{a24b370a16ee} + の成果物を元に翻訳したものです。} + +\makeindex + +\begin{document} + +\maketitle + +\addcontentsline{toc}{chapter}{Contents} +\pagenumbering{roman} +\tableofcontents +\listoffigures +%\listoftables + +\pagenumbering{arabic} + +\include{preface} +\include{intro} +\include{tour-basic} +\include{tour-merge} +\include{concepts} +\include{daily} +\include{collab} +\include{filenames} +\include{branch} +\include{undo} +\include{hook} +\include{template} +\include{mq} +\include{mq-collab} +\include{hgext} + +\appendix +%\include{cmdref} +%\include{mq-ref} +\include{srcinstall} +\include{license} +\addcontentsline{toc}{chapter}{Bibliography} +\bibliographystyle{alpha} +\bibliography{99book} + +\addcontentsline{toc}{chapter}{Index} +\printindex + +\end{document} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book_pdf" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/00book_html.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/00book_html.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,78 @@ +% 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]{jbook} +\documentclass[oneside]{book} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%% +%%%% output format independent part: +%%%% + +%\usepackage{enumerate} +\usepackage{fullpage} +\usepackage{makeidx} +\usepackage{fancyvrb} +\usepackage{custom} + +%%%% +%%%% output format specific part: +%%%% + +\usepackage{graphicx} + +% leave hyperref until last +\usepackage[ + tex4ht, + colorlinks=true, + bookmarks=true, + bookmarksnumbered=true, + bookmarkstype=toc + ]{hyperref} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%% +%%%% output format independent part: +%%%% + +\include{99defs} + +%%%% +%%%% output format specific part: +%%%% + +%\newcommand{\grafix}[1]{\includegraphics{#1}} +%\newcommand{\grafix}[1]{#1} +\newcommand{\grafix}[2][]{\Picture{#2}} % ignore #1 +\newcommand{\grafixL}[1]{\Picture{#1}} + +% Note: blah blah. +\newsavebox{\notebox} +\newenvironment{note}{ + \begin{lrbox}{\notebox} + \begin{minipage}{0.7\textwidth} + \textbf{備考:}\space% +}{ + \end{minipage} + \end{lrbox} + \fbox{\usebox{\notebox}} +} +\newenvironment{caution}{ + \begin{lrbox}{\notebox} + \begin{minipage}{0.7\textwidth} + \textbf{注意:}\space% +}{ + \end{minipage} + \end{lrbox} + \fbox{\usebox{\notebox}} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\input{00book} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: t +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/00book_pdf.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/00book_pdf.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,91 @@ +% 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]{jbook} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%% +%%%% output format independent part: +%%%% + +%\usepackage{enumerate} +\usepackage{fullpage} +\usepackage{makeidx} +\usepackage{fancyvrb} +\usepackage{custom} + +%%%% +%%%% output format specific part: +%%%% + +\usepackage[dvipdfm]{color} % prevents COLOR.STY from using DVIPS.DEF driver +\usepackage[dvipdfm]{graphicx} +\usepackage{pslatex} + +\input{atbegxxx} + +% leave hyperref until last +\usepackage[ + dvipdfm, + colorlinks=true, + bookmarks=true, + bookmarksnumbered=true, + bookmarkstype=toc, + pdftitle={Mercurial による分散構成管理}, + pdfsubject={構成管理}, + pdfkeywords={Mercurial, 構成管理, 分散構成管理}, + pdfauthor={Bryan O'Sullivan} + ]{hyperref} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%% +%%%% output format independent part: +%%%% + +\include{99defs} + +%%%% +%%%% output format specific part: +%%%% + +\newcommand{\grafix}[2][]{\includegraphics[#1]{#2}} +\newcommand{\grafixL}[1]{\includegraphics[angle=90,height=\textwidth]{#1}} + +% Note: blah blah. +\newsavebox{\notebox} +\newenvironment{note}{ + \vspace{1.5\kanjicharheight} + \begin{center} + \begin{lrbox}{\notebox} + \begin{minipage}{0.8\textwidth} + \textbf{備考:}\space +}{ + \end{minipage} + \end{lrbox} + \fbox{\usebox{\notebox}} + \end{center} + \vspace{1.5\kanjicharheight} +} +\newenvironment{caution}{ + \vspace{1.5\kanjicharheight} + \begin{center} + \begin{lrbox}{\notebox} + \begin{minipage}{0.8\textwidth} + \textbf{注意:}\space% +}{ + \end{minipage} + \end{lrbox} + \fbox{\usebox{\notebox}} + \begin{center} + \vspace{1.5\kanjicharheight} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\input{00book} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: t +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/99book.bib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/99book.bib Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,76 @@ +@Unpublished{gruenbacher:2005, + author = {Andreas Gruenbacher}, + title = {How To Survive With Many Patches (Introduction to \texttt{quilt})}, + year = {2005}, + month = {June}, + note = {\url{http://www.suse.de/~agruen/quilt.pdf}}, +} + +@InProceedings{web:europython, + author = {Bryan O'Sullivan}, + title = {Achieving High Performance in Mercurial}, + booktitle = {EuroPython Conference}, + year = {2006}, + month = {July}, + note = {\url{XXX}}, +} + +@Misc{web:diffstat, + author = {Thomas Dickey}, + title = {\texttt{diffstat}--make a histogram of \texttt{diff} output}, + note = {\url{http://dickey.his.com/diffstat/diffstat.html}}, +} + +@Misc{web:quilt, + author = {Andreas Gruenbacher, Martin Quinson, Jean Delvare}, + title = {Patchwork Quilt}, + note = {\url{http://savannah.nongnu.org/projects/quilt}}, +} + +@Misc{web:patchutils, + author = {Tim Waugh}, + title = {\texttt{patchutils}--programs that operate on patch files}, + note = {\url{http://cyberelk.net/tim/patchutils/}}, +} + +@Misc{web:mpatch, + author = {Chris Mason}, + title = {\texttt{mpatch}--help solve patch rejects}, + note = {\url{http://oss.oracle.com/~mason/mpatch/}}, +} + +@Misc{web:wiggle, + author = {Neil Brown}, + title = {\texttt{wiggle}--apply conflicting patches}, + note = {\url{http://cgi.cse.unsw.edu.au/~neilb/source/wiggle/}}, +} + +@Misc{web:mysql-python, + author = {Andy Dustman}, + title = {MySQL for Python}, + note = {\url{http://sourceforge.net/projects/mysql-python}}, +} + +@Misc{web:changelog, + author = {Richard Stallman, GNU Project volunteers}, + title = {GNU Coding Standards---Change Logs}, + note = {\url{http://www.gnu.org/prep/standards/html_node/Change-Logs.html}}, +} + +@Misc{web:macpython, + author = {Bob Ippolito, Ronald Oussoren}, + title = {Universal MacPython}, + note = {\url{http://bob.pythonmac.org/archives/2006/04/10/python-and-universal-binaries-on-mac-os-x/}}, +} + +@Misc{web:putty, + author = {Simon Tatham}, + title = {PuTTY---open source ssh client for Windows}, + note = {\url{http://www.chiark.greenend.org.uk/~sgtatham/putty/}}, +} + +@Misc{web:configparser, + author = {Python.org}, + title = {\texttt{ConfigParser}---Configuration file parser}, + note = {\url{http://docs.python.org/lib/module-ConfigParser.html}}, +} diff -r a24b370a16ee -r d6ca1334a19d ja/99defs.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/99defs.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,183 @@ +% Bug ID. +\newcommand{\bug}[1]{\index{Mercurial + バグデータベース! + \href{http://www.selenic.com/mercurial/bts/issue#1}{バグ~#1} + }\href{http://www.selenic.com/mercurial/bts/issue#1}{Mercurial + バグ番号~#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} ファイル}\texttt{#1}} + +% Directory name, with index entry. +\newcommand{\sdirname}[1]{\index{\texttt{#1} ディレクトリ}\texttt{#1}} + +% Mercurial extension. +\newcommand{\hgext}[1]{\index{\texttt{#1} イクステンション}\texttt{#1}} + +% Command provided by a Mercurial extension. +\newcommand{\hgxcmd}[2]{\index{\texttt{#2} コマンド (\texttt{#1} + イクステンション) + }\index{\texttt{#1} イクステンション!\texttt{#2} コマンド + }``\texttt{hg #2}''} + +% Mercurial command. +\newcommand{\hgcmd}[1]{\index{\texttt{#1} コマンド}``\texttt{hg #1}''} + +% Mercurial command, with arguments. +\newcommand{\hgcmdargs}[2]{\index{\texttt{#1} コマンド}``\texttt{hg #1 #2}''} + +\newcommand{\tplkword}[1]{\index{\texttt{#1} テンプレートキーワード + }\index{テンプレートキーワード!\texttt{#1}}\texttt{#1}} + +\newcommand{\tplkwfilt}[2]{ + \index{\texttt{#1} テンプレートキーワード!\texttt{#2} フィルタ + }\index{テンプレートフィルタ! + \texttt{#2}}\index{\texttt{#2} テンプレートフィルタ}\texttt{#2}} + +\newcommand{\tplfilter}[1]{\index{テンプレートフィルタ!\texttt{#1} + }\index{\texttt{#1} テンプレートフィルタ}\texttt{#1}} + +% Shell/system command. +\newcommand{\command}[1]{\index{\texttt{#1} システムコマンド}\texttt{#1}} + +% Shell/system command, with arguments. +\newcommand{\cmdargs}[2]{\index{\texttt{#1} コマンド}``\texttt{#1 #2}''} + +% Mercurial command option. +\newcommand{\hgopt}[2]{\index{\texttt{#1} コマンド!\texttt{#2} オプション + }\texttt{#2}} + +% Mercurial command option, provided by an extension command. +\newcommand{\hgxopt}[3]{ + \index{\texttt{#2} コマンド(\texttt{#1} イクステンション)! + \texttt{#3} オプション + }\index{\texttt{#1} イクステンション!\texttt{#2} コマンド! + \texttt{#3} オプション}\texttt{#3}} + +% Mercurial global option. +\newcommand{\hggopt}[1]{\index{グローバルオプション!\texttt{#1} オプション + }\texttt{#1}} + +% Shell/system command option. +\newcommand{\cmdopt}[2]{\index{\texttt{#1} コマンド!\texttt{#2} オプション + }\texttt{#2}} + +% Command option. +\newcommand{\option}[1]{\texttt{#1}} + +% Software package. +\newcommand{\package}[1]{\index{\texttt{#1} パッケージ}\texttt{#1}} + +% Section name from a hgrc file. +\newcommand{\rcsection}[1]{\index{\texttt{hgrc} ファイル!\texttt{#1} セクション + }\texttt{[#1]}} + +% Named item in a hgrc file section. +\newcommand{\rcitem}[2]{\index{\texttt{hgrc} ファイル! + \texttt{#1} セクション!\texttt{#2} 項目}\texttt{#2}} + +% hgrc file. +\newcommand{\hgrc}{\index{設定ファイル!\texttt{hgrc}(Linux/Unix) + }\index{\texttt{hgrc} 設定ファイル}\texttt{hgrc}} + +% Mercurial.ini file. +\newcommand{\hgini}{\index{設定ファイル!\texttt{Mercurial.ini}(Windows) + }\index{\texttt{Mercurial.ini} 設定ファイル + }\texttt{Mercurial.ini}} + +% Hook name. +\newcommand{\hook}[1]{\index{\texttt{#1} フック + }\index{フック!\texttt{#1}}\texttt{#1}} + +% Environment variable. +\newcommand{\envar}[1]{\index{\texttt{#1} 環境変数 + }\index{環境変数!\texttt{#1}}\texttt{#1}} + +% Python module. +\newcommand{\pymod}[1]{\index{\texttt{#1} モジュール}\texttt{#1}} + +% Python class in a module. +\newcommand{\pymodclass}[2]{\index{\texttt{#1} モジュール!\texttt{#2} クラス + }\texttt{#1.#2}} + +% Python function in a module. +\newcommand{\pymodfunc}[2]{\index{\texttt{#1} モジュール!\texttt{#2} 関数 + }\texttt{#1.#2}} + +% Code sample, eating 4 characters of leading space. +\DefineVerbatimEnvironment{codesample4}{Verbatim}{ + frame=single, + gobble=4, + xleftmargin=0.1\textwidth, + xrightmargin=0.1\textwidth, + baselinestretch=0.8, + numbers=left, + commandchars=\\\{\} +} + +% Code sample, eating 2 characters of leading space. +\DefineVerbatimEnvironment{codesample2}{Verbatim}{ + frame=single, + gobble=2, + xleftmargin=0.1\textwidth, + xrightmargin=0.1\textwidth, + baselinestretch=0.8, + numbers=left, + commandchars=\\\{\} +} + +% Interaction from the examples directory. +\newcommand{\interaction}[1]{ + \vspace{1.5\kanjicharheight} + \VerbatimInput[ + frame=single, + xleftmargin=0.1\textwidth, + xrightmargin=0.1\textwidth, + baselinestretch=0.8, + numbers=left, + commandchars=\\\{\} + ]{% +% examples/#1.out + examples/#1.lxo + } + \vspace{1.5\kanjicharheight} +} +% Example code from the examples directory. +\newcommand{\excode}[1]{ + \vspace{1.5\kanjicharheight} + \VerbatimInput[ + frame=single, + xleftmargin=0.1\textwidth, + xrightmargin=0.1\textwidth, + baselinestretch=0.8, + numbers=left, + commandchars=\\\{\} + ]{../examples/#1} + \vspace{1.5\kanjicharheight} +} + +% Reference entry for a command. +\newcommand{\cmdref}[2]{\section{\hgcmd{#1}---#2}\label{cmdref:#1} + \index{\texttt{#1} コマンド}} + +% Reference entry for a command option with long and short forms. +\newcommand{\optref}[3]{\subsubsection{\hgopt{#1}{--#3} ないし + \hgopt{#1}{-#2}}} + +% Reference entry for a command option with only long form. +\newcommand{\loptref}[2]{\subsubsection{\hgopt{#1}{--#2} オプション}} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/CONFIRMED.ja.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/CONFIRMED.ja.txt Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,171 @@ +=============================================================================== + 動作確認済み環境情報 +=============================================================================== + +=============================================================================== +[Vine 4.2 2.6.16-76.40vl4] + +TeX tools: + + name version + --------------------+---------------- + tetex |3.0 0vl11.4 + tetex-extra |3.0 0vl11.4 + tetex-macros |3.0 0vl3 + jvf |2.0 0vl1 + dvipdfmx |20070518 0vl1 + --------------------+---------------- + + Vine で生成した PDF には一部に不明なフォントが設定される場合があり + ますが、現状では原因不明です。 + +Graphic tools: + + name version + --------------------+---------------- + ghostscript |7.07 0vl30.1 + ghostscript-fonts |5.50 1vl2 + graphviz |2.6 0vl2 + inkscape |0.44.1 0vl1 + --------------------+---------------- + +Other tools: + + name version + --------------------+---------------- + make |3.80 0vl4 + python |2.4.4 1.4vl4 + perl |5.8.6 0vl3.1 + --------------------+---------------- + +=============================================================================== +[Debian 2.6.26-13lenny2] + +TeX tools: + + name version + --------------------+---------------- + ptex-bin |3.1.10+0.04b-2.1 + ptex-jisfonts |2-21 + vfdata-morisawa5 |0.0.20020122-14 + texlive-latex-base |2007.dfsg.1-5 + texlive-latex-extra |2007.dfsg.17-1~lenny01 + jbibtex-bin |3.1.10+0.04b-2.1 + mendexk |2.6e-3 + dvipdfmx |1:20080607-1 + cmap-adobe-cns1 |0+20060819-3 + cmap-adobe-gb1 |0+20051207-3 + cmap-adobe-japan1 |0+20071201-4 + cmap-adobe-japan2 |0+20020208-4 + --------------------+---------------- + + 上記の dvipdfmx には、文字コード変換用のマップファイルが同梱されな + い問題があります。 + + http://oku.edu.mie-u.ac.jp/~okumura/texfaq/qa/52108.html + http://oku.edu.mie-u.ac.jp/~okumura/texfaq/qa/52944.html + + 版によって添付/削除を繰り返してい模様だが、上記の版では添付されて + いない状態のため、以下の手順で対処が必要です: + + 1. EUC-UCS2 ファイルを入手 + + a. ソースアーカイブを入手して data/EUC-UCS2 を取り出す + + http://project.ktug.or.kr/dvipdfmx/ + + b. あるいは CVS ブラウジングサービス経由で入手 + + http://cvs.ktug.or.kr/viewcvs/dvipdfmx/ + + 2. 入手した EUC-UCS2 ファイルの配置 + + /etc/texmf/texmf.cnf ファイル中の dvipdfmx に関するCMAPINPUTS + 設定パスに応じて EUC-UCS2 ファイルを配置する。 + + ※ 私の環境では /usr/share/fonts/cmap/ 配下でした + +Graphic tools: + + name version + --------------------+---------------- + gs |8.62.dfsg.1-3.2lenny1 + gs-esp |8.62.dfsg.1-3.2lenny1 + gs-cjk-resource |1.20080107-4 + graphviz |2.20.2-3 + inkscape |0.46-2.lenny2 + --------------------+---------------- + +Other tools: + + name version + --------------------+---------------- + make |3.81-5 + python |2.5.2-3 + perl |5.10.0-19 + --------------------+---------------- + +=============================================================================== +[Win32] + +TeX tools: + + README.ja.txt に記載されている URL のページを参考に、基本的には「フ + ルインストール」で導入してください。Win32 向けの配布物は、リリース + 時点のスナップショットに厳密なバージョン付けがされていませんので、 + ここではバージョン表記を行いません(表記できません)。 + + その上で、導入される版によっては以下のパッケージを別途導入する必要 + があります。 + + name version + --------------------+---------------- + XeTeX for W32 |不明 + --------------------+---------------- + + Win32 向けのインストーラで TeX ツール群を導入した場合、cat cp + mkdir や rm といった基本的なコマンドのバイナリも導入されます。 + + PATH 環境変数の設定で、TeX インストール先の bin に対する検索順序を + Cygwin の bin よりも前にしてしまうと、想定外の動作となる可能性があ + りますので注意が必要です。 + + また、Win32 環境で HTML を生成する場合は、導入後に tex4ht の設定ファ + イルに対する修正が必要です。 + + share/texmf/tex4ht/base/win32/tex4ht.env 中の以下の部分が、空白で字 + 下げされている=無効化されている状態なので、行頭の空白文字を除外 + ("Ggswin32c" で始まる行は長すぎるので省略)してください。 + + ======================================== + G.png + Ghterasefile zz%%4.ps + Ghterasefile %%3 + Gdvipsk -E -q -Ppdf -f %%1 -pp %%2 > zz%%4.ps + Ggswin32c -sDEVICE=pngalpha -sOutputFile=%%3 ...... + Ghterasefile zz%%4.ps + ======================================== + +Graphic tools: + + name version + --------------------+---------------- + ghostscript |8.63 ※ W32TeX ページからダウンロード + graphviz |2.24 + inkscape |0.46 + --------------------+---------------- + + ※ いずれも Win32 用バイナリを使用 + +Other tools: + + name version + --------------------+---------------- + make |3.81 + python |2.5.2 + perl |5.10.0 + --------------------+---------------- + + ※ いずれも Cygwin 上のものを使用 + +=============================================================================== diff -r a24b370a16ee -r d6ca1334a19d ja/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/Makefile Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,324 @@ +# This makefile requires GNU make. + +sources := \ + 00book.tex \ + 99book.bib \ + 99defs.tex \ + build_id.tex \ + branch.tex \ + collab.tex \ + concepts.tex \ + custom.sty \ + daily.tex \ + filenames.tex \ + hg_id.tex \ + hgext.tex \ + hook.tex \ + intro.tex \ + mq.tex \ + mq-collab.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) \ + + +bb-pdf := \ + $(image-dot:%.dot=%.bb) \ + $(image-svg:%.svg=%.bb) \ + $(image-png:%.png=%.bb) \ + + +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 \ + + +binary-staffs := \ + kdiff3.png \ + note.png \ + examples/data/netplug-1.2.5.tar.bz2 \ + examples/data/netplug-1.2.8.tar.bz2 \ + + +dist-sources := \ + ../html/hgicon.png \ + ../html/index.html.var \ + ../html/index.en.html \ + + +latex-options = \ + -interaction batchmode \ + -output-directory $(dir $(1)) \ + -jobname $(basename $(notdir $(1))) \ + +hg = $(shell which hg) + +hg-id = + +hg-version = unknown + +######################################## + +SVG2PNG = sh svg2png.sh + +SVG2EPS = sh svg2eps.sh + +GS = gs + +MAKEINDEX= mendex -J -f + +######################################## + +all: pdf html + +pdf: pdf/hgbook.pdf + +define pdf + mkdir -p $(dir $@) + platex $(call latex-options,$@) $(1) \ + || (rm -f $@; exit 1) + cp 99book.bib $(dir $@) + cd $(dir $@) && jbibtex $(basename $(notdir $@)) + cd $(dir $@) && $(MAKEINDEX) $(basename $(notdir $@)) + platex $(call latex-options,$@) $(1) \ + || (rm -f $@; exit 1) + platex $(call latex-options,$@) $(1) \ + || (rm -f $@; exit 1) + export TEXINPUTS=..; cd $(dir $@) && dvipdfmx $(basename $(notdir $@)) +# if grep 'Reference.*undefined' $(@:.pdf=.log); then exit 1; fi +endef + +pdf/hgbook.pdf: $(sources) atbegxxx.tex +pdf/hgbook.pdf: $(image-pdf) +pdf/hgbook.pdf: $(bb-pdf) +#pdf/hgbook.pdf: examples +pdf/hgbook.pdf: 00book_pdf.tex + $(call pdf,00book_pdf.tex) + +html: onepage split + +htlatex := htlatex.sh + +onepage: $(htlatex) +onepage: html/onepage/hgbook.html +onepage: html/onepage/hgbook.css +onepage: $(image-html:%=html/onepage/%) + +html/onepage/%: % + cp $< $@ + +split: $(htlatex) +split: html/split/hgbook.html +split: html/split/hgbook.css +split: $(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 $@) + sh ./htlatex.sh \ + $(1) \ + "bookhtml,html4-uni,$(2)" \ + "$(call latex-options,$@)" \ + || (rm -f $@; exit 1) + cp 99book.bib $(dir $@) + cd $(dir $@) && jbibtex $(basename $(notdir $@)) + cd $(dir $@) \ + && jtex '\def\filename{{hgbook}{idx}{4dx}{ind}} \input idxmake.4ht' \ + && $(MAKEINDEX) \ + -o $(basename $(notdir $@)).ind \ + $(basename $(notdir $@)).4dx + sh ./htlatex.sh \ + $(1) \ + "bookhtml,html4-uni,$(2)" \ + "$(call latex-options,$@)" \ + || (rm -f $@; exit 1) + sh ./htlatex.sh \ + $(1) \ + "bookhtml,html4-uni,$(2)" \ + "$(call latex-options,$@)" \ + || (rm -f $@; exit 1) + cd $(dir $@) && tex4ht -f/$(basename $(notdir $@)) -cvalidate -cunihtf + cd $(dir $@) && t4ht -f/$(basename $(notdir $@)) + python ./fixhtml.py $(dir $@)/*.html + rm $(dir $@)/hgbook.css +endef + +html/onepage/hgbook.html: $(sources) +html/onepage/hgbook.html: $(image-html) +#html/onepage/hgbook.html: examples +html/onepage/hgbook.html: bookhtml.cfg +html/onepage/hgbook.html: 00book_html.tex + $(call htlatex,00book_html.tex) + +html/split/hgbook.html: $(sources) +html/split/hgbook.html: $(image-html) +#html/split/hgbook.html: examples +html/split/hgbook.html: bookhtml.cfg +html/split/hgbook.html: 00book_html.tex + $(call htlatex,00book_html.tex,2) + +# Produce 90dpi PNGs for the web. + +%.png: %.svg + $(SVG2PNG) $@ $< + +%.svg: %.dot + dot -Tsvg -o $@ $< + +# Produce eps/pdf/bb for the pdf + +%.pdf: %.eps + epstopdf $< + +%.eps: %.svg + $(SVG2EPS) $@ $< + +%.eps: %.dot + dot -Tps -o $@ $< + +%.bb: %.pdf + $(GS) -q -sDEVICE=bbox -dAutoRotatePages=/None - -c quit - \ + < $< > $@ 2>&1 + +%.bb: %.png + ebb $< + +examples: $(example-prereqs) $(binary-staffs) examples/.run + +examples/.run: $(example-sources:%=examples/%.run) + touch examples/.run + +examples/%.run: examples/% examples/run-example + cd examples && python ./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-dot:%.dot=%.bb) \ + $(image-svg:%.svg=%.pdf) \ + $(image-svg:%.svg=%.png) \ + $(image-svg:%.svg=%.bb) \ + $(image-png:%.png=%.bb) \ + examples/*.{lxo,run} examples/.run build_id.tex hg_id.tex + rm -f atbegxxx.tex + +install: pdf split $(dist-sources) + rm -rf dist + mkdir -p dist + cp pdf/hgbook.pdf dist + cp html/split/*.{css,html,png} dist + cp $(dist-sources) dist + +rsync: install + rsync -avz --delete dist sp.red-bean.com:public_html/hgbook + +##### these targets are needed because mq can not handle binary files + +kdiff3.png: + cp ../en/kdiff3.png . + +note.png: + cp ../en/note.png . + +examples/data/netplug-1.2.5.tar.bz2: + mkdir -p examples/data + cp ../en/examples/data/netplug-1.2.5.tar.bz2 examples/data + +examples/data/netplug-1.2.8.tar.bz2: + mkdir -p examples/data + cp ../en/examples/data/netplug-1.2.8.tar.bz2 examples/data diff -r a24b370a16ee -r d6ca1334a19d ja/Makefile.linux --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/Makefile.linux Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,11 @@ +# -*- makefile -*- + +include ./Makefile + +#SVG2PNG= +#SVG2EPS= +#GS= +#MAKEINDEX= + +atbegxxx.tex: atbegdvi.tex + cp $< $@ diff -r a24b370a16ee -r d6ca1334a19d ja/Makefile.win32 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/Makefile.win32 Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,11 @@ +# -*- makefile -*- + +include ./Makefile + +SVG2PNG= sh svg2png_w32.sh +SVG2EPS= sh svg2eps_w32.sh +GS= gswin32c +MAKEINDEX= jmakeindex + +atbegxxx.tex: atbegshi.tex + cp $< $@ diff -r a24b370a16ee -r d6ca1334a19d ja/README.ja.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/README.ja.txt Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,182 @@ +=============================================================================== + HGBOOK 翻訳 +=============================================================================== + +本ディレクトリ配下に格納されている成果物は、以下の URL で公開されている +Bryan O'Sullivan 氏による "Mercurial: The Definitive Guide" の翻訳版です。 + + http://hgbook.red-bean.com/ + +**** +**** 注意 +**** + +** 内容に関する注意 + + - 翻訳ベースが 2007-06-17 時点の版なので、1.x 版以降となった現状の + Mercurial にそぐわない内容が含まれています + + +** 翻訳内容に関する注意: + + - 翻訳水準を試行錯誤している頃だったので、「commit」を「確定」と訳す + など、現状の Mercurial メッセージ翻訳の方針とは異なるものが含まれて + います + + ※ 現状の Mercurial メッセージ翻訳方針の詳細に関しては、以下の日本 + 語翻訳プロジェクトの成果物を参照してください + + http://bitbucket.org/foozy/mercurial-translation-ja/wiki/ + + - 原著の以下の Appendix は翻訳版には含まれていません + + - Command reference + - Mercurial Queues reference + + - 原著の以下の Appendix は未翻訳です + + - Open Publication License + + - 適切な訳ができなかった箇所には、"XXXX" マークと共に原文を併記してあ + ります + + +** 翻訳成果に関する注意: + + - 本来は、実際にコマンドを実行した結果を文書に取り込むようになってい + ますが: + + - 期待内容との差を検出した際に、実行結果生成が中断されてしまう + + - Mercurial の版を厳密に一致させないと、差分が検出されてしまう + + - コマンド自動実行が Win32 環境では上手く機能しない + + 以上のことから、実行結果出力は別途提供するものを展開して使用するこ + ととしています + + 別途提供している実行結果出力は、比較的新しい Mercurial を使用して生 + 成しているため、原著者の期待するものとは異なる可能性があります + + - 以下の理由から、翻訳結果ファイルの文字コードには iso-2022-jp を採用 + しています: + + - TeX の Unicode 化が実用的なのは Win32 環境(+ MacOS ?)のみ + + - Linux/Win32 環境の日本語化された TeX が、共に認識可能な文字コー + ドは iso-2022-jp のみ + + - PDF 生成は Linux/Win32 の両環境で確認済み + + - HTML 生成は Win32 環境でのみ確認済み + + 以下の理由から、(パッケージベースで環境構築するのであれば)Win32 環 + 境でのみ HTML 生成を確認済みです。 + + - HTML 化に使用する tex4ht は、ASCII TeX(ptex)ではなく、 + NTT-jTeX が必要(内部での処理の違いに起因) + + - Vine 向けの NTT-jTeX パッケージは流通していない + + - Debian 向けの NTT-jTeX パッケージは版が古すぎる + + - HTML 生成はファイル分割形式のみ + + 単一ファイル形式の HTML 生成は、LaTeX がヒープ領域不足で悲鳴を上げ + てしまうため、現時点では未確認です + + +**** +**** 事前準備 +**** + + 現状、LaTeX ソースからの PDF/HTML 生成は、以下の環境で確認しています。 + + - Vine 4.2 2.6.16-76.40vl4 (Linux) + - Debian 2.6.26-13lenny2 (Linux) + - Windows XP/Vista (Win32) + + 生成に必要なパッケージの導入方法等に関しては、それぞれ以下の URL を参 + 照してください。 + + - Vine: + http://oku.edu.mie-u.ac.jp/~okumura/texwiki/?cmd=read&page=Linux%2Fvine + + - Debian: + http://oku.edu.mie-u.ac.jp/~okumura/texwiki/?cmd=read&page=Linux%2FDebian + ※ 上記ページでの説明は Sarge でのものですが、動作確認済み環 + 境は Lenny です + + - Win32: + http://www.fsci.fuk.kindai.ac.jp/kakuto/win32-ptex/web2c75.html + http://oku.edu.mie-u.ac.jp/~okumura/texwiki/?%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB(Windows) + + 動作確認済みの環境に関する情報は、ja/CONFIRMED.ja.txt を参照してくだ + さい(導入後の手動設定に関する記述もありますので、必ず目を通してくださ + い)。 + + Linux 環境でパッケージ導入する場合は、自動的に依存パッケージの導入が + 行われますが、Win32 上で環境構築する場合は、手動で適宜導入する必要が + あります。 + + パッケージ間の依存関係は、導入する版によって常に変動しますので、ディ + スク容量事情が許すなら、試行錯誤をするよりも全パッケージを導入(「フル + インストール」と呼ばれる状態)するのがお勧めです。 + + + TeX/LaTeX とは別に、以下のツールの導入が必要です。 + + - Inkscape: SVG 画像からの変換処理に使用 + http://www.inkscape.org/ + + - Graphviz: グラフ画像の生成に使用 + http://www.graphviz.org/ + + Debian/Vine 等の Linux 環境ではパッケージ管理ツール経由で導入可能です + が、Win32 環境への導入はダウンロード&インストールを手動で行う必要が + あります。 + + + LaTeX や画像ファイルとは直接関係しませんが、以下のものも必要です。 + + - Perl + - Python + - GNU make + - GNU bash ※ いわゆる B-shell でも可 + + +**** +**** PDF/HTML の生成手順 +**** + + 1. lxo ファイルの展開 + + 以下の URL で表示されるページの "Uploaded files" にある + "hgbook_lxo.tar.gz" をダウンロードし、「HGBOOK のソースツリーのルー + ト位置」で展開してください。 + + http://bitbucket.org/foozy/hgbook-ja/downloads/ + + 2. ja ディレクトリ(このファイルの格納されている位置)に移動 + + ※ 以下の説明は、全てこのディレクトリを起点としています + + 3. Makefile の選択 + + Linux 環境の場合は Makefile.linux を、Win32 環境の場合は + Makefile.win32 を使用します。 + + 以下の "make 実行" に関する箇所において、それぞれ "-f + Makefile.linux" ないし "-f Makefile.win32" を指定するものとし + ます。 + + 4. PDF の生成は "make pdf" を実行 + + pdf ディレクトリ配下に hgbook.pdf が生成されます。 + + 5. HTML の生成は "make split" を実行(※ Win32 環境でのみ生成を確認) + + html/split ディレクトリ配下に HTML ファイルが生成されます。 + 必要なファイルは *.css *.html および *.png フィルです。 + +=============================================================================== diff -r a24b370a16ee -r d6ca1334a19d ja/atbegdvi.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/atbegdvi.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +% encoding specified below depends not on one of file content +% but on TeX processing internal one. +\ifnum 42146=\euc"A4A2 + \AtBeginDvi{\special{pdf:tounicode EUC-UCS2}} +\else + \AtBeginDvi{\special{pdf:tounicode 90ms-RKSJ-UCS2}} +\fi diff -r a24b370a16ee -r d6ca1334a19d ja/atbegshi.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/atbegshi.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,11 @@ +\usepackage{atbegshi} % defnes '\AtBeginShipoutFirst' + +% encoding specified below depends not on one of file content +% but on TeX processing internal one. +\ifnum 42146=\euc"A4A2 + % \AtBeginDvi does not work correctly with current HYPERREF package + \AtBeginShipoutFirst{\special{pdf:tounicode EUC-UCS2}} +\else + % \AtBeginDvi does not work correctly with current HYPERREF package + \AtBeginShipoutFirst{\special{pdf:tounicode 90ms-RKSJ-UCS2}} +\fi diff -r a24b370a16ee -r d6ca1334a19d ja/bookhtml.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/bookhtml.cfg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,18 @@ +% -*- latex -*- + +\Preamble{xhtml} + +% Tex4ht's default definition of lists is complete crap. +% Unfortunately, it can't distinguish between "ul" and "dl" lists. + +\ConfigureList{itemize}% + {\EndP\HCode{}\ShowPar} + {\endItem \def\endItem{\EndP\Tg}\HCode{
  • }} + {\HCode{}} +\def\textbullet{} + +\begin{document} + +\EndPreamble diff -r a24b370a16ee -r d6ca1334a19d ja/branch.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/branch.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,528 @@ +\chapter{Managing releases and branchy development} +\label{chap:branch} + +Mercurial は、 +同時並行的に開発を進めるようなプロジェクトを管理できる仕組みを持っています。 +これらの仕組みを理解するために、 +まずは一般的なソフトウェア開発の仕組みを眺めてみましょう。 + +多くのソフトウェアプロジェクトでは、 +重要な新規機能を含む``メジャー''リリースを間欠的に発行します。 +それと平行して``マイナー''リリースも発行することがあります。 +多くの場合、 +マイナーリリースは元にしたメジャーリリースと同一ですが、 +バグの修正がなされています。 + +この章では、 +「リリース」のようなプロジェクトのマイルストーンの、 +記録を保持する方法から説明を始めたいと思います。 +その後で、 +プロジェクトにおけるフェーズ移行での作業の流れや、 +その際の作業や成果物を +Mercurial によって分離/管理する方法を説明します。 + +\section{Giving a persistent name to a revision} + +特定のリビジョンを``リリース''と呼ぶことに決定したなら、 +そのリビジョンの ID を記録するべきです。 +リビジョンの ID を記録することで、 +後日何らかの理由(例えばバグの再現や、新規プラットフォームへの移植等) +で必要になった際にリリースを再現することができます。 + +\interaction{tag.init} + +\hgcmd{tag} コマンドを利用することで、 +Mercurial は任意のリビジョンに永続的な名前を付与します。 +読者の予想通り、この名前のことを``タグ''と呼びます。 + +\interaction{tag.tag} + +リビジョンにとって、 +タグは``象徴的な名前''(symbolic name) 以外の何者でもありません。 +タグは純粋に利便性のために存在するもので、 +リビジョンを参照する際の手軽で永続的な手段となります。 +Mercurial は、 +利用者の用いるタグ名の意味を解釈したりしません。 +曖昧さが無く解析できることを保証するために必要な少々の制約を除いては、 +タグ名に何らかの制約をつけたりすることもありません。 +以下のいずれの文字もタグ名には使用できません。 + +\begin{itemize} +\item コロン(ASCII 58, ``\texttt{:}'') +\item 行頭移動\footnote{carriage return} (ASCII 13, ``\Verb+\r+'') +\item 改行 (ASCII 10, ``\Verb+\n+'') +\end{itemize} + +\hgcmd{tags} コマンドを使用することで、 +リポジトリが保持しているタグを表示させることができます。 +\hgcmd{tags} コマンドの出力において、 +個々のタグ付けされたリビジョンは、 +始めにタグ名で、次にリビジョン番号で、 +最後に一意のリビジョンハッシュ値で識別されます。 + +\interaction{tag.tags} + +\texttt{tip} タグが \hgcmd{tags} +コマンドの出力に列挙されていることに注意してください。 +\texttt{tip} は、常にリポジトリ中の最新のリビジョンを指す +``流動的な''特殊タグです。 + +\hgcmd{tags} コマンドの出力では、 +タグはリビジョン番号の逆順(降順)で列挙されています。 +これは最新のタグは古いタグよりも先に列挙されることを意味し、 +それは同時に \hgcmd{tags} が出力するタグ一覧の最初に +\texttt{tip} が表示されることも意味します。 + +\hgcmd{log} コマンドの実行時に、 +タグと関連付けられたリビジョンを表示する場合、 +\hgcmd{log} コマンドはタグを表示します。 + +\interaction{tag.log} + +Mercurial コマンドに対してリビジョン識別子を指定する必要がある場合、 +リビジョン識別子を指定する位置では、 +常にタグ名を使用することができます。 +Mercurial の内部では、 +タグ名を対応するリビジョン識別子に変換してから使用しています。 + +\interaction{tag.log.v1.0} + +単一のリポジトリが保持できるタグの数にも、 +単一のリビジョンに付与できるタグの数にも制限はありません。 +現実的な問題として、 +タグは単にリビジョンの特定を補助するものですから、 +``過剰に''(具体的な数はプロジェクトに応じて異なりますが) +タグを付与するのはよろしくありません。 +多くのタグがあると、リビジョンを特定する利便性が早々に減少してしまいます。 + +例えば、 +あるプロジェクトでは数日毎の頻度でマイルストーンを設定しているとすると、 +それぞれのマイルストーンにタグを付与するのは極めて合理的です。 +しかし、全てのリビジョンで確実に綺麗なビルドができる継続的 +(continuous)なビルドシステムがある場合は、 +綺麗なビルド毎にタグを付与すると、大量のノイズを持ち込むことになります。 +その代わりに、 +ビルドが失敗するリビジョン(この事態が稀だと仮定しています!) +にタグを付与するか、 +ビルドの可否を追跡するタグの使用を止めるのが良いでしょう。 + +必要の無くなったタグを削除したい場合は +\hgcmdargs{tag}{--remove} コマンドを使用します。 + +\interaction{tag.remove} + +任意の時点でタグの関連付けを変更することもできますので、 +新規の \hgcmd{tag} コマンド実行により、 +同一のタグが異なるリビジョンを識別するようになります。 +\emph{本当に}タグを更新したいことを Mercurial に伝えるために、 +\hgopt{tag}{-f} オプションを使用しなければなりません。 + +\interaction{tag.replace} + +タグの更新後も、 +タグが以前に識別していたリビジョンに関する永続的な記録が残りますが、 +Mercurial がそれを使用することはありません。 +このように、 +間違ったリビジョンへのタグの付与には何の不利益もありませんので、 +タグ付けを間違ったなら、正しいリビジョンにタグを付与し直せばよいのです。 + +Mercurial は、 +リポジトリ中のリビジョン管理された通常ファイルにタグの情報を格納しています。 +何らかのタグを付与すると、 +\sfilename{.hgtags} ファイル中にそのタグを見つけることができるでしょう。 +\hgcmd{tag} コマンドを実行すると、 +Mercurial はこのファイルを変更し、自動的に変更をコミットします。 +このことは、 +\hgcmd{tag} コマンドを実行した際には、 +常に対応するチェンジセットを \hgcmd{log} コマンドの出力で見ることができる、 +ということを意味しています。 + +\interaction{tag.tip} + +\subsection{Handling tag conflicts during a merge} + +\sfilename{.hgtags} ファイルを気にする必要は殆どありませんが、 +時にはマージの際にその存在が意識されることがあります。 +このファイルの形式は単純で、連続した行から構成されています。 +各行はチェンジセットのハッシュ値で始まり、空白とタグ名が続きます。 + +マージにおける +\sfilename{.hgtags} ファイルの衝突を解消する際には、 +\sfilename{.hgtags} ファイル修正にひねりが必要です。 +リポジトリ中のタグを解析する場合、 +Mercurial は\emph{決して} +\sfilename{.hgtags} ファイルのワーキングコピーを参照することはありません。 +その代わりに、Mercurial +は\emph{最も最近コミットされた}ファイルのリビジョンを調べます。 + +このような設計の残念な結果として、 +マージした \sfilename{.hgtags} ファイルが、 +その変更をコミットした\emph{後も}正しい状態であることを、 +実際に検証することができません。 +マージの際に \sfilename{.hgtags} ファイルの衝突を解消する際には、 +コミット後に \hgcmd{tags} コマンドの実行を忘れずに行ってください。 +\sfilename{.hgtags} ファイルに不正があった場合、 +\hgcmd{tags} コマンドは不正の場所を報告しますので、 +その箇所を修正してコミットすれば良いのです。 +変更内容の正しさを確認するために、 +変更の後で、再度 \hgcmd{tags} コマンドを実行してください。 + +\subsection{Tags and cloning} + +\hgcmd{clone} コマンドが +特定のチェンジセットを指定して厳密な複製を作成するための +\hgopt{clone}{-r} オプションを持っていることに気付いているかもしれません。 +新しい複製は、 +指定したリビジョンよりも後に生じた履歴情報を一切持っていません。 +このことがタグと相互作用した場合、、 +油断していると驚かされる事態になります。 + +タグの生成が、 +\sfilename{.hgtags} ファイルへの格納の際に、 +一つのリビジョンとして扱われることを思い出せば、 +タグが記録されたチェンジセットが、 +タグの付与対象となる(古い)チェンジセットを参照するのは当然のことです。 +タグ \texttt{foo} 時点のリポジトリを複製するために +\hgcmdargs{clone}{-r foo} を実行した場合、 +複製されたリポジトリは、 +複製する際に使用された\emph{タグの作成に関する履歴を持っていません}。 +新しいリポジトリには、 +プロジェクト履歴の完全なサブセットが含まれますが、 +唯一、指定に用いたタグの情報は\emph{含まれていません}。 + +\subsection{When permanent tags are too much} + +Mercurial のタグは構成管理されており、 +プロジェクトの履歴と一体化しているため、 +誰かが作成したタグは、 +一緒に作業を行っている誰もが見ることができます。 +しかし、リビジョンに名前を付けることは、 +リビジョン \texttt{4237e45506ee} が実は \texttt{v2.0.2} である、 +ということを書き留めておく以上の有用性があります。 +巧妙なバグを追跡する際に、 +``アンがこのリビジョンで症状を見かけた''といった類の備忘録として、 +タグを付与したい場合もあるでしょう。 + +このような場合、 +\emph{ローカル}なタグが最適です。 +\hgopt{tag}{-l} オプション付きで +\hgcmd{tag} コマンドを起動することで、 +ローカルタグを作成することができます。 +このコマンド実行の場合、 +タグは \sfilename{.hg/localtags} ファイルに格納されます +\sfilename{.hgtags} と異なり +\sfilename{.hg/localtags} は構成管理されません。 +\hgopt{tag}{-l} によって作成したタグは、 +現在作業をしているリポジトリに留まり続けます +\footnote{訳注: \hgcmd{clone}、\hgcmd{pull} や +\hgcmd{push} によって他のリポジトリにコピーされることがありません}。 + +\section{The flow of changes---big picture vs. little} + +ここで、本章の冒頭で述べた概略に戻り、 +複数の平行した開発が同時に行われているプロジェクトについて考えて見ましょう。 + +新しい``主''リリースや、 +最新の主リリースに対する新たなマイナーバグ修正、 +現在は保守状態にあるような古いリリースに対する予期せぬ``hot fix'' +のための push があるでしょう。 + +開発における様々な平行した方向を参照するための一般的な方法は、 +``ブランチ''と呼ばれるものです。 +しかし、 +Mercurial が\emph{全ての履歴}を +「ブランチとマージの連続」として扱っていることを、 +既に何度も見てきました。 +実際には、 +表面的には関係しているようで、 +その実、たまたま同じ名前であるだけの2つの概念を扱っているのです。 + +\begin{itemize} +\item ``巨視的な''ブランチは、プロジェクト発展の広がりを表し、 + 名前をつけたり、話題に上ったりします。 + +\item ``微視的な''ブランチは、日々の開発活動と、変更マージの成果です。 + このブランチは、コードがどのように開発されていったのかを物語ります。 + +\end{itemize} + +\section{Managing big-picture branches in repositories} + +Mercurial において``巨視的な''ブランチを隔離する最も簡単な方法は、 +隔離用のリポジトリを用意することです。 +例えば、既にある共有リポジトリ---これを \texttt{myproject} と呼称します +---が ``1.0'' というマイルストーンに到達している場合、 +1.0 リリースのために使用したリビジョンにタグを付与することで、 +1.0 版に対する来るべき保守リリースの準備を行います。 + +\interaction{branch-repo.tag} + +タグ付けした時点と同じ内容の +\texttt{myproject-1.0.1} +という名の新しい共有リポジトリを複製します。 + +\interaction{branch-repo.clone} + +その後、 +来る 1.0.1 マイナーリリースに含めるべきバグ修正の作業が必要になったなら、 +\texttt{myproject-1.0.1} リポジトリを複製し変更を行って、 +その成果を反映します。 + +\interaction{branch-repo.bugfix} + +その間、次のメジャーリリースへ向けた開発作業は、 +マイナーリリースに関する作業とは隔離された状態で、 +\texttt{myproject} リポジトリにおいて活発に続けられます。 + +\interaction{branch-repo.new} + +\section{Don't repeat yourself: merging across branches} + +保守用ブランチでバグ修正を行ったとすると、 +多くの場合、プロジェクトのメインブランチに +(そしてそれ以外の保守ブランチにおいても) +同じバグが存在する可能性があります。 +同じバグを何度も直したいと思う開発者は稀ですから、 +同じ作業を繰り返すことなくバグ修正を管理するために +Mercurial が提供する幾つかの方法を見てみましょう。 + +最も単純な方法は、 +作業対象ブランチから複製したローカルリポジトリへ、 +保守ブランチから変更を pull することです。 + +\interaction{branch-repo.pull} + +その上で2つのブランチのそれぞれのヘッドをマージし、 +その成果をメインブランチに反映します。 + +\interaction{branch-repo.merge} + +\section{Naming branches within one repository} + +多くの場合は、 +リポジトリの分離によってブランチを分離するのが適切な遣り方です。 +単純ですから理解も簡単ですし、それ故に間違えることがありません。 +作業しているブランチと、コンピュータ上の(リポジトリ)ディレクトリの間で、 +1対1の関係ができていますので、 +ブランチ/リポジトリ中のファイルに対して、 +(Mercurial を意識しない)通常のツールを使用することもできます。 + +あなたが(そして共同作業者も) +``パワーユーザー''よりも高いレベルにあるのであれば、 +ブランチ(that you can consider XXXX)を扱う別な方法があります。 +前の節では、 +``微視的''ブランチと``巨視的''ブランチの、 +利用者レベルでの区別について言及しました。 +単一のリポジトリ中で、 +常に複数の``微視的な''ブランチ +(例えば、変更の pull 後にマージしていない状態)を扱っている一方で、 +Mercurial は複数の``巨視的な''ブランチを扱うこと\emph{も}できます。 + +Mercurial が``巨視的な''ブランチを扱う際の要点は、 +ブランチに永続的な\emph{名前}を付けるところにあります。 +前述のように \texttt{default} という名前のブランチが常に存在しますので、 +ブランチへの命名を行う前であっても、 +探せば \texttt{default} ブランチの跡を見つけることができます。 + +例えば、 +\hgcmd{commit} コマンドを実行すると、 +エディタが起動されてコミットメッセージを入力できます +\footnote{訳注: Emacs の hg-mode.el を使用している場合は見られません}が、 +末尾の ``\texttt{HG: branch default}'' を含む行を見てください。 +これは、\texttt{default} という名前のブランチに対してコミットしている、 +ということを表しています。 + +ブランチに名前をつけるには、 +まずは \hgcmd{branches} を使用します。 +このコマンドは、リポジトリ中に既に存在する名前付きブランチと、 +個々のブランチにおける先頭(tip)リビジョンがどれかを列挙します。 + +\interaction{branch-named.branches} + +実行例では、 +名前付きブランチを生成する前ですから、 +唯一存在する \texttt{default} だけが表示されます。 + +どれが``現在の''ブランチかを知るには、 +引数無しで \hgcmd{branch} コマンドを実行します。 +このコマンドは、 +現在のチェンジセットの親チェンジセットが、 +どのブランチ上にあるものかを表示します。 + +\interaction{branch-named.branch} + +新しいブランチを作成するには、 +再度 \hgcmd{branch} コマンドを実行しますが、 +今回は生成するブランチ名を引数として指定します。 + +\interaction{branch-named.create} + +ブランチ生成後、 +\hgcmd{branch} コマンドによりどのような副作用を生じたのか、 +怪しむかもしれません。 +\hgcmd{status} や \hgcmd{tip} の出力はどうなっているでしょうか? + +\interaction{branch-named.status} + +作業領域に変更は加えられていませんし、 +履歴に変化もありません。 +このことが示唆しているように、 +\hgcmd{branch} コマンドの実行は何ら永続的な効果を持ちません。 +このコマンドは、 +\emph{次回の}チェンジセットのコミットの際に、 +何というブランチ名を使用するかを +Mercurial に伝えるだけです。 + +変更をコミットすると、 +Mercurial はコミットされたチェンジセットにブランチ名を記録します。 +一旦 \texttt{default} ブランチから他のブランチに切り替えてコミットしたなら、 +\hgcmd{log}、\hgcmd{tip} やそれに類する出力を持つコマンドの出力に、 +新たなブランチ名が表示されていることでしょう。 + +\interaction{branch-named.commit} + +\hgcmd{log} に類するコマンドは、 +\texttt{default} ブランチ以外に属する全てのチェンジセットに対して、 +ブランチ名を表示します。 +そのため、名前付きブランチを使わない限り、 +ブランチに関する情報を見ることはありません。 + +名前付きブランチを作成し、そのブランチ名で変更をコミットしたならば、 +その変更に連なるその後のコミットは、同じブランチ名を引き継ぎます。 +\hgcmd{branch} コマンドにより、 +任意の時点でブランチ名を変更することができます。 + +\interaction{branch-named.rebranch} + +ブランチ名はかなり長い寿命を持つため、 +実際にはこのようなブランチ名の変更はそれほど頻繁に実行することは無いでしょう +(このことは規約ではなく、あくまで感想です)。 + +\section{Dealing with multiple named branches in a repository} + +リポジトリに複数の名前付きブランチがある場合、 +\hgcmd{update} や \hgcmdargs{pull}{-u} といったコマンド実行の際に、 +Mercurial は作業領域ディレクトリが属するブランチを覚えていて、 +``リポジトリ全体''の tip リビジョンではなく、 +そのブランチの tip リビジョンで作業領域ディレクトリを更新します。 +別な名前付きブランチのリビジョンで更新したい場合は、 +\hgcmd{update} コマンドに +\hgopt{update}{-C} オプションを指定しなければなりません。 + +この振る舞いは少々微妙ですから、実例で見てみましょう。 +始めに、 +どのブランチ上で作業しているのかと、 +どんなブランチがリポジトリ中に有るのかを確認します。 + +\interaction{branch-named.parents} + +現在 \texttt{bar} ブランチ上にいますが、 +古い \hgcmd{foo} ブランチも存在します。 + +\texttt{foo} ブランチおよび \texttt{bar} ブランチの +tip リビジョンへの移動は、 +変更履歴上を直線的に前後することしか必要としないため、 +\hgcmd{update} コマンドに +\hgopt{update}{-C} オプションを指定すること無しに、 +それぞれの tip リビジョンへの更新を行うことができます。 + +\interaction{branch-named.update-switchy} + +\texttt{foo} ブランチに戻るために +\hgcmd{update} コマンドを実行すると、 +\texttt{foo} ブランチ上に留まったままで +\texttt{bar} ブランチの tip リビジョンには移動しません。 + +\interaction{branch-named.update-nothing} + +\texttt{foo} ブランチでの変更のコミットにより、 +新たなヘッドが生成されます。 + +\interaction{branch-named.foo-commit} + +\texttt{foo} ブランチから \texttt{bar} ブランチへの更新は、 +履歴を``横っ飛び''しないとできませんから、 +Mercurial は +\hgcmd{update} コマンドへの \hgopt{update}{-C} +オプションの指定を必要とします。 + +\interaction{branch-named.update-bar} + +\section{Branch names and merging} + +お気づきの事とは思いますが、 +Mercurial におけるマージ処理は対称的ではありません。 +リビジョン番号 17 のものと 23 のもの、 +2つのヘッドをリポジトリが持っているものとしましょう。 +リビジョン 17 へと \hgcmd{update} +してからリビジョン 23 と \hgcmd{merge} した場合、 +Mercurial はリビジョン 17 をマージの第1親、 +リビジョン 23 を第2親として記録します。 +一方で、 +リビジョン 23 へと \hgcmd{update} +してからリビジョン 17 と \hgcmd{merge} した場合、 +リビジョン 23 がマージの第1親、 +リビジョン 17 が第2親として記録されます。 + +この振る舞いが、マージを行った際の Mercurial のブランチ名選択に影響します。 +マージ後にその結果をコミットすると、 +Mercurial は第1親のブランチ名を維持しようとします。 +第1親のブランチ名が \texttt{foo} で、 +\texttt{bar} ブランチのリビジョンとマージした場合、 +マージ後のブランチ名は \texttt{foo} のままとなります。 + +リポジトリ中に同じブランチ名の複数のヘッドが存在することは、 +それほど珍しいことではありません。 +例えば、私とあなたが \texttt{foo} ブランチで作業しているとします。 +二人がそれぞれ異なる変更をコミットし、 +私があなたの変更を pull しました。 +この時点で私のリポジトリには、 +\texttt{foo} ブランチ上に2つのヘッドが存在します。 +マージの結果、 +\texttt{foo} ブランチ上の2つのヘッドは期待通り1つになります。 + +しかし、私が \texttt{bar} ブランチで作業していて、 +\texttt{foo} ブランチの成果をマージした場合、 +マージの結果は \texttt{bar} ブランチ上に留まります。 + +\interaction{branch-named.merge} + +より具体的な例として、 +\texttt{bleeding-edge} ブランチで作業していて、 +最新の成果を \texttt{stable} ブランチから持ち込みたいと思ったとします。 +この場合、 +\texttt{stable} ブランチの成果を pull してマージした段階で、 +Mercurial は``適切な''ブランチ名(\texttt{bleeding-edge})を選択します。 + +\section{Branch naming is generally useful} + +寿命の長い複数のブランチが単一リポジトリで共存している状況だけが、 +名前付きブランチの利用できる状況だとは考えないでください。 +リポジトリ1つにブランチ1つの状況であっても、 +名前付きブランチは有用です。 + +単純な例としては、 +ブランチに名前を付与することで、 +チェンジセットがどのブランチに由来するかの恒久的な記録を得ることができます。 +この記録は、 +寿命の長いブランチを持つプロジェクトの履歴を辿る際に、 +多くの情報をもたらすことでしょう。 + +リポジトリを共有して作業している場合、 +\hook{pretxnchangegroup} フックをそれぞれのリポジトリに対して設定することで、 +``不正な''ブランチ名を持つ変更が持ち込まれるのを防ぐことができます。 +この手法は単純ですが、 +``血の滴る刃''とでも言うべき(不安定な)ブランチの成果を、 +誤って``安定した''ブランチへと持ち込むことを防ぐには効果的です。 +このようなフックは、 +共有リポジトリの \hgrc ファイルに以下のように記述します。 + +\begin{codesample2} + [hooks] + pretxnchangegroup.branch = hg heads --template '{branches} ' | grep mybranch +\end{codesample2} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/collab.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/collab.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,1524 @@ +\chapter{Collaborating with other people} +\label{cha:collab} + +Mercurial は完全に非中央集約的なツールであるため、 +利用者相互の連携に関しては何ら制約を課すことをしません。 +ですが、 +分散構成管理に馴染みが無いのであれば、 +いくつかのツールや使用例を知っておくことは、 +妥当な作業手順のモデルを考える際に役に立ちます。 + +\section{Mercurial's web interface} + +Mercurial は、 +いくつかの有用な機能を提供する、 +強力なウェブインタフェースを持っています。 + +対話的な利用の場合、 +ウェブインタフェース経由で1つないし複数のリポジトリの閲覧ができます。 +リポジトリ履歴の参照や、 +個々の変更(コミットメッセージや差分)の検証、 +および各ディレクトリやファイルの内容の参照、 +といったことができます。 + +通知に関しても、 +ウェブインタフェースは、 +リポジトリにおける変更に関する RSS 配信機能を提供します。 +お気に入りのツールを使ってリポジトリを``購読''することもできますし、 +リポジトリにおける活動状況の自動通知を即座に行うこともできます。 +リポジトリ提供者側における追加設定が不要であることから、 +筆者自身は、 +変更通知のメーリングリストよりも、 +「RSS 配信を購読」するモデルの方が非常に便利だと思います。 + +ウェブインタフェースにより、 +遠隔ユーザによるリポジトリの複製や変更の取り込み、 +および(サーバ側でそれを許可しているならば) +変更の受理が可能になります。 +Mercurial の HTTP トンネリングプロトコルでは、 +積極的にデータの圧縮を行いますので、 +狭い帯域のネットワーク接続経由でも効率よく機能します。 + +ウェブインタフェースを触ってみる最も簡単な方法は、 +Mercurial のマスタリポジトリである +\url{http://www.selenic.com/repo/hg?style=gitweb} のような、 +既存のリポジトリにウェブブラウザで接続してみることです。 + +自身でリポジトリのウェブインタフェースを提供することに興味がある場合、 +Mercurial には2つの選択肢があります。 +1つは \hgcmd{serve} コマンドを使用するもので、 +短期間の``軽量な''稼動の場合に最適です。 +このコマンドの利用に関する詳細は、 +\ref{sec:collab:serve}~節を参照してください。 +長期的且つ常時利用可能な稼動を望む場合は、 +Mercurial に組み込まれている +CGI (Common Gateway Interface)機能が、 +一般的な全てのウェブサーバで利用可能です。 +CGI 設定の詳細は、 +\ref{sec:collab:cgi}~節を参照してください。 + +\section{Collaboration models} + +適切な柔軟性を持つツールを使うことで、 +作業手順の決定は、 +技術的な問題から組織工学的(social engineering)な問題へと変わります。 +Mercurial は、 +プロジェクトにおける作業手順の構成に関して殆ど制限を課さないため、 +個別の要望に沿ったモデルの設定と運用は利用者次第となります。 + +\subsection{Factors to keep in mind} + +いずれのモデルにおいても心得ておくべき最も重要な点は、 +それを利用する人々の要望と能力にどれだけ適合するか、 +ということです。 +これは自明に見えるかもしれませんが、 +ほんの少しの間でもこのことを忘れてはいけません。 + +筆者は以前、完璧と思える作業手順モデルを構築したのですが、 +開発チームに少なからぬ量の驚きと不和をもたらしました。 +複雑なブランチ群が必要な理由と、 +それらの間における変更の取り扱いについて説明しようと試みましたが、 +チームのメンバーの何人かが異を唱えてきたのです。 +彼らは聡明な人達でしたが、 +作業における制約に注意を払う\footnote{訳注: 「ルールを守る」の意か?}ことも、 +筆者が唱えるモデルの細部における制約の重要性に向き合おうともしませんでした。 + +近い将来の社会的・技術的な問題から目を背けないでください。 +どんな計画を実施しようとも、 +間違いや問題が発生した場合に備えるべきです。 +予想可能な問題に対して、 +自動的な防御や即時復旧のための仕組みの追加を考慮しましょう。 +例えば、 +リリース向けではない変更のためのブランチを作成しようとした場合、 +他の作業者がリリース用ブランチにうっかりマージしてしまう可能性について、 +早い時点で考慮したほうが良いでしょう。 +不適切なブランチからチェンジセットをマージさせないフックを記述することで、 +この問題に関しては回避可能です。 + +\subsection{Informal anarchy} + +持続可能性の点から +``何でもアリ''なやり方はお薦めしませんが、 +簡単に把握することができるモデルであり、 +いくつかの特異な状況では非常に良く機能します。 + +一つの例として、 +多くのプロジェクトが、 +直接会うことの稀な弱くまとまった協力者グループを持ている +As one example, many projects have a loose-knit group of collaborators +who rarely physically meet each other. +時折の``全力疾走''(sprints)\footnote{訳注: +オフ会とかですね。}を設けることで、 +距離によって隔てられた作業に打ち勝つグループもあります。 +全力疾走の機会では、 +多くの人が共に同じ場所(会社の会議室やホテルの会議室の類) +に集まり、 +数日程度を閉じこもって過ごし、 +少量のプロジェクトに集中してハッキングを行います。 + +全力疾走は、 +大掛かりなサーバインフラを必要としない +\hgcmd{serve} コマンドを利用するのにちょうど良い機会です。 +以下の\ref{sec:collab:serve}~節を読むことで、 +すぐにでも \hgcmd{serve} を使い始めることができます。 +そうしたなら、 +周囲の人達にサーバを実行中であることを伝え、 +インスタントメッセンジャー等を使用して URL を送れば、 +共同作業する上での折り返し地点まで辿り着きました。 +ブラウザに教えられた URL を入力すれば、 +彼らはすぐにでもあなたの変更をレビューすることができますし、 +あなたからバグフィックスを入手してそれを検証したり、 +新機能が含まれるブランチを複製してそれを試してみたりすることができます。 + +その場限りのこのような形式で事を進めることの利点と欠点は、 +あなたによる変更の存在と、どこでアクセス可能かを知る人だけが、 +それを参照することができる、という点にあります。 +このような非公式な手法は、 +複数の異なるリポジトリからの取り込みが各自に要求されるため、 +数人以上に対しては単純に規模の拡大ができません。 + +\subsection{A single central repository} + +小規模なプロジェクトにおいて、 +中央集約的な構成管理ツールからの移行する最も簡単な方法は、 +単一の共有リポジトリを経由して変更のやり取りをする、 +というものです。 +この体制は、 +より野心的な作業手順体系のための最も基本的な``構成要素''でもあります。 + +開発者(contributor)は、 +共有リポジトリの複製を行うことで作業を開始します。 +必要な時にいつでも変更の取り込みを行えますし、 +開発者の何人かは(全員でも可)、 +外部に公開可能になった際に変更を共有リポジトリに反映させる権限を持ちます。 + +このモデルであっても、 +共有リポジトリを経由せずにお互いの変更を直接 \hgcmd{pull} することは、 +開発者にとっては意義のあることです。 +例えば、 +暫定的なバグ修正を行ったものの、 +共有リポジトリにその修正を公開した場合に、 +その修正を取り込んだ他の開発者の作業に支障をきたす恐れがある、 +という場合を考えてみましょう。 +バグ修正を含む自分のリポジトリから一時的なリポジトリを複製し、 +複製先で修正内容を検証してもらえるように他の開発者にお願いすることで、 +潜在的な損害を低減することができます。 +このようにすることで、 +潜在的な危険性を持つ変更であっても、 +簡単な検証が済むまでは公開されないようにすることができます。 + +この種のやり取りの場合は、 +共有リポジトリへの安全な変更反映のために +\command{ssh} プロトコルを使用するのが一般的です +(\ref{sec:collab:ssh}~節参照)。 +読み出し専用リポジトリを、 +CGI を使用して HTTP 経由で公開することも可能です +(\ref{sec:collab:cgi}~節参照)。 +リポジトリへの変更反映が必要ない場合や、 +リポジトリの履歴をウェブブラウザ経由で参照したい場合には、 +HTTP 経由での公開で十分ニーズが満たされます。 + +\subsection{Working with multiple branches} + +一定以上の規模を持つプロジェクトにおいては、 +作業の進展が同時に複数の「前線」で行われることは自然な成り行きです。 +ソフトウェア開発の場合、 +どのプロジェクトでも、 +一定期間ごとに公式リリースを行うのが一般的です。 +各リリースは最初の公開の後に、 +一定期間の``保守状態''(maintenance mode)となることがあります。 +保守リリースではバグ修正のみを扱い、 +新規機能については取り扱わないのが通例です。 +これら保守リリースと平行して、 +(場合によっては複数の)将来のリリースに向けた開発が進行します。 +方向性の少し異なる、これら進行中の個々の開発を指すのに、 +一般的に``ブランチ''という表現を使います。 + +Mercurial は特に、 +複数の異なるブランチを同時に管理することに適しています。 +それぞれの``開発指向''ごとに、 +別々の共有用リポジトリを用意することで、 +必要になる都度、 +あるリポジトリから別のリポジトリへのマージを行えば良いのです。 +各リポジトリは互いに独立していますから、 +誰かが明示的にマージしない限りは +開発ブランチにおける不安定な変更が、 +安定版のためのブランチに影響を与えることはありません。 + +ブランチごとにリポジトリを用意する遣り方の実際の例を以下に示します。 +中央のサーバに``メインブランチ''があるものとします。 + +\interaction{branching.init} + +開発者はメインブランチから複製し、 +変更、変更のテスト、コミットの後に、 +変更をメインブランチのリポジトリに反映します。 + +メインブランチがリリースのマイルストーンに達したならば、 +マイルストーンとなるリビジョンに +\hgcmd{tag} コマンドで永続的な名前を付与します。 + +\interaction{branching.tag} + +メインブランチでは開発が継続しているとします。 + +\interaction{branching.main} + +リリースマイルストーン後の任意の時点でリポジトリを複製した開発者は、 +リリースマイルストーンで記録されたタグを使うことで、 +タグが付与されたリビジョンがコミットされた時点と +厳密に一致する作業領域ディレクトリを +\hgcmd{update} コマンドにより複製することができます。 + +\interaction{branching.update} + +それに加えて、 +メインブランチでのタグ付けの後で、 +サーバ上のメインブランチを、 +新たな``安定版''ブランチ(のリポジトリ)へと複製することもできます\footnote{ +訳注: メインブランチと安定版ブランチの各リポジトリは、 +必ずしも同一サーバで運用される必要はありません。}。 + +\interaction{branching.clone} + +安定版ブランチに対して変更する必要がある場合、 +開発者は\emph{安定版ブランチ}のリポジトリから複製し、 +変更、変更のテスト、コミットの後に、 +変更を安定版ブランチのリポジトリに反映します。 + +\interaction{branching.stable} + +Mercurial のリポジトリはお互いに(物理的に)独立しており、 +リポジトリ間での変更の自動的なやり取りは行われないため、 +安定版ブランチととメインブランはお互いに\emph{隔離}されています。 +メインブランチに加えた変更が安定版ブランチに``漏れ出す''ことはありませんし、 +その逆に関しても同様です。 + +安定版ブランチにおける全てのバグ修正を、 +メインブランチに反映したい場合もあるでしょう。 +メインブランチでバグ修正を再度(手動で)行う代わりに、 +安定版ブランチから取り込んだ変更をメインブランに対してマージすることで、 +安定版ブランチにおける変更をメインブランチに持ち込むことができます。 + +\interaction{branching.merge} + +この時点でのメインブランチは、 +安定版ブランチには無い変更を保持していますが、 +安定版ブランチにおける全てのバグ修正を保持しています。 +安定版ブランチは、 +メインブランチにのみ含まれる変更には影響を受けないままです。 + +\subsection{Feature branches} + +大規模プロジェクトで有効な変更管理方法は、 +開発チームを小さなグループに分割することです。 +プロジェクト全体が参照する単一の``マスター''ブランチから複製した共有ブランチ +(= リポジトリ)を、 +各グループごとにそれぞれ持ちます。 +個々のブランチ上で作業する開発メンバーは、 +他のブランチにおける開発作業とは隔離されています。 + +\begin{figure}[ht] + \centering + \grafix[width=\textwidth]{feature-branches} + \caption{Feature branches} + \label{fig:collab:feature-branches} +\end{figure} + +とある機能が適切な状況\footnote{訳注: +``コンパイルエラーが無くなった''状況なのか、 +``単体テストが完了した''状況なのかは、 +各プロジェクトの構成管理方針次第となります。}に到達したと判断されたなら、 +当該機能の担当チームでは、 +マスターブランチ(のリポジトリ) +から反映した変更を自チームのブランチ(のリポジトリ)へとマージしてから、 +マスターブランチへとマージ結果を反映すれば良いのです。 + +\subsection{The release train} + +プロジェクトによっては、 +``train''モデルで運用されている場合もあります。 +``train'' モデルで運用されているプロジェクトでは、 +リリースは数ヶ月ごとに設定されており、 +``train''が出発準備完了した段階\footnote{訳注: +「数ヶ月ごとに設定されたリリーススケジュール」を +「時刻表通りの発車時刻」に例えている模様。 +}で提供可能な機能だけがリリースに含まれます。 + +このモデルは、先に説明した機能別ブランチによる作業と似ています。 +``train''モデルの場合、 +機能別ブランチが列車に乗りそこなった場合、 +当該機能の担当チームでは、 +自チームの機能ブランチ(リポジトリ)に対して、 +リリース列車に含まれる変更の取り込みおよびマージを行い、 +マージ結果に対して作業を継続する必要がある点が異なります。 +このマージ作業を行うことで、 +次回リリースの際に、 +当該機能が整合性を保つことができます。 + +\subsection{The Linux kernel model} + +Linux カーネルの開発体制は、 +浅い階層構造と、それを取り囲む一見混沌とした群集から成り立っています。 +殆どの Linux 開発者が、 +Mercurial と同等の機能を持った分散構成管理ツールである +\command{git} コマンドを使用しているので、 +Linux カーネル開発における作業手順の説明は +Mercurial 利用にとっても有用性を持っています。 +気に入ったアイデアがあれば、 +ツールを超えて手法を利用することは可能なのですから。 + +コミュニティの中心には、 +Linux を創り出した Linus Torvalds 氏がいます。 +彼は単一のソースリポジトリを公開しており、 +開発コミュニティ全体にとっては、 +このリポジトリが``権威ある''現行ソースツリーとみなされます。 +誰もが Torvalds 氏のソースツリーを複製できますが、 +誰のツリーから変更を取り込むかという点に関して、 +Torvalds 氏は非常に慎重な選択をしています。 + +Torvalds 氏には``信頼できる補佐役''が何人かいます。 +彼ら補佐役が公開している変更は、 +レビューが行われていなくても、 +大概は Torvalds 氏により取り込まれます。 +補佐役のうちの何人かは、 +``保守担当者''として承認されており、 +カーネルの特定のサブシステムに関する責任を負っています +とあるカーネルハッカーがサブシステムへの変更を行い、 +その変更を最終的に Torvalds 氏のツリーに取り込んで欲しいと考えた場合、 +当該サブシステムの保守担当者が誰であるかを調べて、 +その担当者に変更の採用をお願いする必要があります。 +保守担当者が変更のレビューの後に採用に同意した場合、 +その変更は手順に従い Torvalds 氏へと渡されます。 + +個々の補佐役は、 +変更のレビュー・承認および公開に関するそれぞれの手法を持っており、 +Torvalds 氏への変更送付時期の判断に関しても、それは当てはまります。 +それに加えて、 +異なる目的向けの良く知られたブランチがいくつか存在します。 +例えば、 +古い版のカーネルの``安定版''リポジトリが、 +必要に応じて深刻な障害の修正を適用するために、 +少数の人々により保守されています。 +何人かの保守担当者は、 +複数のソースツリーを公開しています。 +1つは実験的な変更のためのもの、 +1つは上流リポジトリから配布しようとしている変更のためのもの、 +といった按配です。 +他の保守担当者は、ソースツリーを1つだけ公開しています。 + +Linux におけるこのモデルは、 +2つの注目に値する特徴を持っています。 +1つ目は``取り込み限定''(pull only)である点です。 +保守担当者以外が変更を反映できるソースツリーが殆ど無く、 +他の人が管理しているソースツリーに変更を反映する方法が無いことから、 +変更を反映させたいソースツリーの保守担当者に対して、 +変更の採用を\emph{お願い}する必要があります。 + +2つ目の特徴は、 +知名度と評判に基づいている点です。 +名の知られていない開発者からの変更依頼の場合、 +Torvalds 氏が依頼メールを受け取ったのなら、 +返信もせずに無視してしまうでしょう。 +しかし、 +サブシステムの保守担当者が依頼メールを受け取った場合、 +内容がレビューされた上で、 +それが保守担当者の基準を満たしていれば、 +おそらくその変更は採用されるでしょう。 +より``良い''変更で貢献する程、 +保守担当者はあなたの判断を信頼するでしょうし、 +あなたの変更依頼が受理度される度合いも増すでしょう。 +あなたが有名になり、 +Torvalds 氏がまだ受理していない息の長いブランチの保守を行うようになれば、 +あなたの作業内容に追従するために、 +似たような興味を持つ人々があなたの変更を定期的に取り込むようになるでしょう。 + +知名度や評判は、 +必ずしもサブシステムや``人的''境界を越えるわけではありません。 +専らストレージ系で著名なハッカーが、 +ネットワークのバグ修正を試みた場合、 +ネットワークサブシステムの保守担当者による監査は、 +全くの部外者による変更と同程度となるでしょう。 + +より整然としたプロジェクト従事の経験を持つ人にとって、 +相当に無秩序な Linux カーネルの開発手順は、 +全く非常識なものに見えることでしょう。 +この開発形態は、個人の気まぐれの影響を受けやすいのです。 +作業は各自の都合の良い時に、驚くべきペース行われます。 +それでもなお Linux は、 +成功を収めた重要なソフトウェアの1つとなっています。 + +\subsection{Pull-only versus shared-push collaboration} + +他の人のリポジトリからは変更の反映のみしかしないモデルと、 +複数の人々が共有リポジトリへの変更反映を行うことができる開発モデルの、 +どちらが``より良い''モデルであるかは、 +オープンソースコミュニティにおいて継続的な議論の的になっています。 +です。 + +共有リポジトリ+反映モデルの支持者は、 +その手法を積極的に使用するツールを使用する傾向にあります。 +Subversion のような中央集約的な構成管理ツールを使用している場合、 +採用するモデルの選択肢はありません。 +共有リポジトリ+反映モデルがツールによって強制されるため、 +他のモデルを使用するには、 +そのツール上で独自の手法(例えば、手動で \command{patch} +を宛てる、など)を駆使する必要があります。 + +Mercurial のような適切な分散構成管理ツールであれば、 +両方のモデルを選択可能です。 +利用者間の連携形態は、 +ツールにより強制される歪んだものではなく、 +固有の要望や好みに基づいて構築することができます。 + +\subsection{Where collaboration meets branch management} + +共有リポジトリを構築し、 +各作業者が手元のリポジトリと共有リポジトリとの間で、 +変更の伝播を開始し始めたなら、 +チーム内の開発の方向性を同時に複数管理するという、 +連携に関することではありつつも、 +微妙に異なる難問に直面することでしょう。 +この問題は開発チームの連携方式と密接に関連してはいるものの、 +改めて取り上げる価値があるほど非常に込み入った話であることから、 +\ref{chap:branch}~章で改めて説明します。 + +\section{The technical side of sharing} + +本章の残りは、 +共同作業者に対してデータの提供を行う上での問題点に割きたいと思います。 + +\section{Informal sharing with \hgcmd{serve}} +\label{sec:collab:serve} + +Mercurial の \hgcmd{serve} コマンドは、 +小さく緊密で足並みの早い集団での利用に大変適しています。 +\hgcmd{serve} コマンドはまた、 +ネットワーク越しでの Mercurial コマンドの利用感を掴むための、 +素晴らしい手段を提供しています。 + +リポジトリ配下において \hgcmd{serve} を実行することで、 +1秒も経たずに特製の HTTP サーバが起動します。 +実行が停止されるまでの間にこの HTTP サーバは、 +任意のクライアントからの接続を受理し、 +当該リポジトリ中のデータの提供を行います。 +たった今起動したばかりのサーバの URL を知っていて、 +ネットワーク越しにサーバが稼動しているコンピュータと通信できるなら、 +ウェブブラウザや Mercurial を利用して、 +誰もがリポジトリからデータを読み出すことができます。 +ノート PC 上で稼動する \hgcmd{serve} プロセスの URL は、 +\Verb|http://my-notepc.local:8000/| のような形式になります。 + +\hgcmd{serve} コマンドは汎用ウェブサーバでは\emph{ありません}。 +このコマンドを使用することで2つの事が可能になります。 + +\begin{itemize} +\item 一般的なウェブブラウザ経由でのサービス対象リポジトリの履歴の閲覧 + +\item Mercurial プロトコルによる通信を行うことで、 + リポジトリ内チェンジセットの \hgcmd{clone} ないし \hgcmd{pull} + +\end{itemize} + +とりわけ遠隔ユーザによる対象リポジトリの\emph{変更}を許可しないことから、 +\hgcmd{serve} は読み出し専用としての利用が想定されています。 + +Mercurial を既に利用し始めているのであれば、 +自身のコンピュータ上のリポジトリを対象として +\hgcmd{serve} を利用することができますから、 +ネットワーク越しに公開されているリポジトリの場合と同様に、 +\hgcmd{clone} や \hgcmd{incoming} +のようなコマンドを使用して、 +\hgcmd{serve} によって起動されたサーバと通信してみましょう。 +ネットワーク経由で公開されているリポジトリに対するコマンドの使用方法を、 +手早く習得する一助に \hgcmd{serve} を使用するのも良いでしょう。 + +\subsection{A few things to keep in mind} + +\hgcmd{serve} は、 +ネットワーク越しの読み出し操作を認証無しで全て許可しているため、 +対象リポジトリからデータを読み出すために誰が接続して来るのかを、 +気にしなくて良い(あるいは完全に制御できる)環境でのみ +\hgcmd{serve} を使うようにすべきです。 + +コンピュータやネットワークへのファイヤウォールの導入状況について、 +\hgcmd{serve} コマンドは一切関知しません。 +ファイヤウォールの検出も制御もできません。 +実行中の \hgcmd{serve} プロセスとの通信ができない場合は、 +(理商社が正しい URL を使用していることを確認した\emph{後}で) +ファイアウォールの設定を確認すべきです。 + +\hgcmd{serve} によるネットワーク接続の受け付けは、 +通常は 8000 番ポートで行われます。 +当該ポートが既に他のプロセスにより使用されていた場合は、 +\hgopt{serve}{-p} オプションを使用することで、 +接続受け付けポート番号を指定することができます。 + +\hgcmd{serve} 起動の際には通常何も出力されませんので、 +少々不安になるかもしれません。 +\hgcmd{serve} が適切に稼動していることを確認したり、 +共同作業者に送付する URL を知りたいのであれば、 +\hggopt{-v} オプション付きで \hgcmd{serve} を起動してください。 + +\section{Using the Secure Shell (ssh) protocol} +\label{sec:collab:ssh} + +Secure Shell (\texttt{ssh})プロトコルを使用することで、 +ネットワーク接続越しに安全に変更内容の取り込み・反映を行うことができます。 +この接続方法を正しく機能させるには、 +クライアントあるいはサーバ側で少々設定が必要かもしれません。 + +ssh に馴染みがないのであれば、 +他のコンピュータと安全に通信するためのネットワークプロトコルである、 +と理解しておいてください。 +Mercurial で ssh を利用するには、 +サーバへのログインおよびコマンド実行ができるように、 +サーバ側にユーザアカウントを(必要であれば複数)用意する必要があります。 + +(ssh について詳しい場合、 +以降の説明はおそらく非常に初歩的に感じるでしょう) + +\subsection{How to read and write ssh URLs} + +ssh プロトコルを利用する場合の URL は、 +概ね以下のような形式を持ちます。 + +\begin{codesample2} + ssh://bos@hg.serpentine.com:22/hg/hgbook +\end{codesample2} + +\begin{enumerate} +\item ``\texttt{ssh://}' 部分が Mercurial に ssh プロトコルの利用を指示します + +\item ``\texttt{bos@}''部分がサーバへのログインにおけるユーザ名を表します。 + サーバでのユーザ名がローカルマシン上のユーザ名と一致する場合は、 + この部分を省略できます。 + +\item ``\texttt{hg.serpentine.com}'' + 部分はログイン先サーバのホスト名を表します。 + +\item ``:22'' 部分はサーバに接続する際のポート番号を表します。 + ssh 接続における既定ポート番号は 22 番ですので、 + 22 番\emph{以外}のポートを使用する場合のみ指定が必要です。 + +\item URL の残りの部分はサーバ上におけるリポジトリのパスを表します。 + +\end{enumerate} + +ssh プロトコルにおける URL 表記のパス要素部分には、 +値の解釈に関する標準的な手法がないために、 +混乱の余地が多々あります。 +一群のプログラムは、 +パス要素部分に関して他のプログラムと異なる振る舞いをします。 +このような状況は理想的ではありませんが、 +状況が変わりそうにはありません。 +ですから以降の説明は注意深く読んでください。 + +Mercurial はパス部分を、 +サーバにログインするユーザの、 +ホームディレクトリに対する相対パスとみなします。 +例えば、 +サーバにおける \texttt{foo} ユーザのホームディレクトリが +\dirname{/home/foo} である場合、 +ssh プロトコルにおける URL のパス要素が \dirname{bar} であれば、 +その URL により\emph{実際に}参照されるのは +\dirname{/home/foo/bar} ディレクトリです。 + +他のユーザのホームディレクトリに対する相対パスを指定する場合は、 +チルダ文字(\texttt{~})にユーザ名(ここでは +\texttt{otheruser} とします)を続けたパスで始まる、 +以下のような表記になります。 + +\begin{codesample2} + ssh://server/~otheruser/hg/repo +\end{codesample2} + +\emph{絶対}パスによる指定を行う場合は、 +以下のようにパス要素をダブルスラッシュで始めます。 + +\begin{codesample2} + ssh://server//absolute/path +\end{codesample2} + +\subsection{Finding an ssh client for your system} + +殆ど全ての Unix ライクなシステムには +OpenSSH が事前導入されています。 +Unix ライクなシステムを使用している場合、 +\Verb|which ssh| と入力することで +\command{ssh} コマンド(通常は \dirname{/usr/bin} にインストールされています) +のインストールの有無を確認することができます。 +予想に反してインストールされていなかった場合には、 +システム添付のドキュメントを参照してインストール方法を調べてください。 + +Windows の場合、 +妥当な ssh クライアントを選択してダウンロードする必要があります。 +主な選択肢は2つあります。 + +\begin{itemize} +\item Simon Tatham 氏による PuTTY~\cite{web:putty} は、 + ssh クライアントコマンド一式を提供しています。 + +\item 面倒な事への耐性が高い方なら、 + Cygwin 上の OpenSSH を使うのも良いでしょう。 + +\end{itemize} + +どちらの場合でも、 +Mercurial が ssh クライアントコマンドを探し出せるように +\hgini\ ファイルを編集する必要があるでしょう。 +例えば PuTTY を使用するなら、 +コマンド行で実行する ssh クライアントとして +\command{plink} を実行することになります。 + +\begin{codesample2} + [ui] + ssh = C:/path/to/plink.exe -ssh -i "C:/path/to/my/private/key" +\end{codesample2} + +\begin{note} + \command{plink} へのパスが空白文字を含む場合、 + Mercurial は \command{plink} コマンドを正しく起動できません + (ですので \dirname{C:\\Program Files} にインストールするのは、 + よくありません)。 +\end{note} + +\subsection{Generating a key pair} + +ssh クライアントを使用する度に、 +毎回パスワード入力を繰り返さなくても良い様に、 +鍵対(key pair)\footnote{訳注: +「公開鍵」(public key)と +「秘密鍵」(private key)の対が生成されます。 +}を生成することをおすすめします。 +Unix ライクなシステム\footnote{訳注: Windows の Cygwin 環境含む}では、 +\command{ssh-keygen} コマンドで鍵対を生成します。 +Windows 上で PuTTY を使用している場合は、 +\command{puttygen} コマンドで鍵対を生成します。 + +鍵対を生成する場合、 +パスフレーズで鍵を守るようにするのが、 +一般には\emph{非常に}賢明とされています +(ssh プロトコルによる安全なネットワークを、 +自動化された処理において使用する場合を除く)。 + +しかし、単に鍵対を生成しただけでは不十分です。 +ネットワーク経由でログインするサーバ側アカウントにおいて、 +承認鍵一覧に公開鍵を追加登録する必要があります。 +OpenSSH が導入されているサーバでの公開鍵の追加は、 +当該アカウントの \sdirname{.ssh} ディレクトリ配下の +\sfilename{authorized\_keys} +ファイルに公開鍵の内容を追加することで行われます。 + +Unix ライク名システムでは、 +公開鍵は通常 \filename{.pub} 拡張子を持っています。 +Windows 上で \command{puttygen} を使用する場合は、 +任意のファイル名で保存可能ですし、 +公開鍵の内容が表示されているウィンドウから +\sfilename{authorized\_keys} へ直接貼り付け(paste)ることも可能です + +\subsection{Using an authentication agent} + +認証エージェントは、 +パスフレーズをメモリ上に格納するデーモンプロセスです +(そのため、ログアウト後に再度ログインした場合、 +パスフレーズは失われます)。 +認証エージェントの稼動を検知すると、 +ssh クライアントは認証エージェントにパスフレーズの問い合わせを行います。 +認証エージェントが稼動していないか、 +あるいは必要なパスフレーズを記憶していない場合は、 +Mercurial によるサーバ連携(例: \hgcmd{push} や \hgcmd{pull})の都度、 +パスフレーズの入力が必要です。 + +認証エージェントによるパスフレーズ保存の欠点は、 +入念に準備した攻撃者にとっては、 +たとえ定期的に再起動しているシステムであっても XXXXXX power-cycled XXXX +パスフレーズの平分を復元可能である点です。 +この問題が許容可能なものか否かは、各自で判断する必要があります。 +認証エージェントを使用することで、 +繰り返しパスフレーズを入力する手間を大幅に低減することができます。 + +Unix ライク名システムでは、 +認証エージェントは \command{ssh-agent} という名前で、 +\command{ssh-add} +コマンドを使ってエージェントの記憶領域にパスフレーズを保存します。 +Windows 上で PuTTY を使用する場合は、 +\command{pageant} コマンドが認証エージェントして振舞います。 +システムトレイに追加されたアイコンをクリックすることで、 +格納されたパスフレーズの管理を行うことができます。 + +\subsection{Configuring the server side properly} + +初心者にとって ssh の設定は面倒なので、 +問題が発生する状況も多岐に渡ります。 +Add Mercurial on top, and +there's plenty more scope for head-scratching. XXXXX +問題発生の可能性は、 +クライアント側ではなくサーバ側の方が高いです。 +ありがたいことに、 +一旦正しく動作する設定ができてしまえば、 +通常は無期限に正しく動作し続けます。 + +Mercurial で ssh サーバと通信をしてみる前に、 +通常の \command{ssh} ないし +\command{putty} コマンドによるサーバとの通信を確認するのが無難です。 +直接コマンドを使用した際に問題が発生したならば、 +Mercurial が機能しないことは確実です。 +更に悪いことに、 +Mercurial を介しての ssh サーバとの連携は、 +根本的な原因が隠れてしまいます。 +ssh に関連する Mercurial の問題を解決する場合は、 +Mercurial の不具合を疑う\emph{前に}、 +ssh クライアントコマンドの直接実行が機能することを確認してください。 + +サーバ側で最初に確認すべき事は、 +あるマシンからサーバマシンへの実際のログインの可否です。 +\command{ssh} ないし \command{putty} でログインできない場合、 +表示されるエラーメッセージから問題特定のヒントが得られるかもしれません。 +よくある問題には以下のようなものがあります。 + +\begin{itemize} +\item ``connection refused'' が表示される場合は、 + ssh サーバプロセスが起動されていないか、 + ファイヤーウォール設定によりネットワーク接続できないことが原因です。 + +\item ``no route to host'' が表示される場合は、 + 接続先のサーバアドレスが間違っているか、 + ファイヤーウォールによって接続が厳重に禁止されていることが原因です。 + +\item ``permission denied'' が表示される場合は、 + サーバ接続の際のユーザ名、パスフレーズ、 + ないしサーバ側ユーザのパスワードの入力を間違えていることが原因です。 + +\end{itemize} + +これまでの話をまとめると、 +サーバマシン上の ssh サーバプロセスとの通信に問題がある場合、 +まずはサーバプロセスの稼動状況を確認してください。 +多くのシステムでは、 +ssh 自体はインストールされていますが、 +初期状態では無効化されている場合があります。 +この確認が済んだなら、 +次に確認するのは、 +ssh サーバプロセスが外部からの接続を受け付けるポート(通常は 22 番) +に対する外部からの接続を、 +サーバのファイヤーウォール設定が許可しているか否かです。 +これら2つの確認を済ませるまでは、 +突拍子もない設定ミスの可能性に関して心配する必要はありません。 + +秘密鍵用パスフレーズの保持のために、 +クライアント側で認証エージェントを使用している場合は、 +パスフレーズやパスワードの問い合わせを受ける事無く、 +サーバにログインできていなければなりません。 +パスフレーズを問い合わせるプロンプトが表示される場合、 +問題の可能性のあるものが幾つかあります。 + +\begin{itemize} +\item \command{ssh-add} ないし \command{pageant} + によるパスフレーズの格納を忘れているのかもしれません。 + +\item 想定しているものとは別な鍵のパスフレーズを格納しているのかもしれません。 + +\end{itemize} + +サーバ側ユーザのパスワードの問い合わせがあった場合、 +別な問題の可能性を検討する必要があります。 + +\begin{itemize} +\item サーバ側ユーザの、ホームディレクトリないし + \sdirname{.ssh} ディレクトリの権限設定が、 + 過度に緩く設定されているのかもしれません。 + ssh サーバプロセスはその場合、 + \sfilename{authorized\_keys} + ファイルの信頼性が低いものとして読み込みを行いません。 + 例えば、 + ホームディレクトリないし \sdirname{.ssh} ディレクトリが、 + グループに対する書き込み権限を設定されている場合、 + パスワード問い合わせが行われる、といった症状が見られます。 + +\item \sfilename{authorized\_keys} + ファイルそのものに問題がある可能性もあります。 + このファイルへの書き込み権限が所有者以外にも設定されている場合、 + ssh サーバプロセスはファイルの信頼性が低いものとして読み込みを行いません。 + +\end{itemize} + +以下のコマンド実行に対して、 +(サーバ側の)現在時刻を表示する1行だけが出力される、 +という状態が理想的です。 + +\begin{codesample2} + ssh myserver date +\end{codesample2} + +上記のような非対話的なコマンド実行の場合にも、 +バナー表示やそれに類する表示が行われるような設定が、 +連携先サーバ側で行われている場合には、 +この先の手順に進む前に、 +対話的な実行\footnote{訳注: 「ssh によるログイン時」の意}の時にのみ、 +これらが表示されるように設定変更してください。 +これを怠ると、 +バナー等の表示が Mercurial の出力を混乱させてしまいます。 +更に問題なことに、 +バナー等の表示は Mercurial コマンドの遠隔実行における潜在的な問題と成り得ます。 +非対話的な \command{ssh} 連携において、 +Mercurial は極力バナー等の表示の検知ならびに無視に努めますが、 +必ずしも全てが無視できるわけではありません +(サーバ側でログイン時実行スクリプトをカスタマイズする場合、 +\Verb|tty -s| コマンドの戻り値を判定することで、 +当該スクリプトが現在対話シェルで実行されているか否かを判定することができます) +\footnote{訳注: ログインスクリプトでの出力以外でも、 +フック実行時に標準出力に対して何らかの表示があった場合、 +Mercurial は「連携における想定外のデータ授受」とみなすため、 +注意が必要です。}。 + +素の ssh によるサーバ連携が機能することを確認したならば、 +次に確認するのは、 +サーバ側での Mercurial 実行の可否です。 +以下のコマンド実行が正しく機能することを確認してください。 + +\begin{codesample2} + ssh myserver hg version +\end{codesample2} + +通常の \hgcmd{version} 出力ではなくエラーメッセージが表示される場合、 +大概は \dirname{/usr/bin} に +Mercurial がインストールされていないことが原因です。 +その場合でも、 +必ずしも \dirname{/usr/bin} にインストールする必要はありません。 +しかし、考え得る以下の幾つかの原因に関して確認が必要です。 + +\begin{itemize} +\item Mercurial は本当にサーバにインストールされていますか? + 変な質問と思われるかもしれませんが、これは非常に重要な確認事項です。 + +\item シェルのコマンドサーチパス(通常は \envar{PATH} 環境変数で設定) + の設定が単に不適切なのかもしれません。 + +\item ひょっとしたら、\envar{PATH} 環境変数が + \command{hg} + コマンドの格納場所を指すように設定されるのは対話的なログイン時にのみ、 + という可能性もあります。 + \envar{PATH} 環境変数の設定を不適当な起動スクリプトで行っている場合に、 + このような現象が発生します。 + 各自の使用しているシェルのドキュメントを確認してみましょう\footnote{訳注: + 例えば bash の場合、対話的ログインか否かで + \sfilename{.bashrc}、 + \sfilename{.bash\_profile}、 + \sfilename{.profile} および + \sfilename{.login} といった各ファイルの読み込みの有無が変化します。 + また、ディストリビューションによっては、 + 非対話的な実行の際には、 + \dirname{/etc/bashrc} による + \dirname{/etc/profile.d} + 配下の設定ファイル読み込みが行われない場合があります + (2.6.x 系カーネルベースのものは読み込まない方針の模様)ので、 + \envar{PYTHONPATH} の件も含めて、 + システムワイドな設定を行う方は注意が必要です。 + \Verb|ssh myserver env| + 実行で出力される環境変数一覧を確認してみましょう。 + }。 + +\item \envar{PYTHONPATH} 環境変数による + Mercurial の Python + モジュール格納ディレクトリの参照が必要であるケースもあります。 + 不適切な設定だったり、対話的ログイン時にのみ設定されている可能性があります。 + +\end{itemize} + +ssh 経由での \hgcmd{version} コマンド実行が成功したなら準備は完了です。 +サーバ・クライアントは共に問題解決済みとなりました。 +サーバ上で公開されている リポジトリに、 +当該ユーザ名による Mercurial でのアクセスが可能になっている筈です。 +ここまでの確認をクリアした上で、 +Mercurial と ssh の連携において問題が発生した場合、 +問題発生の状況をより明確にするために、 +\hggopt{--debug} オプションを付けての実行を試してみてください。 + +\subsection{Using compression with ssh} + +ssh プロトコルを使用する場合、 +ssh プロトコル自身が通信時にデータ圧縮を行うため、 +Mercurial は圧縮を行いません。 +しかし、ssh クライアントの(通常の)基底動作では、 +圧縮を\emph{行いません}。 + +高速な LAN の場合を除けば(無線ネットワークであっても)、 +通信時の圧縮は Mercurial +のネットワーク経由の処理を顕著に高速化します。 +例えば WAN 経由での連携の場合、 +かなり大きなリポジトリの複製に要する時間が 51 分から 17 分に低減した、 +との性能計測報告もあります。 + +\command{ssh} と \command{plink} の両方とも、 +通信時圧縮を有効化する +\cmdopt{ssh}{-C} オプションを受け付けます。 +\hgrc\ ファイルを以下のように編集することで、 +ssh プロトコル利用の際に常に圧縮を行うように Mercurial に対して指定できます。 + +\begin{codesample2} + [ui] + ssh = ssh -C +\end{codesample2} + +\command{ssh} を使用している場合は、 +連携先サーバとの通信の際には常に圧縮を行うように設定することもできます。 +この設定を行うには、 +ホームディレクトリ配下の +\sfilename{.ssh/config} ファイル +(無い場合は新規に作成します)に以下のように記述します。 + +\begin{codesample2} + Host hg + Compression yes + HostName hg.example.com +\end{codesample2} + +上記の記述は、 +\texttt{hg} という別名(alias)を作成します。 +\command{ssh} 実行の際のコマンド行記述や、 +Mercurial の \texttt{ssh} プロトコルにおける URL として、 +\texttt{hg} を(ホスト名として)使用した場合、 +\command{ssh} は通信時圧縮を行いつつ \texttt{hg.example.com} に接続します。 +この設定により、 +入力の便利な省略名と、圧縮指定の両方を手にすることができます。 + +\section{Serving over HTTP using CGI} +\label{sec:collab:cgi} + +意気込み次第では、 +Mercurial の CGI インタフェースの設定は、 +数分のものを数時間にしてしまう可能性があります。 + +最も単純な例から初めて、 +より複雑な設定へと向けて進めてゆきましょう。 +最も基本的なケースですら、 +ウェブサーバの設定ファウルの読み書きを行う必要が出てくることでしょう。 + +\begin{note} + ウェブサーバの設定は複雑で、扱いにくく、且つシステム依存性の高い作業です。 + そのため本書では、 + 発生するであろう問題のケースを全て網羅するような手順を示すことができません。 + 以降の記述は、慎重さと各自の判断をもって読み進めるようにしてください。 + 沢山間違えたり、サーバのエラーログ解析に時間を費やす覚悟が必要でしょう。 +\end{note} + +\subsection{Web server configuration checklist} + +読み進める前に、 +システムの設定状況に関する幾つかの確認を行いましょう。 + +\begin{enumerate} +\item ウェブサーバはインストールされていますか? + Mac OS X は Apache がインストールされた状態で出荷されますが、 + 多くのシステムではウェブサーバはインストールされていません。 + +\item ウェブサーバがインストールされている場合、 + それは実際に稼動していますか? + ウェブサーバがインストールされていた場合でも、 + 多くのシステムの基底状態は、ウェブサーバが無効化されています。 + +\item CGI を稼動させようとしているディレクトリは、 + ウェブサーバの設定で CGI の実行が許可されていますか? + 多くのウェブサーバの基底状態は、 + CGI プログラムの実行機能が明示的に無効化されています。 + +\end{enumerate} + +ウェブサーバがインストールされていない場合や、 +Apache ウェブサーバの設定経験があまり無い場合には、 +Apache ウェブサーバの代わりに +\texttt{lighttpd} ウェブサーバの利用をお薦めします。 +Apache ウェブサーバの設定は、 +凝っていて且つわかりにくいという評判に見合うものがあります。 +\texttt{lighttpd} は Apache ウェブサーバ程の機能は無いものの、 +足りない機能の殆どが Mercurial リポジトリの運用には関係ないものです。 +それに加えて、 +明らかに \texttt{lighttpd} は +Apache ウェブサーバよりも簡単に利用が開始できます。 + +\subsection{Basic CGI configuration} + +Unix 的なシステムを利用している場合、 +ウェブページとして公開するための +\dirname{public\_html} のようなディレクトリを、 +ホームディレクトリ配下に持つのが共通認識となっています。 +このディレクトリ直下に置いた +\filename{foo} という名前のファイルは、 +\texttt{http://www.example.com/\~username/foo} という +URL で参照可能になります。 + +設定を始めるに当たって、 +Mercurial のインストール先に格納されている +\sfilename{hgweb.cgi} スクリプトの所在を確認してください。 +システム上の所在がすぐにはわからなかった場合は、 +Mercurial のマスターリポジトリから +\url{http://www.selenic.com/repo/hg/raw-file/tip/hgweb.cgi} +を直接ダウンロードしてください。 + +上記スクリプトを +\dirname{public\_html} 配下に配置し、 +実行可能となるように権限設定を行います。 + +\begin{codesample2} + cp .../hgweb.cgi ~/public_html + chmod 755 ~/public_html/hgweb.cgi +\end{codesample2} + +\command{chmod} コマンドへの \texttt{755} 引数指定は、 +スクリプトに実行可能権限を付与する以上の付加的な指定を意味します。 +この設定により、スクリプトが誰からも実行可能になると同時に、 +``group'' および ``other'' による書き込み権限が\emph{剥奪}されます。 +これらの書き込み権限を有効なままにした場合、 +Apache の \texttt{suexec} サブシステムは、 +おそらくスクリプトの実行を拒否するでしょう。 +実のところ \texttt{suexec} は、 +スクリプトが配置されている\emph{ディレクトリ}に対する +``group'' および ``other'' による書き込み権限が剥奪されていることも要求します。 + +\begin{codesample2} + chmod 755 ~/public_html +\end{codesample2} + +\subsubsection{What could \emph{possibly} go wrong?} +\label{sec:collab:wtf} + +CGI を配置したならば、 +ウェブブラウザを起動して +\url{http://myhostname/~myuser/hgweb.cgi} に相当する +URL にアクセスしてみましょう。 +但し、ちょっとした失敗には\emph{身構えておいてください}。 +所望の URL へのアクセスが失敗する公算は非常に高く、 +その理由は多岐に渡ります。 +実際のところ、 +以下の起こり得るエラー要因の全てで躓く可能性がありますから、 +この先は注意深く読み進めてください。 +以下で述べる問題は、 +まっさらな状態からインストールした Apache を使い、 +この実例を行うために新たに生成したユーザアカウントで、 +Fedora~7 上で作業を実施した際に、 +筆者が実際に直面した全ての問題です。 + +使用しているウェブサーバは、 +ユーザ毎のディレクトリを無効化しているかもしれません。 +Apache を使用している場合は、 +設定ファイル中に \texttt{UserDir} 指定の有無を確認してください。 +この指定が無い場合、ユーザ毎ディレクトリは無効になります。 +指定が有っても\texttt{無効化されている}場合も、 +ユーザ毎ディレクトリは無効になります。 +有効な \texttt{UserDir} 指定がある場合、 +\texttt{UserDir} 指定で記述されている文字列 +(例えば \dirname{public\_html})が、 +ホームディレクトリ直下で Apache が参照するサブディレクトリ名になります。 + +ファイルのアクセス権限が厳しすぎる可能性もあります。 +ウェブサーバは、 +対象となるユーザのホームディレクトリ、 +および \dirname{public\_html} +配下のファイル・ディレクトリの読み込みができなければなりません。 +適切な権限設定を行うための簡単な手順を以下に示します。 + +\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} + +権限設定に関する他の要因の可能性がある場合は、 +ブラウザでの所望の URL アクセス時に、 +完全に空の画面が表示されることでしょう。 +この場合は、おそらくアクセス権限が\emph{緩すぎる}のでしょう。 +例えば Apache の \texttt{suexec} サブシステムは、 +group ないし other に書き込み権限が付与されたスクリプトは実行しません。 + +使用しているウェブサーバが、 +ユーザ毎ディレクトリ配下の CGI プログラムの実行を、 +禁止するように設定されている可能性も有ります。 +筆者の Fedora~7 システムにおける Apache の、 +初期状態のユーザ毎設定を以下に示します。 + +\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} + +対象となる Apache 設定ファイル中に似たような +\texttt{Directory} 設定がある場合、 +\texttt{Options} 指定に注目してください。 +\texttt{ExecCGI} が指定されていない場合は一覧末尾にこれを追加し、 +ウェブサーバを再起動してください。 + +Apache が CGI を実行するのではなく、 +CGI スクリプトの内容そのものを返却してきた場合は、 +以下の記述を(既に記述があるならば)有効化するなり追加するなりしてください。 + +\begin{codesample2} + AddHandler cgi-script .cgi +\end{codesample2} + +次に問題の発生し得るケースでは、 +Python のバックトレースが表示され、 +\texttt{mercurial} 関連モジュールがインポート +(import)できない旨を伝えていることでしょう。 +所望の結果は得られていませんが、 +ウェブサーバは CGI スクリプトの実行を行うようになったので、 +先程の状態からは前進しています! +インポートができない旨のエラーは、 +システムワイドで利用可能な Mercurial ではなく、 +おそらく個人的にインストールした Mercurial +を実行している場合にのみ発生します。 +ウェブサーバが CGI プログラムを実行する場合、 +各個人の対話的ログインセッションで実施されている環境変数指定が無い、 +ということを忘れないでください。 +このエラーが発生した場合は、 +\envar{PYTHONPATH} 環境変数設定が適切になるように +\sfilename{hgweb.cgi} の記述を編集してください。 + +最終的に、 +\dirname{/path/to/repository} が見つからない旨を伝える +Python のバックトレースが\emph{確実に}表示されることでしょう。 +\sfilename{hgweb.cgi} スクリプトを編集して、 +文字列 \dirname{/path/to/repository} +を実際に公開したいリポジトリへの絶対パスで置き換えてください。 + +ここまで来れば、 +ウェブブラウザでページをリロードした際に、 +綺麗に HTML で整形されたリポジトリ履歴の表示を見ることができる筈です。 +お疲れ様です。 + +\subsubsection{Configuring lighttpd} + +徹底的に実験するために、 +これまで Apache に関して説明したのと同様に、 +近年人気が高まっている \texttt{lighttpd} ウェブサーバで、 +同じリポジトリを公開するための設定記述に挑戦してみました。 +Apache についてこれまで概説してきた全ての問題は既に克服済みですし、 +その殆どはウェブサーバ実装に依存しません。 +結果として、 +ファイル・ディレクトリの権限設定が妥当であることと、 +\sfilename{hgweb.cgi} スクリプトが適切に改変済みであることは、 +ある程度確信できます。 + +一旦 Apache での公開に成功していれば、 +\texttt{lighttpd} でのリポジトリ公開は簡単 +(言い換えるなら、 +\texttt{lighttpd} を使用する場合でも、 +前述の Apache に関する説明を読むべきと言えます +)です。 +初期状態で +\texttt{mod\_cgi} および \texttt{mod\_userdir} が無効化されていた場合、 +これらを有効化するために、 +まずは、 +設定ファイルの \texttt{mod\_access} セクションを編集する必要があります。 +その後、これらのモジュールを設定するために、 +設定ファイル末尾に数行ほど追加します。 + +\begin{codesample2} + userdir.path = "public_html" + cgi.assign = ( ".cgi" => "" ) +\end{codesample2} + +この記述により、 +\texttt{lighttpd} はユーザ毎のディレクトリおよび CGI を認識します。 +Apache よりも前に +\texttt{lighttpd} の設定をしたとしたら、 +殆ど間違いなく、 +Apache +の設定の際に経験したのと同じシステムレベルの設定ミスを犯したことでしょう。 +しかし +Apache の使用経験が10年以上あり、 +且つ初めての \texttt{lighttpd} 使用ではあるものの、 +Apache の設定よりも \texttt{lighttpd} のそれは著しく容易であると思われます。 + +\subsection{Sharing multiple repositories with one CGI script} + +単一のリポジトリのみしか公開できないというのは、 +\sfilename{hgweb.cgi} スクリプトの悩ましい制約です。 +同じスクリプト\footnote{訳注: 厳密には、 +公開対象リポジトリのパスが異なるのですが、 +概ね「同じ」と言って良いでしょう。 +}を異なる名前で複製する、 +という面倒な方法よりは、 +\sfilename{hgwebdir.cgi} スクリプトの使用がお薦めです。 + +\sfilename{hgwebdir.cgi} の設定手順は、 +\sfilename{hgweb.cgi} よりも多少込み入っています。 +まず始めに +スクリプトのコピーを入手します。 +手近に無い場合は +Mercurial のマスターリポジトリから +\url{http://www.selenic.com/repo/hg/raw-file/tip/hgwebdir.cgi} +を直接ダウンロードしてください。 + +\dirname{public\_html} 配下に上記スクリプトを配置し、 +実行可能となるように権限設定を行います。 + +\begin{codesample2} + cp .../hgwebdir.cgi ~/public_html + chmod 755 ~/public_html ~/public_html/hgwebdir.cgi +\end{codesample2} + +基本的な設定が済んだなら、 +ブラウザで \url{http://myhostname/~myuser/hgwebdir.cgi} +にアクセスしてみましょう。 +空のリポジトリリストが表示される筈です。 +何も表示されないか、エラーメッセージが表示される場合は、 +\ref{sec:collab:wtf}~節で説明した潜在的問題一覧を一通り確認してください。 + +\sfilename{hgwebdir.cgi} スクリプトは外部設定ファイルを必要とします。 +基底状態の +\sfilename{hgwebdir.cgi} スクリプトは、 +自身と同じディレクトリに格納された +\sfilename{hgweb.config} ファイルを読み込もうとします。 +このファイルを生成し、 +誰に対しても読み出し権限を付与しなければなりません。 +このファイルの記述形式は、 +Windows における ``ini'' ファイルのそれと同じで、 +Python の +\texttt{ConfigParser}~\cite{web:configparser} +により解析可能な形式です。 + +最も簡単に \sfilename{hgwebdir.cgi} を設定するには、 +\texttt{collections} という名前のセクションを設定してください。 +このセクションを記述することで、 +名付けたディレクトリ配下の\emph{全ての}リポジトリを自動的に公開します。 +このセクションの記述は以下のようになります。 + +\begin{codesample2} + [collections] + /my/root = /my/root +\end{codesample2} + +Mercurial はこの記述を解釈するに当たり、 +``\texttt{=}'' +記号の\emph{右辺}に記述されたディレクトリ階層下でリポジトリを探し、 +``\texttt{=}'' 記号の\emph{左辺}のテキストに合致する部分を、 +ウェブインタフェースでの一覧表示で実際に公開される名前から除外します。 +除外処理の後に残ったパス要素は、``仮想パス''と呼ばれます。 + +例として +\dirname{/my/root/this/repo} にリポジトリがあるとした場合、 +CGI スクリプトは冒頭の +\dirname{/my/root} 部分を名前から除外し、 +仮想パスとして \dirname{this/repo} を持つリポジトリとして公開します。 +CGI スクリプトの基底 URL を +\url{http://myhostname/~myuser/hgwebdir.cgi} とすると、 +このリポジトリの完全な URL は、 +\url{http://myhostname/~myuser/hgwebdir.cgi/this/repo} となります。 + +この設定記述例での左辺を \dirname{/my/root} から +\dirname{/my} に変更した場合、 +\sfilename{hgwebdir.cgi} はリポジトリ名から +\dirname{/my} のみをzy歩外するので、 +仮想パスは \dirname{this/repo} ではなく +\dirname{root/this/repo} となります。 + +\sfilename{hgwebdir.cgi} は、 +設定ファイル中の \texttt{collections} +セクションで列挙された個々のディレクトリに対して、 +再帰的にリポジトリを探しますが、 +見つかったリポジトリから更に下への再帰的探索は\texttt{行いません}。 + +\texttt{collections} の機構は、 +多くのリポジトリを``fire and forget''作法で公開するのに適しています。 +CGI や設定ファイルの記述は一度で事足ります。 +設定が済んだなら、 +\sfilename{hgwebdir.cgi} +に探索を指示したディレクトリ階層配下との間でリポジトリの移動を行うだけで、 +リポジトリの公開・非公開を任意の時点で行うことができます。 + +\subsubsection{Explicitly specifying which repositories to publish} + +\sfilename{hgwebdir.cgi} スクリプトは +\texttt{collections} による公開の仕組みに加えて、 +特定の一覧指定によるリポジトリ公開をすることもできます。 +この方法での公開をするには、 +以下のような形式の内容を持つ +\texttt{paths} セクションを記述する必要があります。 + +\begin{codesample2} + [paths] + repo1 = /my/path/to/some/repo + repo2 = /some/path/to/another +\end{codesample2} + +上記の例では、個々の定義の左辺が仮想パス(URL 中に現れるパス要素)、 +右辺がリポジトリへのパスとなります。 +仮想パスの指定と、 +ファイルシステム上のリポジトリ位置には、 +何の関連性も無い点に注意してください。 + +単一の設定ファイル中で +\texttt{collections} と +\texttt{paths} の両方を同時に使用することも可能です。 + +\begin{note} + 同一の仮想パスに複数のリポジトリが関連付けられている場合、 + \sfilename{hgwebdir.cgi} はエラーを通知しません。 + その代わりに、 + \sfilename{hgwebdir.cgi} の振る舞いは予想できないものとなります。 +\end{note} + +\subsection{Downloading source archives} + +Mercurial のウェブインタフェース経由で、 +任意のリビジョンのアーカイブをダウンロードすることが可能です。 +このアーカイブには、 +当該リビジョンにおける作業領域ディレクトリのスナップショットが格納されますが、 +リポジトリデータ部分は含まれません。 + +この機能は既定状態では無効化されています。 +この機能を有効化するには、 +\rcitem{web}{allow\_archive} 項目を +\hgrc ファイルの \rcsection{web} セクションに追加してください\footnote{訳注: +このことから、 +アーカイブダウンロードの有効化・無効化設定が、 +\sfilename{hgwebdir.cgi} 単位ではなく、 +リポジトリ単位での設定であることがわかります。}。 + +\subsection{Web configuration options} + +Mercurial のウェブインタフェース +(\hgcmd{serve} コマンドおよび +\sfilename{hgweb.cgi} ないし \sfilename{hgwebdir.cgi} スクリプト) +には変更可能な設定項目が多数あります。 +これらの設定項目は +\rcsection{web} セクションに属しています。 + +\begin{description} + +\item[\rcitem{web}{allow\_archive}] + Mercurial のアーカイブダウンロード機能を有効化するか否かを指定。 + この機能を有効化した場合ウェブインタフェースの利用者は、 + リポジトリ中の参照可能な任意のリビジョンのアーカイブをダウンロードできます。 + この機能を有効化するには、 + 以下に列挙されるキーワードの並びを + \rcitem{web}{allow\_archive} 項目に指定する必要があります。 + + \begin{description} + \item[\texttt{bz2}] \texttt{bzip2} 圧縮された \command{tar} アーカイブ形式。 + この形式は最も高い圧縮率を得られますが、 + サーバ側の CPU を最も酷使します。 + + \item[\texttt{gz}] \texttt{gzip} 圧縮された \command{tar} アーカイブ形式。 + + \item[\texttt{zip}] LZW 圧縮された \command{zip} アーカイブ形式。 + この形式は圧縮率が最も劣りますが、Windows 環境では広く使用されています。 + + \end{description} + + 値を指定しなかったり、 + \rcitem{web}{allow\_archive} 項目そのものを指定しなかった場合、 + アーカイブダウンロード機能は無効化されます。 + 利用可能な全てのアーカイブ形式を有効化する記述例を以下に示します。 + + \begin{codesample4} + [web] + allow_archive = bz2 gz zip + \end{codesample4} + +\item[\rcitem{web}{allowpull}] + ウェブインタフェース経由での HTTP 越しの + \hgcmd{pull} および \hgcmd{clone} を許可するか否かを指定する真偽値。 + \texttt{no} ないし \texttt{false} が指定された場合、 + ウェブインタフェースの``人間向け''部分のみが有効化されます。 + +\item[\rcitem{web}{contact}] + リポジトリの管理を行う人物・組織を特定するための任意の + (但し極力簡潔な)文字列。 + 通常この値は、管理者ないしメーリングリストの名前と電子メールアドレスです。 + 多くの場合、 + この情報はリポジトリ毎の \sfilename{.hg/hgrc} ファイルに記述しますが、 + 全てのリポジトリが同一の保守担当により保守されている場合、 + 大域的な \hgrc ファイルに記述するのも良いでしょう。 + +\item[\rcitem{web}{maxchanges}] + ページ毎に表示されるチェンジセットの最大数(既定値)を表す数値。 + +\item[\rcitem{web}{maxfiles}] + ページ毎に表示される変更ファイルの最大数(既定値)を表す数値。 + +\item[\rcitem{web}{stripes}] + テーブル表示における可読性向上のために、 + 各行の色を互い違いに``縞模様''とする際に、 + 何行毎に色を変更するかの数値。 + +\item[\rcitem{web}{style}] + Mercurial がウェブインタフェースを表示する際に使用するテンプレート。 + Mercurial は \texttt{default} および + \texttt{gitweb} の2つのウェブインタフェース用テンプレートを同梱しています + (後者の方が見栄えが良いです)。 + 自前でカスタマイズしたテンプレートを指定することもできます。 + 詳細は\ref{chap:template}~節を参照してください。 + \texttt{gitweb} スタイルの利用方法を以下に示します。 + + \begin{codesample4} + [web] + style = gitweb + \end{codesample4} + +\item[\rcitem{web}{templates}] + テンプレートファイルの参照先ディレクトリを示すパス。 + Mercurial の既定値では、インストール先ディレクトリを参照します。 + +\end{description} + +\sfilename{hgwebdir.cgi} を使用する場合、 +幾つかの設定項目に関しては利便性上、 +\hgrc ファイルに記述する代わりに、 +\sfilename{hgweb.config} ファイルの +\rcsection{web} セクションに記述することができます。 +記述可能な設定項目は、 +\rcitem{web}{motd} および \rcitem{web}{style} です。 + +\subsubsection{Options specific to an individual repository} + +ユーザ毎ないし大域的な \hgrc ファイルではなく、 +リポジトリ毎の \sfilename{.hg/hgrc} で記述すべき +\rcsection{web} セクションの設定項目が幾つかあります。 + +\begin{description} +\item[\rcitem{web}{description}] + リポジトリの内容ないし目的を記述した任意の + (但し極力簡潔な)文字列。 + +\item[\rcitem{web}{name}] + ウェブインタフェースにおけるリポジトリ参照名を示す文字列。 + この値は、 + リポジトリのパス\footnote{訳注: 仮想パス? + }の末尾要素を用いた既定名を上書きします。 + +\end{description} + +\subsubsection{Options specific to the \hgcmd{serve} command} + +\hgrc ファイルの +\rcsection{web} セクションにおける設定項目の幾つかは、 +\hgcmd{serve} コマンド専用の項目です。 + +\begin{description} + +\item[\rcitem{web}{accesslog}] + アクセスログを書き出すファイルのパス。 + \hgcmd{serve} コマンドの基底動作でのアクセスログ出力先は、 + ファイルではなく標準出力です。 + ログ要素は、 + 多くのウェブサーバにおいて利用される標準的な``複合''(combined) + ファイル形式で出力されます。 + +\item[\rcitem{web}{address}] + 外部からの接続を受け付けるアドレスを指定する文字列。 + 基底動作では、\hgcmd{serve} コマンドは全てのアドレスで接続を受け付けます。 + +\item[\rcitem{web}{errorlog}] + エラーログを書き出すファイルのパス。 + \hgcmd{serve} コマンドの基底動作でのエラーログ出力先は、 + ファイルではなく標準エラー出力です。 + +\item[\rcitem{web}{ipv6}] + IPv6 プロトコル利用の有無を指定する真偽値。 + 基底動作では IPv6 はサポートされません。 + +\item[\rcitem{web}{port}] + \hgcmd{serve} コマンドが接続を受け付ける TCP ポートの番号を指定する数値。 + 基底動作では、8000 番ポートが使用されます。 + +\end{description} + +\subsubsection{Choosing the right \hgrc\ file to add \rcsection{web} + items to} + +Apache や \texttt{lighttpd} のようなウェブサーバは、 +リポジトリ所有者とは異なるユーザ権限で稼動する可能性がある、 +という点は重要ですので忘れないようにしてください。 +ウェブサーバによって起動される +\sfilename{hgweb.cgi} のような +CGI スクリプトは通常、 +ウェブサーバと同一のユーザ権限で稼動します。 + +個人の \hgrc ファイルに +\rcsection{web} セクションを記述しても、 +CGI スクリプトはその設定を読み込みません。 +個人の \hgrc ファイルに記述した設定は、 +当該ユーザ自身で \hgcmd{serve} +コマンドを実行した場合にのみ効力を発揮します。 +CGI スクリプトの挙動に所望の設定を反映するには、 +ウェブサーバが稼動される際のユーザのホームディレクトリに +\hgrc ファイルを作成して所望の設定を記述するか、 +あるいはシステムワイドな \hgrc ファイルに所望の設定を追加してください。 + + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/concepts.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/concepts.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,764 @@ +\chapter{Behind the scenes} +\label{chap:concepts} + +多くの構成管理システムと異なり、 +Mercurial が基にしている概念は非常に単純なので、 +Mercurial のプログラムが実際にどのように動作するのかを理解するのは簡単です。 +そのような知識は必要無いかもしれませんが、 +筆者は内情に関する``概念理解''が有用であると考えています。 + +筆者自身は、内情を理解することで、 +Mercurial が\emph{安全性}と\emph{効率}に留意して設計されている、 +という確信を得ることができました。 +また、 +構成管理操作を行った際にソフトウェアがどのように機能するのかを、 +容易に覚えておけるのであれば、 +構成管理ツールの振る舞いに驚かされる機会が減る、 +という点も非常に重要です。 + +この章では、 +最初に +Mercurial の設計における中核的な概念について説明した上で、 +実装における興味深い点に関する詳細を幾つか取り上げようと思います。 + +\section{Mercurial's historical record} + +\subsection{Tracking the history of a single file} + +ファイルの変更を追跡する場合、 +Mercurial はファイルの履歴を +\emph{filelog} と呼ばれるメタデータオブジェクト形式で保存します。 +filelog に記録される個々の要素は、 +追跡対象ファイルの、 +とあるリビジョンを再現するのに十分な情報を保持しています。 +filelog は +\sdirname{.hg/store/data} ディレクトリ配下にファイルとして保存されており、 +履歴情報と、 +Mercurial のリビジョン検索を補助するインデックスの、 +2種類の情報を保持しています。 + +サイズが大きかったり変更履歴の多いファイルの場合、 +filelog を履歴情報(拡張子 ``\texttt{.d}'') +とインデックス(拡張子 ``\texttt{.i}'')の2つに分離して保存されます。 +変更履歴がそれほど無い小さなファイルの場合、 +履歴情報とインデックスは +``\texttt{.i}'' 拡張子を持つ単一のファイルに保存されます。 +作業領域ディレクトリ中のファイルと、 +その変更履歴を追跡するためのリポジトリ中の filelog ファイルの対応を、 +図~\ref{fig:concepts:filelog}に示します。 + +\begin{figure}[ht] + \centering + \grafix{filelog} + \caption{Relationships between files in working directory and + filelogs in repository} + \label{fig:concepts:filelog} +\end{figure} + +\subsection{Managing tracked files} + + +追跡対象ファイルの情報をまとめるために、 +Mercurial は \emph{manifest} と呼ばれる構造を使用しています。 +manifest に記録される個々の要素は、 +当該チェンジセットにおけるファイルの一覧や、 +各ファイルのリビジョン、 +幾つかのファイルのメタデータといった、 +個々のチェンジセットごとのファイルに関する情報を保持しています。 + +\subsection{Recording changeset information} + +\emph{changelog} は、 +チェンジセットのコミット主や、 +コミット時のログメッセージ、 +その他チェンジセットに関する幾つかの情報や、 +manifest のリビジョンといった、 +個々のチェンジセットに関する情報を保持しています。 + +\subsection{Relationships between revisions} + +changelog、manifest ないし filelog における個々のリビジョンは、 +直接の親リビジョン +(マージを行ったリビジョンの場合は、 +マージ対象となった2つの親リビジョン) +への参照を保持しています。 +今述べたように、 +各構造に\emph{またがった}関連性をもち、 +それらは必然的に階層構造を持っています。 + +リポジトリ中の全てのチェンジセットに関して、 +changelog には厳密に1つのリビジョンが保存されます。 +changelog における各リビジョンは、 +manifest 中のリビジョンへの参照を保持しています。 +manifest 中の各リビジョンは、 +チェンジセットが生成された際の各ファイルのリビジョンに対応する +filelog 中のリビジョンへの参照を保持しています。 +この関連性を図~\ref{fig:concepts:metadata}に示します。 + +\begin{figure}[ht] + \centering + \grafix{metadata} + \caption{Metadata relationships} + \label{fig:concepts:metadata} +\end{figure} + +図からもわかるように、 +changelog、manifest および +filelog が保持するリビジョン情報間の関係は、 +必ずしも``1対1''というわけではありません。 +2つのチェンジセットの間で +manifest が変更されていない場合、 +それらのチェンジセットに対応する changelog 要素は、 +manifest 中の同じリビジョンを参照します。 +2つのチェンジセットの間で +Mercurial が追跡するファイルが変更されていない場合、 +それらのチェンジセットに対応する manifest 要素は、 +filelog 中の同じリビジョンを参照します。 + +\section{Safe, efficient storage} + +changelog、manifest および filelog は、 +\emph{revlog} と呼ばれる同じ構造により構成されています。 + +\subsection{Efficient storage} + +revlog は \emph{差分}手法という仕組みを使用して、 +リビジョン情報を効率的に格納しています。 +差分手法では、 +ファイルの各リビジョンごとに完全な複製を保持する代わりに、 +旧リビジョンから新リビジョンへの変形に必要な情報を保持します。 +多くのファイルでのデータ格納において、 +差分手法は一般的に完全な複製の場合の数パーセント程度のサイズになります。 + +旧式の構成管理システムでは、 +テキスト形式のファイルでしか差分手法が適用できないものもあります。 +それらのシステムにおけるバイナリファイルの格納は、 +完全なスナップショットか、 +テキスト表現形式への変換によって行われますが、 +これらは共に不経済な手法です。 +任意のバイナリデータを含むファイルであっても、 +Mercurial は差分を効率的に扱うことができますので、 +テキストを特別扱いする必要はありません\footnote{訳注: +cvs add における +-kb 指定の欠落によるファイル内容の破損、 +といった心配はありません。 +尤も、Mercurial の基底動作では、 +キーワードの置換等を行いませんので、 +そもそも心配する必要が無いのですが…。}。 + +\subsection{Safe operation} +\label{sec:concepts:txn} + +Mercurial は revlog の末尾にデータを\emph{追加}するだけで、 +書き込まれた後からファイルの一部を改変するようなことは行いません。 +既存データの改変を必要とする仕組みと比較した場合、 +この手法は堅牢且つ効率的です。 + +それに加えて、 +Mercurial は複数のファイルにまたがった全ての書き込みを、 +単一の\emph{トランザクション}の一部として扱います。 +トランザクションは\emph{不可分}なものとして扱われますので、 +トランザクション全体が成功すれば結果の全てが利用者に見えるようになりますが、 +トランザクションの一部でも失敗した場合には、 +全ての書き込み操作は取り消されます。 +一方はデータの読み込みを行い他方はデータの書き出しを行うような、 +2つの Mercurial プロセスを同時に実行した場合でも、 +この不可分保証により、 +読み込みを混乱させるような部分的な書き込みデータを、 +データ読み込み側のプロセスが読み込むことはありません +\footnote{訳注: +厳密にはこの記述は正しくありません。 +詳細は \ref{sec:hook:carepretxn}~節を参照してください。}。 + +Mercurial がファイルへの追加しか行わないことが、 +トランザクションの不可分性保証の提供を容易にしています。 +トランザクション保証が容易である程、 +それが正しく機能していることを確信できる筈です。 + +\subsection{Fast retrieval} + +初期の構成管理システムが共に陥っていた\emph{非効率な復旧}問題の落とし穴を、 +Mercurial は上手に回避しています。 +殆どの構成管理システムは、 +``スナップショット''に対する変更の追加的な連続として、 +リビジョンの内容を保持していました。 +この手法の場合、 +特定のリビジョンを再構築するには、 +最初にスナップショットを読み込み、 +続いて対象リビジョンとの間の全ての差分データを読み込む必要があります。 +ファイルの履歴が積み重なるほど、 +差分データを読み込まなければ成らないリビジョンが増加し、 +特定のリビジョンの再構築に時間が必要となります。 + +\begin{figure}[ht] + \centering + \grafix{snapshot} + \caption{Snapshot of a revlog, with incremental deltas} + \label{fig:concepts:snapshot} +\end{figure} + +Mercurial がこの問題の解決に使用している手法は、 +簡単なものですが効果的です。 +前回のスナップショット作成時点から、 +固定された閾値を超えて差分情報が蓄積された際には、 +差分情報の蓄積ではなく、 +新たなスナップショット(勿論圧縮は行います)を保存する、 +というものです。 +この手法は、 +\emph{任意の}リビジョンにおけるファイルを素早く再構築できます。 +この手法は非常に有効であるため、 +他の幾つかの構成管理システムにも取り込まれています。 + +図~\ref{fig:concepts:snapshot}の概要が示すように、 +Mercurial は、 +revlog のインデックスファイルにおける各要素に、 +特定のリビジョンの再構築の際に読み込みが必要とされる、 +データファイル中の要素の範囲を格納します。 + +\subsubsection{Aside: the influence of video compression} + +動画圧縮を熟知しているか、 +ケーブルないし衛星によるデジタルテレビ配信を視聴したことがあるならば、 +たいていの動画圧縮形式において各動画フレームが、 +先行するフレームとの差分で保持されていることをご存知かもしれません。 +加えてそれらの形式では、 +圧縮率を向上させるために``非可逆''圧縮手法を用いていますので、 +フレーム間差分の数に応じて視覚的エラーが蓄積されます。 + +動画配信の場合、 +時折の信号異常による``欠落''が有り得ますし、 +可逆圧縮過程により生じる誤差の蓄積を制限する必要もあるため、 +動画圧縮側では定期的に完全なフレーム +(``キーフレーム''と呼ばれます)を圧縮形式の中に挿入します。 +これは動画信号が中断されても、 +次のキーフレームの到着時点からの再開が可能であることを意味します。 +符号化エラーの蓄積も、 +個々のキーフレームでクリアされます。 + +\subsection{Identification and strong integrity} + +差分ないしスナップショット情報のデータに対して、 +revlog 要素は暗号化に用いられるハッシュ値を計算して保持しています。 +これにより、 +リビジョンに関する情報の偽造を困難にすると同時に、 +不慮の破損の検出が容易になります。 + +ハッシュ値の算出は、 +単なる破損の検出以上のものをもたらします。 +ハッシュ値は各リビジョンの識別子として使用されます。 +Mercurial のエンドユーザとして目にするチェンジセット識別子のハッシュ値は、 +changelog のリビジョンに由来する値です。 +filelog や manifest でもハッシュ値を使用していますが、 +Mercurial ではこれらは舞台裏のみで使用されています。 + +特定リビジョンのファイルを再構築する場合や、 +他のリポジトリからチェンジセットを取り込んだ場合、 +Mercurial はハッシュ値が正しいことを確認します。 +一貫性に問題があることが検出された場合、 +警告を発した上で、 +進行中の全ての処理を停止します。 + +Mercurial が定期的に差し込んでいるスナップショットは、 +特定リビジョンの再構築の際の効率に加えて、 +部分的なデータの破損に対する堅牢性をもたらしてます。 +ハードウェアエラーやシステムのバグによって、 +revlog が部分的に破損した場合、 +破損を免れた revlog のデータから、 +破損した部位の前後共に、 +一部(あるいは殆どの)リビジョンを復旧することが可能です。 +差分のみを保持するモデルを採用する構成管理システムでは、 +このようなことはできません。 + +\section{Revision history, branching, and merging} + +全ての Mercurial の revlog 要素は、 +通常は\emph{親}と言われる直前のリビジョンの識別子を保持しています。 +実際には、 +各 revlog 要素は1つではなく2つの親の情報を保持できます。 +Mercurial は``空識別子''(null ID)と呼ばれる特別なハッシュ値を使って、 +``親不在''を表現します\footnote{訳注: +つまり、多くの revlog 要素は、 +一方の親リビジョンとして空IDを保持しています。}。 +このハッシュ値は単純に0が連続した文字列です。 + +revlog の概念図を図~\ref{fig:concepts:revlog}に見ることができます。 +filelog や manifest、changelog の全てが同じ構造を持っており、 +個々の要素が保持している、 +差分やスナップショットといったデータの種別が異なるだけです。 + +revlog における最初のリビジョン +(図における底位置のリビジョン)は、 +2つの親リビジョン格納領域の両方に空識別子を保持しています。 +``通常の''リビジョンでは、 +第1親の格納領域には親リビジョンの識別子が、 +第2親の格納領域には空識別子が格納され、 +親リビジョンが1つしかないことを表します。 +親リビジョンの識別子として同じ識別子を格納するリビジョン同士は、 +互いにブランチとなります。 +ブランチをマージしたリビジョンは、 +統合された両方のリビジョンの識別子を親リビジョンの識別子として格納します。 + +\begin{figure}[ht] + \centering + \grafix{revlog} + \caption{} + \label{fig:concepts:revlog} +\end{figure} + +\section{The working directory} + +Mercurial は、 +リポジトリで構成管理されているファイルの、 +特定のリビジョンにおけるスナップショットを作業領域ディレクトリに保持します。 + +作業領域ディレクトリは、 +どのリビジョンのスナップショットを保持しているのかを``知っています''。 +作業領域ディレクトリを特定のリビジョンで更新しようとした場合、 +Mercurial は (1) 相応しいリビジョンの manifest を参照し、 +(2) 当該リビジョンのコミット時点での管理対象ファイルを特定し、 +(3) 作業領域ディレクトリ中のファイルが保持すべき内容を決定します。 +その上で、 +当該チェンジセットのコミット時点と同じ内容を持つように、 +作業領域ディレクトリ中に各ファイルの複製を再生成します。 + +dirstate 形式には、 +作業領域ディレクトリがどのチェンジセットで更新されているかとか、 +作業領域で Mercurial により構成管理されているファイルの一覧など、 +作業領域ディレクトリに関する +Mercurial の管理情報が格納されています。 + +個々のリビジョンに関する revlog 要素は、 +2つの親リビジョン識別子を格納する領域を持っていますので、 +通常のリビジョン(1つの親リビジョンだけを参照)も、 +2つのリビジョンをマージするリビジョンも表現可能ですが、 +dirstate 形式も2つの親リビジョン識別子を格納する領域を持っています。 +\hgcmd{update} コマンドを実行した際には、 +指定したチェンジセットは``第1親''(first parent)として保持され、 +第2親は空識別子を保持します。 +チェンジセットとの \hgcmd{merge} を行った際には、 +dirstate 形式が保持する第1親は変化しませんが、 +第2親は \hgcmd{merge} コマンドに指定されたチェンジセットに設定されます。 +\hgcmd{parents} コマンドにより、 +dirstate 形式が保持する親リビジョンの識別子を表示できます。 + +\subsection{What happens when you commit} + +dirstate 形式が親リビジョン情報を保持するのは、 +何も覚え書きのためだけではありません。 +Mercurial は dirstate 形式の持つ親リビジョン情報を、 +コミットの際の\emph{新規チェンジセットの親チェンジセット}として使用します。 + +\begin{figure}[ht] + \centering + \grafix{wdir} + \caption{The working directory can have two parents} + \label{fig:concepts:wdir} +\end{figure} + +図~\ref{fig:concepts:wdir}は、 +1つの親チェンジセットのみを持つ、 +通常の作業領域ディレクトリを表しています。 +図における作業領域ディレクトリの親チェンジセットは、 +リポジトリにおける最新で且つ子を持たないチェンジセットですので、 +\emph{tip} と呼ばれます。 + +\begin{figure}[ht] + \centering + \grafix{wdir-after-commit} + \caption{The working directory gains new parents after a commit} + \label{fig:concepts:wdir-after-commit} +\end{figure} + +作業領域ディレクトリそのものを、 +``コミットしようとしているチェンジセット'' +と捉えるとわかりやすいでしょう。 +Mercurial に対して追加/削除/改名ないし複製を指示したファイルは、 +既に Mercurial により構成管理されているファイルへの変更と同様に、 +そのチェンジセットに反映されます。 +その新たなチェンジセットには、 +作業領域ディレクトリと同じ親チェンジセットが設定されます。 + +コミットが完了したなら、 +Mercurial や作業領域ディレクトリの親チェンジセットの情報を更新します。 +第1親にはコミットにより新たに生成されたチェンジセットの識別子が設定され、 +第2親には空識別子が設定されます。 +コミット後の模式図を、 +図~\ref{fig:concepts:wdir-after-commit}に示します。 +Mercurial はコミットの際に、 +作業領域ディレクトリ中のファイルには一切触れず、 +単に dirstate の親チェンジセット情報を書き換えるだけです。 + +\subsection{Creating a new head} + +現時点での tip 以外のチェンジセットでの作業領域ディレクトリの更新は、 +良くあることです。 +例えば、 +先週火曜日時点でのプロジェクトの状態を調べたり、 +どのチェンジセットがバグを持ち込んだのかを調べる、 +といった状況です。 +このような状況での自然な行為は、 +作業領域ディレクトリを希望のチェンジセットで更新し、 +当該チェンジセットをコミットした時点でのファイルの内容を、 +作業領域ディレクトリ中のファイルを参照して確認する、 +というものでしょう。 +この行為による影響を、 +図~\ref{fig:concepts:wdir-pre-branch}に示します。 + +\begin{figure}[ht] + \centering + \grafix{wdir-pre-branch} + \caption{The working directory, updated to an older changeset} + \label{fig:concepts:wdir-pre-branch} +\end{figure} + +作業領域ディレクトリを以前のチェンジセットで更新した場合、 +何らかの変更を行ってコミットしたなら、 +Mercurial はどのように振舞うのでしょうか? +Mercurial はこれまでに説明してきた場合と同じように振舞います。 +作業領域ディレクトリの親チェンジセットが、 +新規に作成されるチェンジセットの親になります。 +新規作成されるチェンジセットは子を持たず、 +よって新たな tip チェンジセットとなります。 +コミットの結果、 +リポジトリには子を持たないチェンジセットが2つ存在し、 +これらは \emph{head} と呼ばれます。 +この状況を図~\ref{fig:concepts:wdir-branch} に示します。 + +\begin{figure}[ht] + \centering + \grafix{wdir-branch} + \caption{After a commit made while synced to an older changeset} + \label{fig:concepts:wdir-branch} +\end{figure} + +\begin{note} + Mercurial に馴染みの無い方は、 + 引数無しで \hgcmd{pull} コマンドを実行した場合の、 + 良くある「間違い」を気に留めて置いてください。 + \hgcmd{pull} コマンドの基底動作は、 + 作業領域ディレクトリの更新を\emph{行いません}ので、 + リポジトリへの新規チェンジセットの取り込みは行われても、 + 作業領域ディレクトリは \hgcmd{pull} コマンド実行前のままです。 + 作業領域ディレクトリは当該時点での tip と同期していないため、 + \hgcmd{pull} の実行後に何らかの変更を行いコミットした場合、 + 結果として新たな head を生成することになります。 + + 括弧付きで「間違い」と述べたのは、 + この状況を修復するのに必要なことが、 + \hgcmd{merge} してから \hgcmd{commit} すれば良いだけだからです。 + 言い換えるなら、 + このようなケースは全然深刻な状況ではない、ということです。 + Mercurial に慣れていない人はビックリするかもしれませんが…。 + このような事態を回避する別の方法や、 + 初心者にとって意外に感じるこのような振る舞いを Mercurial がとる理由について、 + 後ほど説明したいと思います。 +\end{note} + +\subsection{Merging heads} + +\hgcmd{merge} コマンド実行の際に、 +Mercurial は作業領域ディレクトリの第1親は変更せずに、 +第2親をマージ対象として指定したチェンジセットに変更します。 +この様子を図~\ref{fig:concepts:wdir-merge}に示します。 + +\begin{figure}[ht] + \centering + \grafix{wdir-merge} + \caption{Merging two heads} + \label{fig:concepts:wdir-merge} +\end{figure} + +2つのチェンジセットにおいて管理されるファイルをマージするため、 +Mercurial は作業領域ディレクトリを変更します。 +多少簡便化して説明すると、 +両方のチェンジセットの manifest に含まれる全てのファイルに対して、 +概ね以下のようにマージ処理が実施されます。 + +\begin{itemize} +\item どちらのチェンジセットでもファイルを変更していない場合、 + そのファイルに対しては何も行われません。 + +\item 一方のチェンジセットが変更しているファイルを、 + 他方が変更していない場合、 + 変更内容を反映したファイルを作業領域ディレクトリに複製します。 + +\item 一方のチェンジセットが削除したファイルは、 + 他方の削除に関わらず、 + 作業領域ディレクトリから削除されます。 + +\item 一方のチェンジセットが削除したファイルを、 + 他方が変更していた場合、 + ファイルの変更と削除のどちらを採用するのか、 + ユーザに対して問い合わせます。 + +\item 両方のチェンジセットがファイルを変更している場合、 + 内容のマージ結果をファイルに保存するために、 + 外部マージプログラムが起動されます。 + この場合、ユーザによる対話的操作が必要になるかもしれません。 + +\item 一方のチェンジセットが変更しているファイルを、 + 他方が改名したり複製したりしている場合、 + 変更内容が新しいファイルにも伝播するようにします。 + +\end{itemize} + +他にも細かい話---特にマージに関しては細かい話が沢山あります---がありますが、 +マージに関連する一般的な振る舞いの種類はこの程度です。 +ご覧の様に、殆どの状況が全く自動的に処理されますし、 +実際のマージでも殆どの場合、 +衝突解消のための対話的な入力無しに自動的に完了します。 + +マージ後のコミットの際に処理される事柄を考える場合は、 +先にも述べましたが、 +作業領域ディレクトリを +``コミットしようとしているチェンジセット'' +と捉えるとわかりやすいでしょう。 +\hgcmd{merge} コマンドが完了した後の作業領域ディレクトリは、 +親チェンジセットを2つ持ち、 +コミットによって生成される新たなチェンジセットは、 +これらを親チェンジセットとします。 + +Mercurial では繰り返しマージすることが可能ですが、 +Mercurial はりビジョンおよび作業領域ディレクトリの両方に対して、 +一度に2つの親リビジョンしか追跡できないため、 +個々のマージの都度コミットする必要があります。 +複数のチェンジセットの一括マージは技術的には可能でしょうが、 +ユーザが混乱したり、 +ひどく乱雑なマージが行われるであろうことは目に見えています。 + +\section{Other interesting design features} + +これまでの節で、 +Mercurial が信頼性と性能へ注意深く配慮を払っていることを説明するために、 +設計における最も重要な側面の幾つかに焦点を当ててきました。 +しかし、 +詳細事項への配慮は、 +これだけに留まりません。 +Mercurial の構成において筆者の個人的な興味をそそる側面が多数あります。 +これまでの``big ticket''な側面とは別に、 +いくつかを選んで詳細を説明しようと思いますので、 +これらに興味があれば、 +良い設計のシステムの考案の際に有用な、 +より良い発想を得ることができるでしょう。 + +\subsection{Clever compression} + +Mercurial はスナップショットと差分のそれぞれに対して、 +圧縮が有効である場合には圧縮形式で保存します。 +Mercurial は常にスナップショットないし差分の圧縮を\emph{試行}しますが、 +非圧縮な状態よりもサイズが小さい場合に限り、 +圧縮形式での保存を行います。 + +このことは、 +例えば \texttt{zip} アーカイブや JPEG 画像のように、 +元々圧縮形式の内容を持つファイルの格納の際に、 +Mercurial が``適切な処置''を行うこと意味します。 +これらのファイルは Mercurial による2度目の圧縮の際には、 +最初のサイズよりも大きくなるのが一般的ですので、 +Mercurial は \texttt{zip} や JPEG ファイルをそのまま保存します。 + +圧縮形式のファイルのリビジョン間の差分は、 +一般的にはスナップショットよりも大きくなりますので、 +この場合でも Mercurial は``適切な処置''を行います。 +ファイルのスナップショットそのものを保存する場合の許容範囲を、 +差分情報のサイズが超えることが判明した場合、 +Mercurial はスナップショットを保存しますので、 +繰り返しになりますが、 +差分のみを保持するモデルよりもディスク容量が節約できます。 + +\subsubsection{Network recompression} + +Mercurial はディスクへの履歴保存の際に、 +性能に対する圧縮率がそこそこ良好でバランスの取れている``収縮'' +(deflate)圧縮アルゴリズム +(著名な \texttt{zip} アーカイブ形式が同等のものを使用しています) +を使用しています。 +しかし、 +ネットワーク越しのデータ転送の際には、 +Mercurial は履歴データを圧縮しません。 + +ネットワーク接続が HTTP 経由の場合、 +Mercurial はデータ通信の経路全体を、 +より良い圧縮率を得られる圧縮アルゴリズム +(\texttt{bzip2} 圧縮として広く使用されている +Burrows-Wheeler アルゴリズム)で再圧縮します。 +リビジョン情報個別の圧縮ではなく、 +\texttt{bzip2} アルゴリズムと通信経路全体の圧縮という組み合わせにすることで、 +転送データ量を大幅に低減することができますので、 +殆ど全てのネットワーク形態において良好な性能を発揮できます。 + +(\command{ssh} での接続の場合、 +\command{ssh} 自身が圧縮を行うことができるので、 +Mercurial は接続経路の再圧縮を\emph{行いません} +\footnote{訳注: 訳者の経験では、 +サーバ側の Python が zlib を使用できない場合、 +ssh での push/pull が機能しなかったので、 +Mercurial のサイトにも同様の記述がありますが、 +この記述は少々辻褄が合わない気がします。}) + +\subsection{Read/write ordering and atomicity} + +不完全な書き込み内容が利用されることのないように保証する上では、 +ファイルへの追加書き込みだけが全てではありません。 +もう一度、図~\ref{fig:concepts:metadata}を見ていただければわかるように、 +changelog 中のリビジョン要素は manifest 中のリビジョン要素を、 +manifest 中のリビジョン要素は filelog 中のリビジョン要素を指しています。 +この階層構造は意図的なものなのです。 + +データ書き込みの際には、 +filelog および manifest への書き込みでトランザクションが開始され、 +これらへの書き込みが完了するまでは +changelog への書き込みは行われません。 +読み込みの際には、 +changelog を起点として manifest、filelog の順序で読み込みを行います。 + +changelog への書き込みに先立って、 +常に filelog および manifest への書き込みが完了しているので、 +changelog からの不完全な manifest への参照を読み込むことも、 +manifest からの不完全な filelog への参照を読み込むこともありません。 + +\subsection{Concurrent access} + +読み書き手順と不可分性保証により、 +例え読み込みの最中に書き込みが行われるとしても、 +Mercurial は読み込みにおけるリポジトリの\emph{排他}を必要としません。 +この特性は大規模化の際に非常に影響があります。 +任意の数の Mercurial プロセスが、 +書き出しプロセスの有無に関わらず、 +リポジトリに対して同時読み出しを安全に行うことができます。 + +読み出しにおける排他不要の特性は、 +多ユーザシステム上でリポジトリを公開している際に、 +複製(\hgcmd{clone})や変更の取り込み(\hgcmd{pull})のために、 +他のユーザに(あなたの) +リポジトリへの\emph{書き込み}を許可する必要\footnote{訳注: +プロセス間で排他を行う場合、 +排他用のファイルを用いるか、 +ディレクトリそのものに排他設定を行うのが一般的ですが、 +そのためには書き込み権限が必要です。} +が無いことを意味します。 +読み出しを行う他のユーザには、 +\emph{読み出し}権限のみの公開で済みます +(この性質は構成管理システムに共通の特性では\emph{ありません}ので、 +一般的なものだとは思わないでください。 +多くの構成管理システムでは、 +読み出しユーザであっても、 +安全な読み出しのためにはリポジトリを排他する権限が必要であり、 +そのためには最低でも1つのディレクトリに対する書き込み権限が必要なため、 +安全性と管理上で面倒な問題の原因となり得ます。)。 + +Mercurial が排他を行うのは、 +一度に1つのプロセスのみがリポジトリに書き込むのを保証場合だけです +(排他に適さないと言われる NFS のようなファイルシステム\footnote{訳注: +構成管理システムに限らず、 +排他の実現に creat(EXCL) +で生成されるファイルを使用しているために、 +NFS では適切に排他できないプログラムが多数存在します。 +}であっても、 +安全に排他できる仕組みを用いています)。 +リポジトリが他のプロセスにより排他されている場合、 +書き込みを行うプロセスは、 +リポジトリの排他が解除されるまで暫く待って再度排他を試行しますが、 +長時間に渡って排他されたままの場合は、 +時間切れとみなされます。 +そのため、 +例えば人知れずシステムが停止したとしても、 +自動化された日次処理が停止したままになったり、 +停止しない処理が次々と積み上がったりすることはありません。 + +\subsubsection{Safe dirstate access} + +dirstate 形式ファイルからのリビジョン情報の読み出しに際して、 +Mercurial はファイルに対する排他を行ったりはせず、 +書き込みの際にのみ排他を行います。 +不完全な書き込みを +dirstate 形式ファイルから読み出してしまうことを回避するため、 +Mercurial は +対象 dirstate 形式ファイルと同じディレクトリに特有の名前でファイルを書き出し、 +この一時ファイルを \filename{dirstate} +ファイルへと不可分な操作で改名します。 +そのため、 +\filename{dirstate} という「名前の」ファイルは、 +不完全な書き込みを持たない完全な内容であることが保証されます。 + +\subsection{Avoiding seeks} + +比較的大量のデータ読み込み処理に対してすら、 +ディスクヘッドのシークは非常にコストが高くつくため、 +Mercurial の性能確保の重要な点は、 +ディスクヘッドのシークを極力回避することにあります。 + +例えば dirstate 形式のようなデータが、 +単一のファイルに保存される理由がここにあります。 +Mercurial により構成管理されるディレクトリごとに +\filename{dirstate} ファイルが存在する場合は、 +ディレクトリごとにディスクヘッドのシークが発生し得ます。 +そのようなディスクヘッドのシークを回避するために、 +Mercurial は一度に単一の +\filename{dirstate} ファイル全体を読み込みます\footnote{訳注: +ディスクの利用が進んで空きブロックが断片化された場合、 +不連続なブロックが割り当てられますから、 +必ずしも「単一ファイル」=「ヘッドのシークが回避可能」ではありませんが、 +少なくとも「ヘッドのシークを低減」することは可能です。}。 + +ローカルストレージにおけるリポジトリの複製の際には、 +Mercurial は``書き出し時複製''の仕組みも使用します。 +複製元リポジトリから複製先に個々の revlog ファイルを複製する代わりに、 +``ハードリンク''を使用することで、 +``2つのファイル名が同一内容のファイルを参照'' +することを手早く表明します。 +一方の revlog ファイルに書き込みを行う際には、 +Mercurial は当該ファイルのハードリンクを確認します。 +当該ファイルが複数のリポジトリから参照されいている場合、 +Mercurial は当該リポジトリ用に revlog の新たな複製を作成します。 + +何人かの構成管理ツールの開発者により、 +この方法--- +完全にリポジトリ固有のものとしてファイルを複製する--- +がディスク使用量削減にそれほど効果的でないとの指摘を受けています。 +それは事実ではありますが、 +ディスク容量の確保は安価であり、 +OS への複製要求を遅延することにより高い性能を得ることができます。 +別な仕組みを用いる場合、 +性能が低下しソフトウェアの複雑さが増しますので、 +日々の利用における``体感''に非常に影響を及ぼします\footnote{訳注: +つまり、 +Mercurial でのハードリンクの使用は、 +複製を行うことによるディスクヘッドのシークを低減するのが主眼で、 +ディスク使用量の低減が主眼ではない、 +ということです。}。 + +\subsection{Other contents of the dirstate} +\label{sec:concepts:dirstate} + +ファイルの変更の際の Mercurial への通知が必要ないことから、 +ファイル変更の有無を効率的に判定するために、 +特別な情報を格納した dirstate 形式ファイルを使用します。 +作業領域ディレクトリ中の全てのファイルに対して、 +Mercurial はファイルの最終変更日時とその時点でのサイズを +dirstate 形式ファイルに格納しています。 + +\hgcmd{add}、\hgcmd{remove}、\hgcmd{rename} ないし +\hgcmd{copy} を明示的に使用した場合、 +Mercurial はこの情報を更新しますので、 +コミット時の振る舞いを特定できます。 + +Mercurial が作業領域ディレクトリ中のファイルを確認する場合、 +最初にファイルの変更日時を確認します。 +変更日時が同一ならば、ファイルは変更されていない筈です。 +ファイルサイズが異なっているならば、ファイルは変更されている筈です。 +変更日時が異なっているのにファイルサイズが同一の場合にのみ、 +ファイルの内容が異なっているか否かを判定するために +Mercurial は実際にファイルの内容を読み込みます\footnote{訳注: +Windows 環境での改行変換を行っているような場合、 +バイナリ版とソース版でファイルサイズの算出手順に違いがあるらしく、 +\hgcmd{diff} が何も出力しないのに、 +\hgcmd{state} では「変更」扱いされることが稀にあります。}。 +このように僅かな追加情報を格納することで、 +Mercurial が必要とする読み込みデータ量を劇的に減らすことができ、 +他の構成管理システムと比較して大幅に性能が改善されています。 + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/custom.sty --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/custom.sty Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,67 @@ +\newlength\kanjicharwidth +\settowidth{\kanjicharwidth}{あ} + +\newlength\kanjicharheight +\settoheight{\kanjicharheight}{あ} + +% in 10pt/jbook environment: +% kanjicharwidth=9.62216pt(= Cwd) +% kanjicharheight=7.77588pt + +\setlength{\intextsep}{3.0\kanjicharheight} +\setlength{\textfloatsep}{3.0\kanjicharheight} + +\newlength\defaultleftmargin +\setlength{\defaultleftmargin}{2.0\kanjicharwidth} + +\newlength\defaultrightmargin +\setlength{\defaultrightmargin}{2.0\kanjicharwidth} + +\newlength\defaulttopsep +\setlength{\defaulttopsep}{2.0\kanjicharheight} + +\setlength{\voffset}{-20mm} +\addtolength{\textheight}{30mm} % +\setlength{\hoffset}{-10mm} +\setlength{\oddsidemargin}{-8mm} +\setlength{\evensidemargin}{-8mm} +\addtolength{\textwidth}{16mm} + +%%%%%%%%%%%%%%%%%%%% + +\renewenvironment{itemize}{ + \ifnum \@itemdepth >\thr@@\@toodeep\else + \advance\@itemdepth\@ne + \edef\@itemitem{labelitem\romannumeral\the\@itemdepth} + \list{ + \csname \@itemitem\endcsname + }{ + \def\makelabel##1{\hss\llap{##1}} + \setlength{\leftmargin}{\defaultleftmargin} + \setlength{\rightmargin}{\defaultrightmargin} + \setlength{\topsep}{\defaulttopsep} + } + \fi +}{ + \endlist +} + +\renewenvironment{enumerate}{ + \ifnum \@enumdepth >\thr@@\@toodeep\else + \advance\@enumdepth\@ne + \edef\@enumctr{enum\romannumeral\the\@enumdepth} + \list{ + \csname label\@enumctr\endcsname + }{ + \usecounter{\@enumctr} + \def\makelabel##1{\hss\llap{##1}} + \setlength{\leftmargin}{\defaultleftmargin} + \setlength{\rightmargin}{\defaultrightmargin} + \setlength{\topsep}{\defaulttopsep} + } + \fi +}{ + \endlist +} + +%%%%%%%%%%%%%%%%%%%% diff -r a24b370a16ee -r d6ca1334a19d ja/daily.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/daily.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,533 @@ +\chapter{Mercurial in daily use} +\label{chap:daily} + +\section{Telling Mercurial which files to track} + +ファイルの管理を指示しない限り、 +リポジトリ中のファイルに対して Mercurial は何も行いません。 +\hgcmd{status} コマンドは、 +Mercurial の管理下に無いファイルを +``\texttt{?}'' を表示することで知らせてくれます + +Mercurial による構成管理を指示するには、 +\hgcmd{add} コマンドを使用します。 +ファイルの構成管理を指示したファイルの +\hgcmd{status} による表示は、 +``\texttt{?}'' から ``\texttt{A}'' へと変化します。 + +\interaction{daily.files.add} + +\hgcmd{commit} を実行した直後は、 +コミット前に追加したファイルが +\hgcmd{status} により表示されることはありません。 +これは、 +``興味深い''ファイル--- +変更したり、Mercurial に何らかの操作を要求したファイル +---について表示するのが +\hgcmd{status} の役割だからです。 +数千のファイルから成るリポジトリがある場合、 +構成管理されてはいても特に変更されていないファイルの一覧 +(後述するように、そのようなファイル一覧の情報を得ることもできます) +を欲しいと思うことは稀です。 + +一旦ファイルを追加したとしても、 +そのファイルに対して Mercurial はすぐには何も行いません。 +その代わり、 +次にコミットを行った際にファイル状態のスナップショットを作成します。 +Mercurial はそれ以降、 +構成管理下から除外するまで、 +コミットの際には常に当該ファイルの変更状況を確認します。 + +\subsection{Explicit versus implicit file naming} + +Mercurial の有用な振る舞いとして、 +Mercurial のコマンドにディレクトリ名を指定した場合、 +その指定を +``当該ディレクトリ配下の全てのファイル\footnote{訳注: +当該ディレクトリ直下のファイルならびに、 +サブディレクトリ以下のファイル全て}に対する操作の実施'' +が要求されたものとみなします。 + +\interaction{daily.files.add-dir} + +先の例で \filename{a} +ファイルを構成管理対象に追加した際には、 +Mercurial は追加されたファイルのファイル名を表示していませんが、 +この例では +構成管理対象に追加されたファイルを表示している点に注意してください。 + +先の例では、 +追加するファイル名をコマンドラインで明示的に指定しましたので、 +そのような場合は利用者自身が自分の振る舞いを理解しているものとみなし、 +Mercurial は何も表示しません。 + +しかし、 +ディレクトリ名を指定することでファイル名を\emph{暗示}した場合、 +Mercurial は特別に操作対象となった個々のファイル名を表示します。 +こうすることで何が実施されたのかが明確になるため、 +ひっそりとやっかいな問題が発生する可能性を低減します。 +この振る舞いは殆どの +Mercurial コマンドに共通しています。 + +\subsection{Aside: Mercurial tracks files, not directories} + +ディレクトリは Mercurial による構成管理の対象にはなりません。 +その代わり、 +Mercurial はファイルのパスを構成管理します。 +ファイルの生成の際には、 +それに先立ってパスに含まれる存在しないディレクトリを全て作成します。 +ファイルの削除の際には、 +削除されたファイルへのパスに含まれる空ディレクトリを全て削除します。 +たわいも無いことに聞こえるかもしれませんが、 +Mercurial が完全に空っぽのディレクトリを取り扱えない、 +という小さいながらも実用上重大な性質を示しています。 + +空のディレクトリが有用なことは滅多に無いですし、 +妥当な効果を得るための控えめな回避方法があります。 +Empty directories are rarely useful, and there are unintrusive +workarounds that you can use to achieve an appropriate effect. +それ故に、 +空のディレクトリを扱うことによる限定的な有益性が、 +それに必要とされる複雑さに見合うものではない、 +と Mercurial の開発陣は判断しました。 + +空のディレクトリをリポジトリで管理したい場合、 +複数の実現方法があります。 +1つは当該ディレクトリ直下の``隠し''ファイルを +\hgcmd{add} することです。 +UNIX ライクなシステムでは、 +ピリオド(``\texttt{.}'')で始まる名前のファイルは、 +殆どのコマンドや GUI ツールから隠しファイルとして扱われます。 +この手法を図~\ref{ex:daily:hidden}に示します。 + +\begin{figure}[ht] + \interaction{daily.files.hidden} + \caption{Simulating an empty directory using a hidden file} + \label{ex:daily:hidden} +\end{figure} + +空ディレクトリを必要とする場合のもう一つの解決方法は、 +自動化されたビルドスクリプトで必要になる都度作成する、 +というものです。 + +\section{How to stop tracking a file} + +リポジトリにとって不要になった\footnote{訳注: +構成管理の必要性がなくなった}ファイルがある場合は、 +\hgcmd{remove} コマンドを使用ます。 +このコマンドはファイルを削除しつつ、 +Mercurial に構成管理対象からファイルを除外する旨を通知します。 +削除されたファイルは、 +\hgcmd{status} の出力では +``\texttt{R}'' 付きで表示されます。 + +\interaction{daily.files.remove} + +\hgcmd{remove} によるファイルの削除を行うと、 +作業領域ディレクトリに同名のファイルを再度作成したとしても、 +Mercurial はそのファイルを構成管理対象から除外します。 +同名ファイルを再生成し Mercurial による構成管理を行う場合には、 +単純にそのファイルを \hgcmd{add} してください。 +Mercurial は新規に管理対象に加えられたファイルが、 +以前管理していた同名のファイルとは無関係であるとみなします。 + +\subsection{Removing a file does not affect its history} + +重要な事ですので、 +\hgcmd{remove} コマンドによる操作が持つ影響は2つだけである、 +と理解してください。 + +\begin{itemize} +\item 作業領域ディレクトリから、現時点のファイルを削除します + +\item Mercurial に対して、次回のコミット以降、 + 当該ファイルを構成管理対象から除外するように通知します + +\end{itemize} + +\hgcmd{remove} コマンドによる操作は、 +ファイルの\emph{変更履歴}には一切変更を加え\emph{ません}。 + +作業領域ディレクトリを +\hgcmd{remove} +で削除したファイルがまだ構成管理されていた時点のチェンジセットで更新した場合、 +そのチェンジセットがコミットされた時点の内容で、 +作業領域ディレクトリに当該ファイルが再生成されます。 +その後で、 +当該ファイルが \hgcmd{remove} +で削除された時点のチェンジセットで更新すると、 +Mercurial は再び当該ファイルを作業領域から削除します。 + +\subsection{Missing files} + +\hgcmd{remove} +コマンドを使用せずに作業領域ディレクトリから削除したファイルを、 +Mercurial は\emph{行方不明}とみなします。 +行方不明のファイルは、 +\hgcmd{status} の出力では +``\texttt{!}'' 付きで表示されます。 +Mercurial のコマンド群全般は、 +行方不明のファイルに関しては何も行いません。 + +\interaction{daily.files.missing} + +\hgcmd{status} +が行方不明として表示するファイルがリポジトリ中にある場合\footnote{訳注: +つまり手動でファイルを削除した場合}、 +ファイル削除後の任意の時点で +\hgcmdargs{remove}{\hgopt{remove}{--after}} を実行することで +当該ファイルを構成管理対象から除外する意思があることを +Mercurial に通知することができます。 + +\interaction{daily.files.remove-after} + +その一方で、 +行方不明とされているファイルが意図せずに削除してしまったものなら、 +\hgcmd{revert} に当該ファイル名を指定することで、 +変更されていない状態にファイルを復旧することができます。 + +\interaction{daily.files.recover-missing} + +\subsection{Aside: why tell Mercurial explicitly to remove a file?} + +ファイル削除の意思表示を一々 Mercurial に示す必要性について、 +疑問に思われるかもしれません。 +Mercurial の開発初期における削除方法は、 +そのように思う人にとっては望ましいものかもしれません。 +Mercurial は \hgcmd{commit} コマンド実行時にファイルの不在を自動的に検知し、 +当該ファイルを構成管理対象から除外していたのです。 +実際問題、この削除方法では、 +不慮の事態で通知も無くファイルが削除される事態が容易に起こり得ます。 + +\subsection{Useful shorthand---adding and removing files in one step} + +Mercurial は、 +構成管理対象へのファイルの追加と除外を行う、 +組み合わせコマンドである \hgcmd{addremove} を提供しています。 + +\interaction{daily.files.addremove} + +\hgcmd{commit} コマンドも、 +コミット実施の直前に +\hgcmd{addremove} と同じ方針で構成管理対象への追加/除外を行う +\hgopt{commit}{-A} オプションを提供しています。 + +\interaction{daily.files.commit-addremove} + +\section{Copying files} + +Mercurial はファイルの複製を行う +\hgcmd{copy} コマンドを提供しています。 +このコマンドでファイルを複製した場合、 +Mercurial はそのファイルが元ファイルの複製であることを記録します。 +チェンジセットのマージの際には、 +Mercurial はこの複製ファイルを特別扱いします。 + +\subsection{The results of copying during a merge} + +複製ファイルのマージの際には、 +変更内容が複製ファイルまで``追従''してきます。 +このことが持つ意味を上手く説明するために、 +簡単な例を作成しましょう。 +これまでの例と同様に、 +1つだけファイルを持つ簡易的なリポジトリを作成します。 + +\interaction{daily.copy.init} + +マージを行うためには、 +別々の作業を平行して行う必要がありますので、 +リポジトリを複製しましょう。 + +\interaction{daily.copy.clone} + +最初のリポジトリに戻り、 +\hgcmd{copy} コマンドで最初に作成したファイルを複製します。 + +\interaction{daily.copy.copy} + +複製後の \hgcmd{status} コマンドの出力では、 +複製されたファイルは単に追加された普通のファイルと同じように見えます。 + +\interaction{daily.copy.status} + +しかし +\hgopt{status}{-C} オプション付きで +\hgcmd{status} を実行することで、 +別な行が表示されます。 +この行は、新たに追加されたファイルの複製\emph{元}であることを意味します。 + +\interaction{daily.copy.status-copy} + +複製したリポジトリに戻り、 +平行して変更作業を行います。 +複製元になったファイルに対して行を追加します。 + +\interaction{daily.copy.other} + +このリポジトリでは複製元の \filename{file} が変更されました。 +最初のリポジトリから変更内容を +\hgcmd{pull} して2つの head をマージする際に Mercurial は、 +\filename{file} に対してだけ行った変更内容を、 +その複製である \filename{new-file} にまで伝播させます。 + +\interaction{daily.copy.merge} + +\subsection{Why should changes follow copies?} +\label{sec:daily:why-copy} + +ファイルの複製に対してる変更が伝播される挙動は、 +難解に思えるかもしれませんが、 +多くの場合は非常に好ましい振る舞いとなります。 + +まずは、 +この伝播がマージの時\emph{だけ}に行われる、 +ということに注意してください。 +ファイルを \hgcmd{copy} で複製し、 +それに引き続き複製元ファイルを変更する、 +という通所の作業においては何も特別なことは行われません。 + +もう一点、 +変更を取り込んだリポジトリが、 +ファイルを複製したことを\emph{知らなかった}場合に限り、 +変更内容が複製先ファイルに伝播する、 +ということにも注意してください。 + +Mercurial がこのように振舞うのは以下のような理由のためです。 +例えば筆者が、 +ソースファイルに対して重要なバグ修正を行い、 +変更内容をコミットしたとします。 +その変更作業が行われている間に、 +バグの顕在化やその修正を待つ事無く、 +当該ファイルを \hgcmd{copy} で複製し、 +その複製先ファイルの変更を読者が始めてしまうかもしれません。 + +読者が筆者の変更を取り込んでマージした際に、 +Mercurial が複製への変更の反映を\emph{行わない}場合、 +読者の複製先ファイルはバグを含んでいるため、 +手動でバグ修正を反映させる必要性を思い出さない限り、 +バグは複製先ファイルに\emph{残り続ける}でしょう。 + +バグ修正に関する変更内容の、 +複製元から複製先への自動反映により、 +Mercurial はこの手の問題を回避しています。 +筆者の知る限り Mercurial は、 +複製ファイルに対するこのような変更伝播を行う\emph{唯一の}構成管理システムです。 + +ファイルの複製とそれに続くマージの実施が一旦変更履歴に記録されたなら、 +複製元ファイルから複製先ファイルへのそれ以上の変更反映は通常は不要なので、 +Mercurial はマージ時点までは複製へ変更を伝播させますが、 +それ以上は行いません。 + +\subsection{How to make changes \emph{not} follow a copy} + +仮に、何らかの理由により、 +複製ファイルへの自動的な変更反映が必要ないと判断したなら、 +システムの通常の方法 +(Unix 的なシステムの場合なら \command{cp}) +でファイルを複製し、 +\hgcmd{add} により手動で複製ファイルを構成管理対象に追加してください。 +ですが、その前に\ref{sec:daily:why-copy}節を読み直して、 +Mercurial による自動変更反映の適切性を十分に検討してください。 + +\subsection{Behaviour of the \hgcmd{copy} command} + +\hgcmd{copy} コマンドを使用した場合、 +Mercurial は即座に作業領域ディレクトリに個々のファイルの複製を作成します。 +そのため、 +ファイルに修正を加えた後で、 +その変更をチェンジセットとしてコミットすることなく +\hgcmd{copy} を行った場合、 +複製先ファイルはその時点までの変更内容も含んでいることになります +(この振る舞いについてここで述べたのは、 +少々直感に反するように感じられたからです)。 + +\hgcmd{copy} は +Unix の \command{cp} コマンドと同様に振舞います +(\hgcmd{cp} という別名方が好みであれば、こちらも使用できます)。 +末尾の引数は\emph{複製先}を、 +それ以外の先行する引数は\emph{複製元}を意味します。 +複製元に単一のファイルを、 +複製先に存在しないパスを指定した場合、 +Mercurial は複製先に指定した名前で新たなファイルを生成します。 + +\interaction{daily.copy.simple} + +複製先がディレクトリの場合、 +Mercurial は複製元ファイルを当該ディレクトリに複製します。 + +\interaction{daily.copy.dir-dest} + +ディレクトリの複製の場合は、 +再帰的且つディレクトリ構成を保持しつつ複製されます。 + +\interaction{daily.copy.dir-src} + +複製元と複製先の両方がディレクトリの場合\footnote{訳注: +先の「ディレクトリの複製の場合」は、 +「複製先ディレクトリが存在しない場合」を指します。}、 +複製元のディレクトリ構造は、 +複製先ディレクトリ配下で再構築されます。 + +\interaction{daily.copy.dir-src-dest} + +手動でファイルを複製した後で、 +当該ファイルが複製であることを Mercurial に通知するには、 +\hgcmd{remove} の場合と同様に、 +\hgopt{copy}{--after} 付きで \hgcmd{copy} コマンドを使用します。 + +\interaction{daily.copy.after} + +\section{Renaming files} + +ファイルを複製するよりも、 +むしろ改名の方が必要とされるのではないでしょうか。 +ファイルの改名よりも +\hgcmd{copy} コマンドの方を先に説明したのは、 +Mercurial が複製と改名を本質的には同等に扱っているためです。 +そのため、 +ファイルの複製における Mercurial の挙動を知ることで、 +ファイルの改名で期待される振る舞いを知ることができます。 + +\hgcmd{rename} コマンドを使用した場合、 +Mercurial は個々の改名元ファイルの複製を作成し、 +その上で改名元ファイルを削除し、 +それらを構成管理対象から除外します。 + +\interaction{daily.rename.rename} + +\hgcmd{status} コマンドの出力から、 +新たに複製されたファイルが構成管理対象に追加され、 +改名元ファイルが除外されていることが読み取れます。 + +\interaction{daily.rename.status} + +\hgcmd{copy} 実行の場合と同様に、 +\hgopt{status}{-C} オプション付きで \hgcmd{status} コマンドを実行することで、 +構成管理対象に追加されたファイルが実際には、 +今は削除されてしまったファイルの複製ファイル、 +と Mercurial にみなされていることがわかります。 + +\interaction{daily.rename.status-copy} + +\hgcmd{remove} および \hgcmd{copy} と同様に、 +\hgopt{rename}{--after} オプションを指定することで、 +実際に改名した後で Mercurial にその旨を通知することができます。 +それ以外の殆どの点で、 +\hgcmd{rename} コマンドの振る舞い並びに指定可能なオプションは、 +\hgcmd{copy} コマンドと同じです。 + +\subsection{Renaming files and merging changes} + +Mercurial の改名が「複製と削除」として実装されているため、 +複製の後でのマージの場合と同様に、 +改名の後でマージをした場合には変更が伝播されます。 + +あるユーザがファイルを修正し、 +別のユーザがそのファイルを別なファイルに改名した場合、 +両者がお互いの変更をマージすると、 +一方が行った改名元ファイルへの修正は改名先ファイルへと伝播します +(この振る舞いは``普通の作業''で期待するであろう類のものですが、 +全ての構成管理システムがこのように振舞うわけではありません)。 + +複製先に対する変更の伝播が、 +利用者にとっておそらく有用と思われる機能ですから、 +ファイルの改名においても変更の伝播が重要であろうことは、 +明らかといえるでしょう。 +変更伝播機能が無い場合、 +ファイルの改名によって変更は簡単に行く先を失ってしまうことでしょう。 + +\subsection{Divergent renames and merging} + +名前の広がり(diverging names)は、 +二人の開発者がとあるファイル--- +これを \filename{foo} と呼びます--- +を各自のリポジトリで扱うことで発生します。 + +\interaction{rename.divergent.clone} + +Anne がファイルを \filename{bar} に改名します。 + +\interaction{rename.divergent.rename.anne} + +その一方で、Bob がファイルを \filename{quux} に改名します。 + +\interaction{rename.divergent.rename.bob} + +個々の開発者がファイルの命名に関する異なる意向を表明したわけですから、 +筆者はこの事態を衝突と捉えるのが良いと思います。 + +この場合のマージはどのように振舞うべきだと思いますか? +改名による枝分かれが生じるチェンジセットのマージの場合、 +Merging は常に\emph{両方}の改名先ファイルを維持します。 + +\interaction{rename.divergent.merge} + +筆者個人にとってこの振る舞いは大変意外であり、 +それがここでこの振る舞いを説明している理由でもあります。 +筆者は Mercurial に、 +\filename{bar} を残すか、 +\filename{quux} を残すか、 +あるいは両方を残すか、 +という選択肢による確認を行うことを期待していたのです。 + +実際には、 +ファイルの改名を行った場合、 +改名元ファイルを使用したビルドを行う他のファイル +(例えば makefile)の修正が行われるであろうことを意味します。 +そのため、 +Anne がファイルを改名し、 +改名後のファイルでビルドが実施されるように +\filename{Makefile} を修正した場合、 +一方で Bob が同様の修正を別な名前で行っていますから、 +マージの際には作業領域ディレクトリに異なる名前のファイルのコピーが存在し、 +\emph{且つ} Anne と Bob の +\filename{Makefile} への修正箇所が衝突している筈です。 + +他の利用者もこの振る舞いに意外性を感じているようです。 +詳細は \bug{455} を参照してください。 + +\subsection{Convergent renames and merging} + +異なる\emph{複製元}ファイルが同じファイルを\emph{複製先}とした際に、 +改名による別な種類の衝突が発生します。 +この場合、Mercurial は通常のマージ機構を使用し、 +適切な解決への誘導を要求してきます。 + +\subsection{Other name-related corner cases} + +Mercurial は、 +一方がファイルに使用した名前を他方がディレクトリに使用した場合に、 +マージが失敗するバグが長い間残っています。 +この問題は \bug{29} に詳細があります。 + +\interaction{issue29.go} + +\section{Recovering from mistakes} + +幾つかのありがちな間違いから復旧するために、 +Mercurial は有用なコマンドを幾つか提供しています。 + +\hgcmd{revert} コマンドは、 +作業領域ディレクトリに対する変更を取り消します。 +例えば、うっかりファイルを \hgcmd{add} してしまった場合に、 +追加してしまったファイル名を指定して +\hgcmd{revert} を実行することで、 +ファイルには一切変更を加える事無く +Mercurial による構成管理対象から除外することができます。 +ファイルへの間違った変更を取り消すのにも +\hgcmd{revert} が利用できます。 + +\hgcmd{revert} コマンドは未コミットな変更に対して有効である、 +ということは憶えておきましょう。 +但し、 +一旦変更をコミットした後で変更内容が間違いであることに気が付いた場合でも、 +選択肢は限られてはいますが対処することはできます。 + +\hgcmd{revert} コマンドに関する詳細と、 +コミット済みの変更に関する対処の詳細に関しては、 +\ref{chap:undo}~章を参照してください。 + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/examples/backout --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,83 @@ +#!/bin/bash + +# We have to fake the merges here, because they cause conflicts with +# three-way command-line merge, and kdiff3 may not be available. + +export HGMERGE=$(mktemp) +echo '#!/bin/sh' >> $HGMERGE +echo 'echo first change > "$1"' >> $HGMERGE +echo 'echo third change >> "$1"' >> $HGMERGE +chmod 700 $HGMERGE + +#$ name: init + +hg init myrepo +cd myrepo +echo first change >> myfile +hg add myfile +hg commit -m 'first change' +echo second change >> myfile +hg commit -m 'second change' + +#$ name: simple + +hg backout -m 'back out second change' tip +cat myfile + +#$ name: simple.log +#$ ignore: \s+200[78]-.* + +hg log --style compact + +#$ name: non-tip.clone + +cd .. +hg clone -r1 myrepo non-tip-repo +cd non-tip-repo + +#$ name: non-tip.backout + +echo third change >> myfile +hg commit -m 'third change' +hg backout --merge -m 'back out second change' 1 + +#$ name: non-tip.cat +cat myfile + +#$ name: manual.clone + +cd .. +hg clone -r1 myrepo newrepo +cd newrepo + +#$ name: manual.backout + +echo third change >> myfile +hg commit -m 'third change' +hg backout -m 'back out second change' 1 + +#$ name: manual.log + +hg log --style compact + +#$ name: manual.parents + +hg parents + +#$ name: manual.heads + +hg heads + +#$ name: manual.cat + +cat myfile + +#$ name: manual.merge + +hg merge +hg commit -m 'merged backout with previous tip' +cat myfile + +#$ name: + +rm $HGMERGE diff -r a24b370a16ee -r d6ca1334a19d ja/examples/backout.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.init.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg init myrepo} +$ \textbf{cd myrepo} +$ \textbf{echo first change >> myfile} +$ \textbf{hg add myfile} +$ \textbf{hg commit -m 'first change'} +$ \textbf{echo second change >> myfile} +$ \textbf{hg commit -m 'second change'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/backout.manual.backout.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.manual.backout.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +$ \textbf{echo third change >> myfile} +$ \textbf{hg commit -m 'third change'} +$ \textbf{hg backout -m 'back out second change' 1} +reverting myfile +changeset backs out changeset +the backout changeset is a new head - do not forget to merge +(use "backout --merge" if you want to auto-merge) diff -r a24b370a16ee -r d6ca1334a19d ja/examples/backout.manual.cat.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.manual.cat.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{cat myfile} +first change diff -r a24b370a16ee -r d6ca1334a19d ja/examples/backout.manual.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.manual.clone.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,9 @@ +$ \textbf{cd ..} +$ \textbf{hg clone -r1 myrepo newrepo} +requesting all changes +adding changesets +adding manifests +adding file changes +added 2 changesets with 2 changes to 1 files +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd newrepo} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/backout.manual.heads.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.manual.heads.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg heads} +changeset: +tag: tip +parent: +user: Bryan O'Sullivan + +summary: back out second change + +changeset: +user: Bryan O'Sullivan + +summary: third change + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/backout.manual.log.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.manual.log.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg log --style compact} +3[tip]:1 + back out second change + +2 + third change + +1 + second change + +0 + first change + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/backout.manual.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.manual.merge.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg merge} +merging myfile +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ \textbf{hg commit -m 'merged backout with previous tip'} +$ \textbf{cat myfile} +first change +third change diff -r a24b370a16ee -r d6ca1334a19d ja/examples/backout.manual.parents.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.manual.parents.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg parents} +changeset: +tag: tip +parent: +user: Bryan O'Sullivan + +summary: back out second change + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/backout.non-tip.backout.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.non-tip.backout.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,9 @@ +$ \textbf{echo third change >> myfile} +$ \textbf{hg commit -m 'third change'} +$ \textbf{hg backout --merge -m 'back out second change' 1} +reverting myfile +changeset backs out changeset +merging with changeset +merging myfile +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) diff -r a24b370a16ee -r d6ca1334a19d ja/examples/backout.non-tip.cat.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.non-tip.cat.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cat myfile} +first change +third change diff -r a24b370a16ee -r d6ca1334a19d ja/examples/backout.non-tip.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.non-tip.clone.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,9 @@ +$ \textbf{cd ..} +$ \textbf{hg clone -r1 myrepo non-tip-repo} +requesting all changes +adding changesets +adding manifests +adding file changes +added 2 changesets with 2 changes to 1 files +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd non-tip-repo} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/backout.simple.log.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.simple.log.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ +$ \textbf{hg log --style compact} +2[tip] + back out second change + +1 + second change + +0 + first change + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/backout.simple.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/backout.simple.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,5 @@ +$ \textbf{hg backout -m 'back out second change' tip} +reverting myfile +changeset backs out changeset +$ \textbf{cat myfile} +first change diff -r a24b370a16ee -r d6ca1334a19d ja/examples/bisect --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,89 @@ +#!/bin/bash + +echo '[extensions]' >> $HGRC +#echo 'hgext.bisect =' >> $HGRC + +# XXX There's some kind of horrible nondeterminism in the execution of +# bisect at the moment. Ugh. + +#$ ignore: .* + +#$ name: init + +hg init mybug +cd mybug + +#$ name: commits + +buggy_change=22 + +for (( i = 0; i < 35; i++ )); do + if [[ $i = $buggy_change ]]; then + echo 'i have a gub' > myfile$i + hg commit -q -A -m 'buggy changeset' + else + echo 'nothing to see here, move along' > myfile$i + hg commit -q -A -m 'normal changeset' + fi +done + +#$ name: help + +hg help bisect +hg bisect help + +#$ name: search.init + +hg bisect init + +#$ name: search.bad-init + +hg bisect bad + +#$ name: search.good-init + +hg bisect good 10 + +#$ name: search.step1 + +if grep -q 'i have a gub' * +then + result=bad +else + result=good +fi + +echo this revision is $result +hg bisect $result + +#$ name: search.mytest + +mytest() { + if grep -q 'i have a gub' * + then + result=bad + else + result=good + fi + + echo this revision is $result + hg bisect $result +} + +#$ name: search.step2 + +mytest + +#$ name: search.rest + +mytest +mytest +mytest + +#$ name: search.reset + +hg bisect reset + +#$ name: + +exit 0 diff -r a24b370a16ee -r d6ca1334a19d ja/examples/bisect.commits.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.commits.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/bisect.help.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.help.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/bisect.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.init.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/bisect.search.bad-init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.bad-init.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,1 @@ + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/bisect.search.good-init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.good-init.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/bisect.search.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.init.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/bisect.search.mytest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.mytest.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ + + + + + + + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/bisect.search.reset.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.reset.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,1 @@ + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/bisect.search.rest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.rest.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/bisect.search.step1.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.step1.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,11 @@ + + + + + + + + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/bisect.search.step2.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/bisect.search.step2.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-named --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,74 @@ +#!/bin/bash + +hg init a +cd a +echo hello > myfile +hg commit -A -m 'Initial commit' + +#$ name: branches + +hg tip +hg branches + +#$ name: branch + +hg branch + +#$ name: create + +hg branch foo +hg branch + +#$ name: status + +hg status +hg tip + +#$ name: commit + +echo 'hello again' >> myfile +hg commit -m 'Second commit' +hg tip + +#$ name: rebranch + +hg branch +hg branch bar +echo new file > newfile +hg commit -A -m 'Third commit' +hg tip + +#$ name: parents + +hg parents +hg branches + +#$ name: update-switchy + +hg update foo +hg parents +hg update bar +hg parents + +#$ name: update-nothing + +hg update foo +hg update + +#$ name: foo-commit + +echo something > somefile +hg commit -A -m 'New file' +hg heads + +#$ name: update-bar + +hg update bar +hg update -C bar + +#$ name: merge + +hg branch +hg merge +hg commit -m 'Merge' +hg tip diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-named.branch.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.branch.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg branch} +default diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-named.branches.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.branches.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,9 @@ +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Initial commit + +$ \textbf{hg branches} +default diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-named.commit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.commit.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ +$ \textbf{echo 'hello again' >> myfile} +$ \textbf{hg commit -m 'Second commit'} +$ \textbf{hg tip} +changeset: +branch: foo +tag: tip +user: Bryan O'Sullivan + +summary: Second commit + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-named.create.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.create.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg branch foo} +marked working directory as branch foo +$ \textbf{hg branch} +foo diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-named.foo-commit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.foo-commit.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,18 @@ +$ \textbf{echo something > somefile} +$ \textbf{hg commit -A -m 'New file'} +adding somefile +$ \textbf{hg heads} +changeset: +branch: foo +tag: tip +parent: +user: Bryan O'Sullivan + +summary: New file + +changeset: +branch: bar +user: Bryan O'Sullivan + +summary: Third commit + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-named.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.merge.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,16 @@ +$ \textbf{hg branch} +bar +$ \textbf{hg merge} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ \textbf{hg commit -m 'Merge'} +$ \textbf{hg tip} +changeset: +branch: bar +tag: tip +parent: +parent: +user: Bryan O'Sullivan + +summary: Merge + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-named.parents.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.parents.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,12 @@ +$ \textbf{hg parents} +changeset: +branch: bar +tag: tip +user: Bryan O'Sullivan + +summary: Third commit + +$ \textbf{hg branches} +bar +foo (inactive) +default (inactive) diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-named.rebranch.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.rebranch.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,15 @@ +$ \textbf{hg branch} +foo +$ \textbf{hg branch bar} +marked working directory as branch bar +$ \textbf{echo new file > newfile} +$ \textbf{hg commit -A -m 'Third commit'} +adding newfile +$ \textbf{hg tip} +changeset: +branch: bar +tag: tip +user: Bryan O'Sullivan + +summary: Third commit + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-named.status.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.status.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg status} +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Initial commit + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-named.update-bar.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.update-bar.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg update bar} +abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes +$ \textbf{hg update -C bar} +1 files updated, 0 files merged, 1 files removed, 0 files unresolved diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-named.update-foo.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.update-foo.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg update foo} +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ \textbf{hg update} +0 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{hg parents} +changeset: +branch: foo +user: Bryan O'Sullivan + +summary: Second commit + +$ \textbf{hg update bar} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-named.update-nothing.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.update-nothing.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg update foo} +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ \textbf{hg update} +0 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-named.update-switchy.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.update-switchy.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,19 @@ +$ \textbf{hg update foo} +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ \textbf{hg parents} +changeset: +branch: foo +user: Bryan O'Sullivan + +summary: Second commit + +$ \textbf{hg update bar} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{hg parents} +changeset: +branch: bar +tag: tip +user: Bryan O'Sullivan + +summary: Third commit + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-named.update.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-named.update.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg update foo} +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ \textbf{hg update} +0 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{hg parent} +changeset: +branch: foo +user: Bryan O'Sullivan + +summary: Second commit + +$ \textbf{hg update bar} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-repo --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-repo Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,48 @@ +#!/bin/bash + +hg init myproject +cd myproject +echo hello > myfile +hg commit -A -m 'Initial commit' +cd .. + +#$ name: tag + +cd myproject +hg tag v1.0 + +#$ name: clone + +cd .. +hg clone myproject myproject-1.0.1 + +#$ name: bugfix + +hg clone myproject-1.0.1 my-1.0.1-bugfix +cd my-1.0.1-bugfix +echo 'I fixed a bug using only echo!' >> myfile +hg commit -m 'Important fix for 1.0.1' +#$ ignore: /tmp/branch-repo.* +hg push + +#$ name: new + +cd .. +hg clone myproject my-feature +cd my-feature +echo 'This sure is an exciting new feature!' > mynewfile +hg commit -A -m 'New feature' +hg push + +#$ name: pull + +cd .. +hg clone myproject myproject-merge +cd myproject-merge +hg pull ../myproject-1.0.1 + +#$ name: merge + +hg merge +hg commit -m 'Merge bugfix from 1.0.1 branch' +hg push diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-repo.bugfix.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-repo.bugfix.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,12 @@ +$ \textbf{hg clone myproject-1.0.1 my-1.0.1-bugfix} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd my-1.0.1-bugfix} +$ \textbf{echo 'I fixed a bug using only echo!' >> myfile} +$ \textbf{hg commit -m 'Important fix for 1.0.1'} +$ \textbf{hg push} +pushing to /tmp/branch-repo4rF-PL/myproject-1.0.1 +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-repo.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-repo.clone.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd ..} +$ \textbf{hg clone myproject myproject-1.0.1} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-repo.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-repo.merge.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,11 @@ +$ \textbf{hg merge} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ \textbf{hg commit -m 'Merge bugfix from 1.0.1 branch'} +$ \textbf{hg push} +pushing to +searching for changes +adding changesets +adding manifests +adding file changes +added 2 changesets with 1 changes to 1 files diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-repo.new.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-repo.new.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,14 @@ +$ \textbf{cd ..} +$ \textbf{hg clone myproject my-feature} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd my-feature} +$ \textbf{echo 'This sure is an exciting new feature!' > mynewfile} +$ \textbf{hg commit -A -m 'New feature'} +adding mynewfile +$ \textbf{hg push} +pushing to +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-repo.pull.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-repo.pull.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,12 @@ +$ \textbf{cd ..} +$ \textbf{hg clone myproject myproject-merge} +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd myproject-merge} +$ \textbf{hg pull ../myproject-1.0.1} +pulling from ../myproject-1.0.1 +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +(run 'hg heads' to see heads, 'hg merge' to merge) diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branch-repo.tag.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branch-repo.tag.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{cd myproject} +$ \textbf{hg tag v1.0} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branching --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,63 @@ +#!/bin/bash + +#$ name: init + +hg init main +cd main +echo 'This is a boring feature.' > myfile +hg commit -A -m 'We have reached an important milestone!' + +#$ name: tag + +hg tag v1.0 +hg tip +hg tags + +#$ name: main + +cd ../main +echo 'This is exciting and new!' >> myfile +hg commit -m 'Add a new feature' +cat myfile + +#$ name: update + +cd .. +hg clone -U main main-old +cd main-old +hg update v1.0 +cat myfile + +#$ name: clone + +cd .. +hg clone -rv1.0 main stable + +#$ name: stable + +hg clone stable stable-fix +cd stable-fix +echo 'This is a fix to a boring feature.' > myfile +hg commit -m 'Fix a bug' +#$ ignore: /tmp/branching.* +hg push + +#$ name: + +export HGMERGE=$(mktemp) +echo '#!/bin/sh' > $HGMERGE +echo 'echo "This is a fix to a boring feature." > "$1"' >> $HGMERGE +echo 'echo "This is exciting and new!" >> "$1"' >> $HGMERGE +chmod 700 $HGMERGE + +#$ name: merge + +cd ../main +hg pull ../stable +hg merge +hg commit -m 'Bring in bugfix from stable branch' +cat myfile + +#$ name: + +rm $HGMERGE diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branching.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching.clone.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +$ \textbf{cd ..} +$ \textbf{hg clone -rv1.0 main stable} +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +1 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branching.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching.init.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,5 @@ +$ \textbf{hg init main} +$ \textbf{cd main} +$ \textbf{echo 'This is a boring feature.' > myfile} +$ \textbf{hg commit -A -m 'We have reached an important milestone!'} +adding myfile diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branching.main.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching.main.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,6 @@ +$ \textbf{cd ../main} +$ \textbf{echo 'This is exciting and new!' >> myfile} +$ \textbf{hg commit -m 'Add a new feature'} +$ \textbf{cat myfile} +This is a boring feature. +This is exciting and new! diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branching.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching.merge.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,17 @@ +$ \textbf{cd ../main} +$ \textbf{hg pull ../stable} +pulling from ../stable +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +(run 'hg heads' to see heads, 'hg merge' to merge) +$ \textbf{hg merge} +merging myfile +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ \textbf{hg commit -m 'Bring in bugfix from stable branch'} +$ \textbf{cat myfile} +This is a fix to a boring feature. +This is exciting and new! diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branching.stable.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching.stable.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,12 @@ +$ \textbf{hg clone stable stable-fix} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd stable-fix} +$ \textbf{echo 'This is a fix to a boring feature.' > myfile} +$ \textbf{hg commit -m 'Fix a bug'} +$ \textbf{hg push} +pushing to /tmp/branchingfJgZac/stable +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branching.tag.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching.tag.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,11 @@ +$ \textbf{hg tag v1.0} +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added tag v1.0 for changeset + +$ \textbf{hg tags} +tip +v1.0 diff -r a24b370a16ee -r d6ca1334a19d ja/examples/branching.update.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/branching.update.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +$ \textbf{cd ..} +$ \textbf{hg clone -U main main-old} +$ \textbf{cd main-old} +$ \textbf{hg update v1.0} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cat myfile} +This is a boring feature. diff -r a24b370a16ee -r d6ca1334a19d ja/examples/cmdref --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/cmdref Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,22 @@ +#!/bin/bash + +hg init diff +cd diff +cat > myfile.c <> $HGRC +echo 'showfunc = False' >> $HGRC + +hg diff + +hg diff -p diff -r a24b370a16ee -r d6ca1334a19d ja/examples/cmdref.diff-p.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/cmdref.diff-p.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,22 @@ +$ \textbf{echo '[diff]' >> $HGRC} +$ \textbf{echo 'showfunc = False' >> $HGRC} +$ \textbf{hg diff} +diff -r myfile.c + + +@@ -1,4 +1,4 @@ + int myfunc() + \{ +- return 1; ++ return 10; + \} +$ \textbf{hg diff -p} +diff -r myfile.c + + +@@ -1,4 +1,4 @@ int myfunc() + int myfunc() + \{ +- return 1; ++ return 10; + \} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.copy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,82 @@ +#!/bin/bash + +#$ name: init + +hg init my-copy +cd my-copy +echo line > file +hg add file +hg commit -m 'Added a file' + +#$ name: clone + +cd .. +hg clone my-copy your-copy + +#$ name: copy + +cd my-copy +hg copy file new-file + +#$ name: status + +hg status + +#$ name: status-copy + +hg status -C +hg commit -m 'Copied file' + +#$ name: other + +cd ../your-copy +echo 'new contents' >> file +hg commit -m 'Changed file' + +#$ name: cat + +cat file +cat ../my-copy/new-file + +#$ name: merge + +hg pull ../my-copy +hg merge +cat new-file + +#$ name: + +cd .. +hg init copy-example +cd copy-example +echo a > a +echo b > b +mkdir c +mkdir c/a +echo c > c/a/c +hg ci -Ama + +#$ name: simple + +mkdir k +hg copy a k +ls k + +#$ name: dir-dest + +mkdir d +hg copy a b d +ls d + +#$ name: dir-src + +hg copy c e + +#$ name: dir-src-dest + +hg copy c d + +#$ name: after + +cp a z +hg copy --after a z diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.copy.after.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.after.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{cp a z} +$ \textbf{hg copy --after a z} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.copy.cat.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.cat.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,5 @@ +$ \textbf{cat file} +line +new contents +$ \textbf{cat ../my-copy/new-file} +line diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.copy.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.clone.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd ..} +$ \textbf{hg clone my-copy your-copy} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.copy.copy.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.copy.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{cd my-copy} +$ \textbf{hg copy file new-file} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.copy.dir-dest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.dir-dest.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{mkdir d} +$ \textbf{hg copy a b d} +$ \textbf{ls d} +a b diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.copy.dir-src-dest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.dir-src-dest.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg copy c d} +copying c/a/c to d/c/a/c diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.copy.dir-src.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.dir-src.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg copy c e} +copying c/a/c to e/a/c diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.copy.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.init.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,5 @@ +$ \textbf{hg init my-copy} +$ \textbf{cd my-copy} +$ \textbf{echo line > file} +$ \textbf{hg add file} +$ \textbf{hg commit -m 'Added a file'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.copy.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.merge.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,15 @@ +$ \textbf{hg pull ../my-copy} +pulling from ../my-copy +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +(run 'hg heads' to see heads, 'hg merge' to merge) +$ \textbf{hg merge} +merging file and new-file +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ \textbf{cat new-file} +line +new contents diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.copy.other.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.other.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd ../your-copy} +$ \textbf{echo 'new contents' >> file} +$ \textbf{hg commit -m 'Changed file'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.copy.simple.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.simple.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{mkdir k} +$ \textbf{hg copy a k} +$ \textbf{ls k} +a diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.copy.status-copy.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.status-copy.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg status -C} +A new-file + file +$ \textbf{hg commit -m 'Copied file'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.copy.status.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.copy.status.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg status} +A new-file diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.files --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,93 @@ +#!/bin/bash + +#$ name: add + +hg init add-example +cd add-example +echo a > a +hg status +hg add a +hg status +hg commit -m 'Added one file' +hg status + +#$ name: add-dir + +mkdir b +echo b > b/b +echo c > b/c +mkdir b/d +echo d > b/d/d +hg add b +hg commit -m 'Added all files in subdirectory' + +#$ name: + +cd .. + +#$ name: hidden + +hg init hidden-example +cd hidden-example +mkdir empty +touch empty/.hidden +hg add empty/.hidden +hg commit -m 'Manage an empty-looking directory' +ls empty +cd .. +hg clone hidden-example tmp +ls tmp +ls tmp/empty + +#$ name: remove + +hg init remove-example +cd remove-example +echo a > a +mkdir b +echo b > b/b +hg add a b +hg commit -m 'Small example for file removal' +hg remove a +hg status +hg remove b + +#$ name: + +cd .. + +#$ name: missing +hg init missing-example +cd missing-example +echo a > a +hg add a +hg commit -m 'File about to be missing' +rm a +hg status + +#$ name: remove-after + +hg remove --after a +hg status + +#$ name: recover-missing +hg revert a +cat a +hg status + +#$ name: + +cd .. + +#$ name: addremove + +hg init addremove-example +cd addremove-example +echo a > a +echo b > b +hg addremove + +#$ name: commit-addremove + +echo c > c +hg commit -A -m 'Commit with addremove' diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.files.add-dir.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.add-dir.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ +$ \textbf{mkdir b} +$ \textbf{echo b > b/b} +$ \textbf{echo c > b/c} +$ \textbf{mkdir b/d} +$ \textbf{echo d > b/d/d} +$ \textbf{hg add b} +adding b/b +adding b/c +adding b/d/d +$ \textbf{hg commit -m 'Added all files in subdirectory'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.files.add.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.add.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ +$ \textbf{hg init add-example} +$ \textbf{cd add-example} +$ \textbf{echo a > a} +$ \textbf{hg status} +? a +$ \textbf{hg add a} +$ \textbf{hg status} +A a +$ \textbf{hg commit -m 'Added one file'} +$ \textbf{hg status} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.files.addremove.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.addremove.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg init addremove-example} +$ \textbf{cd addremove-example} +$ \textbf{echo a > a} +$ \textbf{echo b > b} +$ \textbf{hg addremove} +adding a +adding b diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.files.commit-addremove.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.commit-addremove.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{echo c > c} +$ \textbf{hg commit -A -m 'Commit with addremove'} +adding c diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.files.hidden.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.hidden.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg init hidden-example} +$ \textbf{cd hidden-example} +$ \textbf{mkdir empty} +$ \textbf{touch empty/.hidden} +$ \textbf{hg add empty/.hidden} +$ \textbf{hg commit -m 'Manage an empty-looking directory'} +$ \textbf{ls empty} +$ \textbf{cd ..} +$ \textbf{hg clone hidden-example tmp} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{ls tmp} +empty +$ \textbf{ls tmp/empty} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.files.missing.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.missing.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg init missing-example} +$ \textbf{cd missing-example} +$ \textbf{echo a > a} +$ \textbf{hg add a} +$ \textbf{hg commit -m 'File about to be missing'} +$ \textbf{rm a} +$ \textbf{hg status} +! a diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.files.recover-missing.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.recover-missing.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg revert a} +$ \textbf{cat a} +a +$ \textbf{hg status} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.files.remove-after.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.remove-after.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg remove --after a} +$ \textbf{hg status} +R a diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.files.remove.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.files.remove.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg init remove-example} +$ \textbf{cd remove-example} +$ \textbf{echo a > a} +$ \textbf{mkdir b} +$ \textbf{echo b > b/b} +$ \textbf{hg add a b} +adding b/b +$ \textbf{hg commit -m 'Small example for file removal'} +$ \textbf{hg remove a} +$ \textbf{hg status} +R a +$ \textbf{hg remove b} +removing b/b diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.rename --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.rename Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,18 @@ +#!/bin/bash + +hg init a +cd a +echo a > a +hg ci -Ama + +#$ name: rename + +hg rename a b + +#$ name: status + +hg status + +#$ name: status-copy + +hg status -C diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.rename.rename.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.rename.rename.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,1 @@ +$ \textbf{hg rename a b} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.rename.status-copy.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.rename.status-copy.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg status -C} +A b + a +R a diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.rename.status.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.rename.status.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg status} +A b +R a diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.revert --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,74 @@ +#!/bin/bash + +hg init a +cd a +echo 'original content' > file +hg ci -Ama + +#$ name: modify + +cat file +echo unwanted change >> file +hg diff file + +#$ name: unmodify + +hg status +hg revert file +cat file + +#$ name: status + +hg status +cat file.orig + +#$ name: + +rm file.orig + +#$ name: add + +echo oops > oops +hg add oops +hg status oops +hg revert oops +hg status + +#$ name: + +rm oops + +#$ name: remove + +hg remove file +hg status +hg revert file +hg status +ls file + +#$ name: missing + +rm file +hg status +hg revert file +ls file + +#$ name: copy + +hg copy file new-file +hg revert new-file +hg status + +#$ name: + +rm new-file + +#$ name: rename + +hg rename file new-file +hg revert new-file +hg status + +#$ name: rename-orig +hg revert file +hg status diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.revert.add.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.add.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +$ \textbf{echo oops > oops} +$ \textbf{hg add oops} +$ \textbf{hg status oops} +A oops +$ \textbf{hg revert oops} +$ \textbf{hg status} +? oops diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.revert.copy.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.copy.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg copy file new-file} +$ \textbf{hg revert new-file} +$ \textbf{hg status} +? new-file diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.revert.missing.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.missing.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,6 @@ +$ \textbf{rm file} +$ \textbf{hg status} +! file +$ \textbf{hg revert file} +$ \textbf{ls file} +file diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.revert.modify.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.modify.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ +$ \textbf{cat file} +original content +$ \textbf{echo unwanted change >> file} +$ \textbf{hg diff file} +diff -r file + + +@@ -1,1 +1,2 @@ original content + original content ++unwanted change diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.revert.remove.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.remove.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg remove file} +$ \textbf{hg status} +R file +$ \textbf{hg revert file} +$ \textbf{hg status} +$ \textbf{ls file} +file diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.revert.rename-orig.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.rename-orig.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg revert file} +no changes needed to file +$ \textbf{hg status} +? new-file diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.revert.rename.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.rename.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg rename file new-file} +$ \textbf{hg revert new-file} +$ \textbf{hg status} +? new-file diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.revert.status.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.status.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,5 @@ +$ \textbf{hg status} +? file.orig +$ \textbf{cat file.orig} +original content +unwanted change diff -r a24b370a16ee -r d6ca1334a19d ja/examples/daily.revert.unmodify.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/daily.revert.unmodify.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,5 @@ +$ \textbf{hg status} +M file +$ \textbf{hg revert file} +$ \textbf{cat file} +original content diff -r a24b370a16ee -r d6ca1334a19d ja/examples/data/check_whitespace.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/data/check_whitespace.py Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,44 @@ +#!/usr/bin/python + +import re + +def trailing_whitespace(difflines): + added, linenum, header = [], 0, False + + for line in difflines: + if header: + # remember the name of the file that this diff affects + m = re.match(r'(?:---|\+\+\+) ([^\t]+)', line) + if m and m.group(1) != '/dev/null': + filename = m.group(1).split('/', 1)[-1] + if line.startswith('+++ '): + header = False + continue + if line.startswith('diff '): + header = True + continue + # hunk header - save the line number + m = re.match(r'@@ -\d+,\d+ \+(\d+),', line) + if m: + linenum = int(m.group(1)) + continue + # hunk body - check for an added line with trailing whitespace + m = re.match(r'\+.*\s$', line) + if m: + added.append((filename, linenum)) + if line and line[0] in ' +': + linenum += 1 + return added + +if __name__ == '__main__': + import os, sys + + added = trailing_whitespace(os.popen('hg export tip')) + if added: + for filename, linenum in added: + print >> sys.stderr, ('%s, line %d: trailing whitespace added' % + (filename, linenum)) + # save the commit message so we don't need to retype it + os.system('hg tip --template "{desc}" > .hg/commit.save') + print >> sys.stderr, 'commit message saved to .hg/commit.save' + sys.exit(1) diff -r a24b370a16ee -r d6ca1334a19d ja/examples/data/remove-redundant-null-checks.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/data/remove-redundant-null-checks.patch Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,190 @@ + +From: Jesper Juhl + +Remove redundant NULL chck before kfree + tiny CodingStyle cleanup for +drivers/ + +Signed-off-by: Jesper Juhl +Signed-off-by: Andrew Morton +--- + + drivers/char/agp/sgi-agp.c | 5 ++--- + drivers/char/hvcs.c | 11 +++++------ + drivers/message/fusion/mptfc.c | 6 ++---- + drivers/message/fusion/mptsas.c | 3 +-- + drivers/net/fs_enet/fs_enet-mii.c | 3 +-- + drivers/net/wireless/ipw2200.c | 22 ++++++---------------- + drivers/scsi/libata-scsi.c | 4 +--- + drivers/video/au1100fb.c | 3 +-- + 8 files changed, 19 insertions(+), 38 deletions(-) + +diff -puN drivers/char/agp/sgi-agp.c~remove-redundant-null-checks-before-free-in-drivers drivers/char/agp/sgi-agp.c +--- a/drivers/char/agp/sgi-agp.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/char/agp/sgi-agp.c +@@ -329,9 +329,8 @@ static int __devinit agp_sgi_init(void) + + static void __devexit agp_sgi_cleanup(void) + { +- if (sgi_tioca_agp_bridges) +- kfree(sgi_tioca_agp_bridges); +- sgi_tioca_agp_bridges=NULL; ++ kfree(sgi_tioca_agp_bridges); ++ sgi_tioca_agp_bridges = NULL; + } + + module_init(agp_sgi_init); +diff -puN drivers/char/hvcs.c~remove-redundant-null-checks-before-free-in-drivers drivers/char/hvcs.c +--- a/drivers/char/hvcs.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/char/hvcs.c +@@ -1320,11 +1320,12 @@ static struct tty_operations hvcs_ops = + static int hvcs_alloc_index_list(int n) + { + int i; ++ + hvcs_index_list = kmalloc(n * sizeof(hvcs_index_count),GFP_KERNEL); + if (!hvcs_index_list) + return -ENOMEM; + hvcs_index_count = n; +- for(i = 0; i < hvcs_index_count; i++) ++ for (i = 0; i < hvcs_index_count; i++) + hvcs_index_list[i] = -1; + return 0; + } +@@ -1332,11 +1333,9 @@ static int hvcs_alloc_index_list(int n) + static void hvcs_free_index_list(void) + { + /* Paranoia check to be thorough. */ +- if (hvcs_index_list) { +- kfree(hvcs_index_list); +- hvcs_index_list = NULL; +- hvcs_index_count = 0; +- } ++ kfree(hvcs_index_list); ++ hvcs_index_list = NULL; ++ hvcs_index_count = 0; + } + + static int __init hvcs_module_init(void) +diff -puN drivers/message/fusion/mptfc.c~remove-redundant-null-checks-before-free-in-drivers drivers/message/fusion/mptfc.c +--- a/drivers/message/fusion/mptfc.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/message/fusion/mptfc.c +@@ -305,10 +305,8 @@ mptfc_GetFcDevPage0(MPT_ADAPTER *ioc, in + } + + out: +- if (pp0_array) +- kfree(pp0_array); +- if (p0_array) +- kfree(p0_array); ++ kfree(pp0_array); ++ kfree(p0_array); + return rc; + } + +diff -puN drivers/message/fusion/mptsas.c~remove-redundant-null-checks-before-free-in-drivers drivers/message/fusion/mptsas.c +--- a/drivers/message/fusion/mptsas.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/message/fusion/mptsas.c +@@ -1378,8 +1378,7 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc) + return 0; + + out_free_port_info: +- if (hba) +- kfree(hba); ++ kfree(hba); + out: + return error; + } +diff -puN drivers/net/fs_enet/fs_enet-mii.c~remove-redundant-null-checks-before-free-in-drivers drivers/net/fs_enet/fs_enet-mii.c +--- a/drivers/net/fs_enet/fs_enet-mii.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/net/fs_enet/fs_enet-mii.c +@@ -431,8 +431,7 @@ static struct fs_enet_mii_bus *create_bu + return bus; + + err: +- if (bus) +- kfree(bus); ++ kfree(bus); + return ERR_PTR(ret); + } + +diff -puN drivers/net/wireless/ipw2200.c~remove-redundant-null-checks-before-free-in-drivers drivers/net/wireless/ipw2200.c +--- a/drivers/net/wireless/ipw2200.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/net/wireless/ipw2200.c +@@ -1229,12 +1229,6 @@ static struct ipw_fw_error *ipw_alloc_er + return error; + } + +-static void ipw_free_error_log(struct ipw_fw_error *error) +-{ +- if (error) +- kfree(error); +-} +- + static ssize_t show_event_log(struct device *d, + struct device_attribute *attr, char *buf) + { +@@ -1296,10 +1290,9 @@ static ssize_t clear_error(struct device + const char *buf, size_t count) + { + struct ipw_priv *priv = dev_get_drvdata(d); +- if (priv->error) { +- ipw_free_error_log(priv->error); +- priv->error = NULL; +- } ++ ++ kfree(priv->error); ++ priv->error = NULL; + return count; + } + +@@ -1970,8 +1963,7 @@ static void ipw_irq_tasklet(struct ipw_p + struct ipw_fw_error *error = + ipw_alloc_error_log(priv); + ipw_dump_error_log(priv, error); +- if (error) +- ipw_free_error_log(error); ++ kfree(error); + } + #endif + } else { +@@ -11693,10 +11685,8 @@ static void ipw_pci_remove(struct pci_de + } + } + +- if (priv->error) { +- ipw_free_error_log(priv->error); +- priv->error = NULL; +- } ++ kfree(priv->error); ++ priv->error = NULL; + + #ifdef CONFIG_IPW2200_PROMISCUOUS + ipw_prom_free(priv); +diff -puN drivers/scsi/libata-scsi.c~remove-redundant-null-checks-before-free-in-drivers drivers/scsi/libata-scsi.c +--- a/drivers/scsi/libata-scsi.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/scsi/libata-scsi.c +@@ -222,9 +222,7 @@ int ata_cmd_ioctl(struct scsi_device *sc + && copy_to_user(arg + sizeof(args), argbuf, argsize)) + rc = -EFAULT; + error: +- if (argbuf) +- kfree(argbuf); +- ++ kfree(argbuf); + return rc; + } + +diff -puN drivers/video/au1100fb.c~remove-redundant-null-checks-before-free-in-drivers drivers/video/au1100fb.c +--- a/drivers/video/au1100fb.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/video/au1100fb.c +@@ -743,8 +743,7 @@ void __exit au1100fb_cleanup(void) + { + driver_unregister(&au1100fb_driver); + +- if (drv_info.opt_mode) +- kfree(drv_info.opt_mode); ++ kfree(drv_info.opt_mode); + } + + module_init(au1100fb_init); +_ diff -r a24b370a16ee -r d6ca1334a19d ja/examples/extdiff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/extdiff Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,28 @@ +#!/bin/bash + +echo '[extensions]' >> $HGRC +echo 'extdiff =' >> $HGRC + +hg init a +cd a +echo 'The first line.' > myfile +hg ci -Ama +echo 'The second line.' >> myfile + +#$ name: diff + +hg diff + +#$ name: extdiff + +hg extdiff + +#$ name: extdiff-ctx + +#$ ignore: ^\*\*\* a.* + +hg extdiff -o -NprcC5 + +#$ name: + +exit 0 diff -r a24b370a16ee -r d6ca1334a19d ja/examples/extdiff.diff.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/extdiff.diff.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg diff} +diff -r myfile + + +@@ -1,1 +1,2 @@ The first line. + The first line. ++The second line. diff -r a24b370a16ee -r d6ca1334a19d ja/examples/extdiff.extdiff-ctx.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/extdiff.extdiff-ctx.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg extdiff -o -NprcC5} + + +*************** +*** 1 **** + + The first line. ++ The second line. diff -r a24b370a16ee -r d6ca1334a19d ja/examples/extdiff.extdiff.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/extdiff.extdiff.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,6 @@ +$ \textbf{hg extdiff} + + +@@ -1 +1,2 @@ + The first line. ++The second line. diff -r a24b370a16ee -r d6ca1334a19d ja/examples/filenames --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,61 @@ +#!/bin/bash + +hg init a +cd a +mkdir -p examples src/watcher +touch COPYING MANIFEST.in README setup.py +touch examples/performant.py examples/simple.py +touch src/main.py src/watcher/_watcher.c src/watcher/watcher.py src/xyzzy.txt + +#$ name: files + +hg add COPYING README examples/simple.py + +#$ name: dirs + +hg status src + +#$ name: wdir-subdir + +cd src +hg add -n +hg add -n . + +#$ name: wdir-relname + +hg status +hg status `hg root` + +#$ name: glob.star + +hg add 'glob:*.py' + +#$ name: glob.starstar + +cd .. +hg status 'glob:**.py' + +#$ name: glob.star-starstar + +hg status 'glob:*.py' +hg status 'glob:**.py' + +#$ name: glob.question + +hg status 'glob:**.?' + +#$ name: glob.range + +hg status 'glob:**[nr-t]' + +#$ name: glob.group + +hg status 'glob:*.{in,py}' + +#$ name: filter.include + +hg status -I '*.in' + +#$ name: filter.exclude + +hg status -X '**.py' src diff -r a24b370a16ee -r d6ca1334a19d ja/examples/filenames.dirs.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.dirs.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,5 @@ +$ \textbf{hg status src} +? src/main.py +? src/watcher/_watcher.c +? src/watcher/watcher.py +? src/xyzzy.txt diff -r a24b370a16ee -r d6ca1334a19d ja/examples/filenames.files.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.files.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,1 @@ +$ \textbf{hg add COPYING README examples/simple.py} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/filenames.filter.exclude.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.filter.exclude.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg status -X '**.py' src} +? src/watcher/_watcher.c +? src/xyzzy.txt diff -r a24b370a16ee -r d6ca1334a19d ja/examples/filenames.filter.include.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.filter.include.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg status -I '*.in'} +? MANIFEST.in diff -r a24b370a16ee -r d6ca1334a19d ja/examples/filenames.glob.group.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.glob.group.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg status 'glob:*.\{in,py\}'} +? MANIFEST.in +? setup.py diff -r a24b370a16ee -r d6ca1334a19d ja/examples/filenames.glob.question.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.glob.question.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg status 'glob:**.?'} +? src/watcher/_watcher.c diff -r a24b370a16ee -r d6ca1334a19d ja/examples/filenames.glob.range.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.glob.range.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg status 'glob:**[nr-t]'} +? MANIFEST.in +? src/xyzzy.txt diff -r a24b370a16ee -r d6ca1334a19d ja/examples/filenames.glob.star-starstar.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.glob.star-starstar.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg status 'glob:*.py'} +? setup.py +$ \textbf{hg status 'glob:**.py'} +A examples/simple.py +A src/main.py +? examples/performant.py +? setup.py +? src/watcher/watcher.py diff -r a24b370a16ee -r d6ca1334a19d ja/examples/filenames.glob.star.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.glob.star.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg add 'glob:*.py'} +adding main.py diff -r a24b370a16ee -r d6ca1334a19d ja/examples/filenames.glob.starstar.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.glob.starstar.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +$ \textbf{cd ..} +$ \textbf{hg status 'glob:**.py'} +A examples/simple.py +A src/main.py +? examples/performant.py +? setup.py +? src/watcher/watcher.py diff -r a24b370a16ee -r d6ca1334a19d ja/examples/filenames.wdir-relname.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.wdir-relname.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,22 @@ +$ \textbf{hg status} +A COPYING +A README +A examples/simple.py +? MANIFEST.in +? examples/performant.py +? setup.py +? src/main.py +? src/watcher/_watcher.c +? src/watcher/watcher.py +? src/xyzzy.txt +$ \textbf{hg status `hg root`} +A ../COPYING +A ../README +A ../examples/simple.py +? ../MANIFEST.in +? ../examples/performant.py +? ../setup.py +? main.py +? watcher/_watcher.c +? watcher/watcher.py +? xyzzy.txt diff -r a24b370a16ee -r d6ca1334a19d ja/examples/filenames.wdir-subdir.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/filenames.wdir-subdir.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,14 @@ +$ \textbf{cd src} +$ \textbf{hg add -n} +adding ../MANIFEST.in +adding ../examples/performant.py +adding ../setup.py +adding main.py +adding watcher/_watcher.c +adding watcher/watcher.py +adding xyzzy.txt +$ \textbf{hg add -n .} +adding main.py +adding watcher/_watcher.c +adding watcher/watcher.py +adding xyzzy.txt diff -r a24b370a16ee -r d6ca1334a19d ja/examples/hook.msglen --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.msglen Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,14 @@ +#!/bin/sh + +hg init a +cd a +echo '[hooks]' > .hg/hgrc +echo 'pretxncommit.msglen = test `hg tip --template {desc} | wc -c` -ge 10' >> .hg/hgrc + +#$ name: go + +cat .hg/hgrc +echo a > a +hg add a +hg commit -A -m 'too short' +hg commit -A -m 'long enough' diff -r a24b370a16ee -r d6ca1334a19d ja/examples/hook.msglen.go.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.msglen.go.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ +$ \textbf{cat .hg/hgrc} +[hooks] +pretxncommit.msglen = test `hg tip --template \{desc\} | wc -c` -ge 10 +$ \textbf{echo a > a} +$ \textbf{hg add a} +$ \textbf{hg commit -A -m 'too short'} +transaction abort! +rollback completed +abort: pretxncommit.msglen hook exited with status 1 +$ \textbf{hg commit -A -m 'long enough'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/hook.msglen.run.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.msglen.run.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ +$ \textbf{cat .hg/hgrc} +[hooks] +pretxncommit.msglen = test `hg tip --template \{desc\} | wc -c` -ge 10 +$ \textbf{echo a > a} +$ \textbf{hg add a} +$ \textbf{hg commit -A -m 'too short'} +abort: pretxncommit.msglen hook exited with status 1 +transaction abort! +rollback completed +$ \textbf{hg commit -A -m 'long enough'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/hook.simple --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.simple Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,37 @@ +#!/bin/bash + +#$ name: init + +hg init hook-test +cd hook-test +echo '[hooks]' >> .hg/hgrc +echo 'commit = echo committed $HG_NODE' >> .hg/hgrc +cat .hg/hgrc +echo a > a +hg add a +hg commit -m 'testing commit hook' + +#$ name: ext +#$ ignore: ^date of commit.* + +echo 'commit.when = echo -n "date of commit: "; date' >> .hg/hgrc +echo a >> a +hg commit -m 'i have two hooks' + +#$ name: + +echo '#!/bin/sh' >> check_bug_id +echo '# check that a commit comment mentions a numeric bug id' >> check_bug_id +echo 'hg log -r $1 --template {desc} | grep -q "\> check_bug_id +chmod +x check_bug_id + +#$ name: pretxncommit + +cat check_bug_id + +echo 'pretxncommit.bug_id_required = ./check_bug_id $HG_NODE' >> .hg/hgrc + +echo a >> a +hg commit -m 'i am not mentioning a bug id' + +hg commit -m 'i refer you to bug 666' diff -r a24b370a16ee -r d6ca1334a19d ja/examples/hook.simple.ext.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.simple.ext.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,5 @@ +$ \textbf{echo 'commit.when = echo -n "date of commit: "; date' >> .hg/hgrc} +$ \textbf{echo a >> a} +$ \textbf{hg commit -m 'i have two hooks'} +committed + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/hook.simple.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.simple.init.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,11 @@ +$ \textbf{hg init hook-test} +$ \textbf{cd hook-test} +$ \textbf{echo '[hooks]' >> .hg/hgrc} +$ \textbf{echo 'commit = echo committed $HG_NODE' >> .hg/hgrc} +$ \textbf{cat .hg/hgrc} +[hooks] +commit = echo committed $HG_NODE +$ \textbf{echo a > a} +$ \textbf{hg add a} +$ \textbf{hg commit -m 'testing commit hook'} +committed diff -r a24b370a16ee -r d6ca1334a19d ja/examples/hook.simple.pretxncommit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.simple.pretxncommit.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,13 @@ +$ \textbf{cat check_bug_id} +#!/bin/sh +# check that a commit comment mentions a numeric bug id +hg log -r $1 --template \{desc\} | grep -q "\textbackslash{}> .hg/hgrc} +$ \textbf{echo a >> a} +$ \textbf{hg commit -m 'i am not mentioning a bug id'} +transaction abort! +rollback completed +abort: pretxncommit.bug_id_required hook exited with status 1 +$ \textbf{hg commit -m 'i refer you to bug 666'} +committed + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/hook.ws --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.ws Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,31 @@ +#!/bin/bash + +hg init a +cd a +echo '[hooks]' > .hg/hgrc +echo "pretxncommit.whitespace = hg export tip | (! egrep -q '^\\+.*[ \\t]$')" >> .hg/hgrc + +#$ name: simple + +cat .hg/hgrc +echo 'a ' > a +hg commit -A -m 'test with trailing whitespace' +echo 'a' > a +hg commit -A -m 'drop trailing whitespace and try again' + +#$ name: + +echo '[hooks]' > .hg/hgrc +echo "pretxncommit.whitespace = .hg/check_whitespace.py" >> .hg/hgrc +cp $EXAMPLE_DIR/data/check_whitespace.py .hg + +#$ name: better + +cat .hg/hgrc +echo 'a ' >> a +hg commit -A -m 'add new line with trailing whitespace' +sed -i 's, *$,,' a +hg commit -A -m 'trimmed trailing whitespace' + +#$ name: +exit 0 diff -r a24b370a16ee -r d6ca1334a19d ja/examples/hook.ws.better.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.ws.better.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,17 @@ +$ \textbf{cat .hg/hgrc} +[hooks] +pretxncommit.whitespace = .hg/check_whitespace.py +$ \textbf{echo 'a ' >> a} +$ \textbf{hg commit -A -m 'add new line with trailing whitespace'} +a, line 2: trailing whitespace added +commit message saved to .hg/commit.save +transaction abort! +rollback completed +abort: pretxncommit.whitespace hook exited with status 1 +$ \textbf{sed -i 's, *$,,' a} +$ \textbf{hg commit -A -m 'trimmed trailing whitespace'} +a, line 2: trailing whitespace added +commit message saved to .hg/commit.save +transaction abort! +rollback completed +abort: pretxncommit.whitespace hook exited with status 1 diff -r a24b370a16ee -r d6ca1334a19d ja/examples/hook.ws.simple.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/hook.ws.simple.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,11 @@ +$ \textbf{cat .hg/hgrc} +[hooks] +pretxncommit.whitespace = hg export tip | (! egrep -q '^\textbackslash{}+.*[ \textbackslash{}t]$') +$ \textbf{echo 'a ' > a} +$ \textbf{hg commit -A -m 'test with trailing whitespace'} +adding a +transaction abort! +rollback completed +abort: pretxncommit.whitespace hook exited with status 1 +$ \textbf{echo 'a' > a} +$ \textbf{hg commit -A -m 'drop trailing whitespace and try again'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/issue29 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/issue29 Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,22 @@ +#!/bin/bash + +#$ name: go + +hg init issue29 +cd issue29 +echo a > a +hg ci -Ama +echo b > b +hg ci -Amb +hg up 0 +mkdir b +echo b > b/b +hg ci -Amc + +#$ ignore: abort: Is a directory: .* +hg merge + +#$ name: +# This error is expected from the failed merge. + +exit 0 diff -r a24b370a16ee -r d6ca1334a19d ja/examples/issue29.go.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/issue29.go.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,16 @@ +$ \textbf{hg init issue29} +$ \textbf{cd issue29} +$ \textbf{echo a > a} +$ \textbf{hg ci -Ama} +adding a +$ \textbf{echo b > b} +$ \textbf{hg ci -Amb} +adding b +$ \textbf{hg up 0} +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ \textbf{mkdir b} +$ \textbf{echo b > b/b} +$ \textbf{hg ci -Amc} +adding b/b +$ \textbf{hg merge} + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.dodiff --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.dodiff Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,14 @@ +#!/bin/bash + +#$ name: diff + +echo 'this is my first line' > oldfile +echo 'my first line is here' > newfile + +diff -u oldfile newfile > tiny.patch + +cat tiny.patch + +patch < tiny.patch + +cat oldfile diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.dodiff.diff.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.dodiff.diff.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,13 @@ +$ \textbf{echo 'this is my first line' > oldfile} +$ \textbf{echo 'my first line is here' > newfile} +$ \textbf{diff -u oldfile newfile > tiny.patch} +$ \textbf{cat tiny.patch} + + +@@ -1 +1 @@ +-this is my first line ++my first line is here +$ \textbf{patch < tiny.patch} +patching file oldfile +$ \textbf{cat oldfile} +my first line is here diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.guards --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,67 @@ +#!/bin/bash + +echo '[extensions]' >> $HGRC +echo 'hgext.mq =' >> $HGRC + +hg init a +cd a + +#$ name: init + +hg qinit +hg qnew hello.patch +echo hello > hello +hg add hello +hg qrefresh +hg qnew goodbye.patch +echo goodbye > goodbye +hg add goodbye +hg qrefresh + +#$ name: qguard + +hg qguard + +#$ name: qguard.pos + +hg qguard +foo +hg qguard + +#$ name: qguard.neg + +hg qguard hello.patch -quux +hg qguard hello.patch + +#$ name: series + +cat .hg/patches/series + +#$ name: qselect.foo + +hg qpop -a +hg qselect +hg qselect foo +hg qselect + +#$ name: qselect.cat + +cat .hg/patches/guards + +#$ name: qselect.qpush +hg qpush -a + +#$ name: qselect.error + +hg qselect +foo + +#$ name: qselect.quux + +hg qselect quux +hg qpop -a +hg qpush -a + +#$ name: qselect.foobar + +hg qselect foo bar +hg qpop -a +hg qpush -a diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.guards.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.init.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,9 @@ +$ \textbf{hg qinit} +$ \textbf{hg qnew hello.patch} +$ \textbf{echo hello > hello} +$ \textbf{hg add hello} +$ \textbf{hg qrefresh} +$ \textbf{hg qnew goodbye.patch} +$ \textbf{echo goodbye > goodbye} +$ \textbf{hg add goodbye} +$ \textbf{hg qrefresh} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.guards.qguard.neg.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qguard.neg.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg qguard hello.patch -quux} +$ \textbf{hg qguard hello.patch} +hello.patch: -quux diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.guards.qguard.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qguard.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg qguard} +goodbye.patch: unguarded diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.guards.qguard.pos.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qguard.pos.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg qguard +foo} +$ \textbf{hg qguard} +goodbye.patch: +foo diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.guards.qselect.cat.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qselect.cat.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{cat .hg/patches/guards} +foo diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.guards.qselect.error.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qselect.error.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg qselect +foo} +abort: guard '+foo' starts with invalid character: '+' diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.guards.qselect.foo.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qselect.foo.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg qpop -a} +Patch queue now empty +$ \textbf{hg qselect} +no active guards +$ \textbf{hg qselect foo} +number of unguarded, unapplied patches has changed from 1 to 2 +$ \textbf{hg qselect} +foo diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.guards.qselect.foobar.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qselect.foobar.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg qselect foo bar} +number of unguarded, unapplied patches has changed from 0 to 2 +$ \textbf{hg qpop -a} +no patches applied +$ \textbf{hg qpush -a} +applying hello.patch +applying goodbye.patch +Now at: goodbye.patch diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.guards.qselect.qpush.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qselect.qpush.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg qpush -a} +applying hello.patch +applying goodbye.patch +Now at: goodbye.patch diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.guards.qselect.quux.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.qselect.quux.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,6 @@ +$ \textbf{hg qselect quux} +number of guarded, applied patches has changed from 0 to 2 +$ \textbf{hg qpop -a} +Patch queue now empty +$ \textbf{hg qpush -a} +patch series already fully applied diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.guards.series.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.guards.series.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cat .hg/patches/series} +hello.patch #-quux +goodbye.patch #+foo diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.id --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.id Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,28 @@ +#!/bin/sh + +echo '[extensions]' >> $HGRC +echo 'hgext.mq =' >> $HGRC + +hg init a +cd a +hg qinit +echo 'int x;' > test.c +hg ci -Ama + +hg qnew first.patch +echo 'float c;' >> test.c +hg qrefresh + +hg qnew second.patch +echo 'double u;' > other.c +hg add other.c +hg qrefresh + +#$ name: output + +hg qapplied +hg log -r qbase:qtip +hg export second.patch + +#$ name: +exit 0 diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.id.out.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.id.out.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,32 @@ +$ \textbf{hg qapplied} +first.patch +second.patch +$ \textbf{hg log -r qbase:qtip} +changeset: +tag: first.patch +tag: qbase +user: Bryan O'Sullivan + +summary: patch queue: first.patch + +changeset: +tag: second.patch +tag: qtip +tag: tip +user: Bryan O'Sullivan + +summary: patch queue: second.patch + +$ \textbf{hg export second.patch} +# HG changeset patch +# User Bryan O'Sullivan + +# Node ID +# Parent +patch queue: second.patch + +diff -r -r other.c + + +@@ -0,0 +1,1 @@ ++double u; diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.id.output.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.id.output.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,32 @@ +$ \textbf{hg qapplied} +first.patch +second.patch +$ \textbf{hg log -r qbase:qtip} +changeset: +tag: first.patch +tag: qbase +user: Bryan O'Sullivan + +summary: [mq]: first.patch + +changeset: +tag: qtip +tag: second.patch +tag: tip +user: Bryan O'Sullivan + +summary: [mq]: second.patch + +$ \textbf{hg export second.patch} +# HG changeset patch +# User Bryan O'Sullivan + +# Node ID +# Parent +[mq]: second.patch + +diff -r -r other.c + + +@@ -0,0 +1,1 @@ ++double u; diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.qinit-help --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.qinit-help Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +#!/bin/bash + +echo '[extensions]' >> $HGRC +echo 'hgext.mq =' >> $HGRC + +#$ name: help +hg help qinit diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.qinit-help.help.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.qinit-help.help.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,16 @@ +$ \textbf{hg help qinit} +hg qinit [-c] + +init a new queue repository + + The queue repository is unversioned by default. If -c is + specified, qinit will create a separate nested repository + for patches (qinit -c may also be run later to convert + an unversioned patch repository into a versioned one). + You can use qcommit to commit changes to this queue repository. + +options: + + -c --create-repo create queue repository + +use "hg -v help qinit" to show global options diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tarball --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tarball Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,51 @@ +#!/bin/bash + +cp $EXAMPLE_DIR/data/netplug-*.tar.bz2 . +ln -s /bin/true download +export PATH=`pwd`:$PATH + +#$ name: download + +download netplug-1.2.5.tar.bz2 +tar jxf netplug-1.2.5.tar.bz2 +cd netplug-1.2.5 +hg init +hg commit -q --addremove --message netplug-1.2.5 +cd .. +hg clone netplug-1.2.5 netplug + +#$ name: + +cd netplug +echo '[extensions]' >> $HGRC +echo 'hgext.mq =' >> $HGRC +cd .. + +#$ name: qinit + +cd netplug +hg qinit +hg qnew -m 'fix build problem with gcc 4' build-fix.patch +perl -pi -e 's/int addr_len/socklen_t addr_len/' netlink.c +hg qrefresh +hg tip -p + +#$ name: newsource + +hg qpop -a +cd .. +download netplug-1.2.8.tar.bz2 +hg clone netplug-1.2.5 netplug-1.2.8 +cd netplug-1.2.8 +hg locate -0 | xargs -0 rm +cd .. +tar jxf netplug-1.2.8.tar.bz2 +cd netplug-1.2.8 +hg commit --addremove --message netplug-1.2.8 + +#$ name: repush + +cd ../netplug +hg pull ../netplug-1.2.8 +hg qpush -a + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tarball.download.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tarball.download.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +$ \textbf{download netplug-1.2.5.tar.bz2} +$ \textbf{tar jxf netplug-1.2.5.tar.bz2} +$ \textbf{cd netplug-1.2.5} +$ \textbf{hg init} +$ \textbf{hg commit -q --addremove --message netplug-1.2.5} +$ \textbf{cd ..} +$ \textbf{hg clone netplug-1.2.5 netplug} +18 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tarball.newsource.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tarball.newsource.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,12 @@ +$ \textbf{hg qpop -a} +Patch queue now empty +$ \textbf{cd ..} +$ \textbf{download netplug-1.2.8.tar.bz2} +$ \textbf{hg clone netplug-1.2.5 netplug-1.2.8} +18 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd netplug-1.2.8} +$ \textbf{hg locate -0 | xargs -0 rm} +$ \textbf{cd ..} +$ \textbf{tar jxf netplug-1.2.8.tar.bz2} +$ \textbf{cd netplug-1.2.8} +$ \textbf{hg commit --addremove --message netplug-1.2.8} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tarball.qinit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tarball.qinit.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,28 @@ +$ \textbf{cd netplug} +$ \textbf{hg qinit} +$ \textbf{hg qnew -m 'fix build problem with gcc 4' build-fix.patch} +$ \textbf{perl -pi -e 's/int addr_len/socklen_t addr_len/' netlink.c} +$ \textbf{hg qrefresh} +$ \textbf{hg tip -p} +changeset: +tag: qtip +tag: build-fix.patch +tag: tip +tag: qbase +user: Bryan O'Sullivan + +summary: fix build problem with gcc 4 + +diff -r -r netlink.c + + +@@ -275,7 +275,7 @@ netlink_open(void) + exit(1); + \} + +- int addr_len = sizeof(addr); ++ socklen_t addr_len = sizeof(addr); + + if (getsockname(fd, (struct sockaddr *) &addr, &addr_len) == -1) \{ + do_log(LOG_ERR, "Could not get socket details: %m"); + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tarball.repush.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tarball.repush.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,12 @@ +$ \textbf{cd ../netplug} +$ \textbf{hg pull ../netplug-1.2.8} +pulling from ../netplug-1.2.8 +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 12 changes to 12 files +(run 'hg update' to get a working copy) +$ \textbf{hg qpush -a} +applying build-fix.patch +Now at: build-fix.patch diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tools --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tools Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,11 @@ +#!/bin/bash + +cp $EXAMPLE_DIR/data/remove-redundant-null-checks.patch . + +#$ name: tools +diffstat -p1 remove-redundant-null-checks.patch + +filterdiff -i '*/video/*' remove-redundant-null-checks.patch + +#$ name: lsdiff +lsdiff -nvv remove-redundant-null-checks.patch diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tools.lsdiff.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tools.lsdiff.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,21 @@ +$ \textbf{lsdiff -nvv remove-redundant-null-checks.patch} +22 File #1 a/drivers/char/agp/sgi-agp.c + 24 Hunk #1 static int __devinit agp_sgi_init(void) +37 File #2 a/drivers/char/hvcs.c + 39 Hunk #1 static struct tty_operations hvcs_ops = + 53 Hunk #2 static int hvcs_alloc_index_list(int n) +69 File #3 a/drivers/message/fusion/mptfc.c + 71 Hunk #1 mptfc_GetFcDevPage0(MPT_ADAPTER *ioc, in +85 File #4 a/drivers/message/fusion/mptsas.c + 87 Hunk #1 mptsas_probe_hba_phys(MPT_ADAPTER *ioc) +98 File #5 a/drivers/net/fs_enet/fs_enet-mii.c + 100 Hunk #1 static struct fs_enet_mii_bus *create_bu +111 File #6 a/drivers/net/wireless/ipw2200.c + 113 Hunk #1 static struct ipw_fw_error *ipw_alloc_er + 126 Hunk #2 static ssize_t clear_error(struct device + 140 Hunk #3 static void ipw_irq_tasklet(struct ipw_p + 150 Hunk #4 static void ipw_pci_remove(struct pci_de +164 File #7 a/drivers/scsi/libata-scsi.c + 166 Hunk #1 int ata_cmd_ioctl(struct scsi_device *sc +178 File #8 a/drivers/video/au1100fb.c + 180 Hunk #1 void __exit au1100fb_cleanup(void) diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tools.tools.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tools.tools.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,23 @@ +$ \textbf{diffstat -p1 remove-redundant-null-checks.patch} + drivers/char/agp/sgi-agp.c | 5 ++--- + drivers/char/hvcs.c | 11 +++++------ + drivers/message/fusion/mptfc.c | 6 ++---- + drivers/message/fusion/mptsas.c | 3 +-- + drivers/net/fs_enet/fs_enet-mii.c | 3 +-- + drivers/net/wireless/ipw2200.c | 22 ++++++---------------- + drivers/scsi/libata-scsi.c | 4 +--- + drivers/video/au1100fb.c | 3 +-- + 8 files changed, 19 insertions(+), 38 deletions(-) +$ \textbf{filterdiff -i '*/video/*' remove-redundant-null-checks.patch} + + +@@ -743,8 +743,7 @@ void __exit au1100fb_cleanup(void) + \{ + driver_unregister(&au1100fb_driver); + +- if (drv_info.opt_mode) +- kfree(drv_info.opt_mode); ++ kfree(drv_info.opt_mode); + \} + + module_init(au1100fb_init); diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tutorial --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,74 @@ +#!/bin/bash + +echo '[extensions]' >> $HGRC +echo 'hgext.mq =' >> $HGRC + +#$ name: qinit + +hg init mq-sandbox +cd mq-sandbox +echo 'line 1' > file1 +echo 'another line 1' > file2 +hg add file1 file2 +hg commit -m'first change' + +hg qinit + +#$ name: qnew + +hg tip +hg qnew first.patch +hg tip +ls .hg/patches + +#$ name: qrefresh +#$ ignore: \s+200[78]-.* + +echo 'line 2' >> file1 +hg diff +hg qrefresh +hg diff +hg tip --style=compact --patch + +#$ name: qrefresh2 + +echo 'line 3' >> file1 +hg status +hg qrefresh +hg tip --style=compact --patch + +#$ name: qnew2 + +hg qnew second.patch +hg log --style=compact --limit=2 +echo 'line 4' >> file1 +hg qrefresh +hg tip --style=compact --patch +hg annotate file1 + +#$ name: qseries + +hg qseries +hg qapplied + +#$ name: qpop + +hg qapplied +hg qpop +hg qseries +hg qapplied +cat file1 + +#$ name: qpush-a + +hg qpush -a +cat file1 + +#$ name: add + +echo 'file 3, line 1' >> file3 +hg qnew add-file3.patch +hg qnew -f add-file3.patch + +#$ name: +exit 0 diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tutorial.add.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.add.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{echo 'file 3, line 1' >> file3} +$ \textbf{hg qnew add-file3.patch} +$ \textbf{hg qnew -f add-file3.patch} +abort: patch "add-file3.patch" already exists diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tutorial.qinit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qinit.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg init mq-sandbox} +$ \textbf{cd mq-sandbox} +$ \textbf{echo 'line 1' > file1} +$ \textbf{echo 'another line 1' > file2} +$ \textbf{hg add file1 file2} +$ \textbf{hg commit -m'first change'} +$ \textbf{hg qinit} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tutorial.qnew.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qnew.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,20 @@ +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: first change + +$ \textbf{hg qnew first.patch} +$ \textbf{hg tip} +changeset: +tag: qtip +tag: first.patch +tag: tip +tag: qbase +user: Bryan O'Sullivan + +summary: [mq]: first.patch + +$ \textbf{ls .hg/patches} +first.patch series status diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tutorial.qnew2.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qnew2.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,28 @@ +$ \textbf{hg qnew second.patch} +$ \textbf{hg log --style=compact --limit=2} +2[qtip,second.patch,tip] + [mq]: second.patch + +1[first.patch,qbase] + [mq]: first.patch + +$ \textbf{echo 'line 4' >> file1} +$ \textbf{hg qrefresh} +$ \textbf{hg tip --style=compact --patch} +2[qtip,second.patch,tip] + [mq]: second.patch + +diff -r -r file1 + + +@@ -1,3 +1,4 @@ line 1 + line 1 + line 2 + line 3 ++line 4 + +$ \textbf{hg annotate file1} +0: line 1 +1: line 2 +1: line 3 +2: line 4 diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tutorial.qpop.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qpop.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,14 @@ +$ \textbf{hg qapplied} +first.patch +second.patch +$ \textbf{hg qpop} +Now at: first.patch +$ \textbf{hg qseries} +first.patch +second.patch +$ \textbf{hg qapplied} +first.patch +$ \textbf{cat file1} +line 1 +line 2 +line 3 diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tutorial.qpush-a.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qpush-a.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg qpush -a} +applying second.patch +Now at: second.patch +$ \textbf{cat file1} +line 1 +line 2 +line 3 +line 4 diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tutorial.qrefresh.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qrefresh.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,21 @@ +$ \textbf{echo 'line 2' >> file1} +$ \textbf{hg diff} +diff -r file1 + + +@@ -1,1 +1,2 @@ line 1 + line 1 ++line 2 +$ \textbf{hg qrefresh} +$ \textbf{hg diff} +$ \textbf{hg tip --style=compact --patch} +1[qtip,first.patch,tip,qbase] + [mq]: first.patch + +diff -r -r file1 + + +@@ -1,1 +1,2 @@ line 1 + line 1 ++line 2 + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tutorial.qrefresh2.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qrefresh2.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,16 @@ +$ \textbf{echo 'line 3' >> file1} +$ \textbf{hg status} +M file1 +$ \textbf{hg qrefresh} +$ \textbf{hg tip --style=compact --patch} +1[qtip,first.patch,tip,qbase] + [mq]: first.patch + +diff -r -r file1 + + +@@ -1,1 +1,3 @@ line 1 + line 1 ++line 2 ++line 3 + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/mq.tutorial.qseries.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/mq.tutorial.qseries.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,6 @@ +$ \textbf{hg qseries} +first.patch +second.patch +$ \textbf{hg qapplied} +first.patch +second.patch diff -r a24b370a16ee -r d6ca1334a19d ja/examples/rename.divergent --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rename.divergent Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,33 @@ +#!/bin/bash + +hg init orig +cd orig +echo foo > foo +hg ci -A -m 'First commit' +cd .. + +#$ name: clone + +hg clone orig anne +hg clone orig bob + +#$ name: rename.anne + +cd anne +hg mv foo bar +hg ci -m 'Rename foo to bar' + +#$ name: rename.bob + +cd ../bob +hg mv foo quux +hg ci -m 'Rename foo to quux' + +#$ name: merge +# See http://www.selenic.com/mercurial/bts/issue455 + +cd ../orig +hg pull -u ../anne +hg pull ../bob +hg merge +ls diff -r a24b370a16ee -r d6ca1334a19d ja/examples/rename.divergent.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rename.divergent.clone.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg clone orig anne} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{hg clone orig bob} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r a24b370a16ee -r d6ca1334a19d ja/examples/rename.divergent.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rename.divergent.merge.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,26 @@ +# See http://www.selenic.com/mercurial/bts/issue455 +$ \textbf{cd ../orig} +$ \textbf{hg pull -u ../anne} +pulling from ../anne +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +1 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ \textbf{hg pull ../bob} +pulling from ../bob +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +(run 'hg heads' to see heads, 'hg merge' to merge) +$ \textbf{hg merge} +warning: detected divergent renames of foo to: + bar + quux +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ \textbf{ls} +bar quux diff -r a24b370a16ee -r d6ca1334a19d ja/examples/rename.divergent.rename.anne.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rename.divergent.rename.anne.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd anne} +$ \textbf{hg mv foo bar} +$ \textbf{hg ci -m 'Rename foo to bar'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/rename.divergent.rename.bob.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rename.divergent.rename.bob.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd ../bob} +$ \textbf{hg mv foo quux} +$ \textbf{hg ci -m 'Rename foo to quux'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/rollback --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rollback Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,37 @@ +#!/bin/bash + +hg init a +cd a +echo a > a +hg ci -A -m 'First commit' + +echo a >> a + +#$ name: tip + +#$ name: commit + +hg status +echo b > b +hg commit -m 'Add file b' + +#$ name: status + +hg status +hg tip + +#$ name: rollback + +hg rollback +hg tip +hg status + +#$ name: add + +hg add b +hg commit -m 'Add file b, this time for real' + +#$ name: twice + +hg rollback +hg rollback diff -r a24b370a16ee -r d6ca1334a19d ja/examples/rollback.add.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rollback.add.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg add b} +$ \textbf{hg commit -m 'Add file b, this time for real'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/rollback.commit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rollback.commit.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg status} +M a +$ \textbf{echo b > b} +$ \textbf{hg commit -m 'Add file b'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/rollback.rollback.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rollback.rollback.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,12 @@ +$ \textbf{hg rollback} +rolling back last transaction +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: First commit + +$ \textbf{hg status} +M a +? b diff -r a24b370a16ee -r d6ca1334a19d ja/examples/rollback.status.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rollback.status.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,9 @@ +$ \textbf{hg status} +? b +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Add file b + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/rollback.twice.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/rollback.twice.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg rollback} +rolling back last transaction +$ \textbf{hg rollback} +no rollback information available diff -r a24b370a16ee -r d6ca1334a19d ja/examples/run-example --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/run-example Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,391 @@ +#!/usr/bin/env python +# +# This program takes something that resembles a shell script and runs +# it, spitting input (commands from the script) and output into text +# files, for use in examples. + +import cStringIO +import errno +import getopt +import os +import pty +import re +import select +import shutil +import signal +import stat +import sys +import tempfile +import time + +tex_subs = { + '\\': '\\textbackslash{}', + '{': '\\{', + '}': '\\}', + } + +def gensubs(s): + start = 0 + for i, c in enumerate(s): + sub = tex_subs.get(c) + if sub: + yield s[start:i] + start = i + 1 + yield sub + yield s[start:] + +def tex_escape(s): + return ''.join(gensubs(s)) + +def maybe_unlink(name): + try: + os.unlink(name) + return True + except OSError, err: + if err.errno != errno.ENOENT: + raise + return False + +def find_path_to(program): + for p in os.environ.get('PATH', os.defpath).split(os.pathsep): + name = os.path.join(p, program) + if os.access(name, os.X_OK): + return p + return None + +class example: + shell = '/usr/bin/env bash' + ps1 = '__run_example_ps1__ ' + ps2 = '__run_example_ps2__ ' + pi_re = re.compile(r'#\$\s*(name|ignore):\s*(.*)$') + + timeout = 10 + + def __init__(self, name, verbose): + self.name = name + self.verbose = verbose + self.poll = select.poll() + + def parse(self): + '''yield each hunk of input from the file.''' + fp = open(self.name) + cfp = cStringIO.StringIO() + for line in fp: + cfp.write(line) + if not line.rstrip().endswith('\\'): + yield cfp.getvalue() + cfp.seek(0) + cfp.truncate() + + def status(self, s): + sys.stdout.write(s) + if not s.endswith('\n'): + sys.stdout.flush() + + def send(self, s): + if self.verbose: + print >> sys.stderr, '>', self.debugrepr(s) + while s: + count = os.write(self.cfd, s) + s = s[count:] + + def debugrepr(self, s): + rs = repr(s) + limit = 60 + if len(rs) > limit: + return ('%s%s ... [%d bytes]' % (rs[:limit], rs[0], len(s))) + else: + return rs + + timeout = 5 + + def read(self, hint): + events = self.poll.poll(self.timeout * 1000) + if not events: + print >> sys.stderr, ('[%stimed out after %d seconds]' % + (hint, self.timeout)) + os.kill(self.pid, signal.SIGHUP) + return '' + return os.read(self.cfd, 1024) + + def receive(self, hint): + out = cStringIO.StringIO() + while True: + try: + if self.verbose: + sys.stderr.write('< ') + s = self.read(hint) + except OSError, err: + if err.errno == errno.EIO: + return '', '' + raise + if self.verbose: + print >> sys.stderr, self.debugrepr(s) + out.write(s) + s = out.getvalue() + if s.endswith(self.ps1): + return self.ps1, s.replace('\r\n', '\n')[:-len(self.ps1)] + if s.endswith(self.ps2): + return self.ps2, s.replace('\r\n', '\n')[:-len(self.ps2)] + + def sendreceive(self, s, hint): + self.send(s) + ps, r = self.receive(hint) + if r.startswith(s): + r = r[len(s):] + return ps, r + + def run(self): + ofp = None + basename = os.path.basename(self.name) + self.status('running %s ' % basename) + tmpdir = tempfile.mkdtemp(prefix=basename) + + # remove the marker file that we tell make to use to see if + # this run succeeded + maybe_unlink(self.name + '.run') + + rcfile = os.path.join(tmpdir, '.hgrc') + rcfp = open(rcfile, 'w') + print >> rcfp, '[ui]' + print >> rcfp, "username = Bryan O'Sullivan " + + rcfile = os.path.join(tmpdir, '.bashrc') + rcfp = open(rcfile, 'w') + print >> rcfp, 'PS1="%s"' % self.ps1 + print >> rcfp, 'PS2="%s"' % self.ps2 + print >> rcfp, 'unset HISTFILE' + path = ['/usr/bin', '/bin'] + hg = find_path_to('hg') + if hg and hg not in path: + path.append(hg) + def re_export(envar): + v = os.getenv(envar) + if v is not None: + print >> rcfp, 'export ' + envar + '=' + v + print >> rcfp, 'export PATH=' + ':'.join(path) + re_export('PYTHONPATH') + print >> rcfp, 'export EXAMPLE_DIR="%s"' % os.getcwd() + print >> rcfp, 'export HGMERGE=merge' + print >> rcfp, 'export LANG=C' + print >> rcfp, 'export LC_ALL=C' + print >> rcfp, 'export TZ=GMT' + print >> rcfp, 'export HGRC="%s/.hgrc"' % tmpdir + print >> rcfp, 'export HGRCPATH=$HGRC' + print >> rcfp, 'cd %s' % tmpdir + rcfp.close() + sys.stdout.flush() + sys.stderr.flush() + self.pid, self.cfd = pty.fork() + if self.pid == 0: + cmdline = ['/usr/bin/env', '-i', 'bash', '--noediting', + '--noprofile', '--norc'] + try: + os.execv(cmdline[0], cmdline) + except OSError, err: + print >> sys.stderr, '%s: %s' % (cmdline[0], err.strerror) + sys.stderr.flush() + os._exit(0) + self.poll.register(self.cfd, select.POLLIN | select.POLLERR | + select.POLLHUP) + + prompts = { + '': '', + self.ps1: '$', + self.ps2: '>', + } + + ignore = [ + r'\d+:[0-9a-f]{12}', # changeset number:hash + r'[0-9a-f]{40}', # long changeset hash + r'[0-9a-f]{12}', # short changeset hash + r'^(?:---|\+\+\+) .*', # diff header with dates + r'^date:.*', # date + #r'^diff -r.*', # "diff -r" is followed by hash + r'^# Date \d+ \d+', # hg patch header + ] + + err = False + read_hint = '' + + try: + try: + # eat first prompt string from shell + self.read(read_hint) + # setup env and prompt + ps, output = self.sendreceive('source %s\n' % rcfile, + read_hint) + for hunk in self.parse(): + # is this line a processing instruction? + m = self.pi_re.match(hunk) + if m: + pi, rest = m.groups() + if pi == 'name': + self.status('.') + out = rest + if out in ('err', 'lxo', 'out', 'run', 'tmp'): + print >> sys.stderr, ('%s: illegal section ' + 'name %r' % + (self.name, out)) + return 1 + assert os.sep not in out + if ofp is not None: + ofp.close() + err |= self.rename_output(ofp_basename, ignore) + if out: + ofp_basename = '%s.%s' % (self.name, out) + read_hint = ofp_basename + ' ' + ofp = open(ofp_basename + '.tmp', 'w') + else: + ofp = None + elif pi == 'ignore': + ignore.append(rest) + elif hunk.strip(): + # it's something we should execute + newps, output = self.sendreceive(hunk, read_hint) + if not ofp: + continue + # first, print the command we ran + if not hunk.startswith('#'): + nl = hunk.endswith('\n') + hunk = ('%s \\textbf{%s}' % + (prompts[ps], + tex_escape(hunk.rstrip('\n')))) + if nl: hunk += '\n' + ofp.write(hunk) + # then its output + ofp.write(tex_escape(output)) + ps = newps + self.status('\n') + except: + print >> sys.stderr, '(killed)' + os.kill(self.pid, signal.SIGKILL) + pid, rc = os.wait() + raise + else: + try: + ps, output = self.sendreceive('exit\n', read_hint) + if ofp is not None: + ofp.write(output) + ofp.close() + err |= self.rename_output(ofp_basename, ignore) + os.close(self.cfd) + except IOError: + pass + os.kill(self.pid, signal.SIGTERM) + pid, rc = os.wait() + err = err or rc + if err: + if os.WIFEXITED(rc): + print >> sys.stderr, '(exit %s)' % os.WEXITSTATUS(rc) + elif os.WIFSIGNALED(rc): + print >> sys.stderr, '(signal %s)' % os.WTERMSIG(rc) + else: + open(self.name + '.run', 'w') + return err + finally: + shutil.rmtree(tmpdir) + + def rename_output(self, base, ignore): + mangle_re = re.compile('(?:' + '|'.join(ignore) + ')') + def mangle(s): + return mangle_re.sub('', s) + def matchfp(fp1, fp2): + while True: + s1 = mangle(fp1.readline()) + s2 = mangle(fp2.readline()) + if cmp(s1, s2): + break + if not s1: + return True + return False + + oldname = base + '.out' + tmpname = base + '.tmp' + errname = base + '.err' + errfp = open(errname, 'w+') + for line in open(tmpname): + errfp.write(mangle_re.sub('', line)) + os.rename(tmpname, base + '.lxo') + errfp.seek(0) + try: + oldfp = open(oldname) + except IOError, err: + if err.errno != errno.ENOENT: + raise + os.rename(errname, oldname) + return False + if matchfp(oldfp, errfp): + os.unlink(errname) + return False + else: + print >> sys.stderr, '\nOutput of %s has changed!' % base + os.system('diff -u %s %s 1>&2' % (oldname, errname)) + return True + +def print_help(exit, msg=None): + if msg: + print >> sys.stderr, 'Error:', msg + print >> sys.stderr, 'Usage: run-example [options] [test...]' + print >> sys.stderr, 'Options:' + print >> sys.stderr, ' -a --all run all tests in this directory' + print >> sys.stderr, ' -h --help print this help message' + print >> sys.stderr, ' -v --verbose display extra debug output' + sys.exit(exit) + +def main(path='.'): + opts, args = getopt.getopt(sys.argv[1:], '?ahv', + ['all', 'help', 'verbose']) + verbose = False + run_all = False + for o, a in opts: + if o in ('-h', '-?', '--help'): + print_help(0) + if o in ('-a', '--all'): + run_all = True + if o in ('-v', '--verbose'): + verbose = True + errs = 0 + if args: + for a in args: + try: + st = os.lstat(a) + except OSError, err: + print >> sys.stderr, '%s: %s' % (a, err.strerror) + errs += 1 + continue + if stat.S_ISREG(st.st_mode) and st.st_mode & 0111: + if example(a, verbose).run(): + errs += 1 + else: + print >> sys.stderr, '%s: not a file, or not executable' % a + errs += 1 + elif run_all: + names = os.listdir(path) + names.sort() + for name in names: + if name == 'run-example' or name.startswith('.'): continue + if name.endswith('.out') or name.endswith('~'): continue + if name.endswith('.run'): continue + pathname = os.path.join(path, name) + try: + st = os.lstat(pathname) + except OSError, err: + # could be an output file that was removed while we ran + if err.errno != errno.ENOENT: + raise + continue + if stat.S_ISREG(st.st_mode) and st.st_mode & 0111: + if example(pathname, verbose).run(): + errs += 1 + print >> open(os.path.join(path, '.run'), 'w'), time.asctime() + else: + print_help(1, msg='no test names given, and --all not provided') + return errs + +if __name__ == '__main__': + try: + sys.exit(main()) + except KeyboardInterrupt: + print >> sys.stderr, 'interrupted!' + sys.exit(1) diff -r a24b370a16ee -r d6ca1334a19d ja/examples/svn-long.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/svn-long.txt Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,11 @@ +------------------------------------------------------------------------ +r9653 | sean.hefty | 2006-09-27 14:39:55 -0700 (Wed, 27 Sep 2006) | 5 lines +Changed paths: + M /gen2/trunk/src/linux-kernel/infiniband/core/cma.c + +On reporting a route error, also include the status for the error, +rather than indicating a status of 0 when an error has occurred. + +Signed-off-by: Sean Hefty + +------------------------------------------------------------------------ diff -r a24b370a16ee -r d6ca1334a19d ja/examples/svn-short.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/svn-short.txt Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,9 @@ +------------------------------------------------------------------------ +r9653 | sean.hefty | 2006-09-27 14:39:55 -0700 (Wed, 27 Sep 2006) | 5 lines + +On reporting a route error, also include the status for the error, +rather than indicating a status of 0 when an error has occurred. + +Signed-off-by: Sean Hefty + +------------------------------------------------------------------------ diff -r a24b370a16ee -r d6ca1334a19d ja/examples/svn.style --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/svn.style Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +header = '------------------------------------------------------------------------\n\n' +changeset = svn.template diff -r a24b370a16ee -r d6ca1334a19d ja/examples/svn.template --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/svn.template Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,5 @@ +r{rev} | {author|user} | {date|isodate} ({date|rfc822date}) + +{desc|strip|fill76} + +------------------------------------------------------------------------ diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tag --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,44 @@ +#!/bin/bash + +#$ name: init + +hg init mytag +cd mytag + +echo hello > myfile +hg commit -A -m 'Initial commit' + +#$ name: tag + +hg tag v1.0 + +#$ name: tags + +hg tags + +#$ name: log + +hg log + +#$ name: log.v1.0 + +echo goodbye > myfile2 +hg commit -A -m 'Second commit' +hg log -r v1.0 + +#$ name: remove + +hg tag --remove v1.0 +hg tags + +#$ name: replace + +hg tag -r 1 v1.1 +hg tags +hg tag -r 2 v1.1 +hg tag -f -r 2 v1.1 +hg tags + +#$ name: tip + +hg tip diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tag.init.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.init.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,5 @@ +$ \textbf{hg init mytag} +$ \textbf{cd mytag} +$ \textbf{echo hello > myfile} +$ \textbf{hg commit -A -m 'Initial commit'} +adding myfile diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tag.log.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.log.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg log} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added tag v1.0 for changeset + +changeset: +tag: v1.0 +user: Bryan O'Sullivan + +summary: Initial commit + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tag.log.v1.0.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.log.v1.0.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ +$ \textbf{echo goodbye > myfile2} +$ \textbf{hg commit -A -m 'Second commit'} +adding myfile2 +$ \textbf{hg log -r v1.0} +changeset: +tag: v1.0 +user: Bryan O'Sullivan + +summary: Initial commit + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tag.remove.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.remove.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg tag --remove v1.0} +$ \textbf{hg tags} +tip diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tag.replace.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.replace.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ +$ \textbf{hg tag -r 1 v1.1} +$ \textbf{hg tags} +tip +v1.1 +$ \textbf{hg tag -r 2 v1.1} +abort: a tag named v1.1 already exists (use -f to force) +$ \textbf{hg tag -f -r 2 v1.1} +$ \textbf{hg tags} +tip +v1.1 diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tag.tag.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.tag.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,1 @@ +$ \textbf{hg tag v1.0} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tag.tags.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.tags.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{hg tags} +tip +v1.0 diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tag.tip.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tag.tip.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added tag v1.1 for changeset + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.simple --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,96 @@ +#!/bin/bash + +# So many different bits of random output, it would be a nightmare to +# ignore each individually. +#$ ignore: .* + +hg init myrepo +cd myrepo +echo hello > hello +hg commit -Am'added hello' + +echo hello >> hello +echo goodbye > goodbye +echo ' added line to end of <> file.' > ../msg +echo '' >> ../msg +echo 'in addition, added a file with the helpful name (at least i hope that some might consider it so) of goodbye.' >> ../msg + +hg commit -Al../msg + +hg tag mytag +hg tag v0.1 + +#$ name: normal + +hg log -r1 + +#$ name: compact + +hg log --style compact + +#$ name: changelog + +hg log --style changelog + +#$ name: simplest + +hg log -r1 --template 'i saw a changeset\n' + +#$ name: simplesub + +hg log --template 'i saw a changeset: {desc}\n' + +#$ name: keywords + +hg log -r1 --template 'author: {author}\n' +hg log -r1 --template 'desc:\n{desc}\n' +hg log -r1 --template 'files: {files}\n' +hg log -r1 --template 'file_adds: {file_adds}\n' +hg log -r1 --template 'file_dels: {file_dels}\n' +hg log -r1 --template 'node: {node}\n' +hg log -r1 --template 'parents: {parents}\n' +hg log -r1 --template 'rev: {rev}\n' +hg log -r1 --template 'tags: {tags}\n' + +#$ name: datekeyword + +hg log -r1 --template 'date: {date}\n' +hg log -r1 --template 'date: {date|isodate}\n' + +#$ name: manyfilters + +hg log -r1 --template '{author}\n' +hg log -r1 --template '{author|domain}\n' +hg log -r1 --template '{author|email}\n' +hg log -r1 --template '{author|obfuscate}\n' | cut -c-76 +hg log -r1 --template '{author|person}\n' +hg log -r1 --template '{author|user}\n' + +hg log -r1 --template 'looks almost right, but actually garbage: {date}\n' +hg log -r1 --template '{date|age}\n' +hg log -r1 --template '{date|date}\n' +hg log -r1 --template '{date|hgdate}\n' +hg log -r1 --template '{date|isodate}\n' +hg log -r1 --template '{date|rfc822date}\n' +hg log -r1 --template '{date|shortdate}\n' + +hg log -r1 --template '{desc}\n' | cut -c-76 +hg log -r1 --template '{desc|addbreaks}\n' | cut -c-76 +hg log -r1 --template '{desc|escape}\n' | cut -c-76 +hg log -r1 --template '{desc|fill68}\n' +hg log -r1 --template '{desc|fill76}\n' +hg log -r1 --template '{desc|firstline}\n' +hg log -r1 --template '{desc|strip}\n' | cut -c-76 +hg log -r1 --template '{desc|tabindent}\n' | expand | cut -c-76 + +hg log -r1 --template '{node}\n' +hg log -r1 --template '{node|short}\n' + +#$ name: combine + +hg log -r1 --template 'description:\n\t{desc|strip|fill68|tabindent}\n' + +#$ name: rev + +echo 'changeset = "rev: {rev}\n"' > rev +hg log -l1 --style ./rev diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.simple.changelog.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.changelog.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.simple.combine.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.combine.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,6 @@ + + + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.simple.compact.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.compact.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.simple.datekeyword.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.datekeyword.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.simple.keywords.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.keywords.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.simple.manyfilters.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.manyfilters.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.simple.normal.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.normal.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ + + + + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.simple.rev.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.rev.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.simple.simplest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.simplest.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.simple.simplesub.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.simple.simplesub.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ + + + + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.svnstyle --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,70 @@ +#!/bin/bash + +svn() { + cat $EXAMPLE_DIR/svn-short.txt +} + +#$ name: short + +svn log -r9653 + +#$ name: + +hg init myrepo +cd myrepo + +echo hello > hello +hg commit -Am'added hello' + +echo hello >> hello +echo goodbye > goodbye +echo ' added line to end of <> file.' > ../msg +echo '' >> ../msg +echo 'in addition, added a file with the helpful name (at least i hope that some might consider it so) of goodbye.' >> ../msg + +hg commit -Al../msg + +hg tag mytag +hg tag v0.1 + +echo 'changeset = "{node|short}\n"' > svn.style + +#$ name: id + +hg log -r0 --template '{node}' + +#$ name: simplest + +cat svn.style +hg log -r1 --style svn.style + +#$ name: + +echo 'changeset =' > broken.style + +#$ name: syntax.input + +cat broken.style + +#$ name: syntax.error + +hg log -r1 --style broken.style + +#$ name: + +cp $EXAMPLE_DIR/svn.style . +cp $EXAMPLE_DIR/svn.template . + +#$ name: template + +cat svn.template + +#$ name: style + +cat svn.style + +#$ name: result +#$ ignore: \| 200[78].* + +hg log -r1 --style svn.style + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.svnstyle.id.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.id.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,1 @@ +$ \textbf{hg log -r0 --template '\{node\}'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.svnstyle.result.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.result.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,11 @@ +$ \textbf{hg log -r1 --style svn.style} +------------------------------------------------------------------------ + +r1 | bos + +added line to end of <> file. + +in addition, added a file with the helpful name (at least i hope that some +might consider it so) of goodbye. + +------------------------------------------------------------------------ diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.svnstyle.short.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.short.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ +$ \textbf{svn log -r9653} +------------------------------------------------------------------------ +r9653 | sean.hefty | 2006-09-27 14:39:55 -0700 (Wed, 27 Sep 2006) | 5 lines + +On reporting a route error, also include the status for the error, +rather than indicating a status of 0 when an error has occurred. + +Signed-off-by: Sean Hefty + +------------------------------------------------------------------------ diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.svnstyle.simplest.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.simplest.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{cat svn.style} +changeset = "\{node|short\}\textbackslash{}n" +$ \textbf{hg log -r1 --style svn.style} + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.svnstyle.style.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.style.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cat svn.style} +header = '------------------------------------------------------------------------\textbackslash{}n\textbackslash{}n' +changeset = svn.template diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.svnstyle.syntax.error.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.syntax.error.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg log -r1 --style broken.style} +abort: broken.style:1: parse error diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.svnstyle.syntax.input.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.syntax.input.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{cat broken.style} +changeset = diff -r a24b370a16ee -r d6ca1334a19d ja/examples/template.svnstyle.template.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/template.svnstyle.template.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,6 @@ +$ \textbf{cat svn.template} +r\{rev\} | \{author|user\} | \{date|isodate\} (\{date|rfc822date\}) + +\{desc|strip|fill76\} + +------------------------------------------------------------------------ diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,178 @@ +#!/bin/bash + +#$ name: version + +hg version + +#$ name: help + +hg help init + +#$ name: clone + +hg clone http://hg.serpentine.com/tutorial/hello + +#$ name: ls +#$ ignore: ^drwx.* +#$ ignore: ^total \d+ + +ls -l +ls hello + +#$ name: ls-a + +cd hello +ls -a + +#$ name: log + +hg log + +#$ name: log-r + +hg log -r 3 +hg log -r ff5d7b70a2a9 +hg log -r 1 -r 4 + +#$ name: log.range + +hg log -r 2:4 + +#$ name: log-v + +hg log -v -r 3 + +#$ name: log-vp + +hg log -v -p -r 2 + +#$ name: reclone + +cd .. +hg clone hello my-hello +cd my-hello + +#$ name: sed + +sed -i '/printf/a\\tprintf("hello again!\\n");' hello.c + +#$ name: status + +ls +hg status + +#$ name: diff + +hg diff + +#$ name: + +export HGEDITOR='echo Added an extra line of output >' + +#$ name: commit + +hg commit + +#$ name: tip + +hg tip -vp + +#$ name: clone-pull + +cd .. +hg clone hello hello-pull + +#$ name: incoming + +cd hello-pull +hg incoming ../my-hello + +#$ name: pull + +hg tip +hg pull ../my-hello +hg tip + +#$ name: update + +grep printf hello.c +hg update tip +grep printf hello.c + +#$ name: parents + +hg parents + +#$ name: older + +hg update 2 +hg parents +hg update + +#$ name: clone-push + +cd .. +hg clone hello hello-push + +#$ name: outgoing + +cd my-hello +hg outgoing ../hello-push + +#$ name: push + +hg push ../hello-push + +#$ name: push.nothing + +hg push ../hello-push + +#$ name: outgoing.net + +hg outgoing http://hg.serpentine.com/tutorial/hello + +#$ name: push.net + +hg push http://hg.serpentine.com/tutorial/hello + +#$ name: merge.clone + +cd .. +hg clone hello my-new-hello +cd my-new-hello +sed -i '/printf/i\\tprintf("once more, hello.\\n");' hello.c +hg commit -m 'A new hello for a new day.' + +#$ name: merge.cat + +cat hello.c +cat ../my-hello/hello.c + +#$ name: merge.pull + +hg pull ../my-hello + +#$ name: merge.heads + +hg heads + +#$ name: merge.update + +hg update + +#$ name: merge.merge + +hg merge + +#$ name: merge.parents + +hg parents +cat hello.c + +#$ name: merge.commit + +hg commit -m 'Merged changes' + +#$ name: merge.tip + +hg tip diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour-merge-conflict --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour-merge-conflict Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,72 @@ +#!/bin/bash + +hg init scam +cd scam + +#$ name: wife + +cat > letter.txt < letter.txt < letter.txt <]{7} /tmp/.* + +export HGMERGE=merge +hg merge +cat letter.txt + +#$ name: commit + +cat > letter.txt < letter.txt < \textbf{Greetings!} +> \textbf{I am Bryan O'Sullivan, no relation of the former} +> \textbf{Nigerian dictator Sani Abacha.} +> \textbf{EOF} +$ \textbf{hg commit -m 'Send me your money'} +$ \textbf{hg tip} +changeset: +tag: tip +parent: +parent: +user: Bryan O'Sullivan + +summary: Send me your money + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour-merge-conflict.cousin.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour-merge-conflict.cousin.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ +$ \textbf{cd ..} +$ \textbf{hg clone scam scam-cousin} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd scam-cousin} +$ \textbf{cat > letter.txt < \textbf{Greetings!} +> \textbf{I am Shehu Musa Abacha, cousin to the former} +> \textbf{Nigerian dictator Sani Abacha.} +> \textbf{EOF} +$ \textbf{hg commit -m '419 scam, with cousin'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour-merge-conflict.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour-merge-conflict.merge.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,17 @@ +$ \textbf{export HGMERGE=merge} +$ \textbf{hg merge} +merging letter.txt +merge: warning: conflicts during merge +merging letter.txt failed! +0 files updated, 0 files merged, 0 files removed, 1 files unresolved +There are unresolved merges, you can redo the full merge using: + hg update -C 1 + hg merge 2 +$ \textbf{cat letter.txt} +Greetings! + +I am Shehu Musa Abacha, cousin to the former +======= +I am Alhaji Abba Abacha, son of the former + +Nigerian dictator Sani Abacha. diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour-merge-conflict.pull.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour-merge-conflict.pull.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,13 @@ +$ \textbf{cd ..} +$ \textbf{hg clone scam-cousin scam-merge} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd scam-merge} +$ \textbf{hg pull -u ../scam-son} +pulling from ../scam-son +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +not updating, since new heads added +(run 'hg heads' to see heads, 'hg merge' to merge) diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour-merge-conflict.son.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour-merge-conflict.son.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ +$ \textbf{cd ..} +$ \textbf{hg clone scam scam-son} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd scam-son} +$ \textbf{cat > letter.txt < \textbf{Greetings!} +> \textbf{I am Alhaji Abba Abacha, son of the former} +> \textbf{Nigerian dictator Sani Abacha.} +> \textbf{EOF} +$ \textbf{hg commit -m '419 scam, with son'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour-merge-conflict.wife.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour-merge-conflict.wife.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +$ \textbf{cat > letter.txt < \textbf{Greetings!} +> \textbf{I am Mariam Abacha, the wife of former} +> \textbf{Nigerian dictator Sani Abacha.} +> \textbf{EOF} +$ \textbf{hg add letter.txt} +$ \textbf{hg commit -m '419 scam, first draft'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.clone-pull.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.clone-pull.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd ..} +$ \textbf{hg clone hello hello-pull} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.clone-push.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.clone-push.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd ..} +$ \textbf{hg clone hello hello-push} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.clone.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg clone http://hg.serpentine.com/tutorial/hello} +destination directory: hello +requesting all changes +adding changesets +adding manifests +adding file changes +added 5 changesets with 5 changes to 2 files +2 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.commit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.commit.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,1 @@ +$ \textbf{hg commit} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.diff.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.diff.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,11 @@ +$ \textbf{hg diff} +diff -r hello.c + + +@@ -8,5 +8,6 @@ int main(int argc, char **argv) + int main(int argc, char **argv) + \{ + printf("hello, world!\textbackslash{}"); ++ printf("hello again!\textbackslash{}n"); + return 0; + \} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.help.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.help.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,20 @@ +$ \textbf{hg help init} +hg init [-e CMD] [--remotecmd CMD] [DEST] + +create a new repository in the given directory + + Initialize a new repository in the given directory. If the given + directory does not exist, it is created. + + If no directory is given, the current directory is used. + + It is possible to specify an ssh:// URL as the destination. + Look at the help text for the pull command for important details + about ssh:// URLs. + +options: + + -e --ssh specify ssh command to use + --remotecmd specify hg command to run on the remote side + +use "hg -v help init" to show global options diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.incoming.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.incoming.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ +$ \textbf{cd hello-pull} +$ \textbf{hg incoming ../my-hello} +comparing with ../my-hello +searching for changes +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added an extra line of output + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.log-r.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.log-r.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,24 @@ +$ \textbf{hg log -r 3} +changeset: +user: Bryan O'Sullivan + +summary: Get make to generate the final binary from a .o file. + +$ \textbf{hg log -r } +changeset: +user: Bryan O'Sullivan + +summary: Get make to generate the final binary from a .o file. + +$ \textbf{hg log -r 1 -r 4} +changeset: +user: mpm@selenic.com + +summary: Create a makefile + +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Trim comments. + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.log-v.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.log-v.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,9 @@ +$ \textbf{hg log -v -r 3} +changeset: +user: Bryan O'Sullivan + +files: Makefile +description: +Get make to generate the final binary from a .o file. + + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.log-vp.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.log-vp.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,21 @@ +$ \textbf{hg log -v -p -r 2} +changeset: +user: Bryan O'Sullivan + +files: hello.c +description: +Introduce a typo into hello.c. + + +diff -r -r hello.c + + +@@ -11,6 +11,6 @@ + + int main(int argc, char **argv) + \{ +- printf("hello, world!\textbackslash{}n"); ++ printf("hello, world!\textbackslash{}"); + return 0; + \} + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.log.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.log.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,27 @@ +$ \textbf{hg log} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Trim comments. + +changeset: +user: Bryan O'Sullivan + +summary: Get make to generate the final binary from a .o file. + +changeset: +user: Bryan O'Sullivan + +summary: Introduce a typo into hello.c. + +changeset: +user: mpm@selenic.com + +summary: Create a makefile + +changeset: +user: mpm@selenic.com + +summary: Create a standard "hello, world" program + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.log.range.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.log.range.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,17 @@ +$ \textbf{hg log -r 2:4} +changeset: +user: Bryan O'Sullivan + +summary: Introduce a typo into hello.c. + +changeset: +user: Bryan O'Sullivan + +summary: Get make to generate the final binary from a .o file. + +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Trim comments. + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.ls-a.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.ls-a.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +$ \textbf{cd hello} +$ \textbf{ls -a} +. .. .hg Makefile hello.c diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.ls.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.ls.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,5 @@ +$ \textbf{ls -l} +total 4 + +$ \textbf{ls hello} +Makefile hello.c diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.merge.cat.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.cat.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,28 @@ +$ \textbf{cat hello.c} +/* + * Placed in the public domain by Bryan O'Sullivan. This program is + * not covered by patents in the United States or other countries. + */ + +#include + +int main(int argc, char **argv) +\{ + printf("once more, hello.\textbackslash{}n"); + printf("hello, world!\textbackslash{}"); + return 0; +\} +$ \textbf{cat ../my-hello/hello.c} +/* + * Placed in the public domain by Bryan O'Sullivan. This program is + * not covered by patents in the United States or other countries. + */ + +#include + +int main(int argc, char **argv) +\{ + printf("hello, world!\textbackslash{}"); + printf("hello again!\textbackslash{}n"); + return 0; +\} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.merge.clone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.clone.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,6 @@ +$ \textbf{cd ..} +$ \textbf{hg clone hello my-new-hello} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd my-new-hello} +$ \textbf{sed -i '/printf/i\textbackslash{}\textbackslash{}tprintf("once more, hello.\textbackslash{}\textbackslash{}n");' hello.c} +$ \textbf{hg commit -m 'A new hello for a new day.'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.merge.commit.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.commit.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,1 @@ +$ \textbf{hg commit -m 'Merged changes'} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.merge.heads.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.heads.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,13 @@ +$ \textbf{hg heads} +changeset: +tag: tip +parent: +user: Bryan O'Sullivan + +summary: Added an extra line of output + +changeset: +user: Bryan O'Sullivan + +summary: A new hello for a new day. + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.merge.merge.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.merge.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg merge} +merging hello.c +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.merge.parents.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.parents.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,28 @@ +$ \textbf{hg parents} +changeset: +user: Bryan O'Sullivan + +summary: A new hello for a new day. + +changeset: +tag: tip +parent: +user: Bryan O'Sullivan + +summary: Added an extra line of output + +$ \textbf{cat hello.c} +/* + * Placed in the public domain by Bryan O'Sullivan. This program is + * not covered by patents in the United States or other countries. + */ + +#include + +int main(int argc, char **argv) +\{ + printf("once more, hello.\textbackslash{}n"); + printf("hello, world!\textbackslash{}"); + printf("hello again!\textbackslash{}n"); + return 0; +\} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.merge.pull.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.pull.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +$ \textbf{hg pull ../my-hello} +pulling from ../my-hello +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +(run 'hg heads' to see heads, 'hg merge' to merge) diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.merge.tip.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.tip.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,9 @@ +$ \textbf{hg tip} +changeset: +tag: tip +parent: +parent: +user: Bryan O'Sullivan + +summary: Merged changes + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.merge.update.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.merge.update.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,2 @@ +$ \textbf{hg update} +abort: update spans branches, use 'hg merge' or 'hg update -C' to lose changes diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.older.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.older.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ +$ \textbf{hg update 2} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{hg parents} +changeset: +user: Bryan O'Sullivan + +summary: Introduce a typo into hello.c. + +$ \textbf{hg update} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.outgoing.net.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.outgoing.net.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,9 @@ +$ \textbf{hg outgoing http://hg.serpentine.com/tutorial/hello} +comparing with http://hg.serpentine.com/tutorial/hello +searching for changes +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added an extra line of output + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.outgoing.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.outgoing.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,10 @@ +$ \textbf{cd my-hello} +$ \textbf{hg outgoing ../hello-push} +comparing with ../hello-push +searching for changes +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added an extra line of output + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.parents.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.parents.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg parents} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added an extra line of output + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.pull.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.pull.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,22 @@ +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Trim comments. + +$ \textbf{hg pull ../my-hello} +pulling from ../my-hello +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +(run 'hg update' to get a working copy) +$ \textbf{hg tip} +changeset: +tag: tip +user: Bryan O'Sullivan + +summary: Added an extra line of output + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.push.net.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.push.net.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg push http://hg.serpentine.com/tutorial/hello} +pushing to http://hg.serpentine.com/tutorial/hello +searching for changes +ssl required diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.push.nothing.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.push.nothing.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{hg push ../hello-push} +pushing to ../hello-push +searching for changes +no changes found diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.push.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.push.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +$ \textbf{hg push ../hello-push} +pushing to ../hello-push +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.reclone.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.reclone.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{cd ..} +$ \textbf{hg clone hello my-hello} +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{cd my-hello} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.sed.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.sed.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,1 @@ +$ \textbf{sed -i '/printf/a\textbackslash{}\textbackslash{}tprintf("hello again!\textbackslash{}\textbackslash{}n");' hello.c} diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.status.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.status.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +$ \textbf{ls} +Makefile hello.c +$ \textbf{hg status} +M hello.c diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.tip.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.tip.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,21 @@ +$ \textbf{hg tip -vp} +changeset: +tag: tip +user: Bryan O'Sullivan + +files: hello.c +description: +Added an extra line of output + + +diff -r -r hello.c + + +@@ -8,5 +8,6 @@ int main(int argc, char **argv) + int main(int argc, char **argv) + \{ + printf("hello, world!\textbackslash{}"); ++ printf("hello again!\textbackslash{}n"); + return 0; + \} + diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.update.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.update.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,7 @@ +$ \textbf{grep printf hello.c} + printf("hello, world!\textbackslash{}"); +$ \textbf{hg update tip} +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ \textbf{grep printf hello.c} + printf("hello, world!\textbackslash{}"); + printf("hello again!\textbackslash{}n"); diff -r a24b370a16ee -r d6ca1334a19d ja/examples/tour.version.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/examples/tour.version.out Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,6 @@ +$ \textbf{hg version} +Mercurial Distributed SCM (version ) + +Copyright (C) 2005-2007 Matt Mackall and others +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff -r a24b370a16ee -r d6ca1334a19d ja/feature-branches.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/feature-branches.dot Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +digraph feature_branches { + master -> crypto; + master -> filesystems; + master -> ipc; + master -> memory; + master -> network; + master -> security; +} diff -r a24b370a16ee -r d6ca1334a19d ja/filelog.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/filelog.svg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,371 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + .hg/data/README.i + + + + + README + + + + + + + + + .hg/data/src/hello.c.d + .hg/data/src/hello.c.i + + + + + src/hello.c + + + + Working directory + Repository + + diff -r a24b370a16ee -r d6ca1334a19d ja/filenames.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/filenames.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,414 @@ +\chapter{File names and pattern matching} +\label{chap:names} + +Mercurial は、 +一貫性と表現力を兼ね備えた方法でファイル名を扱う仕組みを提供しています。 + +\section{Simple file naming} + +Mercurial は +``under the hood''において、 +ファイル名を取り扱う統一された仕組みを用いています。 +ファイル名に関する全てのコマンドの挙動は統一されています。 +ファイル名に対するコマンドの挙動は、以下のようになっています。 + +コマンド行で実ファイル名を明示的に指定した場合、 +Mercurial は指定されたファイル名に厳密に作用します。 + +\interaction{filenames.files} + +ディレクトリ名を指定した場合、 +Mercurial はその指定を、 +``当該ディレクトリならびにサブディレクトリ中の全てのファイル'' +とみなします。 +Mercurial は当該ディレクトリ配下のファイル・サブディレクトリを、 +アルファベット順に走査します。 +あるディレクトリの走査中にサブディレクトリに遭遇した場合、 +当該ディレクトリの走査よりも先に、 +サブディレクトリの走査を実施します\footnote{訳注: 深さ優先(depth first)}。 + +\interaction{filenames.dirs} + +\section{Running commands without any file names} + +ファイル名を引数に取る Mercurial コマンドは、 +引数ないしパターン指定無しで起動された場合も、 +有用な基底時動作が定められています。 +コマンドに期待される振る舞いは、 +コマンドの用途に依存します。 +ファイル名指定無しの起動において、 +コマンドがどのように振舞うのかを推測するための、 +一般的な目安となる幾つかのルールを以下に示します。 + +\begin{itemize} +\item 殆どのコマンドは作業領域ディレクトリ全体に作用します。 + 例えば、\hgcmd{add} コマンドなどがそうです。 + +\item 復旧が困難あるいは不可能な作用を及ぼすコマンドの場合、 + 少なくとも1つ以上の名前ないしパターン(後述します) + の明示的な指定を求める筈です。 + この挙動により、 + 例えば引数無しの \hgcmd{remove} 起動のような、 + 不慮の事態によるファイルの削除等を防ぐことができます。 + +\end{itemize} + +この振る舞いがそぐわない状況であれば、 +簡単に振る舞いを変えることができます。 +作業領域ディレクトリ全体に作用するコマンドであれば、 +``\dirname{.}'' を指定することで、 +コマンドの作用を現在のディレクトリおよびその配下に限定することができます。 + +\interaction{filenames.wdir-subdir} + +ルート以外のディレクトリでコマンドを実行した場合でも、 +リポジトリのルートに対する相対的なファイル名を表示するコマンドもあります。 +このようなコマンドは、 +明示的な名前を指定することで、 +現在のディレクトリ位置に対する相対的なファイル名を表示するようになります。 +非ルートディレクトリでの \hgcmd{status} 起動の際に +\hgcmd{root} コマンドの出力を指定することで、 +対象を作業領域ディレクトリ全体に維持したまま、 +現在のディレクトリ位置に対する相対的なファイル名を表示させることができます。 + +\interaction{filenames.wdir-relname} + +\section{Telling you what's going on} + +先の節における \hgcmd{add} コマンド実行例は、 +Mercurial コマンドに関するもう一つの有益な事柄を示しています。 +コマンド行で明示的な指定をしていないファイルに対してコマンドが作用する場合、 +通常は対象ファイル名を表示しますので、 +思わぬコマンドの実行結果に後から驚かされることはありません。 + +これは\emph{驚きを最小}にする原則に則ったものです。 +コマンド行で厳密なファイル名を指定した場合には、 +それを復唱する必要は無いでしょう。 +ファイル名・ディレクトリ名ないしパターン(後述します) +を指定しないことで\emph{暗に指定された}対象ファイルに +Mercurial が作用する場合、 +どのファイルを対象とするのかを通知するのは安全性の上で有用です。 + +上記方針に沿って振舞うコマンド群は、 +\hggopt{-q} オプションを指定することで、 +その出力を抑止することができます。 +明示的にファイル名等を指定した場合でも、 +\hggopt{-v} オプションを指定することで、 +全ての対象ファイル名を表示させることができます。 + +\section{Using patterns to identify files} + +ファイル名・ディレクトリ名による指定に加えて、 +Mercurial では\emph{パターン}によるファイル指定機能が使用できます。 +Mercurial のパターン操作は表現力に富んだものです。 + +Linux や MacOS のような Unix 的システムでは、 +ファイル名とパターンとの間の突合せは通常シェルがその役目を負います。 +これらのシステムでは、 +パターンを指定している旨を +Mercurial に対して明示的に指示する必要があります\footnote{訳注: +シェルによる特殊文字展開の抑止の話であれば、 +``Mercurial に対して''ではなく、 +``シェルに対して''なのでは? +それとも Windows バイナリ版では振る舞いが異なる?}。 +Windows においては、 +シェルによるパターンの展開が行われませんので、 +Mercurial は自動的に指定されたものがパターンであると認識し、 +ファイル名へと展開します。 + +コマンド行において、 +ファイル名を指定する場所でパターンを使用するには、 +以下のように記述します。 + +\begin{codesample2} + syntax:patternbody +\end{codesample2} + +パターンの記述は、 +パターンの種類を識別するための短い文字列、コロン、 +そして実際のパターンを連結したものです。 + +Mercurial は2種類のパターン形式に対応しています。 +最も利用頻度が高いものは \texttt{glob} と呼ばれ、 +Unix のシェルによるパターンマッチングと同様の機能を持つもので、 +その振る舞いは Windows のコマンドプロンプトユーザにも馴染みがあることでしょう。 + +Windows において +Mercurial が自動的にパターンマッチングを行う場合、 +\texttt{glob} 形式とみなされます。 +そのため、 +Windows においては ``\texttt{glob:}'' 接頭辞を省略可能ですが、 +明示的に指定することも可能です。 + +\texttt{re} 形式は、 +\texttt{glob} 形式よりも強力で、 +regexps としても知られる正規表現を使用したパターンの記述が可能です。 + +ちなみに、以降の例では、 +全てのパターン指定を注意深く引用符で囲むことで、 +Mercurial の処理の前にシェルによって展開されてしまうことを防いでいる、 +という点に注意してください。 + +\subsection{Shell-style \texttt{glob} patterns} + +\texttt{glob} 形式によるマッチングの際に、 +使用可能なパターンについての概要を以下に示します。 + +パターン ``\texttt{*}'' は、 +同一ディレクトリ内で任意の文字列に合致します。 + +\interaction{filenames.glob.star} + +パターン ``\texttt{**}'' は、 +ディレクトリ境界を超えて任意の文字列に合致します。 +このパターンは Unix における標準的なものではありませんが、 +幾つかの著名なシェル実装で採用されており、 +非常に便利です。 + +\interaction{filenames.glob.starstar} + +パターン ``\texttt{?}'' は、 +単一の文字に合致します。 + +\interaction{filenames.glob.question} + +パターン ``\texttt{[}'' は、 +\emph{文字集合}(character class)の開始を意味します。 +このパターンは当該集合に属する任意の一文字に合致します。 +集合指定は ``\texttt{]}'' によって終了します。 +集合指定には、 +``\texttt{abcdef}'' の省略指定である +``\texttt{a-f}'' 形式の\emph{範囲}指定を、 +複数含めることが可能です。 + +\interaction{filenames.glob.range} + +文字集合指定において +``\texttt{[}'' の直後の文字が ``\texttt{!}'' \footnote{訳注: +正規表現における ``\texttt{\^}'' による反転と異なる点に注意}の場合、 +集合指定は\emph{反転}され、 +集合に属さない任意の一文字に合致します。 + +パターン ``\texttt{\{}'' はサブパターンのグループ化の開始を意味し、 +グループ中の何れかのサブパターンが合致した場合は、 +グループ全体が合致したものとみなされます。 +グループ指定におけるサブパターンの区切りには +``\texttt{,}'' が使用され、 +``\texttt{\}}'' がグループの終了を意味します。 + +\interaction{filenames.glob.group} + +\subsubsection{Watch out!} + +任意のディレクトリにおけるパターン合致が必要な場合は、 +単一ディレクトリ内でのマッチングしか行わない +``\texttt{*}'' を使用すべきでは無い、 +という点は忘れないようにしてください。 +``\texttt{*}'' の代わりに ``\texttt{**}'' を使用しましょう。 +両者の違いを以下で説明します。 + +\interaction{filenames.glob.star-starstar} + +\subsection{Regular expression matching with \texttt{re} patterns} + +Mercurial は(Python の内部的な正規表現エンジンを利用しているので) +Python が受け付けるのと同じ正規表現を受け付けます。 +この正規表現は Perl の正規表現文法を基にしており、 +最も多用されている(例えば Java でも使用されています)方言です。 + +正規表現パターンはそれほど多用されるものではないので、 +Mercurial の正規表現の詳細に関してここでは説明しません。 +Perl 形式の正規表現は様々な形式で、 +多くのウェブサイトや出版物において余す所無く説明されています。 +その代わりここでは、 +Mercurial で正規表現を使用する必要に迫られた際に、 +知っておくべき幾つかの事柄について説明しようとおもいます。 + +正規表現は、 +リポジトリルートからの相対的なファイル名全体に対して適用されます。 +言い換えるなら、 +\dirname{foo} サブディレクトリで作業している場合でも、 +このディレクトリ配下のファイルに対してマッチングを行うなら、 +指定するパターンは +``\texttt{foo/}'' で始まっていなければなりません。 + +Perl 形式の正規表現に馴染んでいる場合、 +Mercurial の正規表現は \emph{rooted} である点に注意してください\footnote{訳注: +暗黙のうちに ``\texttt{\^}'' が付与される、と理解すれば良いでしょう。}。 +正規表現は文字列先頭からマッチングを実施しますので、 +文字列途中に対するマッチングは行われません。 +任意の位置に対してマッチングを実施させたい場合、 +パターンの記述を ``\texttt{.*}'' で始める必要があります。 + +\section{Filtering files} + +Mercurial が多様な方法を提供しているものは、 +ファイルの指定方法だけではありません。 +Mercurial は\emph{フィルタ}によるファイル選別の機能も提供しています。 +ファイル名指定を受け付けるコマンドは、 +以下の2つのフィルタリングオプションも受け付けます。 + +\begin{itemize} +\item \hggopt{-I} ないし \hggopt{--include} により、 + 合致したファイルのみを処理対象とみなすパターンを指定できます。 + +\item \hggopt{-X} ないし \hggopt{--exclude} により、 + 合致したファイルを処理対象から\emph{除外}するパターンを指定できます。 + +\end{itemize} + +複数の \hggopt{-I} および \hggopt{-X} オプションを、 +コマンド行で好きなように混在させることができます。 +Mercurial の基底動は、 +指定されたパターンを ``\texttt{glob}'' 形式とみなして解釈します +(必要であれば明示的に ``\texttt{glob}'' を指定することも可能です)。 + +\hggopt{-I} フィルタは、 +``合致したファイルのみを処理対象とする'' +ものと解釈すれば良いでしょう。 + +\interaction{filenames.filter.include} + +\hggopt{-X} フィルタは、 +``合致しないものを処理対象とする'' +ものと解釈することができます。 + +\interaction{filenames.filter.exclude} + +\section{Ignoring unwanted files and directories} + +※ 原文未稿 + +\section{Case sensitivity} +\label{sec:names:case} + +Linux(ないし他の Unix 系 OS)と、 +MacOS ないし Windows が混在する開発環境で作業する場合、 +ファイル名における文字の大小(``N'' と ``n'')の扱い方針が全く異なる、 +という知識を心に留めておく必要があります。 +良くある事では無いかもしれませんし、 +容易に解決できる可能性もありますが、 +知らない状況で遭遇した場合、 +非常に驚かされる問題でもあります。 + +OS およびファイルシステムに応じて、 +ファイルおよびディレクトリ名の\emph{文字の大小}の扱いは異なります。 +名前における文字の大小の一般的な扱い方を、 +以下に3つ示します。 + +\begin{itemize} +\item 完全に文字の大小を無視: + ファイルの生成およびその後の扱いにおいて、 + 文字の大文字・小文字は同じものとして扱われます。 + 古い DOS 風のシステムで一般的な扱い方です。 + +\item 文字の大小は保持されるが無視: + ファイルないしディレクトリ生成の際には、 + 名前における文字の大小は保存され、 + OS による検索や表示が可能です。 + 存在するファイルが検索される場合、文字の大小は無視されます。 + Windows や MacOS では標準的な仕様です。 + \filename{foo} と \filename{FoO} は同じファイルとみなされます。 + 大文字と小文字の互換性ある扱いは、 + \emph{ケースフォールディング}(case folding)とも呼ばれます。 + +\item 文字の大小を区別: + 名前における文字の大小は常に意味を持ちます。 + \filename{foo} と \filename{FoO} は異なるファイルとして区別されます。 + これは Linux や Unix における通常の振る舞いです。 + +\end{itemize} + +Unix 的なシステムの上では、 +上記の大文字・小文字の取り扱い形式のうちの``任意''のものが +(あるいは全てが同時に)要求される可能性があります。 +例えば、 +FAT32 ファイルシステムでフォーマットされた +USB 小型メモリモジュールを Linux で使用する場合、 +そのファイルシステム上での Linux の振る舞いは、 +文字の大小は保持しつつ無視するものとなります。 + +\subsection{Safe, portable repository storage} + +Mercurial のリポジトリ格納機能は、 +文字大小の区別の可否に\emph{影響を受けません}。 +リポジトリの保存先ファイル名は元ファイル名を変換したものなので、 +ファイルシステムにおける大文字小文字の区別の可否に関わり無く、 +構成管理情報を格納できます。 +つまり、OS の標準的な複製ツールを使用して、 +Mercurial のリポジトリを例えば USB 小型メモリモジュールに複製し、 +Mac、Windows PC および Linux の間で持ち運ぶことができます。 + +\subsection{Detecting case conflicts} + +作業領域ディレクトリにおける操作の際には、 +Mercurial は作業領域を載せているファイルシステムの命名方針に従います。 +ファイルシステムが文字の大小は保持しつつ無視するものであった場合、 +文字の大小のみが異なる名前を Mercurial は同じものとみなします。 + +この方針の重要な点は、 +文字大小を区別する(一般的な Linux や Unix における) +ファイルシステムにおいて、 +文字大小を区別できない(Windows や MacOS の) +ユーザが取り扱えないようなチェンジセットをコミットすることが可能である点です。 +Linux の利用者が +\filename{myfile.c} と +\filename{MyFile.C} +という名前の2つのファイルに対する変更をコミットした場合、 +変更内容はリポジトリに正しく保存されます。 +他の Linux 利用者の作業領域ディレクトリにおいても、 +これらのファイルは異なるファイルとして正しく存在します。 + +Mercurial のリポジトリ格納機構が文字大小の扱いの可否に影響を受けないため、 +Windows ないし MacOS 利用者がこの変更を取り込んでも、 +最初は問題が発生しません。 +しかし、 +作業領域ディレクトリを当該チェンジセットで \hgcmd{update} +しようとした場合、 +あるいは当該チェンジセットと +\hgcmd{merge} しようとした場合、 +ファイルシステムが同じファイルとして扱う2つのファイルの衝突を見つけた +Mercurial によって、 +\hgcmd{update} ないし \hgcmd{merge} は禁止されます。 + +\subsection{Fixing a case conflict} + +他のメンバーが Linux や Unix を使用している混在環境で +Windows ないし MacOS を使用していて、 +\hgcmd{update} あるいは \hgcmd{merge} の際に +Mercurial が文字大小の衝突を報告する場合、 +問題の解決手順は簡単です。 + +手近な Linux ないし Unix 利用者を探し、 +問題のリポジトリを \hgcmd{clone} してから、 +問題のファイルないしディレクトリを大文字小文字の衝突が発生しないように、 +Mercurial の \hgcmd{rename} コマンドで改名をすれば良いのです。 +その後、 +変更をコミットし、 +\hgcmd{pull} ないし \hgcmd{push} で +Windows や MacOS に変更を取り込み、 +\hgcmd{update} によって衝突しない名前で変更内容を取り出します。 + +大文字小文字の衝突を生じさせるチェンジセットそのものは、 +プロジェクトの履歴に残っており、 +当該チェンジセットを Windows や +MacOS 上で作業領域ディレクトリに取り出すことはできませんが、 +開発を継続することは可能です\footnote{訳注: +文字の大小とは関係ありませんが、 +Windows は ``\texttt{con}'' や +``\texttt{aux}'' が特別扱いされるため、 +例えばこれらの名前を利用したディレクトリがある場合などは、 +リポジトリの \hgcmd{pull} そのものができません。}。 + +\begin{note} + 0.9.3 版以前の Mercurial は、 + 大文字小文字に影響を受けないリポジトリ格納機構も、 + 大文字小文字の名前衝突検知機能もありませんでした。 + Mercurial の旧版を Windows や MacOS で使用している場合、 + Mercurial の更新をお薦めします。 +\end{note} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/fixhtml.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/fixhtml.py Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# +# This script attempts to work around some of the more bizarre and +# quirky behaviours of htlatex. +# +# - We've persuaded htlatex to produce UTF-8, which unfortunately +# causes it to use huge character sequences to represent even the +# safe 7-bit ASCII subset of UTF-8. We fix that up. +# +# - BUT we have to treat angle brackets (for example, redirections in +# shell script snippets) specially, otherwise they'll break the +# generated HTML. (Reported by Johannes Hoff.) +# +# - For some reason, htlatex gives a unique ID to each fancyvrb +# environment, which makes writing a sane, small CSS stylesheet +# impossible. We squish all those IDs down to nothing. + +import os +import sys +import re + +angle_re = re.compile(r'([CE];)') +unicode_re = re.compile(r'�([0-7][0-9A-F]);') +fancyvrb_re = re.compile(r'id="fancyvrb\d+"', re.I) +ligature_re = re.compile(r'ྰ([0-4]);') + +tmpsuffix = '.tmp.' + str(os.getpid()) + +def hide_angle(m): + return m.group(1).lower() + +def fix_ascii(m): + return chr(int(m.group(1), 16)) + +ligatures = ['ff', 'fi', 'fl', 'ffi', 'ffl'] + +def expand_ligature(m): + return ligatures[int(m.group(1))] + +for name in sys.argv[1:]: + tmpname = name + tmpsuffix + ofp = file(tmpname, 'w') + for line in file(name): + line = angle_re.sub(hide_angle, line) + line = unicode_re.sub(fix_ascii, line) + line = ligature_re.sub(expand_ligature, line) + line = fancyvrb_re.sub('id="fancyvrb"', line) + ofp.write(line) + ofp.close() + os.rename(tmpname, name) diff -r a24b370a16ee -r d6ca1334a19d ja/hgbook.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/hgbook.css Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,441 @@ +body { + font: 12px/1.5 Verdana, sans-serif; + padding-top: 50px; + padding-left: 80px; + padding-right: 80px; + padding-bottom: 90px; +} +.ptmr7t- { + font-family: monospace; +} +.ptmr7t-x-x-172 { + font-size: 172%; + font-family: monospace; +} +.ptmr7t-x-x-120 { + font-size: 120%; +} +.zpzccmry-x-x-120 { + font-size: 120%; + font-weight: bold; + font-style: italic; +} +.zpzccmry-x-x-120 { + font-weight: bold; + font-style: italic; +} +.pcrr7tn- { + font-family: monospace; +} +.ptmri7t- { + font-style: italic; +} +.ptmr7t-x-x-50 { + font-size: 50%; + font-family: monospace; +} +.ptmb7t- { + font-weight: bold; +} +.zptmcmr- { + font-style: italic; +} +.zptmcmrm- { + font-style: italic; +} +.zpzccmry- { + font-weight: bold; + font-style: italic; +} +.pcrb7t- { + font-family: monospace; + font-weight: bold; +} +.pcrro7t- { + font-family: monospace; + font-style: oblique; +} +p.noindent { + text-indent: 0em; + margin: 0em; +} +p.nopar { + text-indent: 0em; +} +p.indent { + text-indent: 1.5em; + margin: 0em; +} +a img { + border-top: 0; + border-left: 0; + border-right: 0; +} +center { + margin-top: 1em; + margin-bottom: 1em; +} +td center { + margin-top: 0em; + margin-bottom: 0em; +} +.Canvas { + position: relative; +} +img.math { + vertical-align: middle; +} +li p.indent { + text-indent: 0em; +} +.enumerate1 { + list-style-type: decimal; +} +.enumerate2 { + list-style-type: lower-alpha; +} +.enumerate3 { + list-style-type: lower-roman; +} +.enumerate4 { + list-style-type: upper-alpha; +} +div.newtheorem { + margin-bottom: 2em; + margin-top: 2em; +} +.obeylines-h,.obeylines-v { + white-space: nowrap; +} +div.obeylines-v p { + margin-top: 0; + margin-bottom: 0; +} +.overline { + text-decoration: overline; +} +.overline img { + border-top: 1px solid black; +} +td.displaylines { + text-align: center; + white-space: nowrap; +} +.centerline { + text-align: center; +} +.rightline { + text-align: right; +} +div.verbatim { + font-family: monospace; + white-space: nowrap; +} +table.verbatim { + width: 100%; +} +.fbox { + background: url(note.png) no-repeat #cec; + padding-left: 65px; + padding-top: 1em; + padding-bottom: 1em; + padding-right: 1em; + text-indent: 0pt; + border: dotted black 1px; +} +div.center div.fbox { + text-align: center; + clear: both; + padding-left: 3.0pt; + padding-right: 3.0pt; + text-indent: 0pt; + border: solid black 0.4pt; +} +table.minipage { + width: 100%; +} +div.center, div.center div.center { + text-align: center; + margin-left: 1em; + margin-right: 1em; +} +div.center div { + text-align: left; +} +div.flushright, div.flushright div.flushright { + text-align: right; +} +div.flushright div { + text-align: left; +} +div.flushleft { + text-align: left; +} +.underline { + text-decoration: underline; +} +.underline img { + border-bottom: 1px solid black; + margin-bottom: 1pt; +} +.framebox-c, .framebox-l, .framebox-r { + padding-left: 3.0pt; + padding-right: 3.0pt; + text-indent: 0pt; + border: solid black 0.4pt; +} +.framebox-c { + text-align: center; +} +.framebox-l { + text-align: left; +} +.framebox-r { + text-align: right; +} +span.thank-mark { + vertical-align: super +} +span.footnote-mark sup.textsuperscript, span.footnote-mark a sup.textsuperscript { + font-size: 80%; +} +div.tabular, div.center div.tabular { + text-align: center; + margin-top: 0.5em; + margin-bottom: 0.5em; +} +table.tabular td p { + margin-top: 0em; +} +table.tabular { + margin-left: auto; + margin-right: auto; +} +div.td00 { + margin-left: 0pt; + margin-right: 0pt; +} +div.td01 { + margin-left: 0pt; + margin-right: 5pt; +} +div.td10 { + margin-left: 5pt; + margin-right: 0pt; +} +div.td11 { + margin-left: 5pt; + margin-right: 5pt; +} +table[rules] { + border-left: solid black 0.4pt; + border-right: solid black 0.4pt; +} +td.td00 { + padding-left: 0pt; + padding-right: 0pt; +} +td.td01 { + padding-left: 0pt; + padding-right: 5pt; +} +td.td10 { + padding-left: 5pt; + padding-right: 0pt; +} +td.td11 { + padding-left: 5pt; + padding-right: 5pt; +} +table[rules] { + border-left: solid black 0.4pt; + border-right: solid black 0.4pt; +} +.hline hr, .cline hr { + height : 1px; + margin: 0px; +} +.tabbing-right { + text-align: right; +} +span.TEX { + letter-spacing: -0.125em; +} +span.TEX span.E { + position: relative;top: 0.5ex;left: -0.0417em; +} +a span.TEX span.E { + text-decoration: none; +} +span.LATEX span.A { + position: relative; + top: -0.5ex; + left: -0.4em; + font-size: 85%; +} +span.LATEX span.TEX { + position: relative; + left: -0.4em; +} +div.float img, div.float .caption { + text-align: center; +} +div.figure img, div.figure .caption { + text-align: center; +} +.marginpar { + width: 20%; + float: right; + text-align: left; + margin-left: auto; + margin-top: 0.5em; + font-size: 85%; + text-decoration: underline; +} +.marginpar p { + margin-top: 0.4em; + margin-bottom: 0.4em; +} +table.equation { + width: 100%; +} +.equation td { + text-align: center; +} +td.equation { + margin-top: 1em; + margin-bottom: 1em; +} +td.equation-label { + width: 5%; + text-align: center; +} +td.eqnarray4 { + width: 5%; + white-space: normal; +} +td.eqnarray2 { + width: 5%; +} +table.eqnarray-star, table.eqnarray { + width: 100%; +} +div.eqnarray { + text-align: center; +} +div.array { + text-align: center; +} +div.pmatrix { + text-align: center; +} +table.pmatrix { + width: 100%; +} +span.pmatrix img { + vertical-align: middle; +} +div.pmatrix { + text-align: center; +} +table.pmatrix { + width: 100%; +} +img.cdots { + vertical-align: middle; +} +.partToc a, .partToc, .likepartToc a, .likepartToc { + line-height: 200%; + font-weight: bold; + font-size: 110%; +} +.chapterToc a, .chapterToc, .likechapterToc a, .likechapterToc, .appendixToc a, .appendixToc { + line-height: 200%; + font-weight: bold; +} +.caption td.id { + font-weight: bold; + white-space: nowrap; +} +table.caption { + text-align: center; +} +h1.partHead { + text-align: center; +} +p.bibitem { + text-indent: -2em; + margin-left: 2em; + margin-top: 0.6em; + margin-bottom: 0.6em; +} +p.bibitem-p { + text-indent: 0em; + margin-left: 2em; + margin-top: 0.6em; + margin-bottom: 0.6em; +} +.paragraphHead, .likeparagraphHead { + margin-top: 2em; + font-weight: bold; +} +.subparagraphHead, .likesubparagraphHead { + font-weight: bold; +} +.quote { + margin-bottom: 0.25em; + margin-top: 0.25em; + margin-left: 1em; + margin-right: 1em; + text-align: justify; +} +.verse { + white-space: nowrap; + margin-left: 2em} +div.maketitle { + text-align: center; +} +h2.titleHead { + text-align: center; +} +div.maketitle { + margin-bottom: 2em; +} +div.author, div.date { + text-align: center; +} +div.thanks { + text-align: left; + margin-left: 10%; + font-size: 85%; + font-style: italic; +} +div.author { + white-space: nowrap; +} +.quotation { + margin-bottom: 0.25em; + margin-top: 0.25em; + margin-left: 1em; +} +h1.partHead { + text-align: center; +} +img.graphics { + margin-left: 10%; +} +.figure { + width: 100%; +} +P.fancyvrb { + white-space: nowrap; +} +hr { + border: 0; + height: 1px; +} +div#fancyvrb { + white-space: nowrap; + background: #eee; + padding: 1em; +} diff -r a24b370a16ee -r d6ca1334a19d ja/hgext.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/hgext.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,591 @@ +\chapter{Adding functionality with extensions} +\label{chap:hgext} + +Mercurial は機能性の見地から見た場合には申し分無い一方で、 +変り種の機能群は故意に除外されています。 +簡潔さを保つ遣り方は、 +保守担当と利用者の両方に対してソフトウェアの扱いやすさを維持します。 + +しかし Mercurial は、 +利用者を杓子定規なコマンド群の只中に利用者を閉じ込めるようなことはしません。 +\emph{イクステンション} +(この種のものは\emph{プラグイン}と呼ばれることもあります) +として機能を追加することができるのです。 +幾つかのイクステンションについては、 +既に前の章で話題にしています。 + +\begin{itemize} +\item \ref{sec:tour-merge:fetch}~節では + \hgext{fetch} イクステンションを取り上げています。 + このイクステンションは、新たな変更の取得と手元の変更へのマージを、 + 単一のコマンド \hgxcmd{fetch}{fetch} で実施します。 + +\item \hgext{bisect} は、 + バグの原因となる変更を効率的に検索するイクステンションで、 + \ref{sec:undo:bisect}~節で取り上げました。 + +\item \ref{chap:hook}~章では、 + フックに関連した有用な機能を持つイクステンションを取り上げました。 + \hgext{acl} はアクセスコントロールリストの機能を、 + \hgext{bugzilla} は Bugzilla バグ追跡システムとの統合を、 + \hgext{notify} は変更追加時における電子メール通知の機能を、 + Mercurial に追加します。 + +\item Mercurial Queues パッチ管理イクステンションは、 + 2つの章と1つの appendix を丸々費やすに値する価値を持っています。 + \ref{chap:mq}~章は基本を、 + \ref{chap:mq-collab}~章はより進んだ話題を、 + そして appendix~\ref{chap:mqref} は各コマンドの詳細を取り上げています。 + +\end{itemize} + +本章では、 +上記以外の Mercurial で利用可能な幾つかのイクステンションについて取り上げ、 +その上で、 +自分でイクステンションを実装する際に必要と思われる仕組みについて、 +簡単に触れようと思います。 + +\begin{itemize} +\item \ref{sec:hgext:inotify}~節では、 + \hgext{inotify} + イクステンションによる\emph{絶大な}性能改善の可能性について取り上げます。 + +\end{itemize} + +\section{Improve performance with the \hgext{inotify} extension} +\label{sec:hgext:inotify} + +一般的な Mercurial の操作が 100 倍速くなることに興味があるのでしたら、 +ぜひこの節を読んでください。 + +Mercurial は通常の環境であっても高い性能でを発揮します。 +XXXX 否定の接続の筈 XXXX +例えば \hgcmd{status} コマンドの実行の際には、 +ファイルの状態を表示するために、 +リポジトリ配下の殆ど全てのディレクトリとファイルに対する走査が必要です。 +他の多くの Mercurial コマンドも、 +舞台裏では同様の作業を必要としています。 +例えば \hgcmd{diff} コマンドは、 +状態比較機構\footnote{訳注: \ref{sec:concepts:dirstate}~節参照 +}を用いることで、 +明らかに変更されていないファイルに対して、 +実行コストの高い比較処理が実施されることを回避しています。 + +ファイル状態の取得は性能確保上重要なことなので、 +Mercurial の開発者達は、 +ギリギリのところまでこの部分の実装を最適化してきました。 +しかし \hgcmd{status} 実行の際には、 +前回の確認以降の変更の有無を知るために、 +コストの高いシステムコールを、 +Mercurial の管理下にあるファイル毎に最低1回発行する必要がある、 +という事実は回避しようがありません。 +一定以上の大きさのリポジトリでは、 +この処理には長い時間がかかります。 + +影響の大きさを数値化すべく、 +150,000 のファイルを管理するリポジトリで実験を行った結果、 +いずれのファイルも変更\emph{されていない}場合であっても、 +\hgcmd{status} の実行には10秒を要します。 + +多くの近代的 OS は、ファイル更新の通知機構を備えています。 +適切なサービスにプログラムを登録しておくことで、 +対象となるファイルに関する生成・変更・削除といったイベントが発生する都度、 +OS が通知をしてくれます。 +Linux 環境では、 +\texttt{inotify} +と呼ばれるカーネルコンポネントが通知機構を提供します。 + +Mercurial の \hgext{inotify} イクステンションは、 +カーネルの \texttt{inotify} と連携することで、 +\hgcmd{status} コマンドを最適化します。 +\hgext{inotify} イクステンションは2つの要素から構成されています。 +デーモン部分がバックグラウンドで稼動することで、 +\texttt{inotify} カーネルコンポネントから通知を受け取ります。 +デーモン部分は、 +通常の Mercurial コマンドからの接続要求も受け付けます。 +\hgext{inotify} イクステンションは、 +ファイルシステムの走査の代替としてデーモンを必要とするため、 +Mercurial の挙動そのものを改変します。 +デーモンはリポジトリ状態に関する完全な情報を保持しているので、 +リポジトリ配下のディレクトリやファイルを走査すること無しに、 +即座に応答を返すことができます。 + +先に述べたとおり、 +通常の Mercurial では、 +150,000 のファイルを管理するリポジトリでの +\hgcmd{status} 実行に10秒を要しました。 +\hgext{inotify} イクステンションを有効にすることで、 +実行に要する時間は\emph{1000倍}早い0.1~秒まで低減できました。 + +話を先に進める前に、 +以下の点に注意してください。 + +\begin{itemize} +\item \hgext{inotify} は Linux 環境固有のイクステンションです。 + Linux の \texttt{inotify} サブシステムと直接連携するため、 + 他の OS 環境下では機能しません。 + +\item 2005 年初旬以後にリリースされた + Linux ディストリビューションでの利用をお薦めします。 + それ以前のディストリビューションは、 + \texttt{inotify} が組み込まれていないか、 + 必要な API を \texttt{glibc} が提供していないものと思われます\footnote{ + 訳注: man ページによれば、 + \texttt{inotify} の利用に当たっては、 + 2.6.13 版以後のカーネルと 2.4 版以後の \texttt{glibc} が必要だそうです。 + }。 + +\item 全てのファイルシステムが + \hgext{inotify} イクステンションの利用に適しているとは限りません。 + 典型的な例としては、 + 同一のネットワークファイルシステムを、 + Mercurial を稼動させる複数のシステムでマウントしているような場合です。 + カーネルの \texttt{inotify} サブシステムは、 + リモートホストでの変更を知る術を持ちません。 + 殆どのローカルファイルシステム(例えば ext3、XFS や ReiserFS)は、 + 上手く機能する筈です。 + +\end{itemize} + +\hgext{inotify} イクステンションは、 +2007 年 5 月の時点では Mercurial に同梱されていません\footnote{訳注: +2007 年 10 月の 0.9.5 版段階でも同梱されていません}ので、 +他のイクステンションと比較して多少の準備作業が必要ですが、 +性能向上にはそれだけの価値があります。 + +\hgext{inotify} イクステンションは目下、 +Mercurial ソースコードへのパッチと、 +\texttt{inotify} サブシステム連携の +Python バインディングライブラリの2つの要素から構成されています。 + +\begin{note} + \texttt{inotify} の + Python バインディングライブラリには\emph{2種類}あります。 + 1つは \texttt{pyinotify} と呼ばれるもので、 + 幾つかの Linux ディストリビューションには + \texttt{python-inotify} という名前で同梱されています。 + 実用に供するには非常にバグが多く効率も悪いので、 + このライブラリは使うべきでは\emph{ありません}。 +\end{note} + +事を進めるに当たっては、 +既に機能しているインストール済み +Mercurial を複製するのが良いでしょう。 +To get going, it's best to already have a functioning copy of +Mercurial installed. XXXXXX + +\begin{note} + 以下の手順を踏む場合、 + 最も最新の``最先端な''Mercurial 実装で、 + 既にインストール済みの Mercurial を\emph{置き換える}ことになります。 + これは警告です。 +\end{note} + +\begin{enumerate} +\item \texttt{inotify} の Python バインディングのリポジトリを複製します。 + ビルドおよびインストールを行ってください。 + + \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 Mercurial の \dirname{crew} リポジトリを複製します。 + Mercurial Queues により + \dirname{crew} リポジトリのローカルコピー\footnote{訳注: + ここでは \dirname{crew} から更に \dirname{inotify} を複製していますが、 + \hgext{inotify} イクステンション利用のためだけにビルドする場合、 + 直接 \dirname{crew} で作業しても問題無い筈です。 + }にパッチを当てる為に、 + \hgext{inotify} パッチのリポジトリも複製してください。 + + \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 Mercurial Queues イクステンション(\hgext{mq}) + が利用可能であることを確認してください。 + MQ を利用したことが無い場合、 + まずは \ref{sec:mq:start}~節を読んでください。 + +\item \dirname{inotify} (ローカル)リポジトリに移動して、 + \hgxcmd{mq}{qpush} コマンドの \hgxopt{mq}{qpush}{-a} オプションを使用して、 + 全ての \hgext{inotify} パッチを適用してください。 + + \begin{codesample4} + cd inotify + hg qpush -a + \end{codesample4} + + \hgxcmd{mq}{qpush} がエラーメッセージを表示した場合は、 + 作業を継続せずに開発コミュニティに助けを求めてください。 + +\item パッチ適用版の Mercurial をビルドおよびインストールします。 + + \begin{codesample4} + python setup.py build --force + sudo python setup.py install --skip-build + \end{codesample4} + +\end{enumerate} + +適切にパッチが適用された版の Mercurial が一旦できてしまえば、 +\hgext{inotify} イクステンションを有効にするために必要なことは、 +\hgrc ファイルに以下の記述を追加することだけです。 + +\begin{codesample2} + [extensions] + inotify = +\end{codesample2} + +\hgext{inotify} イクステンションが有効化されると、 +リポジトリの状態を必要とするコマンドの初回起動の時点で、 +Mercurial は自動的且つ透過的に状態管理用デーモンを起動します。 +状態管理デーモンは、リポジトリごとに起動されます。 + +状態管理デーモンはひそやかに起動され、バックグラウンドで実行し続けます。 +\hgext{inotify} イクステンションを有効にした複数のリポジトリで、 +幾つかのコマンドを実行した後に、 +実行中のプロセス一覧を見れば、 +カーネルからの通知と +Mercurial からの問い合わせの両方を待っている複数の +\texttt{hg} プロセスを見ることができる筈です。 + +\hgext{inotify} イクステンションを有効にした際でも、 +リポジトリにおける Mercurial コマンドの初回起動は、 +通常の Mercurial コマンド実行と同程度の性能で実行されます。 +これは状態管理デーモンによる通常の状態走査が必要なためで、 +後にカーネルからの更新通知を受け取る際の基底状態となります。 +しかし、これ以降の状態確認の必要な\emph{全ての}コマンド実行は、 +どんなに小さなサイズのリポジトリであっても、 +目に見えて速くなっている筈です。 +リポジトリが大きければ大きいほど、 +目に見えて性能が大きく改善されることでしょう。 +\hgext{inotify} デーモンは、 +どんなサイズのリポジトリであっても、 +状態取得操作を殆ど瞬時に終了させることができます。 + +\hgxcmd{inotify}{inserve} コマンドにより、 +状態管理デーモンを手動で起動することもできます。 +手動での起動により、 +デーモンの実行に関して幾分明瞭な制御を手にすることができます。 +このコマンドの起動は、 +当然 \hgext{inotify} +イクステンションが有効になっている場合に限り使用可能です。 + +\hgext{inotify} イクステンションを使用している際には、 +状態関連コマンドの実行全般がそれ以前と比較して速くなっている点を除けば、 +Mercurial の挙動は\emph{全く変わらない}筈です。 + +とりわけ、コマンドの出力は異ならず、同じ結果を返す筈です。 +\hgext{inotify} イクステンションの有無で異なる結果が変える場合、 +障害として報告をしてください。 + +\section{Flexible diff support with the \hgext{extdiff} extension} +\label{sec:hgext:extdiff} + +Mercurial の組み込み \hgcmd{diff} コマンドは、 +unified 差分をそのまま出力します。 + +\interaction{extdiff.diff} + +変更内容の表示に外部ツールを使いたい場合は、 +\hgext{extdiff} イクステンションが良いでしょう。 +\hgext{extdiff} イクステンションにより、 +変更内容表示に例えばグラフィカルな外部差分ツールが利用できるようになります。 + +\hgext{extdiff} イクステンションは +Mercurial に同梱されているので簡単に利用できます。 +\hgrc ファイルの +\rcsection{extensions} セクションに、 +イクステンションを有効にする記述を1行追加するだけで良いのです。 + +\begin{codesample2} + [extensions] + extdiff = +\end{codesample2} + +この設定により、 +\hgxcmd{extdiff}{extdiff} コマンドが利用可能になりますが、 +基底状態ではこのコマンドは、 +組み込みの \hgcmd{diff} コマンドと同じ形式の unified 差分を、 +システムの \command{diff} コマンドにより生成します。 + +\interaction{extdiff.extdiff} + +組み込みの \hgcmd{diff} コマンドの結果出力と厳密には一致しません\footnote{ +訳注: どの部分を指して「一致しない」と言っているのか?}が、 +同じオプションを指定してもシステム\footnote{訳注: +ここで言う「system」とは? XXXXX}ごとに +(システムの)\command{diff} コマンドの出力が異なるからです。 + +上記の出力結果に ``\texttt{making snapshot}'' +行が含まれていることからも察することができますが、 +\hgxcmd{extdiff}{extdiff} +コマンドはソースツリーに関するスナップショットを2つ作成します。 +1つ目のスナップショットはソースのリビジョンのもので、 +2つ目は作業領域ディレクトリにおける対象リビジョンのものです\footnote{訳注: +作業領域ディレクトリの「親リビジョン」と「現行状態」}。 +\hgxcmd{extdiff}{extdiff} +コマンドはこれらのスナップショットを一時ディレクトリに作成し、 +これらのディレクトリ名を引数にして外部の差分表示ツールを起動し、 +その後一時ディレクトリを削除します。 +実行効率上、 +2つのリビジョンの間で差分のあるディレクトリ・ +ファイルのスナップショットだけが作成されます。 + +スナップショットディレクトリの名前は、 +元となるリポジトリのベース名と同じ名前を持ちます。 +\dirname{/quux/bar/foo} というリポジトリの場合、 +個々のスナップショットのディレクトリ(ベース)名は \dirname{foo} となります。 +対応するチェンジセットIDがある場合、 +スナップショットのディレクトリ名にはチェンジセットIDが付与されます。 +\texttt{a631aca1083f} 版に対するスナップショットのディレクトリ名は +\dirname{foo.a631aca1083f} となります。 +作業領域ディレクトリの現行状態に対するスナップショットは、 +チェンジセットIDが付与されませんので、 +この例では単に \dirname{foo} という名前になります。 +実際の挙動を見るために、 +再度前出の \hgxcmd{extdiff}{extdiff} の実行例を見てみましょう。 +差分出力のヘッダ部に、 +スナップショットディレクトリの名前が埋め込まれているのに気付くことでしょう。 + +\hgxcmd{extdiff}{extdiff} コマンドには、 +2つの重要なオプションがあります。 +\hgxopt{extdiff}{extdiff}{-p} オプションは、 +システムの \command{diff} +コマンドの代替として使用される差分表示プログラムを指定します。 +\hgxopt{extdiff}{extdiff}{-o} オプションは、 +\hgxcmd{extdiff}{extdiff} +が外部の差分表示プログラム起動時に指定するオプション +(デフォルトでは ``\texttt{-Npru}'' が指定され、 +\command{diff} を使用する場合にのみ意味を持ちます) +を指定します。 +それ以外の点では、 +\hgxcmd{extdiff}{extdiff} コマンドは +組み込みの \hgcmd{diff} コマンドと同様に振舞いますので、 +オプション名やオプション指定の文法、 +比較対象リビジョンを指定する引数、 +比較したいファイル名の指定などは、 +組み込みの \hgcmd{diff} と同じように指定できます。 + +実行例として、 +(通常の \hgcmd{diff} による)unified 差分の代わりに、 +システム標準の \command{diff} コマンドによる context 差分 +(\cmdopt{diff}{-c} オプション使用)を、 +デフォルトの3行ではなく5行の context 行 +(\cmdopt{diff}{-C} オプションでの \texttt{5} 指定) +で表示する方法を示します。 + +\interaction{extdiff.extdiff-ctx} + +グラフィカルな差分ツールの起動は非常に簡単です。 +\command{kdiff3} 起動の例を示します。 + +\begin{codesample2} + hg extdiff -p kdiff3 -o '' +\end{codesample2} + +利用する差分表示コマンドがディレクトリ指定を扱えない場合でも、 +簡単なスクリプトを使うことでその問題を解決できます。 +そのようなスクリプトによる +\hgext{mq} イクステンションと +\command{interdiff} コマンドの連携例は、 +\ref{mq-collab:tips:interdiff}~節を参照してください。 + +\subsection{Defining command aliases} + +\hgxcmd{extdiff}{extdiff} コマンドや利用する差分表示コマンドの、 +両方のオプションを覚えておくのは面倒ですので、 +\hgext{extdiff} イクステンションは、 +使用する差分表示コマンドを正しいオプションで起動する +\emph{新しい}コマンドを定義できるようになっています。 + +新しいコマンド定義のために必要なのは、 +\hgrc ファイルを編集し、 +\rcsection{extdiff} という名前のセクションを追加するだけです。 +このセクションでは、 +複数のコマンドを定義することができます。 +以下に \texttt{kdiff3} コマンドを追加する例を示します。 +一旦定義してしまえば、 +``\texttt{hg kdiff3}''と入力するだけで +\hgext{extdiff} イクステンションが +\command{kdiff3} を起動します。 + +\begin{codesample2} + [extdiff] + cmd.kdiff3 = +\end{codesample2} + +定義の右辺を上記例のように空にした場合、 +\hgext{extdiff} イクステンションは、 +定義したコマンドの名前を実行すべき外部プログラムの名前と見なします。 +しかし、これらの名前が一致している必要はありません。 +以下の例では、 +\command{kdiff3} を実行するコマンドを +``\texttt{hg wibble}'' という名前で定義しています。 + +\begin{codesample2} + [extdiff] + cmd.wibble = kdiff3 +\end{codesample2} + +差分表示プログラム起動の際のデフォルトオプションも指定することができます。 +``\texttt{opts.}'' 接頭辞に続いて、 +オプションを適用したいコマンド名を記述してください。 +以下の例では、 +\command{vim} エディタの \texttt{DirDiff} 拡張を実行する +``\texttt{hg vimdiff}'' コマンドを定義しています。 + +\begin{codesample2} + [extdiff] + cmd.vimdiff = vim + opts.vimdiff = -f '+next' '+execute "DirDiff" argv(0) argv(1)' +\end{codesample2} + +\section{Cherrypicking changes with the \hgext{transplant} extension} +\label{sec:hgext:transplant} + +※ Brendan とチャットでの話し合いが必要 + +\section{Send changes via email with the \hgext{patchbomb} extension} +\label{sec:hgext:patchbomb} + +多くのプロジェクトでは、 +共有リポジトリに最終成果をコミットする前に、 +変更内容をメーリングリストに投稿して査読や論評を行う +``変更レビュー''の文化を持っています。 +リポジトリへのアクセス権を持たない人々からの変更依頼を適用する、 +門番の役割を果たす人々がいるプロジェクトもあります。 + +Mercurial の \hgext{patchbomb} イクステンションを利用することで、 +レビューや提案のための電子メールによる変更送信が容易になります。 +このイクステンションの名前は、 +変更がパッチ形式で整形され、 +1チェンジセット毎に1つの電子メールで送信されることに由来しています。 +電子メールによる一連の変更の送信が、 +受信者のメールボックスにとって``爆撃''(bombing) +のようであることから、``patchbomb''と呼ばれています。 + +\hgext{patchbomb} イクステンションの基本的な設定記述は、 +いつものように \hgrc への1行か2行程度の記述だけです。 + +\begin{codesample2} + [extensions] + patchbomb = +\end{codesample2} + +一旦イクステンションを有効にしたならば、 +\hgxcmd{patchbomb}{email} という新たなコマンドが利用可能になります。 + +\hgxcmd{patchbomb}{email} コマンドの安全且つ最善の実行手順は、 +\emph{必ず} \hgxopt{patchbomb}{email}{-n} +オプションを付けて一旦実行してみることです。 +\hgxopt{patchbomb}{email}{-n} オプション付きの実行は、 +実際の電子メール送信は行わずに、 +送信\emph{されるであろう}内容を表示します。 +変更内容にざっと目を通して、 +送信内容が適切であることを確認したならば、 +\hgxopt{patchbomb}{email}{-n} +オプション抜きで再度 +\hgxcmd{patchbomb}{email} コマンドを実行してください。 + +\hgxcmd{patchbomb}{email} コマンドは、 +他の Mercurial コマンドと同様のリビジョン指定が可能です。 +例えば以下の実行例では、 +リビジョン 7 から \texttt{tip} までの全てのリビジョン +(リビジョン 7 および \texttt{tip} も含みます)が送信されます。 + +\begin{codesample2} + hg email -n 7:tip +\end{codesample2} + +比較対象の\emph{リポジトリ}を指定することもできます。 +リビジョン指定無しでリポジトリを指定した場合、 +\hgxcmd{patchbomb}{email} コマンドは、 +遠隔リポジトリに存在しないローカルリポジトリの全てのリビジョンを送信します。 +リビジョンないし(\hgxopt{patchbomb}{email}{-b} オプションによる) +ブランチ名を追加指定することで、 +送信されるリビジョンを制限することができます。 + +送信先アドレスを指定しない +\hgxcmd{patchbomb}{email} 実行は完璧に安全で、 +その場合には \hgxcmd{patchbomb}{email} は対話的に入力を求めてきます +(Linux や Unix ライクなシステムを利用している場合、 +これらのヘッダ値入力の際には、 +\texttt{readline} 様式の編集機能が利用可能です)。 + +単一のリビジョンだけを送信する場合、 +\hgxcmd{patchbomb}{email} コマンドの基底動作では、 +コミットメッセージの最初の1行を送信する電子メールのサブジェクトに利用します。 + +複数のリビジョンを送信する場合、 +\hgxcmd{patchbomb}{email} コマンドはチェンジセット毎に電子メールを送信します。 +この場合、 +送信しようとする一連の変更の目的を記述した前置きの電子メールを、 +一連のメール送信の先触れとして送信します。 + +\subsection{Changing the behaviour of patchbombs} + +電子メールによる変更内容送信の形式が、 +全てのプロジェクトで厳密に同じわけでは無いことから、 +\hgext{patchbomb} イクステンションは、 +コマンド行でのオプション指定による幾つかの適合処理を実施します。 + +\begin{itemize} +\item コマンド行での \hgxopt{patchbomb}{email}{-s} オプションにより、 + 前置きメッセージのサブジェクトを指定できます。 + このオプションには、 + サブジェクトとして使用するテキストを指定します。 + +\item \hgxopt{patchbomb}{email}{-f} オプションにより、 + 電子メールの送信元アドレスを変更できます。 + このオプションには、 + 送信元アドレスとして使用する電子メールアドレスを指定します。 + +\item 基底動作では、電子メールごとに unified 差分( + 形式の詳細に関しては \ref{sec:mq:patch}~節を参照してください) + を送信します。 + \hgxopt{patchbomb}{email}{-b} オプションを指定することで、 + バイナリバンドル形式での送信を選択できます。 + +\item unified 差分の通常の出力\footnote{訳注: + 「Mercurial における通常の出力」の意味? + それとも「patchbomb における通常の出力」の意味? + }はメタデータヘッダから始まります。 + \hgxopt{patchbomb}{email}{--plain} オプションを指定することで、 + これらを省略した簡素な形式の差分を送信することができます。 + +\item 差分部分は通常、 + パッチの説明部分と同じ MIME パートに``並べて''送信されます。 + メールの最初の MIME パートからしか引用できないメールツールもあるため、 + 最も多くの読み手にとって、 + 一番容易に差分を引用して返信できるのがこの形式です。 + 説明部分と差分部分を別々の MIME パートとして送信したい場合は、 + \hgxopt{patchbomb}{email}{-a} オプションを指定してください。 + +\item \hgxopt{patchbomb}{email}{-m} オプションを指定することで、 + 電子メールでの送信の替わりに、 + \texttt{mbox} 形式のメールフォルダへの書き込みを行うことができます。 + このオプションには、書き込み先ファイル名を指定します。 + +\item 各パッチおよび前置きメッセージに対して、 + \command{diffstat} 形式の要約を付与したい場合は、 + \hgxopt{patchbomb}{email}{-d} オプションを指定してください。 + \command{diffstat} コマンドは、 + パッチ適用先ファイル名と、 + 影響を受ける行数、 + および各ファイル毎の変更量を表すヒストグラムを一覧表示します。 + メールの読み手は、 + この情報からパッチの複雑度に関する質的な一覧性を得ることができます。 + +\end{itemize} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/hook.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/hook.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,1910 @@ +\chapter{Handling repository events with hooks} +\label{chap:hook} + +Mercurial は、リポジトリに発生したイベントに応じて、 +自動的な処理を実行する強力な仕組みを提供しています。 +幾つかの状況では、 +イベントに対する Mercurial の応答結果を制御することもできます。 + +Mercurial が利用するこれらの処理は、 +\emph{フック}(hook)と呼ばれています。 +構成管理システムによってはフックを``トリガ''と呼ぶこともありますが、 +これらは共に同じ考え方を指します。 + +\section{An overview of hooks in Mercurial} + +Mercurial が提供するフックの簡単なリストを示します。 +これらのフックに関する詳細は \ref{sec:hook:ref}~節で説明します。 + +\begin{description} + +\item[\small\hook{changegroup}] + 外部リポジトリからチェンジセット群が持ち込まれた後に実行されます。 + +\item[\small\hook{commit}] + ローカルリポジトリにおいて新たなチェンジセットが作成された後に実行されます。 + +\item[\small\hook{incoming}] + 外部リポジトリから持ち込まれた新たなチェンジセット毎に1回づつ実行されます。 + 持ち込まれたチェンジセットの\emph{まとまり}の単位で起動される + \hook{changegroup} との違いに注意してください。 + +\item[\small\hook{outgoing}] + 外部リポジトリへチェンジセット群が転送された後に実行されます。 + +\item[\small\hook{prechangegroup}] + 外部リポジトリからチェンジセット群が持ち込まれる前に実行されます。 + +\item[\small\hook{precommit}] + 制御用。 + ローカルリポジトリへのコミット前に実行されます。 + +\item[\small\hook{preoutgoing}] + 制御用。 + 外部リポジトリへチェンジセット群が転送される前に実行されます。 + +\item[\small\hook{pretag}] + 制御用。タグ生成前に実行されます。 + +\item[\small\hook{pretxnchangegroup}] + 制御用。 + 外部からローカルリポジトリへとチェンジセット群が持ち込まれた際に、 + 変更を恒久的なものにするトランザクションが完了する前に実行されます。 + +\item[\small\hook{pretxncommit}] + 制御用。 + ローカルリポジトリにおいて新たなチェンジセットが作成された際に、 + 変更を恒久的なものにするトランザクションが完了する前に実行されます。 + +\item[\small\hook{preupdate}] + 制御用。 + 作業領域ディレクトリの更新・マージが実施される前に実行されます。 + +\item[\small\hook{tag}] + タグが生成された後に実行されます。 + +\item[\small\hook{update}] + 作業領域ディレクトリの更新・マージが完了した後に実行されます。 + +\end{description} + +``制御用''と書かれているフックは、 +処理の継続性の可否を判定する機能を持っています。 +フックの実行が成功した場合、 +フックに対応する処理は継続されますが、 +フックの実行が失敗した場合、 +対応する処理は許可されないか実行しなかったことになります +(どちらになるかはフックに応じて決まります)。 + +\section{Hooks and security} + +\subsection{Hooks are run with your privileges} + +リポジトリにおいて Mercurial のコマンドを実行し、 +そのコマンドがフックを起動することになった場合、 +\emph{コマンド実行者の}システム上において、 +\emph{コマンド実行者の}ユーザアカウントにより、 +\emph{コマンド実行者の}権限レベルで実行されます。 +フックは任意の実行コードですから、 +十分な配慮を持って扱う必要があります。 +誰が作成して何をするフックなのかを熟知している確信無しに、 +フックをインストールしないでください。 + +時には、 +自分でインストールしたのではないフックに晒されるかもしれません。 +馴染みの無いシステム上で Mercurial を使用する際には、 +Mercurial がシステム共通の \hgrc\ +ファイルで定義されたフックを実行するかもしれません。 + +他のユーザが所有するリポジトリで作業する場合、 +Mercurial はそのユーザのリポジトリで定義されたフックを実行できますが、 +それは``あなたの''権限で実行されます。 +例えば、あるリポジトリから \hgcmd{pull} した際に、 +そのリポジトリの \sfilename{.hg/hgrc} ファイルが +\hook{outgoing} フックを定義していた場合、 +リモートリポジトリの所有者で無かったとしても、 +フックはあなたのアカウントで実行されます。 + +\begin{note} + この原則は、 + ローカルファイルシステムかネットワークファイルシステム上のリポジトリから + pull した場合にのみ適用されます。 + http や ssh 経由で pull した場合、 + フックが実行される際のアカウントは、 + サーバ上でサーバプロセスを実行するアカウントです。 +\end{note} + +リポジトリにおけるフックの定義状況を見るには、 +\hgcmdargs{showconfig}{hooks} コマンドが利用できます。 +あるリポジトリで作業中に、 +自分の所有していない別なリポジトリ +との連携(例: \hgcmd{pull} ないし \hgcmd{incoming})が必要になった場合、 +リポジトリのフック定義状況を確認すべきです\footnote{訳注: +``XXX'' が付与されていることから原文未完?}。 + +\subsection{Hooks do not propagate} + +Mercurial では、フック設定の構成管理は行われないため、 +リポジトリの clone ないし pull の際に、 +フック設定は伝播しません。 +その理由は簡単で、フックは完全に任意の実行コードだからです。 +フックは、 +コマンド実行者のマシン上において、 +コマンド実行者のユーザアカウントにより、 +コマンド実行者の権限レベルで実行されます。 + +フックの構成管理の実装は、 +構成管理システム利用者のアカウントを弱体化させる上で、 +容易に悪用可能な方法を提供してしまうため、 +あらゆる分散構成管理システムにとって極めて無謀と言えます。 + +Mercurial はフックを伝播しないため、 +共通のプロジェクトでの他のメンバーとの連携の際には、 +彼らが自分と同じ Mercurial のフックを利用していることや、 +彼らがフックを正しく設定していることを仮定してはいけません。 +彼らにフックの使用を期待するのであれば、 +それを文書化すべきです。 + +企業のイントラネットの場合、 +例えば Mercurial の``標準的な''インストールを NFS 上で行い、 +組織で共通の \hgrc\ ファイルで全てのユーザが使用すべきフックを定義する、 +といったことが可能であるため、 +フックの管理は幾分容易になります。 +しかし、それでも後述するような制限が生じます。 + +\subsection{Hooks can be overridden} + +Mercurial は、再定義によるフックの上書きを許しています。 +フック指定に空文字列を設定することでフック設定を無効にすることもできますし、 +希望通りに振る舞いを変えることもできます。 + +幾つかのフックを定義した、 +マシンないし組織共通の \hgrc\ ファイルを配備したとしても、 +利用者によるフックの無効化や上書きが行われる可能性があることを、 +理解しておく必要があります。 + +\subsection{Ensuring that critical hooks are run} + +他のメンバーに実施して欲しくない事柄について纏めた方針を、 +強制したいことも時にはあるかもしれません。 +例えば、全てのチェンジセットには必ず厳密なテスト一式に通っていて欲しい、 +と思うかもしれません。 +この要望を実現するために、 +組織共通の \hgrc\ ファイルでフックを定義したとしても、 +モバイル PC からアクセスする遠隔ユーザ等には機能しませんし、 +勿論ローカルユーザにとってもフックの上書きによって無効化が可能です。 + +(プロジェクトにおける)Mercurial の利用方針として、 +メンバーが変更伝播する際には、 +関門の機能を果たすように適切に設定された周知の``正規''サーバを通す、 +と策定することで、 +フックによる利用方針の強制を代替することが可能です。 + +実現方法の一つとして、 +ソーシャルエンジニアリングと技術の組み合わせによるものがあります。 +アクセス制限付きアカウントを用意し、 +当該アカウントで管理されたリポジトリに、 +各メンバーはネットワーク経由で変更を push できるようにしますが、 +そのアカウントでログインしたり、 +通常のシェルコマンドを実行したりできないようにします。 +このままでは、 +メンバーは「ゴミ」を含むようなチェンジセットのコミットも可能です。 + +メンバーが pull するサーバーへと誰かがチェンジセットを push した場合、 +そのチェンジセットが永続化される前にサーバーはテストを実施\footnote{ +訳注: テスト実施はフックで実現されますが、 +(1) フックの実行はアクセス制限付きアカウントの権限で実行され、 +(2) リモートからの push の場合はフックの上書きができない、 +ということから、セキュリティ・フック設定の問題が共に解消されます。}し、 +テスト一式に通らなければそのチェンジセットを拒否します。 +メンバーがこのフィルタサーバからしかチェンジセットの pull をしないのであれば、 +メンバーが pull する全てのチェンジセットは、 +自動的に点検されていることが保証されます。 + +\section{Care with \texttt{pretxn} hooks in a shared-access repository} +\label{sec:hook:carepretxn} + +多くの人により共有されているリポジトリに対して、 +フックによる自動実行を設定する場合、 +実施方式には注意が必要です。 + +Mercurial がリポジトリにロックを掛けるのは、 +リポジトリに書き込みを行う時だけであり、 +且つロックに対して注意を払うのは、 +Mercurial の書き込み処理の部分的な箇所だけです。 +書き込みロックは、 +複数の処理の同時書き込みによるリポジトリ破損を防ぐことで、 +お互いの書き込み内容を保護します。 + +Mercurial はデータの読み込み書き出し順序に注意を払っていますから、 +リポジトリからのデータ読み込みの際にロックは必要ありません。 +Mercurial がリポジトリからデータを読み込む際には、 +ロックに対して注意を払いません。 +ロックを必要としないこの仕組みは、 +性能と平行性を大きく向上させています。 + +しかしながら、 +「ロックされない」ということは、 +それを知らないと、 +大きな性能向上と引き換えにトラブル発生の潜在的な危険性を持っています。 +この危険性について説明するには、 +リポジトリへのチェンジセットの追加、 +およびそれらチェンジセットの読み出しを、 +Mercurial がどういった手順で行うかについて、 +幾分詳細な知識が必要となります。 + +Mercurial がメタデータを\emph{書き出す}際には、 +対象ファイルにメタデータを直接書き出します。 +最初に filelog にメタデータを書き出し、 +次に manifest のデータ(これには、 +filelog に書き出した新しいデータへのポインタが含まれます)、 +そしてchangelogのデータ(これには、 +manifest に書き出した新しいデータへのポインタが含まれます)が書き出されます。 +個々のファイルへの最初の書き出しの前に、 +Mercurial は個々のファイルの終端位置情報をトランザクションログに記録します。 +Mercurial によりトランザクションが巻き戻される際には、 +トランザクション開始時点のサイズにまで個々のファイルが切り詰められます。 + +Mercurial がメタデータを\emph{読み込む}際には、 +changelog を読み込んだ後でその他のファイルの読み込みを行います。 +データ読み込みの際には、 +先に読み込んだ changelog から到達可能な +manifest や filelog の部分にしかアクセスしないので、 +不十分な書き出し中のデータを読むことはありません。 + +幾つかの制御用フックの(\hook{pretxncommit} や \hook{pretxnchangegroup}) +は、トランザクションの完了直前に実行されます。 +この時点で全てのメタデータは書き出し済みですが、 +Mercurial はトランザクションを巻き戻すことで、 +新たに書き出されたデータを破棄することができます。 + +トランザクション完了前のチェンジセットは永続性が確定しておらず、 +そのため``本当に存在する''とみなすことができないことから、 +トランザクション完了前に実行される制御用フックが終了までに長時間を要する場合、 +永続性が確定していないチェンジセットのメタデータが、 +平行して動作している他の処理により読み出される時間帯が発生します。 +フックの実行時間が長くなる程、この時間帯が長くなります。 + +\subsection{The problem illustrated} + +原則的に \hook{pretxnchangegroup} フックは、 +集約用リポジトリでの受け入れ前に、 +新規チェンジセットのビルドやテストを自動化するのに適しています。 +この用法は +``ビルドを失敗させる''変更が集約用リポジトリに反映されないことを保証します。 +しかし、 +\hook{pretxnchangegroup} フックによるテスト途上の変更を、 +他の利用者が pull できてしまうようでは、 +テストの有用性が無くなってしまいます。 +リポジトリ内容の整合性に疑いを持たない利用者は、 +ビルドを失敗させる潜在的な可能性を持つテスト未実施の変更を、 +自身のリポジトリへと反映してしまうからです。 + +このような難題への最も安全な技術的解法は、 +``門番''リポジトリの利用を\emph{単方向}に限定してしまうことです。 +門番リポジトリは、 +外部からのチェンジセットの push は許しても、 +pull はできないようにします +(\hook{preoutgoing} フックでそのような行為を禁止します)。 +新しいチェンジセットにおけるビルドないしテストが成功したならば、 +そのチェンジセットを別なリポジトリへと push するように +\hook{changegroup} フックを設定し、 +利用者はそちらのリポジトリから pull \emph{できる}ようにしましょう。 + +実際問題、 +このような集約されたボトルネックを設けることは、 +あまり良いアイディアではなく(XXXX ?)、 +In practice, putting a centralised bottleneck like this in place is +not often a good idea +トランザクションの漏洩\footnote{訳注: +永続化未確定のチェンジセットが見えてしまうこと}は問題になりません。 +チェンジセットを取り扱う時間よりもそれをテストするのに時間を要する状況では、 +プロジェクトの大きさ---およびビルド・テストに要する時間 +---が増加するほど、 +``購入前の試用''手法により壁の内側に素早く走りこめます。XXXXX ???? +As the size of a project---and the time it takes to +build and test---grows, you rapidly run into a wall with this ``try +before you buy'' approach, where you have more changesets to test than +time in which to deal with them. +避けられない結果は、 +すべてが巻き込まれた部分におけるフラストレーションです。XXXXXXX ???? +The inevitable result is frustration +on the part of all involved. + +より大規模化可能な手法は、 +push 前に各自でビルド・テストを実施してもらい、 +push の \emph{後}に中央で自動的にビルド・テストを行うことで、 +全てのチェンジセットが良好であることを確認する、というものです。 +この手法の利点は、 +リポジトリにおけるチェンジセットの受理進度に関して、 +制限が課されることが無い点にあります。 + +\section{A short tutorial on using hooks} +\label{sec:hook:simple} + +Mercurial のフックは簡単に書けます。 +\hgcmd{commit} が完了した際に実行され、 +作成したばかりのチェンジセットのハッシュ値を表示するだけの、 +簡単なフックを書いてみましょう。 + +\begin{figure}[ht] + \interaction{hook.simple.init} + \caption{A simple hook that runs when a changeset is committed} + \label{ex:hook:init} +\end{figure} + +全てのフックは、\ref{ex:hook:init} の例における形式を踏襲します。 + \hgrc\ ファイルの +\rcsection{hooks} セクションにエントリを追加します。 +左辺は実行契機になるイベントの名前で、 +右辺は実行される処理です。 +見てわかるように、 +フックにおいては任意のシェルコマンドを実行できます。 +環境変数 +(例における \envar{HG\_NODE} を参照してください)を用いて、 +Mercurial はフックに付加情報を渡します。 + +\subsection{Performing multiple actions per event} + +\ref{ex:hook:ext} の例に示すような、 +特定の種類のイベントに対して1つ以上のフックを定義したい状況が、 +しばしば発生することでしょう。 +Mercurial では、 +フック名の末尾に\emph{拡張子}を付与することで、 +同一イベントへの複数フックの定義が可能になります。 +拡張子の付与は、 +フック名に、 +ピリオド(``\texttt{.}'' 文字)と任意に選んだ文字列を続けることで行います。 +例えば、 +\texttt{commit} が発生した場合、 +Mercurial は +\texttt{commit.foo} および +\texttt{commit.bar} フックを実行します。 + +\begin{figure}[ht] + \interaction{hook.simple.ext} + \label{ex:hook:ext} + \caption{Defining a second \hook{commit} hook} +\end{figure} + +あるイベントに複数のフックが定義されている際に、 +その実行順序を明確に定義するために、 +Mercurial はフックを拡張子で整列させ、 +フックコマンドをこの整列された順序で実行します。 +上記の例では、 +\texttt{commit.foo} +の前に +\texttt{commit.bar} を、 +これらの前に +\texttt{commit} を実行します。 + +新しいフックを定義する際に、 +何らかの説明的な拡張子を使用するのは良いアイディアです。 +そうすることで、 +そのフックが何をするためのものかを思い出しやすくなります。 +フックの実行が失敗した場合、 +フック名と拡張子を含むエラーメッセージが表示されますから、 +フックが失敗した理由に関して、 +説明的な拡張子から即製のヒントを得ることができます +(例に関しては、\ref{sec:hook:perm}~節を参照してください)。 + +\subsection{Controlling whether an activity can proceed} +\label{sec:hook:perm} + +先の例では、 +コミット操作が完了した後で実行される +\hook{commit} フックを使用しました。 +このフックは、 +操作が完了した後で実行される Mercurial のフックの1つです。 +これらのフックは、操作そのものに影響を及ぼすことはありません。 + +Mercurial では、 +操作が開始される前や、 +操作が完了するまでの間に発生するイベントが定義されています。 +これらのイベントの際に起動されるフックは、 +操作を継続可能か中断すべきかを判断することができます。 + +\hook{pretxncommit} フックは、 +コミット操作が概ね終了した後、コミットが完了する前の段階で起動されます。 +言い換えるなら、 +チェンジセットを表すメタデータがディスクに書き込まれてはいるものの、 +トランザクションが未だ完了していない状況で起動されます。 +\hook{pretxncommit} フックは、 +トランザクションを完了させるのか、 +あるいは巻き戻すべきかを決定することができます。 + +\hook{pretxncommit} フックが終了状態値として0を返却した場合、 +トランザクションは完了し、コミット操作は終了しますので、 +\hook{commit} フックが実行されます。 +\hook{pretxncommit} フックが終了状態として非0を返却した場合、 +トランザクションは巻き戻され、 +チェンジセットを表すメタデータは削除され、 +\hook{commit} フックは実行されません。 + +\begin{figure}[ht] + \interaction{hook.simple.pretxncommit} + \label{ex:hook:pretxncommit} + \caption{Using the \hook{pretxncommit} hook to control commits} +\end{figure} + +例~\ref{ex:hook:pretxncommit} 中のフックは、 +コミット時のコメントがバグIDを含んでいることを確認しています。 +コメントがバグIDを含んでいる場合、コミットは完了します。 +そうでなければ、コミット操作は巻き戻されます。 + +\section{Writing your own hooks} + +\hggopt{-v} オプション付き、 +あるいは\rcitem{ui}{verbose} 設定項目を``true''にして +Mercurial を実行するのが、 +フック実装の際には有用であることに気付くかもしれません。 +このようにして Mercurial を実行することで、 +それぞれのフックを起動する際に事前にメッセージを表示します。 + +\subsection{Choosing how your hook should run} +\label{sec:hook:lang} + +フックを実装する際には、通常のプログラム---典型的にはシェルスクリプト--- +としても実装できますが、 +Python 関数としても実装でき、 +その場合は Mercurial プロセス内で実行されます。 + +外部プログラムとしてフックを実装する利点は、 +Mercurial の内部事情に関して知る必要が無い点にあります。 +付加的な情報の取得のために、 +通常の Mercurial コマンドを起動することもできます。 +その利点と引き換えに、外部(プログラムとしての)フックは、 +プロセス内フックよりも低速\footnote{訳注: +後述されますが、 +外部プログラムによるフックが「低速」であるのは、 +(1)外部プロセスとしてのフック起動と、 +(2)Mercurial リポジトリへのアクセスに関する部分で、 +外部プロセスの実行そのものが低速なわけではありません。}です。 + +Python 関数によるプロセス内フックは、 +全ての Mercurial API にアクセスでき、 +他のプロセスを``生成''する必要はありませんので、 +基本的に外部フックよりも高速です。 +フックが必要とする多くの情報の入手も、 +Mercurial コマンドから得るよりも、 +Mercurial API から得る方が容易です。 + +Python の利用が苦にならないか、 +高い実行性能が要求される場合、 +Python でのフック実装を選択すべきです。 +しかしながら、 +簡単なフックで、 +性能を気にする必要が無い(おそらく多くのフックがそうです)のであれば、 +シェルスクリプトでの実装で十分です。 + +\subsection{Hook parameters} +\label{sec:hook:param} + +Mercurial がフックを起動する際には、 +明確に定義されたパラメータがフックに渡されます。 +Python でのフック実装の場合、 +パラメータはキーワード引数としてフック関数に渡されます。 +外部プログラムでのフック実装の場合、 +パラメータは環境変数として渡されます。 + +フック実装が Python ・シェルスクリプトのいずれであるかで、 +フック固有のパラメータ名とその値が決まります\footnote{訳注: +原文は +「Whether your hook is written in Python or as a shell script, the +hook-specific parameter names and values will be the ``same''」} +真偽値パラメータは、Python フックでは真偽値型として表現されますが、 +外部フックに対しては ``1''(``true'' 値として)ないし +``0''(``false'' 値として)を持つ環境変数で表現されます。 +フックパラメータが \texttt{foo} という名前である場合、 +Python フックのキーワード引数の名前も \texttt{foo} ですが、 +外部フックの環境変数名は \texttt{HG\_FOO} となります。 + +\subsection{Hook return values and activity control} + +実行が成功したフックは、外部フックの場合は終了コード0で、 +プロセス内フックの場合は真偽値``False''で終了しなければなりません +\footnote{訳注:Mercurial の配布物に含まれる hgext 配下のフックは、 +結構な確率で、 +False 無しの return や、 +明示的な return 無しの実装ですが、 +Python の言語仕様上、 +これらは False と``ほぼ等価''な None とみなされます。}。 +フックの実行失敗は、 +外部フックの場合は非0の終了コードで、 +プロセス内フックの場合は真偽値``true''で表されます。 +プロセス内ふっくが例外を浮揚した場合、 +フック実行は失敗したと見做されます。 + +操作の継続性を制御できるフックの場合、 +0/false は継続の``許可''を、 +非0/true/例外は``拒否''を意味します。 + +\subsection{Writing an external hook} + +\hgrc\ ファイルに外部フックを記述した場合、 +\hgrc\ ファイルに記述したフックの内容は、 +シェルプロセスに渡され、 +そのシェルプロセスによって解釈されます。 +これは、フック記述の本体に、 +通常のシェルコマンドラインと同様の構造を用いることができる、 +ということを意味しています。 + +実行可能なフックは、 +常にリポジトリのルートディレクトリ直下で実行されます。 + +個々のフックパラメータは環境変数経由で渡されますが、 +環境変数名には、 +大文字化され、接頭辞として``\texttt{HG\_}''が付与された名前が用いられます。 + +フックパラメータを例外とすれば、 +Mercurial はフック実行時に環境変数の改変を行いません。 +それぞれに異なる環境変数設定をしている多くのユーザによって実行される、 +組織全体で共用されるフックを実装する際には、 +この知識が役に立つでしょう。 +複数ユーザにより実行される状況下では、 +フックの試験環境で設定されていた環境変数が、 +実行時に設定されていることを期待してはいけません。 + +\subsection{Telling Mercurial to use an in-process hook} + +プロセス内フックを \hgrc\ ファイルで設定する際の文法は、 +実行可能フック\footnote{訳注: 「外部フック」の意 +}設定の際のそれとは少々異なります +フック設定は、 +接頭辞``\texttt{python:}''に続き、 +フックとして使用する呼び出し可能オブジェクト\footnote{訳注: +callable object}の完全修飾された名前が記述されていなければなりません。 + +フック定義が存在するモジュールは、 +フック実行時に自動的に import されます。 +モジュール名と \envar{PYTHONPATH} 設定が正しければ、 +きっと動作する筈です\footnote{訳注: ``just work'' のニュアンスは?}。 + +以下に示す \hgrc\ ファイルの引用例は、 +前述した表記に関する文法と意味を例示しています。 + +\begin{codesample2} + [hooks] + commit.example = python:mymodule.submodule.myhook +\end{codesample2} + +Mercurial が \texttt{commit.example} フックを起動する際には、 +\texttt{mymodule.submodule} を import し、 +\texttt{myhook} という名前の呼び出し可能オブジェクトを探し出して起動します。 + +\subsection{Writing an in-process hook} + +以下に示す最も単純なプロセス内フックは、 +フックとしては何もしませんが、 +フック API の基本的な概要を例示できます。 + +\begin{codesample2} + def myhook(ui, repo, **kwargs): + pass +\end{codesample2} + +Python フック\footnote{訳注:プロセス内フックの意}の最初の引数は、 +常に \pymodclass{mercurial.ui}{ui} オブジェクトです。 +第2引数はリポジトリオブジェクトですが、 +現在の Mercurial の実装では、 +そのインスタンスは常に \pymodclass{mercurial.localrepo}{localrepository} です。 +これらに続くその他の引数はキーワード引数として渡されます。 +渡される内容は起動されるフック(の種類)に依存しますが、 +上記例における \texttt{**kwargs} のように、 +キーワード引数辞書に落とし込む\footnote{XXXXX: +Python 固有の訳語を確認}ことで、 +興味の無い引数を無視することができます。 + +\section{Some hook examples} + +\subsection{Writing meaningful commit messages} + +有用なコミットメッセージが非常に短い、 +という状況は想像し難いものがあります。 +図~\ref{ex:hook:msglen.go} に示す単純な +\hook{pretxncommit} フックは、 +10バイトよりも短いメッセージでのチェンジセットのコミットを妨げます。 + +\begin{figure}[ht] + \interaction{hook.msglen.go} + \caption{A hook that forbids overly short commit messages} + \label{ex:hook:msglen.go} +\end{figure} + +\subsection{Checking for trailing whitespace} + +コミットに関する興味深いフックの利用は、 +綺麗なコードでの実装を補助するというものです。 +簡単な``綺麗なコード''の例としては、 +変更が追加する新しい行には``末尾空白''が含まれていてはならない、 +という格言があります。 +末尾空白とは、 +空白文字およびタブ(tab)文字の連続が行末にあることを意味します。 +多くの場合、 +末尾空白は必要の無い不可視の雑音みたいなものですが、 +時には問題を含むことから、 +それらが取り除かれることを望みます。 + +\hook{precommit} と \hook{pretxncommit} のいずれのフックでも、 +末尾空白問題を通知することが可能です。 +\hook{precommit} フックを使用した場合、 +フックはコミット対象ファイルを知ることができないので、 +リポジトリ中の変更されたファイル全てに対して末尾空白を確認してしまいます。 +そうすると、 +ファイル \filename{foo} の変更のみをコミットしたい場合でも、 +\filename{bar} ファイルが末尾空白を含んでいたなら、 +\hook{precommit} フックでのチェックは、 +\filename{bar} の問題を理由に \filename{foo} のコミットを妨げてしまいます。 +これではいけません。 + +\hook{pretxncommit} フックで実現する場合、 +コミットのトランザクションが完了する直前までチェックが行われません。 +このため、末尾空白問題の確認を、 +厳密にコミット対象のファイルだけに行うことができます。 +しかし、 +コミットメッセージを対話的に入力した後であっても、 +フックの実行が失敗\footnote{ +訳注: 末尾空白が検出されることでの「失敗」}した場合、 +トランザクションは巻き戻されてしまいますので、 +末尾空白を取り除いた後で再び \hgcmd{commit} コマンド実行した際には、 +もう一度コミットメッセージを入力する必要があります。 + +\begin{figure}[ht] + \interaction{hook.ws.simple} + \caption{A simple hook that checks for trailing whitespace} + \label{ex:hook:ws.simple} +\end{figure} + +図~\ref{ex:hook:ws.simple} では、 +末尾空白をチェックする簡単な +\hook{pretxncommit} フックを紹介しています。 +このフックは短いですが、非常に有用です。 +変更により何れかのファイルに対して末尾空白を含む行が追加された場合、 +このフックはエラーステータスで終了しますが、 +不愉快なファイルや行の特定を補助する情報を何ら表示しません\footnote{訳注: +フック実行のコマンドラインからわかるように、 +export 出力(= patch 形式)に対して (e)grep を適用していますから、 +ファイル名や行番号に対しては何ら認識されていません。}。 +このフックは、 +改変されていない行には注意を払わず、 +末尾空白問題を持ち込む行にのみ注意を払う、 +という優れた特質も持っています。 + +\begin{figure}[ht] + \interaction{hook.ws.better} + \caption{A better trailing whitespace hook} + \label{ex:hook:ws.better} +\end{figure} + +図~\ref{ex:hook:ws.better} は先の例よりは複雑ですが、 +より有用なフックの例を示しています\footnote{訳注: +check\_whitespace.py の内容が不明。 +図中でソースを cat すべき XXXX}。 +このフックは unified diff 形式を解析して、 +末尾空白を追加する行の有無を判定し、 +そのようなファイルの名前と行番号を表示します。 +それに加えてこのフックは、 +チェンジセットが末尾空白を追加することを検知した場合、 +実行を終了して Mercurial にトランザクションの巻き戻しを伝える前に、 +コミットメッセージを保存してそのファイル名を表示しますので、 +問題点を修正した後のコミットの際には、 +\hgcmdargs{commit}{\hgopt{commit}{-l}~\emph{filename}} +を使ってコミットメッセージを再利用することができます。 + +図~\ref{ex:hook:ws.better} +ファイルから末尾空白を取り除く +\command{perl} の一行記述の用法を示します。 +この方法はここに再掲するに足るだけの、 +簡潔さと有用性を持っています\footnote{訳注: +コードの表示が(HTML 形式だと)2行に分割されている XXXX}。 + +\begin{codesample2} + perl -pi -e 's,\\s+\$,,' filename +\end{codesample2} + +\section{Bundled hooks} + +Mercurial の配布版には、幾つかのフックが添付されています。 +添付フックは Mercurial ソースツリーの +\dirname{hgext} ディレクトリに格納されています。 +Mercurial のバイナリ配布版を使用している場合には、 +パッケージのインストーラーが +Mercurial をインストールした位置にある +\dirname{hgext} ディレクトリに格納されています。 + +\subsection{\hgext{acl}---access control for parts of a repository} + +\hgext{acl} 拡張により、 +ネットワーク上のサーバに対してチェンジセットを +push 可能な遠隔ユーザを制限することができます。 +リポジトリの一部(勿論全体も)を保護することができますので、 +特定のユーザに対しては、 +保護された部分に影響を及ぼさないチェンジセットのみの push が可能です。 + +この拡張は +push 対象のチェンジセットをコミットしたユーザ\emph{ではなく}、 +push を実施するユーザの身元情報を元にアクセス制御を行います。 +遠隔ユーザを認証する監禁(lock-downed)サーバが存在する環境で、 +特定のユーザだけが監禁サーバへのチェンジセットの +push が許されることを確実にしたい場合でなければ、 +このフックの使用は意味がありません。 + +\subsubsection{Configuring the \hook{acl} hook} + +持ち込まれるチェンジセットを管理するために、 +\hgext{acl} フックは +\hook{pretxnchangegroup} フックとして用います。 +\hook{pretxnchangegroup} フックとして用いられることで、 +外来のチェンジセットにより変更されるファイルを知ることができるため、 +``禁止されている''ファイルへの変更を行うチェンジセット群に対しては、 +トランザクションの巻き戻しが行われます。 + +\begin{codesample2} + [hooks] + pretxnchangegroup.acl = python:hgext.acl.hook +\end{codesample2} + +\hgext{acl} 拡張は3つのセクションで設定されます。 + +\rcsection{acl} セクションには、 +フックが注意を払うべき外来チェンジセットの出所を列挙する +\rcitem{acl}{sources} エントリだけが記述されます。 +通常はこのセクションを設定する必要はありません。 + +\begin{description} +\item[\rcitem{acl}{serve}] リモートリポジトリからの http ないし ssh + 経由のチェンジセットに対して制御を行います。 + これは \rcitem{acl}{sources} の既定値で、 + 通常はこの設定項目に対して行う唯一の設定です。 + +\item[\rcitem{acl}{pull}] ローカルリポジトリからの + pull 経由のチェンジセットに対して制御を行います。 + +\item[\rcitem{acl}{push}] ローカルリポジトリからの + push 経由のチェンジセットに対して制御を行います。 + +\item[\rcitem{acl}{bundle}] 他のリポジトリからの + bundle 経由のチェンジセットに対して制御を行います。 + +\end{description} + +\rcsection{acl.allow} セクションは、 +リポジトリへのチェンジセット追加を許可されているユーザを決定します。 +このセクションが存在しない場合、 +明示的に禁止されていないユーザは、 +誰でもチェンジセットの追加をできます。 +このセクションが存在する場合、 +明示的に許可されていないユーザは、 +誰もチェンジセットの追加ができません( +ですので、このセクションを空にした場合、 +全てのユーザがチェンジセットの追加を禁止されます)。 + +\rcsection{acl.deny} セクションは、 +リポジトリへのチェンジセット追加を禁止されているユーザを決定します。 +このセクションが記述されない場合、 +全てのユーザはチェンジセットの追加を許可されます\footnote{訳注: +原文は「no users are denied」ですが、 +acl.py の実装上は「禁止しない」と「許可」は等価です。}。 + +\rcsection{acl.allow} および \rcsection{acl.deny} +セクションの文法は同一です。 +各エントリの左辺は、 +リポジトリルート相対でのファイルないしディレクトリのマッチングパターンで、 +右辺はユーザ名となっています。 + +以下の例では、 +ユーザ \texttt{docwriter} がリポジトリの +\dirname{docs} 配下に対する変更の +push のみが許可されている一方で、 +ユーザ \texttt{intern} は +\dirname{source/sensitive} +以外の任意のディレクトリ・ファイルに対する変更を +push 可能です +\footnote{訳注: +設定の判定順序は (1) 禁止 (2) 許可の順序で行われ、 +(1) 禁止設定があり、当該ユーザのアクセスが明示的に禁止されている場合と、 +(2) 許可設定があり、当該ユーザのアクセスが明示的に許可されて「いない」場合に、 +不正アクセスとみなされ、 +それ以外の場合はアクセスが許可されます。}。 + +\begin{codesample2} + [acl.allow] + docs/** = docwriter + + [acl.deny] + source/sensitive/** = intern +\end{codesample2} + +\subsubsection{Testing and troubleshooting} + +\hgext{acl} フックを試してみたい場合、 +Mercurial のデバッグ出力を有効にして実行しましょう。 +\hggopt{--debug} オプションを指定し難い(あるいは不可能な) +サーバ上で実行することもあるでしょうから、 +サーバ側の \hgrc ファイルでデバッグ出力を有効化できることをお忘れなく。 + +\begin{codesample2} + [ui] + debug = true +\end{codesample2} + +これを有効にすることで、 +当該ユーザによる push +を許可・禁止する理由を判断するに足る情報を表示することでしょう。 + +\subsection{\hgext{bugzilla}---integration with Bugzilla} + +\hgext{bugzilla} 拡張は、 +コミットメッセージにバグIDを検知した際に +Bugzilla バグへのコメント追加を行います。 +このフックを共有サーバに設定することで、 +このサーバへのリモートからの変更伝播の際には、 +常にこのフックが実行されます。 + +このフックは Bugzilla バグに、 +以下のようなコメントを追加します +(方法は後述しますが、コメント内容は変更できます)。 + +\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} + +このフックの価値は、 +チェンジセット(のコミットメッセージ)がバグを参照している際に、 +バグ情報を更新する手順を自動化する点にあります。 +フックの設定を適切に行うことで、 +Bugzilla バグから参照元チェンジセットへと、 +一直線に到達することが容易になります。 + +このフックの実装を足掛りにして、 +より高度な Bugzilla との統合を図ることも可能です。 +例えば: + +\begin{itemize} +\item サーバに push される全てのチェンジセットには、 + コミットメッセージに適切なバグ~IDが含まれていることを要求: + この場合、\hook{pretxncommit} + フックに当該条件を検証するフックを設定するのが良いでしょう。 + コミットメッセージがバグ~IDを含まないチェンジセットは、 + フックによって拒否されるようになります。 + +\item 新規のチェンジセットに対して、 + 簡単なコメントの付与と同様に、 + バグの\emph{状態}の自動的な変更を許可: + 例えば、``fixed bug 31337'' というコミットメッセージの文字列を、 + バグ 31337 の状態の ``requires testing'' への更新、 + と認識させる、といった拡張も考えられます。 + +\end{itemize} + +\subsubsection{Configuring the \hook{bugzilla} hook} +\label{sec:hook:bugzilla:config} + +\hook{bugzilla} フックは、 +サーバ側の \hgrc\ 中で \hook{incoming} フックとして設定しなければなりません。 + +\begin{codesample2} + [hooks] + incoming.bugzilla = python:hgext.bugzilla.hook +\end{codesample2} + +機能特化されたフックの性質と、 +Bugzilla が元々この種の統合を念頭に置いていないことから、 +このフックの設定は何かと複雑になります。 + +フックの設定に先立って、 +フックが実行されるホスト(群)に対して、 +MySQL の Python バインディングをインストールしてください。 +対象ホストにおいてバイナリパッケージが見当たらない場合、 +\cite{web:mysql-python} からダウンロードできます。 + +フックの設定は、 + \hgrc\ ファイルの +\rcsection{bugzilla} セクションに記述されます。 + +\begin{description} + +\item[\rcitem{bugzilla}{version}] サーバにインストールされている + Bugzilla のバージョン。 + Bugzilla のデータベーススキーマは時折変更されますので、 + どのスキーマが使用されているのかを厳密に知っている必要があります。 + 今のところ、サポート対象は \texttt{2.16} のみです。 + +\item[\rcitem{bugzilla}{host}] Bugzilla のデータが格納されている + MySQL サーバが稼動しているホスト名。 + MySQL サーバは、\hook{bugzilla} フックが実行される全てのホストに対して、 + 接続を許可している必要があります。 + +\item[\rcitem{bugzilla}{user}] MySQL サーバへの接続時に使用するユーザ名。 + MySQL サーバは、\hook{bugzilla} フックが実行される全てのホストに対して、 + このユーザ名での接続を許可している必要があります。 + このユーザは、 + Bugzilla が使用するテーブルに対して読み取り・変更の両方の権限が必要です。 + この項目の既定値は、 + MySQL サーバにおける Bugzilla の標準的なユーザ名である + \texttt{bugs} です。 + +\item[\rcitem{bugzilla}{password}] 上記ユーザの + MySQL サーバにおけるパスワード。 + この値は平文で格納されるため、 + 権限を持たないユーザがこの情報の書かれた \hgrc + ファイルを覗くことが無いようにしなければなりません。 + +\item[\rcitem{bugzilla}{db}] MySQL サーバにおける + Bugzilla データベースの名前。 + この項目の既定値は、 + MySQL サーバにおける Bugzilla の標準的なデータベース名である + \texttt{bugs} です。 + +\item[\rcitem{bugzilla}{notify}] フックによるバグへのコメント付与時に、 + Bugzilla による購読者への電子メール通知を実施したい場合、 + データベースを更新する毎にコマンドを実行させる必要があります。 + 実行するコマンドは Bugzilla のインストール場所に依存しますが、 + \dirname{/var/www/html/bugzilla} にインストールしたとすると、 + 通常は以下のようになります。 + + \begin{codesample4} + cd /var/www/html/bugzilla && ./processmail %s nobody@nowhere.com + \end{codesample4} + + Bugzilla の \texttt{processmail} プログラムは、 + バグ~ID(フックにより ``\texttt{\%s}'' が バグ~ID に置換されます)と、 + 電子メールアドレスを必要とします。 + このプログラムは、 + 実行時ディレクトリへのファイル書き出しの権限も必要とします。 + Bugzilla とフックが同じサーバ上にインストールされていない場合、 + Bugzilla がインストールされているサーバ上で + \texttt{processmail} を起動する方法を見つけ出す必要があります。 + +\end{description} + +\subsubsection{Mapping committer names to Bugzilla user names} + +既定状態の \hgext{bugzilla} フックは、 +チェンジセットをコミットしたユーザの電子メールアドレスを、 +バグの更新を行う Bugzilla ユーザ名として使用することを試みます。 +この挙動が状況に即さない場合、 +\rcsection{usermap} セクションを使用して、 +チェンジセットをコミットしたユーザの電子メールアドレスを +Bugzilla のユーザ名に変換することができます。 + +\rcsection{usermap} セクションの個々の要素は、 +左辺に電子メールアドレス、 +右辺に Bugzilla ユーザ名を保持します。 + +\begin{codesample2} + [usermap] + jane.user@example.com = jane +\end{codesample2} + +通常の \hgrc ファイルに +\rcsection{usermap} データを直接保持することもできますが、 +\hgext{bugzilla} フックに外部の +\filename{usermap} ファイルから情報を読み込むように指示することもできます。 +後者の場合、例えば \filename{usermap} データそのものを、 +利用者が改変可能なリポジトリに格納することもできます。 +そうすることで、 +利用者自身が +\rcitem{bugzilla}{usermap} 中の各自の要素を保守することができます。 +この場合の \hgrc\ ファイルは以下のように記述されます。 + +\begin{codesample2} + # 通常の hgrc ファイルは usermap 外部ファイルを参照 + [bugzilla] + usermap = /home/hg/repos/userdata/bugzilla-usermap.conf +\end{codesample2} + +\filename{usermap} が参照するファイルの内容は、 +以下のようになります。 + +\begin{codesample2} + # bugzilla-usermap.conf は hg リポジトリ内に配置 + [usermap] + stephanie@example.com = steph +\end{codesample2} + +\subsubsection{Configuring the text that gets added to a bug} + +Mercurial のテンプレート形式で記述することで、 +\hgext{bugzilla} フックが追加するコメントの内容を設定することが可能です。 +幾つかの(\rcsection{bugzilla} セクションにおける) \hgrc\ 要素により、 +(テンプレートの?)振る舞いを制御することができます。 + +\begin{description} +\item[\texttt{strip}] URL における部分パス名(a + partial path for a URL)を生成する際に、 + リポジトリにおけるパス名から取り除くパス要素の数を指定します。 + 例えば、サーバにおけるリポジトリ群が \dirname{/home/hg/repos} 配下にあり、 + \dirname{/home/hg/repos/app/tests} のリポジトリを対象とする場合、 + \texttt{strip} を \texttt{4} とすることで、 + \dirname{app/tests} という部分パスを得ることができます。 + \hgext{bugzilla} フックはこの部分パス名を、 + テンプレートの適用の際に \texttt{webroot} という名前で利用可能にします。 + +\item[\texttt{template}] 使用するテンプレートテキストを指定します。 + 通常のチェンジセット関連の置換に加えて、 + このテンプレートでは \texttt{hgweb}(後述例にあるように + \texttt{hgweb} 項目で設定します) + および \texttt{webroot}(前述のように + \texttt{strip} によって生成されるパスです)が使用できます。 + +\end{description} + +これらに加えて、 + \hgrc\ ファイルの \rcsection{web} セクションに +\rcitem{web}{baseurl} 項目を追加することができます。 +Bugzilla コメントからのチェンジセット参照に使用するリンクの +URL を構築する際の基底文字列として +\hgext{bugzilla} フックはテンプレート展開の際にこの値を使用します。 +例えば: + +\begin{codesample2} + [web] + baseurl = http://hg.domain.com/ +\end{codesample2} + +\hgext{bugzilla} フックの設定例を以下に示します\footnote{訳注: +原文の ``\\n'' が正しく機能していないため、 +例示のレイアウトが乱れている}。 + +\begin{codesample2} + [bugzilla] + host = bugzilla.example.com + password = mypassword + version = 2.16 + # サーバ側リポジトリは /home/hg/repos にあるため、 + # 冒頭の 4 つのセパレータ\footnote{訳注: パス区切り ``/''}を除外 + strip = 4 + hgweb = http://hg.example.com/ + usermap = /home/hg/repos/notify/bugzilla.conf + template = Changeset \{node|short\}, made by \{author\} in the \{webroot\} + repo, refers to this bug.\\nFor complete details, see + \{hgweb\}\{webroot\}?cmd=changeset;node=\{node|short\}\\nChangeset + description:\\n\\t\{desc|tabindent\} +\end{codesample2} + +\subsubsection{Testing and troubleshooting} + +\hgext{bugzilla} フック設定において最も良くある問題は、 +Bugzilla の \filename{processmail} スクリプト実行に関するものと、 +コミットユーザ名から Bugzilla ユーザ名への変換に関するものです。 + +先の \ref{sec:hook:bugzilla:config}~節からの説明で述べたように、 +Mercurial プロセスをサーバで実行するユーザが、 +\filename{processmail} スクリプトを実行するユーザでもあります。 +\filename{processmail} スクリプトは +Bugzilla が設定ディレクトリ中のファイルに何らかの情報を書き出す契機となるため、 +通常 Bugzilla の設定ファイルは +Bugzilla が動作するウェブサーバの実行者の権限下にあります。 + +\filename{processmail} 実行の際には、 +\command{sudo} コマンドを利用するなどして適切なユーザ権限で実行しましょう。 +\filename{sudoers} フィルの設定例を以下に示します。 + +\begin{codesample2} + hg_user = (httpd_user) NOPASSWD: /var/www/html/bugzilla/processmail-wrapper %s +\end{codesample2} + +この例では、\texttt{hg\_user} ユーザは、 +\filename{processmail-wrapper} プログラムを +\texttt{httpd\_user} ユーザの権限下で実行することができます。 + +\filename{processmail} プログラムは +Bugzilla をインストールしたディレクトリ直下での実行が必要ですが、 +\filename{sudoers} ファイルにはそのような制約を記述することができないので、 +このような間接実行のためのラッパースクリプトが必要となります。 +ラッパースクリプトの内容は以下のように簡単なものです。 + +\begin{codesample2} + #!/bin/sh + cd `dirname $0` && ./processmail "$1" nobody@example.com +\end{codesample2} + +\filename{processmail} +に指定する電子メールアドレスは、 +どのようなものでも構いません。 + +\rcsection{usermap} が正しく設定されていない場合、 +チェンジセットをサーバに push した際に +\hgext{bugzilla} フックによりエラーメッセージが表示されます。 +エラーメッセージは以下のようなものです。 + +\begin{codesample2} + cannot find bugzilla user id for john.q.public@example.com +\end{codesample2} + +このメッセージは、 +コミットしたユーザの電子メールアドレス +\texttt{john.q.public@example.com} +が有効な Bugzilla ユーザ名ではないか、 +\texttt{john.q.public@example.com} +を有効な Bugzilla ユーザ名に変換するエントリが +rcsection{usermap} に記述されていないことを意味します。 + +\subsection{\hgext{notify}---send email notifications} + +Mercurial の組み込みウェブサーバにより、 +全てのリポジトリに対してチェンジセット情報の RSS 配信機能が提供されますが、 +電子メールによる変更通知が選択される場合が多いです。 +\hgext{notify} フックは、 +購読者が興味を持つ新たなチェンジセットごとに、 +電子メールアドレス(群)に宛てて通知を行います。 + +\hgext{notify} はテンプレート駆動型のフックですので、 +\hgext{bugzilla} フックと同様に、 +送信される通知の内容をカスタマイズすることができます。 + +既定状態では +\hgext{notify} フックはチェンジセットごとの差分情報を取り込みますが、 +差分情報の量を制限したり、 +この機能を完全に停止することもできます。 +購読者による変更の即時レビューを想定する場合、 +指定された URL をクリックするよりも、 +差分情報を取り込むほうが有用です。 + +\subsubsection{Configuring the \hgext{notify} hook} + +\hgext{notify} フックは、 +新たなチェンジセットごとに1通の電子メールを送信することもできれば、 +(単独の \hgcmd{pull} ないし \hgcmd{push} によりリポジトリに追加される +)新たなチェンジセット群ごとに送信することもできます。 + +\begin{codesample2} + [hooks] + # チェンジセット群ごとに1通のメールを送信 + changegroup.notify = python:hgext.notify.hook + # チェンジセットごとに1通のメールを送信 + incoming.notify = python:hgext.notify.hook +\end{codesample2} + +このフックの設定情報は、 + \hgrc\ ファイルの +\rcsection{notify} セクションに記述されます。 + +\begin{description} +\item[\rcitem{notify}{test}] 既定状態では、 + このフックは全くメールを送信しません。 + その替わり、送信する\emph{であろう}メッセージを表示します。 + この項目を \texttt{false} にすることで電子メールが送信されるようになります。 + 基底状態で電子メールの送信が停止されているのは、 + この拡張(/フック)をきちんと設定するのには幾分かの試行錯誤が必要なので、 + 設定試行中に``壊れた''通知を購読者に送信してしまうためです。 + +\item[\rcitem{notify}{config}] 購読情報を保持している設定ファイルへのパス。 + この情報は \hgrc\ とは分離されているので、 + このファイルそのものを対象リポジトリで管理することも可能です。 + こうすることで、 + 対象リポジトリを複製し、購読設定を更新した上で、 + 変更をサーバに \hgcmd{push} で戻すことができます。 + +\item[\rcitem{notify}{strip}] リポジトリに対する購読者の有無を判定する際に、 + リポジトリのパス冒頭から取り除くパス区切りの数\footnote{訳注: + ここでは strip 対象を + ``leading path separator characters'' と表現しているが、 + \rcsection{bugzilla} の説明では + ``leading path elements'' と表現している。 + 統一的な表現が必要と思われる。}。 + 例えば、 + サーバ上のリポジトリが \dirname{/home/hg/repos} 配下にあり、 + \hgext{notify} が + \dirname{/home/hg/repos/shared/test} というリポジトリを認識している場合、 + \rcitem{notify}{strip} を \texttt{4} に設定することで + \hgext{notify} による購読者とのパターンマッチングは、 + パスを \dirname{shared/test} と認識した上で行われます。 + +\item[\rcitem{notify}{template}] + メッセージ送信の際に使用されるテンプレートテキスト。 + このテンプレートは、メッセージのヘッダとボディの両方の内容を指定します。 + +\item[\rcitem{notify}{maxdiff}] + メッセージ末尾に付与される差分データの最大行数。 + この行数よりも大きい場合、差分データは切り詰められます。 + この値の既定値は 300 に設定されています。 + この値を \texttt{0} にした場合、 + 通知の電子メールに差分データは付与されません。 + +\item[\rcitem{notify}{sources}] 配慮すべきチェンジセットの由来元の一覧。 + この設定により例えば、 + 遠隔ユーザがサーバを経由して当該リポジトリへ + \hgcmd{push} したチェンジセットに対してのみ + \hgext{notify} が電子メールで通知する、 + といった設定をすることができます。 + ここで記述可能な由来元の一覧は、\ref{sec:hook:sources}~節を参照してください。 + +\end{description} + +\rcsection{web} セクションで +\rcitem{web}{baseurl} 項目を設定している場合、 +テンプレート中で \texttt{webroot} として使用することができます。 + +\hgext{notify} 設定情報の一式を以下に示します。 + +\begin{codesample2} + [notify] + # 実際に電子メールを送るか否か + test = false + # 通知を行うリポジトリ自身の中に置かれている購読者情報 + config = /home/hg/repos/notify/notify.conf + # リポジトリが /home/hg/repos 配下にあるので "/" 文字を4つ除去 + strip = 4 + template = X-Hg-Repo: \{webroot\}\\n\\\\ + Subject: \{webroot\}: \{desc|firstline|strip\}\\n\\\\ + From: \{author\}\\n\\\\ + \\n\\\\ + changeset \{node|short\} in \{root\}\\n\\\\ + details: \{baseurl\}\{webroot\}?cmd=changeset;node=\{node|short\}\\n\\\\ + description:\\n\\\\ + \\t\{desc|tabindent|strip\} + + [web] + baseurl = http://hg.example.com/ +\end{codesample2} + +この設定により、 +以下のようなメッセージが生成されます。 + +\begin{codesample2} + X-Hg-Repo: tests/slave + Subject: tests/slave: Handle error case when slave has no buffers + Date: Wed, 2 Aug 2006 15:25:46 -0700 (PDT) + + changeset 3cba9bfe74b5 in /home/hg/repos/tests/slave + details: http://hg.example.com/tests/slave?cmd=changeset;node=3cba9bfe74b5 + description: + Handle error case when slave has no buffers + diffs (54 lines): + + diff -r 9d95df7cf2ad -r 3cba9bfe74b5 include/tests.h + --- a/include/tests.h Wed Aug 02 15:19:52 2006 -0700 + +++ b/include/tests.h Wed Aug 02 15:25:26 2006 -0700 + @@ -212,6 +212,15 @@ static __inline__ void test_headers(void *h) + [...snip...] +\end{codesample2} + +\subsubsection{Testing and troubleshooting} + +既定値のままでは \hgext{notify} 拡張は +\emph{一切のメールを送信しません}ので、 +\rcitem{notify}{test} 項目を明示的に +\texttt{false} で設定することを忘れないでください。 +この設定を行うまでは、 +\hgext{notify} 拡張は送信する\emph{であろう}メッセージを表示します。 + +\section{Information for writers of hooks} +\label{sec:hook:ref} + +\subsection{In-process hook execution} + +プロセス内フックは、以下の引数形式で起動されます。 + +\begin{codesample2} + def myhook(ui, repo, **kwargs): + pass +\end{codesample2} + +\texttt{ui} 引数は +\pymodclass{mercurial.ui}{ui} オブジェクト、 +\texttt{repo} 引数は +\pymodclass{mercurial.localrepo}{localrepository} オブジェクトです。 +\texttt{**kwargs} パラメータの持つ名前と値は、 +起動されるフックの種類に依存し、 +以下の共通の特徴を持っています。 + +\begin{itemize} +\item \texttt{node} ないし \texttt{parent\emph{N}} という名前の引数は、 + 16進数のチェンジセットIDを保持しています。 + 空の文字列は、 + 0 続きの文字列の代わりに ``null チェンジセットID'' を意味します。 + +\item \texttt{url} という名前の引数は、 + それが特定可能であれば、遠隔リポジトリの URL を表します。 + +\item 真偽値引数は、Python の \texttt{bool} オブジェクトで表されます。 + +\end{itemize} + +プロセス内フックは、 +(外部フックがリポジトリ直下で実行されるのと違い) +プロセスの作業ディレクトリを変更せずに起動されます。 +プロセスの作業ディレクトリを移動させると、 +Mercurial API の呼び出しが失敗する要因と成りえますので、 +プロセス内フックは作業ディレクトリを変更してはいけません。 + +(プロセス内)フックが真偽値 ``false'' を返却した場合、 +フック呼び出しは成功したものとみなされます。 +真偽値 ``true'' が返却されるか、 +例外が浮揚された場合、 +フック呼び出しは失敗したものとみなされます。 +起動の慣習を理解するには、 +``失敗したか否かを通知する''と覚えるのが良いでしょう。 + +チェンジセットIDは、 +Mercurial API が常用しているバイナリハッシュ形式ではなく、 +Python フックに16進文字列の形式で渡される点に注意してください。 +16進ハッシュ値をバイナリハッシュ値形式に変換するには、 +\pymodfunc{mercurial.node}{bin} 関数を使用してください。 + +\subsection{External hook execution} + +プロセス外フック(の起動文字列)は、 +Mercurial を実行しているシェルに渡されます。 +そのため、 +変数置換やコマンド出力のリダイレクトといった、 +シェルの機能が利用可能です。 +プロセス外フックは、 +(プロセス内フックが Mercurial が起動されたディレクトリで実行されるのと違い) +リポジトリルート直下で実行されます。 + +フック引数は、環境変数を経由して渡されます。 +個々の環境変数の名前は、 +大文字で且つ ``\texttt{HG\_}'' 接頭辞が付与された形式に変換されます。 +例えば、 +引数名が ``\texttt{node}'' の場合、 +当該引数を表す環境変数の名前は ``\texttt{HG\_NODE}'' となります。 + +真偽値引数は、 +``true'' が文字列 ``\texttt{1}'' で、 +``false'' が文字列 ``\texttt{0}'' で表されます。 +環境変数 +\envar{HG\_NODE}、\envar{HG\_PARENT1} ないし \envar{HG\_PARENT2} は、 +チェンジセットIDを16進文字列で保持します。 +``空のチェンジセットID''は、 +``0'' の連続ではなく空の文字列として表現されます。 +環境変数 \envar{HG\_URL} は、 +それが特定可能な場合に限り、遠隔リポジトリの URL を保持します。 + +プロセス外フックが終了コード0で終了した場合、 +フックの実行は成功したものとみなされます。 +終了コードが0以外の場合、 +フックの実行は失敗したものとみなされます。 + +\subsection{Finding out where changesets come from} + +ローカルリポジトリと他のリポジトリの間のチェンジセットの転送に関わるフックは、 +``向こう側''の情報を知ることができる場合があります。 +Mercurial は、 +チェンジセットが\emph{どのようにして}転送されたのかと、 +多くの場合、 +\emph{どのリポジトリ}との間でチェンジセットが転送されるのかも知っています。 + +\subsubsection{Sources of changesets} +\label{sec:hook:sources} + +Mercurial はリポジトリ間でチェンジセットを転送する意図を、 +フックに対して事前(ないし事後に)通知します。 +この情報は、 +Python によるプロセス内フックの場合は \texttt{source} という名前の引数で、 +外部フックの場合は \envar{HG\_SOURCE} という名前の環境変数で、 +Mercurial からフックに渡されます。 + +\begin{description} +\item[\texttt{serve}] 遠隔リポジトリとの間を、 + http ないし ssh 経由でチェンジセットが転送されます。 + +\item[\texttt{pull}] あるリポジトリから他のリポジトリへ、 + \hgcmd{pull} によりチェンジセットが転送されます。 + +\item[\texttt{push}] あるリポジトリから他のリポジトリへ、 + \hgcmd{push} によりチェンジセットが転送されます。 + +\item[\texttt{bundle}] あるリポジトリから他のリポジトリへ、 + \hgcmd{bundle} によりチェンジセットが転送されます。 + +\end{description} + +\subsubsection{Where changes are going---remote repository URLs} +\label{sec:hook:url} + +Mercurial は、 +リポジトリ間でのチェンジセット転送処理における``向こう側''の位置を、 +可能であればフックに知らせます。 +この情報は、 +Python によるプロセス内フックの場合は \texttt{url} という名前の引数で、 +外部フックの場合は \envar{HG\_URL} という名前の環境変数で、 +Mercurial からフックに渡されます。 + +この情報は常にわかるというわけではありません。 +http ないし ssh +経由でサービスを提供しているリポジトリにおいてフックが起動された場合、 +Mercurial は遠隔リポジトリを特定することはできませんが、 +クライアントがどのアドレスから接続しているのかは特定することができます。 +このような場合、URL は以下のいずれかの形式になります。 + +\begin{itemize} +\item \texttt{remote:ssh:\emph{ip-address}}---与えられた IP アドレスからの + ssh 遠隔接続。 + +\item \texttt{remote:http:\emph{ip-address}}---与えられた IP アドレスからの + http 遠隔接続。 + クライアントが SSL を使用した場合、 + \texttt{remote:https:\emph{ip-address}} 形式になります。 + +\item Empty---遠隔接続に関する情報を取得できなかった場合。 + +\end{itemize} + +\section{Hook reference} + +\subsection{\hook{changegroup}---after remote changesets added} +\label{sec:hook:changegroup} + +このフックは、 +例えば \hgcmd{pull} ないし \hgcmd{unbundle} によって、 +あらかじめ存在しているチェンジセットの一群が、 +リポジトリに追加された後に実行されます。 +これらの操作は任意個のチェンジセットを追加できますが、 +このフックは各操作毎に1回づつ実行されます。 +このことは、 +チェンジセットがまとまって追加されるか否かに関わらず、 +\hook{incoming} フックの実行がチェンジセット毎に実行されるのと対照的です。 + +追加されたチェンジセットに対する自動化されたビルド・テストの開始契機としたり、 +バグデータベースの更新、 +リポジトリが新たなチェンジセットを取り込んだことの購読者への通知、 +といったものが、 +このフックに想定される用途の一部です。 + +このフックに渡されるパラメータは: + +\begin{description} +\item[\texttt{node}] チェンジセットID。 + 追加される一群の中の最初のチェンジセットのID。 + このチェンジセットから + \index{tags!\texttt{tip}}\texttt{tip} + まで(\texttt{tip} 自身も含む)の全てのチェンジセットが、 + 単独の \hgcmd{pull}、\hgcmd{push} ないし \hgcmd{unbundle} + 操作により追加されたことになります。 + +\item[\texttt{source}] 文字列。 + チェンジセットの由来元を表します。 + 詳細は\ref{sec:hook:sources}~節を参照してください。 + +\item[\texttt{url}] URL。 + 特定できる場合に限り、遠隔リポジトリの場所を表します。 + 詳細は\ref{sec:hook:url}~節を参照してください。 + +\end{description} + +要別途参照: +\hook{incoming} (\ref{sec:hook:incoming}~節)、 +\hook{prechangegroup} (\ref{sec:hook:prechangegroup}~節)、 +\hook{pretxnchangegroup} (\ref{sec:hook:pretxnchangegroup}~節) + +\subsection{\hook{commit}---after a new changeset is created} +\label{sec:hook:commit} + +このフックは、新しいチェンジセットが作成された後で実行されます。 + +このフックに渡されるパラメータは: + +\begin{description} +\item[\texttt{node}] チェンジセットID。 + 新しくコミットされたチェンジセットのID。 + +\item[\texttt{parent1}] チェンジセットID。 + 新しくコミットされたチェンジセットにとって、 + 第1親となるチェンジセットのID。 + +\item[\texttt{parent2}] チェンジセットID。 + 新しくコミットされたチェンジセットにとって、 + 第2親となるチェンジセットのID。 + +\end{description} + +要別途参照: \hook{precommit} (\ref{sec:hook:precommit}~節)、 +\hook{pretxncommit} (\ref{sec:hook:pretxncommit}~節) + +\subsection{\hook{incoming}---after one remote changeset is added} +\label{sec:hook:incoming} + +このフックは、 +例えば \hgcmd{push} によって、 +あらかじめ存在しているチェンジセットが、 +リポジトリに追加された後に実行されます。 +複数のチェンジセットが単一の操作で追加された場合でも、 +このフックは追加された個々のチェンジセット毎に実行されます。 + +このフックを \hook{changegroup} フック(\ref{sec:hook:changegroup}~節参照) +と同様の目的に使用することができます。 +一群のチェンジセット毎のフック起動の方が便利な場合もありますが、 +時にはチェンジセットごとのフック起動も便利です。 + +このフックに渡されるパラメータは: + +\begin{description} +\item[\texttt{node}] チェンジセットID。 + 新しく追加されたチェンジセットのID。 + +\item[\texttt{source}] 文字列。 + チェンジセットの由来元を表します。 + 詳細は\ref{sec:hook:sources}~節を参照してください。 + +\item[\texttt{url}] URL。 + 特定できる場合に限り、遠隔リポジトリの場所を表します。 + 詳細は\ref{sec:hook:url}~節を参照してください。 + +\end{description} + +要別途参照: +\hook{changegroup} (\ref{sec:hook:changegroup}~節)、 +\hook{prechangegroup} (\ref{sec:hook:prechangegroup}~節)、 +\hook{pretxnchangegroup} (\ref{sec:hook:pretxnchangegroup}~節) + +\subsection{\hook{outgoing}---after changesets are propagated} +\label{sec:hook:outgoing} + +このフックは、 +例えば \hgcmd{push} ないし \hgcmd{bundle} によって、 +他のリポジトリへとチェンジセットの一群が伝播した後に実行されます。 + +チェンジセットが外部に伝播したことの管理者への通知などは、 +このフックに想定される用途の1つです。 + +このフックに渡されるパラメータは: + +\begin{description} +\item[\texttt{node}] チェンジセットID。 + 他のリポジトリへと伝播する一群の中の最初のチェンジセットのID。 + +\item[\texttt{source}] 文字列。 + 伝播操作の発行由来を表します(\ref{sec:hook:sources}~節を参照してください)。 + 遠隔クライアントからの \hgcmd{pull} 要求の場合、 + \texttt{source} は \texttt{serve} となります。 + チェンジセット群を取得しようとするクライアントがローカルホスト上に居る場合、 + クライアントの操作種別に応じて、 + \texttt{source} の値は + \texttt{bundle}、\texttt{pull} ないし \texttt{push} のいずれかになります。 + +\item[\texttt{url}] URL。 + 特定できる場合に限り、遠隔リポジトリの場所を表します。 + 詳細は\ref{sec:hook:url}~節を参照してください。 + +\end{description} + +要別途参照: +\hook{preoutgoing} (\ref{sec:hook:preoutgoing}~節) + +\subsection{\hook{prechangegroup}---before starting to add remote changesets} +\label{sec:hook:prechangegroup} + +この制御用フックは、 +他のリポジトリからのチェンジセット群の追加が +Mercurial により開始される直前に実行されます。 + +このフックはチェンジセット群の転送開始が許可される前に実行されるため、 +フック自体は追加されるチェンジセットに関する情報を得ることができません。 +このフックの実行が失敗した場合、チェンジセット群は転送されません。 + +このフックの用途の一つに、 +リポジトリに対する外部からのチェンジセット追加の禁止があります。 +例えば、 +ローカルホスト上の管理者がリポジトリを変更できる一方で、 +利用者がサーバ経由で変更を \hgcmd{push} できないように、 +一時的ないし永久に``凍結''することもできます。 + +このフックに渡されるパラメータは: + +\begin{description} + +\item[\texttt{source}] 文字列。 + チェンジセットの由来元を表します。 + 詳細は\ref{sec:hook:sources}~節を参照してください。 + +\item[\texttt{url}] URL。 + 特定できる場合に限り、遠隔リポジトリの場所を表します。 + 詳細は\ref{sec:hook:url}~節を参照してください。 + +\end{description} + +要別途参照: +\hook{changegroup} (\ref{sec:hook:changegroup}~節)、 +\hook{incoming} (\ref{sec:hook:incoming}~節)、 +\hook{pretxnchangegroup} (\ref{sec:hook:pretxnchangegroup}~節) + +\subsection{\hook{precommit}---before starting to commit a changeset} +\label{sec:hook:precommit} + +このフックは、 +Mercurial が新たなチェンジセットをコミットする前に実行されます。 +コミットされるファイル、コミットメッセージないし日付といった、 +コミットに関するメタデータを +Mercurial が揃える前に実行されます。 + +このフックの用途の一つに、 +チェンジセットの受け入れを許す一方での、 +新たなチェンジセットのコミットの禁止があります。 +他の用途ととしては、 +ビルドやテストを実施し、 +それらが成功した場合にのみコミットを許可する、 +というものもあります。 + +このフックに渡されるパラメータは: + +\begin{description} +\item[\texttt{parent1}] チェンジセットID。 + 作業領域ディレクトリにとって、 + 第1親となるチェンジセットのID。 + +\item[\texttt{parent2}] チェンジセットID。 + 作業領域ディレクトリにとって、 + 第2親となるチェンジセットのID。 + +\end{description} + +コミットが進行した場合、 +作業領域ディレクトリの(両)親が、 +新たなチェンジセットの親となります。 + +要別途参照: +\hook{commit} (\ref{sec:hook:commit}~節)、 +\hook{pretxncommit} (\ref{sec:hook:pretxncommit}~節) + +\subsection{\hook{preoutgoing}---before starting to propagate changesets} +\label{sec:hook:preoutgoing} + +このフックは、 +Mercurial が外部に転送されるチェンジセットを特定する直前に実行されます。 + +チェンジセットが他のリポジトリへ転送されるのを防ぐことは、 +このフックに想定される用途の1うです。 + +このフックに渡されるパラメータは: + +\begin{description} +\item[\texttt{source}] 文字列。 + 当該リポジトリに対するチェンジセットの取得要求の発行由来を表します + (\ref{sec:hook:sources}~節を参照してください)。 + このパラメータが取り得る値に関しては、 + \hook{outgoing} の \texttt{source} パラメータに関する + \ref{sec:hook:outgoing}~節の記述を参照してください。 + +\item[\texttt{url}] URL。 + 特定できる場合に限り、遠隔リポジトリの場所を表します。 + 詳細は\ref{sec:hook:url}~節を参照してください。 + +\end{description} + +要別途参照: +\hook{outgoing} (\ref{sec:hook:outgoing}~節) + +\subsection{\hook{pretag}---before tagging a changeset} +\label{sec:hook:pretag} + +この制御フックは、 +タグが生成される前に実行されます。 +フックの実行が成功した場合、タグの生成は継続され、 +フックの実行が失敗した場合、タグは生成されません。 + +このフックに渡されるパラメータは: + +\begin{description} +\item[\texttt{local}] 真偽値。 + タグがリポジトリに対してローカルなもの + (\sfilename{.hg/localtags} に情報が格納される)なのか、 + Mercurial に管理されるもの(\sfilename{.hgtags} に情報が格納) + なのかを表します。 + +\item[\texttt{node}] チェンジセットID。 + タグ付けされるチェンジセットのID。 + +\item[\texttt{tag}] 文字列。 + 作成されるタグの名前。 + +\end{description} + +生成されるタグが構成管理対象となる場合、 +\hook{precommit} (\ref{sec:hook:commit}~節)および +\hook{pretxncommit} (\ref{sec:hook:pretxncommit}~節) +フックも実行されます。 + +要別途参照: +\hook{tag} (\ref{sec:hook:tag}~節) + +\subsection{\hook{pretxnchangegroup}---before completing addition of + remote changesets} +\label{sec:hook:pretxnchangegroup} + +この制御フックは、 +トランザクション--- +このトランザクションは、 +他のリポジトリからの一群のチェンジセットの追加を管理します +---が完了する前に実行されます。 +フックの実行が成功した場合、トランザクションは完了し、 +全てのチェンジセットがリポジトリにおいて永続化されます。 +フックの実行が失敗した場合、 +トランザクションは巻き戻され、 +チェンジセットに関するデータは破棄されます。 + +このフックは、 +「ほぼ追加された」チェンジセットに関するメタデータにアクセスできますが、 +永続化されるような操作\footnote{訳注: +例えば、外部の DBMS へのデータ格納や、 +公開用ファイルへの書き出し等。} +をこれらのデータに基づいて行うべきではありません +作業ディレクトリも変更すべきではありません。 + +このフックの実行中に、 +他の Mercurial プロセスが同じリポジトリにアクセスしてきた場合、 +このプロセスからは、 +「ほぼ追加された」チェンジセットが永続化されたもののように見えます。 +この状況を回避する手順を踏まないと、 +競合状態になりかねません。 + +このフックは、チェンジセット群に対する診断に利用可能です。 +フックの実行が失敗した場合、 +トランザクションが巻き戻され、 +全てのチェンジセットが``拒否''されます。 + +このフックに渡されるパラメータは: + +\begin{description} +\item[\texttt{node}] チェンジセットID。 + 追加される一群の中の最初のチェンジセットのID。 + このチェンジセットから + \index{tags!\texttt{tip}}\texttt{tip} + まで(\texttt{tip} 自身も含む)の全てのチェンジセットが、 + 単独の \hgcmd{pull}、\hgcmd{push} ないし \hgcmd{unbundle} + 操作により追加されたことになります。 + +\item[\texttt{source}] 文字列。 + チェンジセットの由来元を表します。 + 詳細は\ref{sec:hook:sources}~節を参照してください。 + +\item[\texttt{url}] URL。 + 特定できる場合に限り、遠隔リポジトリの場所を表します。 + 詳細は\ref{sec:hook:url}~節を参照してください。 + +\end{description} + +要別途参照: +\hook{changegroup} (\ref{sec:hook:changegroup})、 +\hook{incoming} (\ref{sec:hook:incoming})、 +\hook{prechangegroup} (\ref{sec:hook:prechangegroup}) + +\subsection{\hook{pretxncommit}---before completing commit of new changeset} +\label{sec:hook:pretxncommit} + +この制御フックは、 +トランザクション--- +このトランザクションは、 +新たなチェンジセットのコミットを管理します +---が完了する前に実行されます。 +フックの実行が成功した場合、トランザクションは完了し、 +チェンジセットがリポジトリにおいて永続化されます。 +フックの実行が失敗した場合、 +トランザクションは巻き戻され、 +コミットに関するデータは破棄されます。 + +このフックは、 +「ほぼ新規作成された」チェンジセットに関するメタデータにアクセスできますが、 +永続化されるような操作をこれらのデータに基づいて行うべきではありません +作業ディレクトリも変更すべきではありません。 + +このフックの実行中に、 +他の Mercurial プロセスが同じリポジトリにアクセスしてきた場合、 +このプロセスからは、 +「ほぼ新規作成された」チェンジセットが永続化されたもののように見えます。 +この状況を回避する手順を踏まないと、 +競合状態になりかねません。 + +このフックに渡されるパラメータは: + +\begin{description} + +\item[\texttt{node}] チェンジセットID。 + 新しくコミットされたチェンジセットのID。 + +\item[\texttt{parent1}] チェンジセットID。 + 新しくコミットされたチェンジセットにとって、 + 第1親となるチェンジセットのID。 + +\item[\texttt{parent2}] チェンジセットID。 + 新しくコミットされたチェンジセットにとって、 + 第2親となるチェンジセットのID。 + +\end{description} + +要別途参照: +\hook{precommit} (\ref{sec:hook:precommit}~節) + +\subsection{\hook{preupdate}---before updating or merging working directory} +\label{sec:hook:preupdate} + +この制御フックは、 +作業領域ディレクトリにおける \hgcmd{update} ないし \hgcmd{merge} +の実施前に実行されます。 +このフックは、 +Mercurial の \hgcmd{update} +実施前確認が \hgcmd{update} ないし \hgcmd{merge} +を実行可能と判断した場合にしか実行されません。 +フックの実行が成功した場合、 +\hgcmd{update} ないし \hgcmd{merge} の実行は継続されますが、 +フックの実行が失敗した場合、 +\hgcmd{update} ないし \hgcmd{merge} は実行されません。 + +このフックに渡されるパラメータは: + +\begin{description} +\item[\texttt{parent1}] チェンジセットID。 + 作業領域ディレクトリが \hgcmd{update} される親チェンジセットのID。 + 作業領域ディレクトリが \hgcmd{merge} される場合は、 + 現在の親チェンジセットと同じになります。 + +\item[\texttt{parent2}] チェンジセットID。 + 作業領域ディレクトリが \hgcmd{merge} される場合にのみ設定されます。 + 作業領域ディレクトリの \hgcmd{merge} 対象となるチェンジセットのID。 + +\end{description} + +要別途参照: +\hook{update} (\ref{sec:hook:update}~節) + +\subsection{\hook{tag}---after tagging a changeset} +\label{sec:hook:tag} + +このフックは、タグが生成された後で実行されます。 + +このフックに渡されるパラメータは: + +\begin{description} +\item[\texttt{local}] 真偽値。 + タグがリポジトリに対してローカルなもの + (\sfilename{.hg/localtags} に情報が格納される)なのか、 + Mercurial に管理されるもの(\sfilename{.hgtags} に情報が格納) + なのかを表します。 + +\item[\texttt{node}] チェンジセットID。 + タグ付けされるチェンジセットのID。 + +\item[\texttt{tag}] 文字列。 + 作成されるタグの名前。 + +\end{description} + +生成されるタグが構成管理対象となる場合、 +このフックの実行に先立って +\hook{commit} フック(\ref{sec:hook:commit}~節)が実行されます。 + +要別途参照: +\hook{pretag} (\ref{sec:hook:pretag}~節) + +\subsection{\hook{update}---after updating or merging working directory} +\label{sec:hook:update} + +このフックは、 +作業領域ディレクトリにおける \hgcmd{update} ないし \hgcmd{merge} +が完了した際に実行されます。 +\hgcmd{merge} は失敗し得る +(外部コマンドの \command{hgmerge} +が各ファイルにおける衝突の解消に失敗した場合)ので、 +このフックには \hgcmd{update} ないし \hgcmd{merge} +の成否が伝えられます。 + +\begin{description} +\item[\texttt{error}] 真偽値。 + \hgcmd{update} ないし \hgcmd{merge} 実行が成功したか否かを表します。 + +\item[\texttt{parent1}] チェンジセットID。 + 新しくコミットされたチェンジセットにとって、 + 第1親となるチェンジセットのID。 + +\item[\texttt{parent2}] チェンジセットID。 + 新しくコミットされたチェンジセットにとって、 + 第2親となるチェンジセットのID。 + +\end{description} + +要別途参照: +\hook{preupdate} (\ref{sec:hook:preupdate}節) + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/htlatex.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/htlatex.sh Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +#!/bin/sh + +# USAGE: htlatex.sh +#LATEX=latex +#LATEX=platex +LATEX=jlatex + +${LATEX} $3 '\makeatletter\def\HCode{\futurelet\HCode\HChar}\def\HChar{\ifx"\HCode\def\HCode"##1"{\Link##1}\expandafter\HCode\else\expandafter\Link\fi}\def\Link#1.a.b.c.{\g@addto@macro\@documentclasshook{\RequirePackage[#1,html]{tex4ht}}\let\HCode\documentstyle\def\documentstyle{\let\documentstyle\HCode\expandafter\def\csname tex4ht\endcsname{#1,html}\def\HCode####1{\documentstyle[tex4ht,}\@ifnextchar[{\HCode}{\documentstyle[tex4ht]}}}\makeatother\HCode '$2'.a.b.c.\input ' $1 diff -r a24b370a16ee -r d6ca1334a19d ja/intro.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/intro.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,648 @@ +\chapter{Introduction} +\label{chap:intro} + +\section{About revision control} + +構成管理とは、 +複数の版を持つ情報群を管理する手順のことです。 +最も単純な手法では、 +多くの人々がこれを手動で行います。 +ファイル更新時には、 +直前の版に利用した値よりも大きな値を割り当ててから、 +その値を含めた新しい名前でファイルを保存する、 +といった具合です。 + +しかしながら、たった1つのファイルであっても、 +複数の版を手動で管理する作業は間違いがちですので、 +この手順を自動化するソフトウェアツールには長い歴史があります。 +初期の構成管理を自動化するツールは、 +単一ユーザによる単一ファイルの版管理の補助を意図していました。 +ここ数十年の間に、 +構成管理ツールの適用範囲は大変拡大されてきました。 +現在では、 +複数のファイルに対する複数のユーザの共同作業を管理するまでになっています。 +今時の最善の構成管理ツールは、 +共同作業する数千人のユーザによって、 +数十万のフィルからなるプロジェクトのデータが複製されても、 +びくともしません。 + +\subsection{Why use revision control?} + +プロジェクトにおいて、読者であるあなたや、 +あなたのチームが自動化された構成管理ツールを使用したくなるのは、 +以下のような理由があるからではないでしょうか。 + +\begin{itemize} +\item プロジェクトの歴史と発展を記録してくれるので、 + 自分でそれを記録する必要が無いため。 + 構成管理ツールを使用することで、 + 変更毎に、\emph{何時}、\emph{誰が}、\emph{何故}、 + \emph{何を}変更したかの記録を見ることができます。 + +\item 他のメンバーとの共同作業が容易になるため。 + 例えば、潜在的に両立しない変更がほぼ同時に行われた際に、 + 構成管理ツールはそのことを検出した上で、 + このような衝突の解消を手助けしてくれます。 + +\item 間違いからの復旧を手助けしてくれるため。 + 変更実施した後で間違いに気付いた場合、 + 複数のファイルに渡る間違いであっても、 + 以前の状態に復旧することができます。 + 実のところ、 + \emph{本当に}良い構成管理ツールであれば、 + 問題が混入した時点の厳密な割り出しを効果的に探し出すことができるでしょう + (詳細は、\ref{sec:undo:bisect}~節を参照してください)\footnote{訳注: + つまり、それができる Mercurial は\emph{本当に}良い構成管理ツールだ、 + ということですね(笑)}。 + +\item プロジェクトの複数の版の間での同時作業や、 + 版の間での行き来を補助してくれるため。 + +\end{itemize} + +これらの理由の殆どが--- +少なくとも理屈の上では +---一人きりのプロジェクトでも、 +百人と共同作業するプロジェクトでも有効です。 + +これら2つの規模の異なるケース +(``lone hacker'' と ``huge team'')のそれぞれにおいて、 +構成管理ツールの実用性に関する重要な問題は、 +ツールから得られる\emph{利益}とその\emph{コスト}をどのように比較するか、 +という点にあります。 +理解や使用が難しい構成管理ツールは、 +コストが高く付くでしょう。 + +構成管理のツールとプロセス抜きでは、 +500 人からなるプロジェクトはおそらく自分自身の重みで、 +すぐにでも崩れてしまうでしょう。 +この場合、 +構成管理ツール\emph{抜き}には失敗が保証されたようなものですから、 +それを思えば、 +構成管理ツールを利用するコストについては考えるまでも無いでしょう。 + +一方で、一人での``quick hack''の場合、 +構成管理ツールを使うコストはプロジェクト全体のコストと同一の筈ですから、 +構成管理を使う余地は殆ど無いように見えるかもしれません。 +しかし、それは本当でしょか? + +Mercurial はこれら\emph{両方}の規模の開発を上手にサポートします。 +わずか数分で基本を習得でき、 +その低オーバヘッドのお陰で +最も小さなプロジェクトにも簡単に構成管理を適用できます。 + +構成管理ツールの単純さは、 +難解な概念や、 +\emph{本当に}やろうとしていることと心理的に競合するコマンド列といったものを、 +大量に身に付ける必要が無いことを意味します。 +同時に、 +Mercurial の高性能さと P2P 的特性は、 +大きなプロジェクトへの利用へと苦も無く拡大できます。 + +運営の下手なプロジェクトを救える構成管理ツールはありませんが、 +良いツールを選択することで、 +プロジェクトでの作業における滑らかさが全く違ってきます。 + +\subsection{The many names of revision control} + +構成管理は多様な領域なので、 +実際には統一された名前や頭字語語がありません。 + +よく目にする一般的な名称および略称を以下に列挙します。 + +\begin{itemize} +\item Revision control (RCS) +\item Software configuration management (SCM), or configuration management +\item Source code management +\item Source code control, or source control +\item Version control (VCS) +\end{itemize} + +これらの用語は実際にはそれぞれ異なる意味を持っている、 +と主張する人もいますが、 +実際にはお互いに非常に重複した意味を持っているので、 +これらに対して個別にあれこれ言うことには賛同もできませんし、 +有用性もありません\footnote{訳注: +昨今のソフトウェア開発における用法を鑑みて、 +原文で ``revision control'' となっている箇所は、 +意図的に``構成管理''(configuration management)と訳しています。}。 + +\section{A short history of revision control} + +最も有名な昔の構成管理ツールは、 +Bell Labs の Marc Rochkind が 1970 年代初頭に実装した +SCCS (Source Code Control System)です。 +SCCS は個別のファイルに対して機能し、 +プロジェクトに従事する全ての作業者は、 +単一システム上の共有作業領域へのアクセス権が必要でした。 +ある時点でのあるファイルの変更は、ただ一人の作業者のみが可能で、 +ファイルのアクセスはロックにより調停されていました。 +ファイルをロックしたまま開放し忘れてしまい、 +管理者の補助無しには他の人がファイルを変更できなくしてしまうことは、 +良くあることでした。 + +SCCS のフリーな代替ツールとして +1980 年代初頭に Walter Tichy が +RCS (Revison Control System)と呼ぶプログラムを開発しました。 +SCCS と同様、 +RCS の利用には、 +単一の共有作業領域での作業と、 +複数の作業者が同時に改変するのを防ぐためのロックが必要でした。 + +1980 年代後期、Dick Grune は RCS を用いて、 +当初 cmt と呼ばれるシェルスクリプト群を実装し、 +後にこれらは CVS (Concurrent Versions System)と改名されました。 +CVS における大きな変革は、 +各開発者ごとの作業領域において、 +開発者が平行且つ幾分独立した作業ができるようになったことです。 +SCCS や RCS では良くあった、 +いつでも他人の足を踏んでしまう状況が、 +開発者ごとの作業領域の導入によって防がれるようになりました。 +各開発者は、 +プロジェクトに関する全てのファイルの複製を持ち、 +各自の複製を独立して変更することができました。 +中央のリポジトリへの変更のコミットに先立って、 +変更内容のマージをする必要がありました。 + +Brian Berliner は +Grune のオリジナルスクリプトを元に C で書き直し、 +以来現代版の CVS へと発展するコードを 1989 にリリースしました。 +CVS はその後、 +「クライアント・サーバ」アーキテクチャの導入により、 +ネットワーク接続越しの操作を可能とする機能を獲得しました。 +CVS のアーキテクチャは中央集約的なもので、 +サーバのみがプロジェクトの履歴のこぴーを持っています。 +クライアント側の作業領域は、 +プロジェクトファイルの最新版を複製したものと、 +サーバの場所等を知るためのわずかなメタデータを持っているだけです。 +CVS は非常に成功していて、 +おそらく世界で最も広く使用されている構成管理システムでしょう。 + +Sun Microsystems は 1990 年代初頭に、 +TeamWare と呼ばれる分散構成管理システムのはしりとなるものを開発しました。 +TeamWare における(個人の)作業領域は、 +プロジェクトの完全な複製を格納しています。 +TeamWare には「中央リポジトリ」という概念がありません +(CVS は履歴格納を RCS に依存していましたが、 +TeamWare は SCCS を利用していました)。 + +1990 年代が進むにつれて、 +問題意識から CVS に関する問題が多く顕在化してきました。 +例えば CVS は、 +複数のファイルに対する同時更新を、 +論理的に不可分な単一の作用としてまとめる替わりに、 +ファイルごとに個別に記録しています。 +また、ファイル階層を上手く管理できないため、 +ファイルやディレクトリを改名することで、 +容易にリポジトリを混乱させることができます。 +なお悪いことに、 +CVS 自身のソースコードは読むにも保守するにも難解なため、 +アーキテクチャ上の問題点を修正する``苦痛度''は法外なものでした。 + +CVS の開発を行っていた +Jim Blandy および Karl Fogel の二人は、 +より良いアーキテクチャを持ち、 +尚且つコードが綺麗なツールで CVS を置き換えるプロジェクトを、 +2001 年に始めました。 +結果として生み出された Subversion は、 +CVS の中央集約型クライアント/サーバモデルからは離れなかったものの、 +複数ファイルの不可分コミットや、 +より良い名前空間の管理、 +および CVS よりも概ね良好なツールと言うに足るその他の多くの機能を持っています。 +初回のリリース以来、その人気は速やかに上昇しています。 + +それと概ね同時期に、 +Graydon Hoare は Monotone +と呼ばれる野心的な分散構成管理システムに取り掛かり始めました。 +Monotone は、 +CVS 設計上の多くの問題に取り組み、P2P アーキテクチャを持つ一方で、 +多くの革新的な点において初期の(そしてその後の) +構成管理ツールから飛び抜けています。 +Monotone は、 +暗号で用いられるハッシュ値を識別子として使用しており、 +異なる由来のコードにとって不可欠な``信頼''の概念を持っています。 + +Mercurial は 2005 年に誕生しました。 +設計上の幾つかの見地において Monotone から影響を受ける一方で、 +Mercurial は利用の簡便性、性能の高さ、 +および大規模プロジェクトへの適用性に主眼を置いています。 + +\section{Trends in revision control} + +過去40年に渡る構成管理ツールの開発と利用における紛れも無い傾向として、 +構成管理ツールの利用者は、 +利用しているツールの機能に精通すると共に、 +ツールの制約によって抑制されるようです。XXXXXX +There has been an unmistakable trend in the development and use of +revision control tools over the past four decades, as people have +become familiar with the capabilities of their tools and constrained +by their limitations. + +最初の世代は、 +単一ファイルを各自のコンピュータで管理することから始まりました。 +この世代のツールは、 +手動による場当たりな構成管理に比べれば大きな前進ではありましたが、 +排他による操作モデルと、 +単一コンピュータ上での利用を前提とした設計のため、 +小さく緊密なチームでの利用に限定されていました。 + +第二世代は、 +ネットワーク主体のアーキテクチャへの移行と、 +プロジェクト全体の一括管理によって、 +これらの制約を緩和しました。 +しかし、プロジェクト規模が大きくなればなるほど、新たな問題が発生しました。 +クライアントはサーバと頻繁に連携する必要があるため、 +サーバは大規模プロジェクトへの適用が問題になりました。 +信頼性の低いネットワーク接続では、 +遠隔ユーザがサーバと全く連携ができないこともありました。 +オープンソースプロジェクトが匿名の読み込み専用アクセスを開放するにつれ、 +リポジトリへのコミット権限を持たない人々は、 +構成管理ツールの通常の方法では自分たちの変更が記録できず、 +それ故にプロジェクトに対して働きかけることができないことに気付き始めました。 + +現世代の構成管理ツールは、事実上 P2P です。 +これらは、 +単一の中央サーバに対する依存を持たず、 +そのため構成管理データを必要な場所に分散することが可能です。 +インターネットを介した連携における課題は、 +技術的な制約に関するものから、 +選択(of what ?)と合意(of what)形成の問題へと移行しつつあります XXXX。 +Collaboration over the Internet +has moved from constrained by technology to a matter of choice and +consensus. +最新のツールは、 +オフライン状況でも無制限に独立して操作でき、 +ネットワーク接続は他のリポジトリとの同期にのみ必要とされます。 + +\section{A few of the advantages of distributed revision control} + +前世代への対抗馬として、 +ここ数年の間に分散構成管理ツールが堅牢且つ便利になってきてはいるものの、 +古いツールを利用している人々は、 +必ずしも分散構成管理ツールの長所に気付いているわけではありません。 +中央集約的型(ツール)と比較して、 +分散型(ツール)の優れている点が幾つかあります。 + +開発者個人にとっては、 +中央集約型と比較した場合、 +概ねいつでも分散型の方が高速です。 +これは、 +中央集約型では殆どのメタデータが中央サーバ上にしか存在しないため、 +多くの定型処理の度にネットワーク越しにサーバとの通信が必要、 +という単純な理由のためです。 +分散型の場合は、全てのメタデータを手元に格納しています。 +他の全てが同じだとしても、 +ネットワーク越しの通信は中央集約型にとってのオーバヘッドとなります。 +構成管理ツールとの対話に多くの時間を費やそうと言うのですから、 +テキパキと動く応答性の良いツールの価値を軽視してはいけません。 + +繰り返しになりますが、 +分散型はメタデータを何箇所にも複製できるので、 +サーバ環境の気まぐれ\footnote{訳注: +特定のサーバの動作不良等}は気になりません。 +中央集約型でサーバが火を噴いた場合には、 +バックアップメディアの信頼性と、 +最後のバックアップが最近のものであることを祈るに違いありません。 +分散型の場合、 +各開発者のコンピュータ上に無数のバックアップが存在することになります。 + +分散型は中央集約型の場合よりも、 +ネットワークの信頼性による影響を受けません。 +それどころか、 +非常に限定的な幾つかのコマンドを除けば、 +中央集約型ではネットワーク接続抜きには何もできません。 +分散型の場合、 +作業中にネットワーク接続が切れても、 +その事に気付かないかもしれません。 +他のコンピュータ上のリポジトリとの連携だけはできなくなりますが、 +手元のリポジトリとの連携と比べれば、 +そのような連携が必要な事態はわずかなものです。 +分散しているな共同作業チームの場合には、 +これは重要です。 + +\subsection{Advantages for open source projects} + +ソースをハッキングしてみようと思ったオープンソースのプロジェクトが、 +分散構成管理ツールを使用していた場合、 +自身をプロジェクトの``中核''とみなす人達と直ちに対等になれます。 +彼らがリポジトリを公開していれば、 +内部の人達と同じツール・同じ手順で、 +プロジェクトの履歴のコピーや、変更の実施、作業の記録といったことを、 +すぐにでも行うことができます。 +中央集約型の場合はそれとは対照的に、 +中央のサーバに対する変更コミットの権限を与えられない限り、 +``読み込み専用''モードでしか使うことができません。 +コミット権限が付与されるまでは変更の記録はできず、 +中央のリポジトリとの同期の際には常に手元での変更が破損する危険を抱えています。 + +\subsubsection{The forking non-problem} + +分散構成管理ツールは、 +プロジェクトを``分裂''させ易くしてしまうため、 +オープンソースプロジェクトにとってある種の危険要因となる、 +と言われてきました。 +分裂は、 +これ以上一緒に開発を継続できないと結論付ける原因となるような、 +開発グループ間での意見や特性の相違のがある場合に発生します。 +両陣営は、 +プロジェクトのソースコードの概ね完全なコピーを持って、 +お互いの方向へと分かれてゆきます。 + +時には、分裂した各陣営が、 +お互いの相違に折り合いを付ける決定をすることがあります。 +中央集約型の構成管理システムでは、 +折り合いを付けるための\emph{技術的な}処理が苦しく、 +大部分は手動で実施しなければなりません。 +誰の変更履歴が``生き残る''のかを決定した上で、 +何とかして他のチームの変更をソースツリーに移植しなければなりません。 +この作業は通常、 +他方の履歴情報の一部ないし全部を失うことになります。 + +分散型にとっては、 +分裂こそがプロジェクトを発展させる\emph{唯一の}方法なのです。 +個々の変更は、全て潜在的な分裂点なのです。 +分裂は常に発生している全く基本的な事象なので、 +分散構成管理は実際に分裂を上手く\emph{マージ}できなければならない、 +という点にこの考え方の強みがあります。 + +全ての人の全ての作業が、 +常に分裂とマージの観点から組み立てられた場合、 +オープンソース世界が``分裂''として言及するものは、 +\emph{純粋に}社会的な問題となるでしょう。 +どちらかといえば、 +分散型は分裂の可能性を\emph{低下}させています。 + +\begin{itemize} +\item 中央集約型が招いてしまう``内部''(コミット権限を持つ人々) + と``外部''(持たざる人々)といった社会的区分を無くします。 + +\item 構成管理ソフトウェアの視点では、単なるマージに過ぎませんので、 + 社会的分裂の後の和解を容易にします。 + +\end{itemize} + +プロジェクト全般への緊密な統治の維持が中央集約型ツールによって得られる、 +と信じているために、分散型に抵抗する人もいます。 +しかし、そういった期待の元で +CVS ないし Subversion によるリポジトリを公開しても、 +無数に存在するツールによって、 +プロジェクト全体の履歴を(例え遅いとは言え)取り出し、 +あなたの制御の及ばない場所で再構築することができてしまいます。 +``プロジェクト全般への緊密な統治の維持''が錯覚である一方、 +So while your control in this case is illusory, you are +foregoing the ability to fluidly collaborate with whatever people feel +compelled to mirror and fork your history. +XXXXXX + +\subsection{Advantages for commercial projects} + +多くの商業プロジェクトは、 +世界中に散らばったチームが請け負っています。 +中央のサーバから遠く離れたメンバーは、 +コマンド実行の遅さや、 +おそらく殆ど信頼性の無いサーバとの接続を目にすることでしょう。 +商業的な構成管理システムは、 +遠隔サイト複製\footnote{訳注: +``保守が大変''と言っていることから、 +この場合の複製は``サーバの複製''を指しているのかな? +}の追加機能によるこれらの問題を解決しようとしていますが、 +通常、こういった機能は高価で保守が大変です。 +分散型の場合は、 +そもそもこういった問題で悩む必要がありません。 +更に、例えばサイトごとに一台ずつという塩梅で、 +信頼できるサーバを複数立ち上げることも簡単ですので、 +高価で距離のあるネットワーク経路越しのリポジトリ間で、 +余計な通信をする必要はありません。 + +中央集約型の構成管理システムは、 +相対的にスケーラビリティが低い傾向にあります。 +高価な中央集約システムだからといって、 +平行利用する数ダースのユーザの負荷によってダウンしてしまうことは、 +有り得ないことではありません。 +繰り返しになりますが、 +高負荷におけるダウンに対する典型的な対応は、 +高価で古臭い複製機能の利用です。 +分散型ツールを使用する場合、中央サーバ-- +仮に持っているとしても一台だけでしょうが +--における負荷は非常に低いので、 +もっと大人数のチームの要求を単一の安価なサーバで捌くことができますし、 +負荷分散は単にスクリプト作成の問題となります。 + +顧客の元に出て問題対応するメンバーがいる場合、 +分散構成管理は有益です。 +他のビルドからは隔離された状態で特別なビルドのために複数の修正を試したり、 +障害や退行の要因をソースの修正履歴から効果的に検索したりといったことを、 +客先環境で自社のネットワークに接続すること無しに行うことができます。 + +\section{Why choose Mercurial?} + +Mercurial は、 +とりわけ構成管理システムとして良い選択をしたと言える、 +類を見ない特徴を持っています。 + +\begin{itemize} +\item 習得・利用が容易 +\item 軽量 +\item 規模拡大に耐え得る +\item 改造が容易 +\end{itemize} + +構成管理システムに慣れ親しんでいるのであれば、 +Mercurial を使えるようになるのに5分も掛からない筈です。 +そうでない場合でも、 +更に数分以上は掛からないでしょう。 +Mercurial のコマンドや機能群は、 +全体的に統一性と一貫性が保たれていますので、 +沢山の例外事項ではなく、 +少数の一般的な方法だけを覚えておけば良いのです。 + +小さなプロジェクトの場合、 +すぐにでも Mercurial を使い始めることができるでしょう。 +新たな変更やブランチを生成し、 +変更を(同一ホストないしネットワーク越しで)持ち歩いたり、 +履歴参照や状態確認といった全ての操作が高速です。 +元来非常に高速な操作に加えて、 +目に見えるオーバーヘッドが少ないために、 +Mercurial は俊敏さを保ち、 +利用者の作業を妨げることを避けることができます。 + +Mercurial の有用性は小さなプロジェクトに限定されません。 +数百から数千のメンバを持ち、 +ソースコードが数万ファイル・ +数百メガバイトに及ぶプロジェクトでも採用されています。 + +Mercurial の基本機能に満足できない場合でも、 +容易に拡張することができます。 +Mercurial は処理のスクリプト化に適しており、 +Python を使って綺麗に実装されていることが、 +「イクステンション」という形式での機能追加を容易にしています。 +「障害特定の補助」から「性能向上」といった広い範囲で、 +評判の良い有用な多くのイクステンションが既に提供されています。 + +\section{Mercurial compared with other tools} + +この先を読む前に、 +著者自身の経験/関心/(あえて言いますが)偏見といったものが、 +本節に反映せざるを得ない点をご理解ください。 +著者は、以下にあげる構成管理ツールのそれぞれを、 +最長で数年程度使用した経験があります。 + +\subsection{Subversion} + +Subversion は CVS の置き換えを目指して開発された、 +評判のよい構成管理ツールです。 +Subversion は中央集約型の「クライアント/サーバ」 +アーキテクチャを持っています。 + +Subversion と Mercurial は、 +同じ作用を持つ似たような名前のコマンドを持っているので、 +一方に馴染みのあるユーザは他方の用法を容易に習得できます。 +これらは両方とも全ての著名な OS 上で利用可能です。 + +Subversion は履歴を意識したマージ機能を持っていないので、 +どのリビジョンのブランチ間でマージすべきかを、 +ユーザ自身が厳密に指定することを強制します。 +この指定ができなかったり間違えたりした場合、 +マージにおける不必要な衝突を手動で解決する羽目になります。 + +著者がベンチマーク計測した限りでは、 +Subversion の全ての構成管理操作において、 +Mercurial は性能の面で相当に優位にいます。 +筆者の比較によると、 +Subversion の 1.4.3~版における +\emph{ra\_local} ファイル格納 +(利用可能な最速のアクセス機能)と比較した場合、 +2倍から6倍程度の優位性がありました。 +ネットワーク越しのリポジトリを必要とする、 +より現実的な配置の場合、 +Subversion は相当に不利な状況になるでしょう。 +多くの Subversion コマンドはサーバとの連携が必要な上に、 +Subversion は有用な複製機能を持っていないため、 +少々大きめのプロジェクトの場合、 +サーバの性能がボトルネックとなるでしょう。 + +それに加えて、 +ファイルの更新の検索(\texttt{status}) +や現行版との差分表示(\texttt{diff})といった、 +幾つかの共通操作におけるネットワーク処理を回避するために、 +Subversion は相当な格納オーバヘッドを抱え込んでいます。 +Mercurial のリポジトリがプロジェクトの完全な履歴を保持しているにも関わらず、 +Subversion が抱え込む作業コピーは、 +Mercurial リポジトリと作業領域ディレクトリのサイズと、 +結果としておおよそ同サイズか、あるいはそれ以上になることが多いです。 + +構成管理関連のサードパーティツールに関しては、 +その差は徐々に埋まってはいるもの、 +Mercurial と比較して、 +現時点では Subversion の方がより多くのサポートを受けることができます。 +また、Mercurial と同様に +Subversion は素晴らしいユーザマニュアルがあります。 + +Subversion リポジトリから Mercurial リポジトリへの、 +正確で完全な変更履歴の取り込みを行うツールが幾つもありますので、 +古いツールからの移行は比較的容易です。 + +\subsection{Git} + +git は、 +Linux カーネルソースツリーを管理するために開発された分散構成管理ツールです。 +Mercurial と同様に、 +その初期の設計は Monotone から影響を受けています。 + +git は圧倒的なまでのコマンド群を持っており、 +1.5.0~版においては 139~個の独立したコマンドがあります。 +これらは習得が難しいとの評判です。 +ユーザマニュアルが存在せず、 +個別のコマンドに関する文書があるのみです。 + +性能の面では git は非常に高速です。 +少なくとも Linux においては、 +Mercurial よりも git の方が早いケースが幾つかあります。 +しかしながら本書の執筆時点では、 +Windows 環境における性能(および一般的なサポート)に関しては +Mercurial に及びません。 + +Mercurial のリポジトリは保守の必要がありませんが、 +git リポジトリは手動によるメタデータの``詰め直し''を頻繁に行う必要があります。 +この詰め直しをしない場合、 +利用領域が速やかに増加する一方で、性能が低下してしまいます。 +厳格且つ頻繁に詰め直しをしない git リポジトリを沢山抱えるサーバは、 +バックアップの間、非常に disk-bound になりますし、 +結果として、 +日時バックアップ処理に24時間以上を要するようになってしまった例が、 +いくつもあります。 +詰め替えによって鮮度が保たれている git リポジトリは、 +Mercurial のリポジトリよりもわずかに小さいですが、 +詰め替えされていない場合はかなりの大きさです。 + +git の基本部分は C で実装されています。 +多くの git コマンドはシェルないし Perl のスクリプトにより実装されていますが、 +その品質は非常に幅が広いです。 +致命的とみなすべきエラーが発生している中で闇雲に処理を続けるスクリプトを、 +何度か見かけたことがあります。 + +\subsection{CVS} + +CVS はおそらく世界中で最も広く使用されている構成管理ツールです。 +その歴史の長さと、内部的なまとまりの無さから、 +長い間、本質的には保守されてきませんでした。 + +CVS は中央集約型の「クライアント/サーバ」 +アーキテクチャを持っています。 +CVS は関連するファイルの変更を不可分コミットへとグループ化しないため、 +例えば、 +「ある利用者による成果のコミットが、 +マージの必要性から部分的にしか成功しなかった場合、 +他の利用者からは彼の意図した変更の一部しか見ることができない」といった、 +``ビルドを乱す''行為が容易に行えてしまいます。 +これは、プロジェクト履歴に対する作業の進め方にも影響します。 +とあるタスクの一部として、 +あるメンバが行った変更を全て表示しようとした場合、 +関連する各ファイル(どのファイルがそうであるかを知っていれば、 +の話ですが)に対して行われた変更の、 +個々のコミットログと日付を手動で確認する必要があります。 + +CVS のタグやブランチの考え方は混乱しているため、 +それについて説明する気にもなれません。 +ファイルやディレクトリの改名がサポートされていないため、 +リポジトリが簡単に雑然としてしまいます。 +内部的な整合性をチェックする機能も持たないため、 +リポジトリが破損しているのか否かを判定したり、 +どのように破損しているのかをしることは、一般には不可能です。 +現存・新規のいずれのプロジェクトに対しても、 +CVS はお薦めできません。 + +Mercurial は CVS のリポジトリを取り込むことができます。 +しかし、いくつかの注意が必要で、 +これは CVS のリポジトリを取り込むことのできる、 +他の構成管理ツールに対しても同様です。 +CVS は不可分コミットを持っておらず、 +ファイルシステム階層の履歴管理も行っていないため、 +CVS から履歴を正確且つ厳密に再構築することは不可能です。 +幾分かの推測が必要であり、改名は通常検知できません。 +高度な CVS 管理の多くが手動で行われ、それ故に間違いやすいことから、 +CVS からの取り込みを行うツールにとって、 +破損したリポジトリからの取り込みは複数の問題に行き当たるのが常です +(筆者の個人的経験から思い出せる、面白くも無い問題の例としては、 +完全に偽物のタイムスタンプや、 +10年以上ロックされたままのファイルなどがあります)。 + +\subsection{Commercial tools} + +Perforce は中央集約型の「クライアント/サーバ」 +アーキテクチャを持っていますが、 +クライアント側では全くキャッシュを行っていません。 +近年の構成管理ツールと異なり、 +編集対象となる全てのファイルに関して、 +Perforce はコマンド実行によるサーバへの通知をユーザに対して要求します。 + +Perforce の性能は小規模なチームでは非常に良好ですが、 +ユーザ数が数ダースを超える頃から急速に低下します。 +少々大規模な開発向けの Perforce インストールは、 +ユーザアクセスによる負荷を上手く処理するために、 +「プロキシ」の配置が要求されます。 + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/license.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/license.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,139 @@ +\chapter{Open Publication License} +\label{cha:opl} + +Version 1.0, 8 June 1999 + +\section{Requirements on both unmodified and modified versions} + +The Open Publication works may be reproduced and distributed in whole +or in part, in any medium physical or electronic, provided that the +terms of this license are adhered to, and that this license or an +incorporation of it by reference (with any options elected by the +author(s) and/or publisher) is displayed in the reproduction. + +Proper form for an incorporation by reference is as follows: + +\begin{quote} + Copyright (c) \emph{year} by \emph{author's name or designee}. This + material may be distributed only subject to the terms and conditions + set forth in the Open Publication License, v\emph{x.y} or later (the + latest version is presently available at + \url{http://www.opencontent.org/openpub/}). +\end{quote} + +The reference must be immediately followed with any options elected by +the author(s) and/or publisher of the document (see +section~\ref{sec:opl:options}). + +Commercial redistribution of Open Publication-licensed material is +permitted. + +Any publication in standard (paper) book form shall require the +citation of the original publisher and author. The publisher and +author's names shall appear on all outer surfaces of the book. On all +outer surfaces of the book the original publisher's name shall be as +large as the title of the work and cited as possessive with respect to +the title. + +\section{Copyright} + +The copyright to each Open Publication is owned by its author(s) or +designee. + +\section{Scope of license} + +The following license terms apply to all Open Publication works, +unless otherwise explicitly stated in the document. + +Mere aggregation of Open Publication works or a portion of an Open +Publication work with other works or programs on the same media shall +not cause this license to apply to those other works. The aggregate +work shall contain a notice specifying the inclusion of the Open +Publication material and appropriate copyright notice. + +\textbf{Severability}. If any part of this license is found to be +unenforceable in any jurisdiction, the remaining portions of the +license remain in force. + +\textbf{No warranty}. Open Publication works are licensed and provided +``as is'' without warranty of any kind, express or implied, including, +but not limited to, the implied warranties of merchantability and +fitness for a particular purpose or a warranty of non-infringement. + +\section{Requirements on modified works} + +All modified versions of documents covered by this license, including +translations, anthologies, compilations and partial documents, must +meet the following requirements: + +\begin{enumerate} +\item The modified version must be labeled as such. +\item The person making the modifications must be identified and the + modifications dated. +\item Acknowledgement of the original author and publisher if + applicable must be retained according to normal academic citation + practices. +\item The location of the original unmodified document must be + identified. +\item The original author's (or authors') name(s) may not be used to + assert or imply endorsement of the resulting document without the + original author's (or authors') permission. +\end{enumerate} + +\section{Good-practice recommendations} + +In addition to the requirements of this license, it is requested from +and strongly recommended of redistributors that: + +\begin{enumerate} +\item If you are distributing Open Publication works on hardcopy or + CD-ROM, you provide email notification to the authors of your intent + to redistribute at least thirty days before your manuscript or media + freeze, to give the authors time to provide updated documents. This + notification should describe modifications, if any, made to the + document. +\item All substantive modifications (including deletions) be either + clearly marked up in the document or else described in an attachment + to the document. +\item Finally, while it is not mandatory under this license, it is + considered good form to offer a free copy of any hardcopy and CD-ROM + expression of an Open Publication-licensed work to its author(s). +\end{enumerate} + +\section{License options} +\label{sec:opl:options} + +The author(s) and/or publisher of an Open Publication-licensed +document may elect certain options by appending language to the +reference to or copy of the license. These options are considered part +of the license instance and must be included with the license (or its +incorporation by reference) in derived works. + +\begin{enumerate} +\item To prohibit distribution of substantively modified versions + without the explicit permission of the author(s). ``Substantive + modification'' is defined as a change to the semantic content of the + document, and excludes mere changes in format or typographical + corrections. + + To accomplish this, add the phrase ``Distribution of substantively + modified versions of this document is prohibited without the + explicit permission of the copyright holder.'' to the license + reference or copy. + +\item To prohibit any publication of this work or derivative works in + whole or in part in standard (paper) book form for commercial + purposes is prohibited unless prior permission is obtained from the + copyright holder. + + To accomplish this, add the phrase ``Distribution of the work or + derivative of the work in any standard (paper) book form is + prohibited unless prior permission is obtained from the copyright + holder.'' to the license reference or copy. + +\end{enumerate} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/metadata.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/metadata.svg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,328 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Changelog + Manifest + Filelogs + + diff -r a24b370a16ee -r d6ca1334a19d ja/mq-collab.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/mq-collab.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,524 @@ +\chapter{Advanced uses of Mercurial Queues} +\label{chap:mq-collab} + +Mercurial Queues の用法を真っ正直に話題にするのは簡単ですが、 +少々抑制を効かせて、込み入った開発環境での作業に役立つような、 +あまり利用されない機能を幾つか説明しようと思います。 + +この章では、 +Linux カーネル向けの Infiniband デバイスドライバ開発において、 +管理に用いていた技法を使用例として取り上げます。 +このデバイスドライバは +(一般のデバイスドライバ程度には)大きく、 +35 のソースファイルにまたがった 25,000 行からなっており、 +少数の開発チームにより保守されています。 + +この章で扱っている対象は Linux に特化したものですが、 +自身が所有していないコードを元に多くの開発を行う必要がある局面で、 +同様の方針が適用できるでしょう。 + +\section{The problem of many targets} + +Linux カーネルは頻繁に変更され、 +内部的には決して安定していません。 +開発者はリリースの間に度々思い切った変更を行います。 +このため、 +Linux カーネルの特定のリリース版で機能するドライバーの版は、 +概して他の版においては\emph{コンパイル}すら通らない場合があります。 + +ドライバの保守を行うためには、 +いくつかの個別の Linux の版を意識する必要があります。 + +\begin{itemize} +\item 第一には、メインの Linux カーネル開発ツリーです。 + この場合のコードの保守は、カーネルコミュニティの他の開発者と共有され、 + 彼らがカーネルのサブシステムに対して行うのと同程度に、 + ``開発しながらの''変更が行われます。 + +\item 開発しているドライバを利用することができない古い + Linux ディストリビューションを使用している顧客の要望に応えるために、 + 古い Linux カーネルの版に対する幾つかの``バックポート''の保守も必要です。 + (コードの\emph{バックポート}には、 + そのコードの開発対象となる版よりも古い版の環境で稼動させるための、 + コードの改変が必要です) + +\item 最後になりますが、顧客の利用しているカーネルやディストリビューションの、 + 全体に対する更新を強いることなく新規機能を提供するために、 + ソフトウェアのリリーススケジュールは、 + Linux ディストリビューションやカーネル開発者が利用しているカーネルと、 + 必ずしも足並みを揃えるわけではありません。 + +\end{itemize} + +\subsection{Tempting approaches that don't work well} + +複数の異なる環境を対象としなければならない一連のソフトウェアの保守には、 +2つの``標準的な''方法があります。 + +1つ目の方法は、 +それぞれが単一の環境を対象とする複数のブランチを管理する方法です。 +この方法の問題点は、 +リポジトリ間での変更の往来\footnote{訳注: いわゆる「マージ」のこと}において、 +鉄の規律でもって望む必要が有ることです。 +新しい機能やバグの修正は``真新しい''リポジトリで始めなければならず、 +その後で全てのバックポート用リポジトリに浸透させます。 +バックポートでの変更は、その伝播が更にブランチ限定されます。 +所属外のブランチに適用されるようなバックポート向けの変更は、 +おそらくドライバのコンパイルを妨げるでしょう。 + +2つ目の方法は、 +個々のコード片の有効/無効を、 +意図する対象に依存して切り替えるための条件文で埋められた、 +単一のソースツリーを保守する方法です。 +これらの``ifdef''記述は、 +Linux カーネルツリーでは許されていないので、 +これらを取り除いて綺麗なツリーを生成するための、 +手動ないし自動の手順が必要です。 +この流儀で保守されるコードベースは早々に、 +理解も保守も困難な条件分岐の「鼠の巣」となるでしょう。 + +これらのいずれの手法も、 +正当なソースツリーのコピーを``所有''していない状況には適合しません。 +標準カーネルと共に配布される Linux ドライバの場合、 +Linus 氏のソースツリーは、 +世界中が正統とみなすコードのコピーから構成されます。 +上流リポジトリにおける``私の''ドライバは、 +Linus 氏のソースツリー上に改変内容が反映されるまでには、 +知らないうちに見知らぬ人々によって異なる版に改変されているかもしれません。 + +これらの手法は、 +上流リポジトリへのパッチの体裁を整えるのを難しくしてしまう、 +という欠点も持っています。 + +Mercurial Queues は、 +これまで述べてきた状況での開発を管理するための、 +良い候補と言えます。 +まさにこのような状況において、 +MQ は作業を快適にする更に幾つかの付加的機能を持っています。 + +\section{ガードによる条件付きパッチ適用} + +おそらく、 +多くの対象環境に対する健全性を保守する方法は、 +所定の状況ごとに適用される特定のパッチを選択できること、 +と言えるでしょう。 +MQ は、 +上記の機能を持つ``ガード''(quilt の \texttt{guards} コマンドに由来します) +と呼ばれる機能を提供します。 +まずはじめに、 +実験のための簡素なリポジトリを作成しましょう。 + +\interaction{mq.guards.init} + +この手順により、 +異なるファイルを操作するので互いには依存性の無い2つのパッチを持つ、 +小さなリポジトリが得られます。 + +条件付き適用の考え方は、 +任意の単純な文字列からなる\emph{ガード}された``札'' +(tag)をパッチに付与しておき、 +パッチ適用の際に、使用すべき特定のガードを MQ に対して教える、 +というものです。 +あらかじめ選択しておいたガードに応じて、 +MQ はガードされたパッチを適用するか見送るかを決定します。 + +個々のパッチは任意の数のガードを持つことができ、 +それぞれのガードは\emph{ポジティブ} +(``ガード選択時にパッチを適用する場合'')か\emph{ネガティブ} +(``ガード選択時にパッチ適用を見送る'')のどちらかです。 +ガードを持たないパッチは常に適用されます。 + +\section{パッチのガードを制御する} + +\hgxcmd{mq}{qguard} コマンドは、 +どのガードをパッチに適用するかを決定するか、 +さもなくば現時点で有効なガードを表示します。 +引数が無い場合、現在の最上位パッチのガードを表示します。 + +\interaction{mq.guards.qguard} + +パッチにポジティブなガードを設定するには、 +ガード名の接頭辞として ``\texttt{+}'' を付与します。 + +\interaction{mq.guards.qguard.pos} + +パッチにネガティブなガードを設定するには、 +ガード名の接頭辞として ``\texttt{-}'' を付与します。 + +\interaction{mq.guards.qguard.neg} + +\begin{note} + \hgxcmd{mq}{qguard} コマンドは、パッチにガードを設定しますが、 + パッチのガード設定を\emph{変更}したりはしません。 + つまり、 + パッチに \hgcmdargs{qguard}{+a +b} を適用した後に、 + 同じパッチに \hgcmdargs{qguard}{+c} を適用した場合、 + このパッチに設定されているガードは \texttt{+c} \emph{だけ}となります。 +\end{note} + +Mercurial は、 +解釈・手動編集が共に容易な形式で、 +ガード情報を \sfilename{series} に格納します +(言い換えるなら、 +\hgxcmd{mq}{qguard} コマンドを利用する必要は無く、 +\sfilename{series} ファイルを直接編集しても構いません)。 + +\interaction{mq.guards.series} + +\section{使用するガードの選択} + +\hgxcmd{mq}{qselect} コマンドは、有効にするガードを決定します。 +ガードが決定することで、 +次に \hgxcmd{mq}{qpush} を実行した際に MQ が適用するパッチが決定されます。 +このコマンドはそれ以外の働きをしません。 +特に、既に適用済みのパッチに対しては、一切何も行いません。 + +引数が指定されない場合、 +\hgxcmd{mq}{qselect} コマンドは、 +現時点で有効になっているガードを1行に1つづつ表示します。 +個々の引数は、適用されるガードの名前とみなされます。 + +\interaction{mq.guards.qselect.foo} + +現在選択されているガードの一覧が +\sfilename{guards} ファイルに格納されていますので、 +興味があれば見てみるのも良いでしょう。 + +\interaction{mq.guards.qselect.cat} + +\hgxcmd{mq}{qpush} を実行することで、 +ガード選択の効果を見ることができます。 + +\interaction{mq.guards.qselect.qpush} + + +``\texttt{+}'' ないし ``\texttt{-}'' +で始まる名前はガード名にはできません。 +空白文字を含むものもガード名にはなれませんが、 +それいがいの大抵の文字は使用可能です。 +不正なガード名の使用は、MQ により警告されます。 + +\interaction{mq.guards.qselect.error} + +ガード選択の変更は、適用されるパッチを切り替えます。 + +\interaction{mq.guards.qselect.quux} + +ネガティブなガードがポジティブなガードに優先することを、 +以下の例で見ることができます。 + +\interaction{mq.guards.qselect.foobar} + +\section{MQ のパッチ適用ルール} + +パッチ適用の有無を判定する際に、MQ は以下のルールを使用します。 + +\begin{itemize} +\item ガード無しパッチは常に適用されます。 + +\item 現在選択されているガードにマッチするネガティブガードがある場合、 + パッチは適用されません。 + +\item 現在選択されているガードにマッチするポジティブガードがある場合、 + パッチは適用されます。 + +\item 現在選択されているガードにマッチするガードが何も無い場合、 + パッチは適用されません。 + +\end{itemize} + +\section{Trimming the work environment} + +先に述べた +Linux カーネル向けの Infiniband +デバイスドライバ開発でのパッチ適用では、 +Linux カーネルの通常のソースツリーは使用しません。 +その代わり、 +Infiniband デバイスドライバ開発に関連するソース/ +ヘッダのみを含むリポジトリを作成し、 +そこに対してパッチを適用するようにします。 +このリポジトリのサイズはカーネルリポジトリの 1\% に収まるため、 +作業を行うのも簡単です。 + +縮小版のリポジトリを作成したならば、 +パッチの``適用対象''となるバージョンを選択します\footnote{訳注: +ここで言う「choose」(選択)は、 +\hgcmd{update} 実行を指すのではないか? +そうであれば、次文が「これは〜スナップショットだ」というのも理解できる。}。 +XXXXXXXXXXXX +This is a snapshot of the Linux kernel tree as of a revision +of my choosing. +XXXXXXXXXXXX +適用対象を選択する際に筆者は、 +当該リビジョンのカーネルリポジトリにおけるチェンジセットIDを、 +コミットメッセージ\footnote{訳注: パッチの? XXXXXX}中に記録しておきます。 +カーネルツリー中の開発に関連する部位に関して、 +スナップショットによって``状況''と内容が特定できるため、 +縮小版リポジトリと通常版のカーネルツリーのいずれに対しても、 +パッチの適用が可能になります。XXXXXX +Since the snapshot +preserves the ``shape'' and content of the relevant parts of the +kernel tree, I can apply my patches on top of either my tiny +repository or a normal kernel tree. + +通常は、 +パッチの適用対象となるソースツリーのベースには、 +上流リポジトリの直近のスナップショットを使用すべきです。 +そうすることで、 +作成したパッチを上流リポジトリの担当者へ送付する際に、 +殆ど(あるいは全く)改変の必要が無くなるでしょう。 + +\section{Dividing up the \sfilename{series} file} + +筆者は、\sfilename{series} に列挙されるパッチを、 +幾つかの論理的なまとまりに分類しています。 +それぞれのパッチ分類は、 +その後に列挙されるパッチの意図を記述したコメントブロックで開始されます。 + +筆者の扱っているパッチ分類は、以下のような並びになっています。 +分類の順序は重要なので、分類を紹介した後で説明します。 + +\begin{itemize} +\item ``受理済み(accepted)''分類: + 開発チームが Infiniband サブシステムの保守担当に送付して、 + 既に受理はされているものの、 + 縮小版リポジトリが元にしているスナップショットには、 + まだ反映されていないパッチの分類です。 + これらは、 + 上流リポジトリの保守担当のリポジトリと同じ状態を得るために、 + ソースツリーを変換する``読み出し限定''パッチです。 + +\item ``再作業(rework)''分類: + 上流リポジトリの保守担当に送付したものの、 + 受理に当たって変更を要求されたパッチの分類。 + +\item ``保留(pending)''分類: + 上流リポジトリの保守担当に送付こそしていないものの、 + 既に作業を終えたパッチの分類。 + しばらくの間は``読み出し限定''として扱われます。 + 上流リポジトリの保守担当により受理されれば、 + このパッチを``受理済み''分類の末尾へと移動します。 + 受理に当たって変更が要求された場合、 + ``再作業''分類の先頭へと移動します。 + +\item ``作業中(in progress)''分類: + 目下のところ活発に作業が行われているパッチの分類。 + この分類のパッチは、外部に公開すべきではありません。 + +\item ``バックポート(backport)''分類: + 古い版のカーネルのソースツリーに適合させるためのパッチの分類。 + +\item ``内部用(do not ship)''分類: + 何らかの理由により、上流リポジトリの保守担当へは送付されないパッチの分類。 + このようなパッチの例としては、 + ドライバ識別用の埋め込み文字列の変更を行うことで、 + ソースツリーのものとは異なるドライバ実装の版 + \footnote{訳注: 開発中のドライバのこと?}と、 + ディストリビューションベンダによって配布されるドライバ実装の版の間で、 + 動作確認等における区別を容易にするパッチがあります。 + +\end{itemize} + +ではここで、パッチ分類尾をこの順番にする理由に戻りましょう。 +コンテキストの変更が発生することで、 +スタック上方のパッチへの再作業 +\footnote{訳注: \hgxcmd{mq}{qrefresh} の実行によるパッチの修正のこと} +が必要になることが無いように、 +スタック中で底にあるパッチほど安定していて欲しいものです。 +変更されにくいパッチ群を +\sfilename{series} ファイルの冒頭に置くことで、 +この目的を達成することができます。 + +他のパッチの適用を極力上流リポジトリの状態に近いソースツリーへ行うために、 +ソースツリーの変換に必要と思われるパッチも重要です。 +受理済みのパッチも暫くの間保持しているのはそのためです。 + +``バックポート''および``内部用''パッチは、 +\sfilename{series} 末尾近辺を転々とします。 +バックポートパッチは他の全てのパッチ適用の上で適用されなければなりませんし、 +その上、 +``内部用''パッチは不都合が無いように内部に留まり続ける必要があります。 + +\section{Maintaining the patch series} + +筆者の作業の際には、 +パッチ適用を制御するために複数のガードを使用しています。 + +\begin{itemize} +\item ``受理済み''パッチには、\texttt{accepted} ガードが付与されます。 + このガードは殆どの場合に有効とされます。 + 既にパッチが適用されているソースツリーにパッチを適用する際には、 + パッチを適用させないようにすることが + \footnote{訳注: \texttt{accepted} ガード付きパッチを無効にすることで} + できるので、 + 後続のパッチ群は綺麗に適用されます。 + +\item 作業は``完了''しているものの、 + 上流リポジトリの保守担当に送付されていないパッチ + \footnote{訳注: 先の分類で言うところの``保留(pending)''}には、 + 何もガードが付与されません。 + 上流リポジトリのコピーに対してパッチスタックを適用する場合、 + 特に何もガードを指定しなくても、 + 適度に安全なソースツリーを得ることができます。 + +\item 上流リポジトリの保守担当への(再)送付に当たって、 + 再作業が必要なパッチには \texttt{rework} ガードが付与されます。 + +\item 目下開発作業中にあるパッチ + \footnote{訳注: 先の分類で言うところの``作業中(in progress)''}には、 + \texttt{devel} ガードが付与されます。 + +\item バックポートパッチには、 + 適用対象カーネルのバージョンを指定する複数のガードが付与されます。 + 例えば、~2.6.9 版へのバックポートを行うパッチには、 + \texttt{2.6.9} ガードが付与されます。 + +\end{itemize} + +これらのガード分類により、 +最終的にどのようなソースツリーが得られるかを決定する際に、 +少なからぬ柔軟性を得ることができます。 +多くの場合、 +適切なガードの選択は構築手順の中で自動化されていますが、 +特別な状況向けにガードの調整を手動で行うことも可能です。 + +\subsection{The art of writing backport patches} + +MQ を使用することで、 +バックポートパッチの作成は単純な作業となります。 +旧版のカーネル配下においてもドライバが正常に稼動するように、 +旧版のカーネルにおいて提供されていない機能を使用するコードの変更が、 +バックポートパッチのすべきことの全てです。 + +良いバックポートパッチを書く際のゴールは、 +対象とする旧版カーネル向けに書いたかのように、 +あなたのコードを変更するようなパッチにすることです。 +パッチがでしゃばらない程、理解と保守が容易になります。 +コード中の大量の \texttt{\#ifdef}(条件に応じて適用されるコード片) +による``鼠の巣''化を避けるためにバックポートパッチ群を書くのであれば、 +バージョン依存な \texttt{\#ifdef} をパッチに持ち込むべきではありません。 +バージョン依存な \texttt{\#ifdef} を使用する替わりに、 +個々のパッチはバージョンに依存しない変更を行うようにして、 +パッチの適用をガードによって制御すべきです。 + +``通常''のパッチと、 +その適用結果を更に変更するバックポートパッチとを、 +別個のグループに分離するのには2つの理由があります。 +第1の理由は、 +これらのパッチが混ざり合った場合に、 +上流リポジトリの保守担当へのパッチ送付の自動化の際に、 +\hgext{patchbomb} 拡張のようなツールを使うことが難しくなるためです。 +第2の理由は、 +後続の通常パッチの適用コンテキスト +\footnote{訳注: \command{patch} ファイルにおける「コンテキスト」} +をバックポートパッチが混乱させてしまい、 +通常パッチの適用前に適用されたバックポートパッチ\emph{抜き}では、 +通常パッチを綺麗に適用することができなくなってしまうためです。 + +\section{Useful tips for developing with MQ} + +\subsection{Organising patches in directories} + +MQ を利用した実在するプロジェクトで作業をしているのであれば、 +多くのパッチを蓄積することも難しいことではありません。 +例えば、筆者は 250 を超えるパッチを抱えたパッチリポジトリを持っています。 + +パッチを個別の論理的なまとまりに分類できるのであれば、 +MQ はパッチ名にパス区切りが含まれていても問題ないので、 +それぞれのパッチを異なるディレクトリに格納することもできます +\footnote{訳注: MQ はパッチ内容の保存先として、 +パッチ名と同名のファイルを作成するため、 +パッチ名にパス区切りが含まれる場合、 +MQ は自動的にサブディレクトリを作成します}。 + +\subsection{Viewing the history of a patch} +\label{mq-collab:tips:interdiff} + +長期間にわたってパッチの開発を行う場合、 +\ref{sec:mq:repo} 節で述べたように、 +パッチをリポジトリで管理するのが良いでしょう。 +その場合は早々に、 +パッチの変更履歴の参照に +\hgcmd{diff} が使えないことに気付くことでしょう。 +これは実際のコードの二次派生物(差分の差分)を見ていること以外にも、 +タイムスタンプやパッチ更新時のディレクトリ名等を改変することで +MQ が雑音を加えてしまっていることに原因があります。 + +Mercurial に同梱されている \hgext{extdiff} 拡張を使うことで、 +2つの版のパッチ差分を幾分読みやすいものにすることができます。 +この拡張を使うためには、 +サードパーティーパッケージである +\package{patchutils}~\cite{web:patchutils} が必要です。 +このパッケージが提供する \command{interdiff} というコマンドは、 +差分間の差分を1つの差分として表示します。 +同じ差分の2つの版 +\footnote{訳注: 「同じパッチの異なる版」の意か?} +に対してこのコマンドを適用すると、 +最初の版から次の版へと変更するための差分を生成します。 + +いつものように、 +\hgrc ファイルの \rcsection{extensions} +セクションに行を追加することで、 +\hgext{extdiff} 拡張を有効化することができます。 + +\begin{codesample2} + [extensions] + extdiff = +\end{codesample2} + +\command{interdiff} コマンドは2つのファイル名の指定が必要ですが、 +\hgext{extdiff} 拡張は、 +それぞれ任意の数のファイルを配下に持つ、 +2つのディレクトリに対して動作するプログラムの指定が必要です。 +そのため、 +これら2つのディレクトリ配下の個々のファイル対に対して +\command{interdiff} を実行する小さなプログラムが必要です。 +本書のソースコードリポジトリにおける +\dirname{examples} ディレクトリ配下に、 +\sfilename{hg-interdiff} として格納されています。 + +\excode{hg-interdiff} + +\sfilename{hg-interdiff} がシェルのコマンド検索パス上に有る場合、 +MQ のパッチディレクトリから以下のようにして起動することができます。 + +\begin{codesample2} + hg extdiff -p hg-interdiff -r A:B my-change.patch +\end{codesample2} + +おそらくこの長たらしいコマンドを何度も使うことになるでしょうから、 +再度 \hgrc を編集して、 +\hgext{hgext} を Mercurial の普通のコマンド並に使えるようにしましょう。 + +\begin{codesample2} + [extdiff] + cmd.interdiff = hg-interdiff +\end{codesample2} + +この記述により \texttt{interdiff} が +\hgext{hgext} から利用できるようになりますので、 +先の \hgxcmd{extdiff}{extdiff} 起動も短くなって幾分使いやすくなるでしょう。 + +\begin{codesample2} + hg interdiff -r A:B my-change.patch +\end{codesample2} + +\begin{note} + \command{interdiff} コマンドは、 + 場合だけ正しく機能します。 + The \command{interdiff} command works well only if the underlying + files against which versions of a patch are generated remain the + same. + パッチの生成・ファイルの変更およびパッチの更新を行った場合、 + \command{interdiff} は有用な出力を生成しないことがあります。 +\end{note} + +\hgext{extdiff} 拡張は、 +MQ パッチの表示機能の向上に留まらない有用なものです。 +\hgext{extdiff} 拡張に関する詳細は、 +\ref{sec:hgext:extdiff} 節を参照してください。 + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/mq-stack.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/mq-stack.svg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,270 @@ + + + + + + + + + image/svg+xml + + + + + + + prevent-compiler-reorder.patch + + namespace-cleanup.patch + + powerpc-port-fixes.patch + + report-devinfo-correctly.patch + { + { + present in series,but not applied + patches applied,changesets present + topmostapplied patch + 201ad3209902 + 126b84e593ae + a655daf15409 + e50d59aaea3a + + forbid-illegal-params.patch + + fix-memory-leak.patch + + diff -r a24b370a16ee -r d6ca1334a19d ja/mq.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/mq.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,1235 @@ +\chapter{Managing change with Mercurial Queues} +\label{chap:mq} + +\section{パッチ管理問題} +\label{sec:mq:patch-mgmt} + +ソフトウェアパッケージをソースからインストールする必要があるのに、 +パッケージ使用前に修正しておかなければならないバグをソース中に発見してしまう、 +というような事態はよくあることです。 + 変更の後、暫くパッケージのことを忘れていると、 +数ヵ月後にパッケージを新しい版で更新する必要が出てきたとします。 + パッケージの新しい版が未だにバグを残していたなら、 +古い版のソースツリーから修正内容を抽出して、 +新しい版に適用しなければなりません。 + このような作業は退屈で間違いを起こしやすいものです。 + +これは``パッチ管理''問題の単純なケースです。 + 自分では変更することができない``上流''のソースツリーがあるとします。 +上流のソースツリーの上でローカルな修正を行う必要があるなら、 +上流ソースの新しい版に対してローカルな修正を適用できるように、 +そういった修正を別途管理したいと思うでしょう。 + +パッチ管理問題はさまざまな状況で発生します。 +オープンソースソフトウェアプロジェクトのユーザが、 +プロジェクトのメンテナンス担当へ、 +バグ修正や新規機能をパッチ形式で送付する状況が、 +おそらく最もわかりやすい状況でしょう。 + +オープンソースソフトウェアを含むオペレーティングシステムの配布者は、 +配布するパッケージに対する変更を頻繁に行うので、 +自分たちの環境においてビルドを行うのは当然のことです。 + +整備の上で幾つか変更を行いたい場合、 +標準的な +\command{diff} および \command{patch} プログラム +(これらのツールに関する議論は \ref{sec:mq:patch} 節を参照のこと) +を使用して、単一のパッチを管理することは簡単です。 +しかし、一旦変更の数が増え始めると、 +単一のパッチの管理は関連性の無い``成果の塊''に感じ始めるため、 +例えば、単一のパッチは単一のバグ修正のみを含む +(パッチは複数のファイルを修正するかもしれませんが、 +``単一の事''しか行わない)ようになるでしょうから、 +異なるバグやローカルな修正に必要とされるパッチを、 +いくつも抱えることになるかもしれません。 + このような状況で、 +上流のパッケージ保守担当者にバグ修正のパッチを送ったとすると、 +彼らはその後のリリースにおいてその修正を取り込むでしょうから、 +新しい版への更新の際には、 +そのパッチの適用を取りやめることができます。 + +上流のソースツリーに対して単一のパッチを保守することは、 +退屈で間違いやすいですが難しくはありません。 + しかし、保守しなければならないパッチの数が増えるにしたがい、 +問題の複雑さはすみやかに増加します。 + すくなからぬパッチを抱え込むことで、 +適用の有無を把握したり、それらを保守することが、 +「面倒なこと」から「圧倒されること」へと変化するでしょう。 + +幸いなことに、Mercurial は +Mercurial Queues +(あるいは単に ``MQ'')と呼ばれる、 +パッチ管理問題を簡素化する強力な拡張機能を持っています。 + +\section{Mercurial Queues 以前} +\label{sec:mq:history} + +1990 年代後半、何人かの Linux カーネル開発者達は、 +Linux カーネルの挙動を変える``パッチ系列''の保守を始めていました。 +幾つかの系列は安定性に、幾つかは網羅性に、 +その他の系列はより実験的な部分に焦点を当てていました。 + +これらのパッチのサイズは速やかに巨大化しました。 +2002 年、Andrew Morton が、 +自分のパッチキュー管理作業を自動化するのに用いていた、 +幾つかのシェルスクリプトを発表しました。 +Andrew は、 +Linux カーネルソース上での数百(時には数千)のパッチの管理に、 +これらのスクリプトを上手に利用していました。 + +\subsection{A patchwork quilt(訳注:継ぎはぎの上掛け)} +\label{sec:mq:quilt} + +2003 年の初頭、 +Andreas Gruenbacher と Martin Quinson は、 +Andrew によるスクリプトの手法を取り入れて、 + +``patchwork quilt''~\cite{web:quilt} あるいは単に ``quilt'' +(これについて述べた論文は~\cite{gruenbacher:2005}を参照のこと) +と呼ばれるツールを発表しました。 + パッチ管理が大幅に自動化されることから、 +quilt はオープンソース開発者の間で瞬く間に大きな支持を得ました。 + +quilt は、 +最上位のディレクトリにおいて\emph{パッチのスタック}を管理します。 +管理開始の際には、 +quilt に対してディレクトリツリーを管理する旨と、 +どのファイルを管理したいのかを伝えます。 +quilt はこれらのファイルの名前と内容を別な場所に保存します。 + バグの修正の際には、 +新しいパッチを(単一のコマンドを使用して)作成し、 +修正する必要の有るファイルの編集を行い、 +パッチを``refresh''します。 + +refresh の段階で quilt はディレクトリツリーを走査します。 +quilt は実施された全ての変更でパッチを更新します。 +最上位のディレクトリにおいて作成した別なパッチを用いることで、 +``1つのパッチが適用されたツリー''から +``2つのパッチが適用されたツリー''へと変化させるために必要な変更を、 +追跡することができます。 + +ツリーに対するパッチの適用状況を\emph{変更}することもできます。 +パッチを``pop''すると、 +そのパッチによる変更はディレクトリツリーから取り除かれます。 +しかし、 +quilt はどのパッチが取り除かれたのかを覚えているので、 +取り除かれたパッチを再び``push''することができ、 +ディレクトリツリーには当該パッチによる変更が復元されます。 +最も重要な点は、 +``refresh''コマンドの実行と、 +それによる最上位のパッチの内容更新が任意の時点にできることです。 +これは、 +パッチの適用状況と、そのパッチによる変更内容の両方を、 +任意の時点で変更できることを意味します。 + +quilt は変更制御ツールを意識しないため、 +展開された tarball の最上位ディレクトリにおいても、 +Subversion リポジトリにおいても同等に機能します。 + +\subsection{patchwork quilt から Mercurial Queues へ} +\label{sec:mq:quilt-mq} + +2005 年中旬、 +quilt 的な振る舞いを Mercurial に追加するための、 +Mercurial Queues と呼ばれる拡張機能が、 +Chris Mason により実装されました。 + +quilt と MQ の大きな違いは、 +quilt が変更制御システムを意識しないのに対して、 +MQ が Mercurial に\emph{統合}されていることです。 + push される個々のパッチは、 +Mercurial のチェンジセットとして表現されます。 + パッチを pop することで、チェンジセットは取り除かれます。 + +変更制御システムを意識しないことから、 +Mercurial と MQ を利用できない状況について知る上で、 +依然として quilt は非常に有用なソフトウェアです。 + +\section{MQ の大きな利点} + +パッチと変更管理の統一を通して MQ が提供するものの価値を、 +誇張し過ぎることはありません。 + +フリーソフトウェアおよびオープンソースの世界でパッチが利用され続けるのは、 +変更管理ツールが年々その機能を向上させているにも関わらず、 +パッチが\emph{軽快さ}を持っていることが大きな理由の一つです。 + +伝統的な変更制御ツールは、 +実施したことに関する全てを、 +永続的で取り消しの出来ないものとして記録します。 +この振る舞いに大きな価値がある一方で、 +幾分堅苦しくもあります。 +過激な実験を行おうとする場合、 +自分が行おうとすることに慎重になるか、 +必要とされない〜なお悪いことには、誤解や不安定の元となる〜 +失敗と間違いの記録を、 +永続的な履歴記録中に残す危険を冒す必要があります。 + +対照的に、 +MQ における分散履歴管理とパッチの結合により、 +あなたの作業を容易に隔離することができます。 +あなたのパッチは通常の変更履歴の上で存続し続け、 +望む時にそれらの実施/取り消しを行うことが出来ます。 +そのパッチが気に入らない場合、それを取りやめることができます。 +そのパッチが完全には望むものでない場合、 +望む姿に洗練させるまで、必要なだけ何度でも修正することが出来ます。 + +例えば、 +パッチと変更管理の統合により、 +パッチの理解とその効果〜および元になったコードとの連携〜のデバッグが、 +\emph{非常に}簡単になります。 +全ての適用済みパッチが関連したチェンジセットを持っているので、 +どのチェンジセットとパッチがそのファイルに影響を及ぼしているのかを、 +\hgcmdargs{log}{\emph{filename}} によって見ることが出来ます。 +\hgext{bisect} 拡張を用いることで、 +バグが持ち込まれたり修正された時点を見るために、 +全てのチェンジセットと適用済みパッチを通しての二分探索を行うことができます。 +\hgcmd{annotate} コマンドを用いることで、 +ソースファイルの特定の行を変更したのが、 +どのチェンジセットやパッチであるかを見ることが出来ます。 + +\section{パッチの理解} +\label{sec:mq:patch} + +MQ は、それがパッチ指向の特性を持つことを表に出しているため、 +パッチがどういったものであるかや、 +パッチとともに機能するツールに関することがらを理解する手助けになります。 + +伝統的な Unix の \command{diff} コマンドは、 +2つのファイルを比較し両者の違いを表示します。 +\command{patch} コマンドは、 +この違いをファイルに対する\emph{変更}とみなします。 +これらのコマンドの簡単な動作例として、 +図~\ref{ex:mq:diff}を見てください。 + +\begin{figure}[ht] + \interaction{mq.dodiff.diff} + \label{ex:mq:diff} + \caption{\command{diff} および \command{patch} コマンドの利用例} +\end{figure} + +\command{diff} が生成する(そして、\command{patch} が入力する) +ファイルの形式は``パッチ(patch)''ないし``差分(diff)''と呼ばれます。 +パッチと差分の間に違いはありません +(以後は、より一般的に使用される``パッチ''という呼称を使用します)。 + +パッチファイルは、任意のテキストから始めることができます。 +\command{patch} コマンドはこのテキストを無視しますが、 +MQ はチェンジセットを生成する際のコミットメッセージとみなします。 +パッチ内容を開始を見つけるために、 +\command{patch} は ``\texttt{diff~-}'' で始まる最初の行を探します。 + +MQ は \emph{unified} 差分と共に機能します +(\command{patch} はそれ以外の何種類かの差分形式でも機能しますが、 +MQ は \emph{unified} 差分でないと機能しません)。 +unified 差分は2種類のヘッダを持っています。 +\emph{ファイルヘッダ header}には、 +変更対象となるファイルのファイル名が記述され、 +\command{patch} コマンドが新規のファイルヘッダを見つけた際には、 +変更を行うために当該する名前のファイルを探します。 + +ファイルヘッダに続いて、\emph{hunk} 列が記述されます。 +それぞれの hunk はヘッダで開始され、 +その hunk により変更される対象の、 +ファイルにおける行番号の範囲を識別します。 +ヘッダに続く hunk は、 +ファイルの改変されない部分からなる数行のテキストが前後に付加されます。 +これらの改変されない部分のことを、hunk に対する\emph{コンテキスト}と呼びます。 +後続の hunk との間に少量のコンテキストしかない場合、 +\command{diff} は新たな hunk ヘッダを表示しません。 +変更内容の間に数行のコンテキスト行を置いて、 +hunk をそのまま続けます。 + +コンテキストの個々の行は空白文字で始まります。 +hunk 内部では、 +``\texttt{-}'' で始まる行は``削除される行''を、 +``\texttt{+}'' で始まる行は``挿入される行''を意味します。 +例えば、変更される行は、1行の削除と1行の挿入で表現されます。 + +パッチのより微妙な側面に関しては後ほど(~\ref{sec:mq:adv-patch}節にて) +説明しますが、MQ を利用するに当たってはここまでの知識で十分です。 + +\section{Mercurial Queues の利用} +\label{sec:mq:start} + +MQ は Mercurial の拡張として実装されているので、 +利用の前に明示的に有効化する必要があります +(ダウンロードの必要はありません。 +MQ は通常の Mercurial の配布物に含まれています)。 +MQ を有効にするには、 +\tildefile{.hgrc} ファイルを編集し、 +~\ref{ex:mq:config} に示す行を追加してください。 + +\begin{figure}[ht] + \begin{codesample4} + [extensions] + hgext.mq = + \end{codesample4} + \label{ex:mq:config} + \caption{MQ 拡張有効化のために \tildefile{.hgrc} に追加する内容} +\end{figure} + +拡張が有効化されると、 +いくつかの新しいコマンドが有効化されます。 +\hgcmd{help} を使って \hgxcmd{mq}{qinit} コマンドの利用可否を見ることで、 +拡張が機能することを確認できます。 +~\ref{ex:mq:enabled} の例を参照してください。 + +\begin{figure}[ht] + \interaction{mq.qinit-help.help} + \caption{MQ 利用可否の確認} + \label{ex:mq:enabled} +\end{figure} + +MQ は\emph{全ての} Mercurial リポジトリで利用でき、 +コマンドはそのリポジトリにしか作用しません。 +利用開始の際には、 +\hgxcmd{mq}{qinit} コマンドによりリポジトリの準備を行います +(~\ref{ex:mq:qinit} 参照)。 +このコマンドは、\sdirname{.hg/patches} と呼ばれる空のディレクトリを作成し、 +MQ はこのディレクトリにメタデータを格納します。 +多くの Mercurial コマンドと同様、 +\hgxcmd{mq}{qinit} コマンドは実行が正常に終了した場合には、 +特に何も表示しません。 + +\begin{figure}[ht] + \interaction{mq.tutorial.qinit} + \caption{MQ 利用に向けたリポジトリの準備} + \label{ex:mq:qinit} +\end{figure} + +\begin{figure}[ht] + \interaction{mq.tutorial.qnew} + \caption{新しいパッチの作成} + \label{ex:mq:qnew} +\end{figure} + +\subsection{新しいパッチの作成} + +新しいパッチで作業を開始するには、 +\hgxcmd{mq}{qnew} コマンドを使います。 +このコマンドは作成するパッチの名前を引数に取ります。 +例~\ref{ex:mq:qnew}に示すように、 +MQ はこれを \sdirname{.hg/patches} +ディレクトリ中の実ファイルの名前とみなします。 + +\sdirname{.hg/patches} ディレクトリ配下にはそれ以外にも、 +\sfilename{series} と \sfilename{status} +という2つの新しいファイルが作成されます。 +\sfilename{series} は、 +そのリポジトリにおいて MQ が管理する全てのパッチの一覧を、 +1行1パッチで保持しています。 +\sfilename{status} は +そのリポジトリにおいて MQ が\emph{適用}した全てのパッチを追跡するための、 +内部帳簿的な用途に使用されます。 + +\begin{note} + 例えば、パッチの適用順序を変更したいような場合、 + \sfilename{series} を手動で変更したい場合があるかもしれません。 + しかし、MQ の認識状況を容易に損なうことから、 + 手動での \sfilename{status} 編集は殆ど全ての場合において不適切です。 +\end{note} + +新しいパッチを作成したならば、 +普段と同じように作業領域ディレクトリのファイルを編集できます。 +\hgcmd{diff} や \hgcmd{annotate} といった、 +Mercurial の全ての通常コマンドはそれ以前と全く同様に機能します。 + +\subsection{パッチの refresh} + +作業内容を保存する段階になったなら、 +作業中のパッチを更新するために \hgxcmd{mq}{qrefresh} を使用します +(図~\ref{ex:mq:qnew}参照)。 +このコマンドは、 +作業領域ディレクトリでの変更内容をパッチへと格納し、 +対応するチェンジセットを、それらの変更内容を保持するように更新します。 + +\begin{figure}[ht] + \interaction{mq.tutorial.qrefresh} + \caption{パッチの refresh} + \label{ex:mq:qrefresh} +\end{figure} + +\hgxcmd{mq}{qrefresh} コマンドはいつでも何度でも実行できるので、 +作業の``チェックポイント''として利用するのも良いでしょう。 +都合の良い時にパッチの refresh を実施することで、 +実験的な作業を行ってみて、それがうまく機能しない場合には、 +直近の refresh 時点までの変更を、 +\hgcmd{revert} コマンドにより取り消すことができます。 + +\begin{figure}[ht] + \interaction{mq.tutorial.qrefresh2} + \caption{複数回のパッチ refresh による変更の蓄積} + \label{ex:mq:qrefresh2} +\end{figure} + +\subsection{パッチの積み重ねと追跡} + +パッチに対する作業を終えるか、 +他のパッチに対する作業が必要になったなら、 +再度 \hgxcmd{mq}{qnew} コマンドを実行することで、 +新しいパッチを作成します。 +Mercurial は、新規に作成したこのパッチを、 +既存のパッチの最上位に適用します。 +図~\ref{ex:mq:qnew2}を参照してください。 +先に作業していたパッチに含まれる変更は、 +この新しいパッチの文脈の一部として含まれます +(\hgcmd{annotate} 出力を見れば、このことは明らかです)。 + +\begin{figure}[ht] + \interaction{mq.tutorial.qnew2} + \caption{1つ目の上に積み重ねられる2つ目のパッチ} + \label{ex:mq:qnew2} +\end{figure} + +これまでは、 +\hgxcmd{mq}{qnew} と \hgxcmd{mq}{qrefresh} を除いて、 +Mercurial の通常コマンドのみを使用するように注意してきました。 +しかし、 +図~\ref{ex:mq:qseries} に示すように、 +パッチに関する作業を行う際により便利な多くのコマンドを、 +MQ は提供しています。 + +\begin{itemize} +\item \hgxcmd{mq}{qseries} コマンドは + MQ が当該リポジトリ中で管理している全てのパッチの一覧を、 + 古いものから新しいもの(最も最近\emph{作成されたもの}) + の順序で一覧表示します。 + +\item \hgxcmd{mq}{qapplied} コマンドは、 + MQ が当該リポジトリで\emph{適用した}全てのパッチの一覧を、 + 古いものから新しいもの(最も最近適用されたもの) + の順序で一覧表示します。 +\end{itemize} + +\begin{figure}[ht] + \interaction{mq.tutorial.qseries} + \label{ex:mq:qseries} + \caption{\hgxcmd{mq}{qseries} および + \hgxcmd{mq}{qapplied} によるパッチの積み重ねの習得} +\end{figure} + +\subsection{パッチの積み重ねの操作} + +``管理されている''パッチと``適用されている''それの間に違いがあることを、 +先の記述では暗に示していますが、 +実際に両者の間には違いがあります。 +MQ は適用すること無しに、パッチをリポジトリ中で管理することができます。 + +\emph{適用された}パッチは、 +リポジトリ中に対応するチェンジセットを持ち、 +パッチとチェンジセットの効果は作業領域ディレクトリにおいて見ることができます。 +\hgxcmd{mq}{qpop} コマンドを使用して、 +パッチの適用を取り消すこともできます。 + +MQ は取り除かれたパッチを\emph{管理}し続けますが、 +そのパッチはもはやリポジトリ中に対応するチェンジセットを持たず、 +作業領域ディレクトリにはパッチによる変更の痕跡は残されていません。 +図~\ref{fig:mq:stack}に、 +適用されたパッチと追跡されているそれの違いを示します。 + +\begin{figure}[ht] + \centering + \grafix{mq-stack} + \caption{MQ のパッチの積み重ねにおける適用済みパッチと未適用パッチ} + \label{fig:mq:stack} +\end{figure} + +\hgxcmd{mq}{qpush} コマンドを使用することで、 +未適用パッチの再適用、ないし取り除きを行うことができます。 +この操作によりパッチに対応する新しいチェンジセットが作成され、 +パッチによる変更は再び作業領域ディレクトリに現れます。 +図~\ref{ex:mq:qpop}に、 +\hgxcmd{mq}{qpop} および \hgxcmd{mq}{qpush} の実施例を示します。 +図のように1つないし2つのパッチを一度取り除いても、 +\hgxcmd{mq}{qseries} の出力は変化しませんが、 +その一方で \hgxcmd{mq}{qapplied} の出力は変化します。 + +\begin{figure}[ht] + \interaction{mq.tutorial.qpop} + \caption{適用パッチの積み重ねの変更} + \label{ex:mq:qpop} +\end{figure} + +\subsection{複数パッチの適用(push)および取り消し(pop)} + +\hgxcmd{mq}{qpush} および \hgxcmd{mq}{qpop} のそれぞれが、 +デフォルトでは一度に一つのパッチに対して処理を行う一方で、 +一度に複数のパッチの適用や取り消しを行うこともできます。 +\hgxcmd{mq}{qpush} に +\hgxopt{mq}{qpush}{-a} オプションを指定することにより、 +全ての未適用パッチの適用が、 +\hgxcmd{mq}{qpop} に +\hgxopt{mq}{qpop}{-a} オプションを指定することにより、 +全ての適用済みパッチの取り消しを行うことができます。 +(それ以外の複数パッチの適用/取り消しの方法に関しては、 +~\ref{sec:mq:perf} 節を参照してください。) + +\begin{figure}[ht] + \interaction{mq.tutorial.qpush-a} + \caption{全ての未適用パッチの適用} + \label{ex:mq:qpush-a} +\end{figure} + +\subsection{安全確認とその無効化} + +いくつかの MQ コマンドは、 +処理の前に作業領域ディレクトリの確認を行い、 +何らかの改変が検出された場合には処理を中断します。 +この確認は、 +パッチに取り込まれていない変更内容を失わないために行われます。 +図~\ref{ex:mq:add} に例を示します。 +\hgxcmd{mq}{qnew} コマンドは未取り込みの変更 +(このケースでは \filename{file3} の \hgcmd{add} に起因するもの)がある場合、 +新しいパッチを生成しません。 + +\begin{figure}[ht] + \interaction{mq.tutorial.add} + \caption{強制的なパッチの生成} + \label{ex:mq:add} +\end{figure} + +作業領域ディレクトリを確認するコマンドは、 +すべて``了解済み''オプションを取ることができ、 +そのオプションは常に \option{-f} と名づけられています。 +\option{-f} オプションの厳密な意味はコマンドごとに異なります。 +例えば、 +\hgcmdargs{qnew}{\hgxopt{mq}{qnew}{-f}} +は新たに生成されるパッチに未取り込みの変更を全て取り込みますが、 +\hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-f}} +は取り消されるパッチが影響を及ぼすファイルに対する変更を元に戻します +\footnote{訳注: +「パッチの影響を元に戻す」のではなく、 +「パッチが影響を及ぼすファイル」を全て元に戻す、の意}。 +利用する前に各コマンドの \option{-f} オプションのドキュメントを確認しましょう! + +\subsection{複数パッチの一括処理} + +\hgxcmd{mq}{qrefresh} コマンドは、 +常に\emph{最上位の}適用済みパッチを更新します。 +これは、あるパッチに対する操作を(refresh することで)中断し、 +取り消し(pop)ないし適用(push)により別のパッチを最上位に持ってくることで、 +そのパッチに対して作業することができることを意味します。 + +この機能によって可能になることを例によって示します。 +2つのパッチによって新しい機能を開発しているものとしましょう。 +1つ目のパッチはソフトウェアの中核機能の変更を、 +そして2つ目のパッチは --- 1つ目のパッチの上で --- +中核機能の変更を使用するためのユーザーインタフェース(UI)の変更を行います。 +UI へのパッチの作業中に、 +中核機能へのパッチにバグを見つけたとしても、 +それを修正するのは簡単なことです。 +UI へのパッチに対する \hgxcmd{mq}{qrefresh} により作業中の変更を保存した後に、 +\hgxcmd{mq}{qpop} により操作対象パッチを中核機能へのそれに変更します +(パッチスタックを下へと移動します)。 +中核機能へのパッチのバグを修正し、 +\hgxcmd{mq}{qrefresh} によってパッチへの反映を行った後に、 +\hgxcmd{mq}{qpush} により操作対象パッチを UI へのパッチに戻すことで、 +やりかけの作業を継続することができます。 + +\section{パッチに関して更に詳しく} +\label{sec:mq:adv-patch} + +MQ はパッチの適用に GNU \command{patch} コマンドを使用しますので、 +\command{patch} コマンドの動作とパッチそのものに関して、 +より詳細な情報を知ることは有用です。 + +\subsection{除去数} + +パッチのファイルヘッダを見ると、 +実際のパス名には現れない余分な要素を先頭に持っていることに気が付くでしょう。 +これは以前にパッチが生成されていた方法の名残です +(今でもこの方法を用いていますが、 +近年の構成管理ツールでは稀です)。 + +Alice が tarball を展開してファイルを編集した後で、 +パッチを作成しようと考えたとします。 +作業領域ディレクトリを改名し、 +再度 tarball を展開(この展開のために改名することが必要になります)し、 +\command{diff} コマンドに +\cmdopt{diff}{-r} および \cmdopt{diff}{-N} オプションを指定することで、 +改変前のディレクトリと改変後のディレクトリの間で再帰的にパッチを生成します。 +一方には改変前のディレクトリ名が全てのファイルのパス冒頭に付加され、 +他方には改変後のディレクトリ名が同様に付加されます。 + +Alices からパッチを受け取った人物の環境に、 +改変前と改変後ディレクトリの両方と厳密に一致する名前のディレクトリがある、 +というのはありそうもない事ですから、 +\command{patch} コマンドは、 +パッチ適用時にパス名要素の何番目までを取り除くかを指す +\cmdopt{patch}{-p} オプションを持っています。 +このオプションに指定される数を\emph{除去数}(strip count)と呼びます。 + +``\texttt{-p1}'' オプションは、 +``除去数を1とみなす''ことを意味します。 +\command{patch} コマンドが、 +ファイルヘッダにおいてファイル名 \filename{foo/bar/baz} を検知した場合、 +\filename{foo} 部分を除去した +\filename{bar/baz} というファイルに対してパッチをあてます +(厳密なことを言えば、 +除去数は除去される\emph{パス区切り}(およびそれに付随する要素)の数を指します。 +除去数1は、\filename{foo/bar} を \filename{bar} にしますが、 +\filename{/foo/bar}(先頭のスラッシュに注意)は +\filename{foo/bar} になります)。 + +パッチにおける``標準の''除去数は1ですので、 +ほとんど全てのパッチは取り除かれる先頭要素を1つ含んでいます。 +Mercurial の \hgcmd{diff} コマンドはこの形式でパス名を生成しますので、 +\hgcmd{import} コマンドや MQ は除去数1のパッチを期待しています。 + +除去数が1ではないパッチをパッチキューに追加しようとした場合、 +現時点で \texttt{-p} オプションを持っていない +\hgxcmd{mq}{qimport} (~\bug{311} 参照のこと)では取り込むことができません。 +その場合、 +\hgxcmd{mq}{qnew} で新規パッチを MQ 上に作成し、 +\cmdargs{patch}{-p\emph{N}} によりパッチを適用、 +\hgcmd{addremove} でパッチにより追加/削除されたファイルを特定し、 +\hgxcmd{mq}{qrefresh} を行うのが最善の方法です。 +このような面倒な手順はいずれ不要になるかもしれません。 +詳細は ~\bug{311} を参照してください。 + +\subsection{パッチ適用手順} + +\command{patch} が hunk を適用する際には、 +it tries a handful of +(successively はどこに掛かる?) +successively less accurate strategies to try to make the hunk apply XXXXX +用心深いこの方法により、古い版のファイルで生成されたパッチであっても、 +新しい版のファイルに適用することが、多くの場合で可能となります。 + +\command{patch} コマンドは、 +最初は hunk における行番号、 +コンテキストおよび変更対象テキストの厳密一致を試みます。 +厳密一致ができない場合、 +行番号に関する情報を無視し、 +コンテキストのみの厳密一致を試みます。 +これが成功した場合、 +\command{patch} コマンドは、 +hunk が適用されたことと、 +元の行番号から\emph{オフセット分}ずれていることを表示します。 + +コンテキストのみによる一致が失敗した場合、 +\command{patch} は冒頭および末尾行を取り除いたコンテキストを用いて、 +\emph{縮小}コンテキストのみによる一致を試みます。 +縮小コンテキストによる hunk 適用が成功した場合、 +\emph{あいまいな要因}を元に hunk が適用されたことを表示します +(この時示される数値は、 +\command{patch} コマンドがパッチ適用前にコンテキストから取り除いた行数です)。 + +これらのどの技法でも適用できない場合、 +\command{patch} コマンドは争点となっている hunk が却下された旨を表示します。 +\command{patch} コマンドは却下された hunk (単に ``reject'' とも呼ばれます) +を同名で \sfilename{.rej} 拡張子を持つファイルに保存します。 +更にその上で、 +パッチ適用前のファイルのコピーを \sfilename{.orig} 拡張子付きで保存します。 +拡張子無しのファイルは、 +適切にの適用\emph{された} hunk による変更を含んでいます。 +ファイル \filename{foo} を変更する6つの hunk を持つパッチがあり、 +そのうちの1つが適用できなかった場合、 +変更前の内容を持つ \filename{foo.orig}、 +適用できなかった hunk を1つ持つ \filename{foo.rej} および +適用できた5つの hynk による変更を含む \filename{foo} +の3つのファイルができます。 + +\subsection{パッチの実現上の癖} + +\command{patch} コマンドのファイルへの作用を知る上で、 +有用な事がいくつかあります。 + +\begin{itemize} +\item わかりきった事ですが、\command{patch} はバイナリファイルを扱えません。 + +\item 実行ビットも扱えませんので、新しいファイルを作成する際には、 + 読み取り可能にはしますが、実行可能にはしません。 + +\item \command{patch} は、削除対象ファイルと空ファイルの差分をもって、 + ファイルの削除を表します。 + そのため、``ファイルを削除する''ことは、 + パッチにおいては``全ての行が削除される''ように見えます。 + +\item 空のファイルと追加対象ファイルの差分をもって、 + ファイルの追加を表します。 + そのため、``ファイルを追加する''ことは、 + パッチにおいては``全ての行が追加される''ように見えます。 + +\item 古い名前のファイルの削除と新しい名前のファイルの追加をもって、 + ファイルの改名を表します。 + これは、ファイルの改名を行うパッチのサイズ + (footprint)が大きくなることを意味します + (パッチにおけるファイルの改名やコピーを Mercurial が推測することは、 + 現状では行われないことにも留意してください) 。 + +\item \command{patch} は空のファイルを表現できませんので、 + ``空のファイルをツリーに追加する''ことをパッチで表現することは出来ません。 + +\end{itemize} + +\subsection{あいまいさに注意} + +オフセット付きや、あいまいな要因を元にしている場合であっても、 +パッチの適用は完全に成功することが多いのですが、 +一方でこのような厳密性を欠いた適用手法は、 +おのずとファイルへのパッチ適用が不完全である可能性を残してしまいます。 +最も典型的な事例は、 +パッチを2度適用してしまうことや、 +不適切な位置に適用してしまうことです。 +\command{patch} や \hgxcmd{mq}{qpush} +がオフセットやあいまい要因に関して言及した際には、 +ファイルが適切に変更されていることを後から確認してください。 + +オフセット付きや、あいあまいな要因を元に適用されたパッチを refresh するのが、 +多くの場合においておすすめなのは、 +パッチの refresh が、 +パッチを綺麗に適用するための新しいコンテキスト情報を生成するからです。 +ただし、パッチを refresh することで、 +元ファイルの異なる版に対してパッチの適用が失敗するようになる場合があるため、 +``多くの場合''おすすめですが、``常に''ではありません。 +ソースツリーの複数の版に対して適用可能なパッチを保守するような場合、 +パッチ適用処理の結果を検証する機会を得ることが出来るので、 +パッチにあいまい要因を持たせておくのは許容範囲です。 + +\subsection{却下された hunk の取り扱い} + +パッチの適用に失敗すると、 +\hgxcmd{mq}{qpush} はエラーメッセージを表示して終了します。 +\sfilename{.rej} ファイルが残されている場合、 +それ以上のパッチを push したり他の作業をする前に、 +却下された hunk の修正を行うことが一般的には最善です。 + +パッチの適用対象であるソースの更新により、 +\emph{それまでは}きちんと適用できていたパッチが適用できなくなった場合の +Mercurial Queues の使い方の詳細に関しては、 +~\ref{sec:mq:merge} 節を参照してください。 + +残念なことに、却下された hunk を扱うための決定的な技法は存在しません。 +多くの場合、\sfilename{.rej} ファイルを参照しながら、 +対象ファイルを編集し、 +却下された hunk を手動で適用しなければなりません。 + +思い切った事も辞さないのであれば、 +パッチの適用に関しては \command{patch} よりも強力な、 +\command{wiggle}~\cite{web:wiggle} と呼ばれるツールが、 +Linux カーネルハッカーの Neil Brown により書かれています。 + +\command{patch} により却下された hunk の適用を自動化するために、 +簡便な手法を用いる \command{mpatch}~\cite{web:mpatch} と呼ばれるツールも、 +別の Linux カーネルハッカーの Chris Mason +(Mercurial Queues の作者です)により書かれています。 +\command{mpatch} は、 +4つのよくある理由で却下された hunk の適用を助けることができます。 + +\begin{itemize} +\item hunk 中程のコンテキストが変更された。 +\item hunk のコンテキストの、先頭あるいは末尾の一方が見当たらない。 +\item 大きな hunk よりも---全部なり一部なりが--- + 小さな hunk に分割された方が適用しやすい。 +\item 現時点でのファイルとわずかに内容の異なる行を + hunk が削除しようとしている。 +\end{itemize} + +\command{wiggle} ないし \command{mpatch} を使用する際には、 +実施結果に対して二重に注意を払う必要があります。 +実のところ \command{mpatch} は、 +処理の完了時に自動的にマージプログラムへと誘導することで、 +ツール出力の二重確認の手法を強要していますので、 +\command{mpatch} の実行結果を確認し、 +残されたマージ処理を完了させることが出来ます。 + +\section{MQ で最高性能を出すために} +\label{sec:mq:perf} + +MQ は大量のパッチの取り扱いを効率よく実施します。 +2006 EuroPython conference~\cite{web:europython} での講演のために、 +2006 年中旬に性能実験を実施しました。 +適用パッチとして、 +1,738 個のパッチを持つ Linux 2.6.17-mm1 パッチ系列を使用しています。 +Linux 2.6.12-rc2 から Linux 2.6.17 にかけての、 +27,472 のリビジョン全てを持つ Linux カーネルリポジトリに対して、 +これらのパッチを適用したのです。 + +旧式の遅いラップトップ PC 上で、 +1,738 個のパッチ全てを +\hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-a}} するのに 3.5 分、 +それらを \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} するのに 30 秒かかりました +(新しいラップトップなら、 +全てのパッチを push する時間は2分まで下がりました)。 +最も大きなパッチの1つ(22,779 行の変更を 287 のファイルに対して行います) +を 6.6 秒で \hgxcmd{mq}{qrefresh} できています。 + +MQ が巨大なソースツリーで作業するのに適しているのは明らかですが、 +最高の性能を出すために知っておいたほうが良い幾つかのコツがあります。 + +最初のコツは、``一括''操作を行うことです。 +\hgxcmd{mq}{qpush} および \hgxcmd{mq}{qpop} の実行の際には、 +何ら変更がされていないことと、 +\hgxcmd{mq}{qrefresh} し忘れがないことを確認するために、 +常に作業領域ディレクトリを走査しています。 +小さなソースツリーの場合は、 +この走査に要する時間は気になりません。 +しかし、中程度(10,000 ファイル程度)のソースツリーでは、 +1秒からそれ以上の時間が必要です。 + +\hgxcmd{mq}{qpush} および \hgxcmd{mq}{qpop} コマンドでは、 +複数パッチを一括して push および pop する際に、 +作業を切り上げる``到達パッチ''を指定することができます。 +到達パッチ指定付きで実行することで、 +\hgxcmd{mq}{qpush} +は指定したパッチが適用スタックの最上位になるまでパッチの適用を行います。 +\hgxcmd{mq}{qpop} の場合は、 +到達パッチが適用スタックの最上位になるまでパッチの取り消しを行います。 + +到達パッチの指定には、パッチの名前か数値が使用できます。 +数値指定の場合、パッチは0から数え始めるため、 +最初のパッチは0、次のパッチの1となります。 + +\section{元ソース変更時のパッチの更新} +\label{sec:mq:merge} + +直接変更することのできないリポジトリに対して、 +パッチスタックを持つことはよくある事です。 +第三者のソースに対する変更や、 +元ソースの更新頻度よりも開発に時間の掛かる機能を実装している場合、 +元ソースの更新との同期や、 +適用できなくなったパッチの hunk を修正する必要があります。 +このような作業は、パッチ系列の\emph{リベース}と呼ばれます。 + +リベースの一番単純な方法は、 +パッチに対して \hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} を行い、 +\hgcmd{pull} で元ソースの変更をリポジトリに取り込み、 +最後に \hgcmdargs{qpush}{\hgxopt{mq}{qpop}{-a}} でパッチを再適用します。 +MQ によるパッチ適用では、 +衝突が検出されている間は適用できないパッチの適用を止めることで、 +衝突の解消とパッチの \hgxcmd{mq}{qrefresh} を行う機会を設けつつ、 +パッチスタック中の全てのパッチを更新し終わるまでパッチの適用を継続します。 + +元ソースの変更がパッチの適用具合に悪影響を及ぼす心配が無いのであれば、 +この手法は手軽で且つ上手く機能するでしょう。 +しかしながら、 +元ソースで頻繁に更新される部分に触れるようなパッチスタックの場合、 +却下された hunk の手動での修正は、 +すぐにでも面倒な作業と化すでしょう。 + +リベース処理を部分的に自動化する事は可能です。 +元ソースの幾つかのリビジョンに対してきちんと適用できるパッチであれば、 +異なるリビジョンとパッチとの間での衝突に対して、 +事前の適用情報を用いた解消を MQ により行うことができます。 + +手順は少々込み入っています。 + +\begin{enumerate} +\item 開始に当たって、 + パッチがきちんと適用できている最上位リビジョンに対して + \hgcmdargs{qpush}{-a} により全てのパッチを適用します。 + +\item \hgcmdargs{qsave}{\hgxopt{mq}{qsave}{-e} \hgxopt{mq}{qsave}{-c}} + を用いてパッチディレクトリのバックアップを保存します。 + このコマンドの実行の際には、パッチを保存したディレクトリの名前を表示します。 + \texttt{\emph{N}} を小さい整数とした場合、 + \sdirname{.hg/patches.\emph{N}} + という形式の名前のディレクトリにパッチが保存されます。 + 適用されたパッチ以外に、 + ``保存されたチェンジセット''もコミットしますが、 + これは内部的な情報と、 + \sfilename{series} および \sfilename{status} の状態を記録するためです。 + +\item hgcmd{pull} により、更新をリポジトリに取り込みます + (\hgcmdargs{pull}{-u} を用いない理由は、以降の記述を参照してください)。 + +\item \hgcmdargs{update}{\hgopt{update}{-C}} を用いて最新の + tip リビジョンに更新することで、適用したパッチを無効にしてください。 + +\item \hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-m} + \hgxopt{mq}{qpush}{-a}} を用いて全てのパッチをマージします。 + \hgxcmd{mq}{qpush} への \hgxopt{mq}{qpush}{-m} オプション指定により、 + パッチ適用に失敗した際に、MQ は 3-way マージを実施します。 + +\end{enumerate} + +\hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-m}} 実施の際には、 +\sfilename{series} +ファイルに列挙されたそれぞれのパッチは通常通り適用されます。 +あいまい要因を元にパッチが適用されたり、パッチの適用が却下された場合、 +MQ は \hgxcmd{mq}{qsave} により保存されたパッチキューを参照し、 +パッチに対応するチェンジセットを用いた 3-way マージを行います。 +このマージ処理には Mercurial の通常のマージ機構が利用されますので、 +衝突の解消の際には GUI マージツールが起動されるかもしれません。 + +パッチの影響を解消し終えると、 +マージ結果を元に MQ によるパッチの refresh が行われます。 + +この手順を終えたリポジトリには、 +古いパッチキューに相当するチェンジセットを元にした余分な head と、 +\sdirname{.hg/patches.\emph{N}} に保存された古いパッチキューが残ります。 +余分な head の削除は、 +\hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a} \hgxopt{mq}{qpop}{-n} patches.\emph{N}} +ないし \hgcmd{strip} で行うことができます。 +バックアップとしての必要性がなくなったなら、 +\sdirname{.hg/patches.\emph{N}} も削除してしまって構いません。 + +\section{パッチの指定} + +パッチを操作する MQ コマンドにおけるパッチの指定は、 +パッチの名前か数値で行います。 +名前による指定は非常にわかりやすいでしょう。 +例えば、\hgxcmd{mq}{qpush} コマンドへの +\filename{foo.patch} の指定により、 +\filename{foo.patch} が適用されるまでパッチの適用が繰り返されます。 + +短縮形式として、名前と数値オフセットの両方を指定することもできます。 +\texttt{foo.patch-2} は +``\texttt{foo.patch} パッチの2つ前''を、 +\texttt{bar.patch+4} は +``\texttt{bar.patch} パッチの4つ後ろ''を意味します。 + +数値によるパッチの指定はそれほど難しくありません。 +\hgxcmd{mq}{qseries} により最初に表示されるパッチは0、 +2番目は1、となっています +(そう、0から数え始める仕組みです)。 + +MQ は、通常の Mercurial コマンドの利用時におけるパッチ操作も簡便にします。 +チェンジセット識別子を受け付ける全てのコマンドは、 +適用済みのパッチ名も受け付けます。 +リポジトリ中に元々あった通常のタグに加えて、 +パッチ適用の際の起点となるリビジョンにタグ\footnote{ +\index{tags!特殊タグ名!\texttt{qbase}}\texttt{qparent} +}が付与されます。 +それに加えて、 +\index{tags!特殊タグ名!\texttt{qbase}}\texttt{qbase} および +\index{tags!特殊タグ名!\texttt{qtip}}\texttt{qtip} タグにより、 +最下位および最上位の適用ずみパッチをそれぞれ指定できます。 + +Mercurial の通常タグに対するこれらの拡張は、 +パッチの取り扱いをより簡便にします。 + +\begin{itemize} + +\item 最新の一連の変更を元に、メーリングリストへパッチ爆弾(patchbomb) + を投稿したい場合には? + \begin{codesample4} + hg email qbase:qtip + \end{codesample4} + (``パッチ爆弾''については \ref{sec:hgext:patchbomb} 節を参照してください) + +\item \texttt{foo.patch} 以降のパッチで、 + 特定のディレクトリ配下のファイルに関与しているものを、 + 全て知りたい場合には? + \begin{codesample4} + hg log -r foo.patch:qtip \emph{subdir} + \end{codesample4} + +\end{itemize} + +パッチの名前を利用可能にするために、 +MQ は Mercurial の持つ内部タグ機能を使用しているので、 +パッチを名前で指定する場合には、 +その名前を全て入力する必要はありません。 + +\begin{figure}[ht] + \interaction{mq.id.output} + \caption{MQ のタグ機能を使用したパッチの操作} + \label{ex:mq:id} +\end{figure} + +パッチの名前をタグで実現することで、 +\hgcmd{log} コマンドの実行時に、 +その出力の一部としてタグとしてのパッチ名が表示される、 +という副作用も得られます。 +このことにより、 +適用済みのパッチと``通常の''リビジョンを、 +視覚的に識別することを容易にします。 +適用済みパッチと連携する Mercurial の通常コマンドの実行例を +図 ~\ref{ex:mq:id} に示します。 + +\section{知っておくと便利な事柄} + +MQ の利用に関して、独立した節を設ける程ではないものの、 +知っておいたほうが良い事柄が幾つかあります。 +ここでは、そういった事柄を集めてみました。 + +\begin{itemize} +\item \hgxcmd{mq}{qpop} でパッチを取り消した後に、 + \hgxcmd{mq}{qpush} で再度適用した場合、 + その時点での適用済みパッチに相当するチェンジセットは、 + pop/push する前のチェンジセットとは\emph{異なる識別子}を持ちます。 + 識別子が異なる理由は ~\ref{sec:mqref:cmd:qpush} 節を参照してください。 + +\item 少なくとも、 + パッチスタック上のパッチによるチェンジセット群の + ``パッチ性''を保ちたいのであれば、 + 他のブランチとそれらを\hgcmd{マージ}すべきではありません。 + \hgcmd{マージ}した場合、それ自体は成功するでしょうが、 + 結果として MQ が混乱してしまうでしょう。 +\end{itemize} + +\section{リポジトリにおけるパッチの管理} +\label{sec:mq:repo} + +MQ が利用する \sdirname{.hg/patches} ディレクトリが +Mercurial の作業領域ディレクトリの外にあるため、 +MQ の``下にある''Mercurial のリポジトリは、 +パッチの管理や存在に関して何も認識していません。 + +このことは、 +パッチディレクトリの内容をそれ自身の Mercurial リポジトリを用いて管理できる、 +という興味深い可能性をもたらします。 +例えば、 +パッチに関する作業を行い、\hgxcmd{mq}{qrefresh} をした後で、 +パッチの現状を \hgcmd{commit} することで、 +後からその状態へとパッチを``巻き戻す''(roll back)することができるなど、 +有用な機能を提供します。 + +複数のリポジトリの間で、 +同一パッチスタックの異なる版を共有することも出来ます。 +筆者は Linux カーネル機能の開発の際にこの手法を使用しています。 +複数の CPU アーキテクチャごとにそれぞれ真新しいカーネルソースのコピーを用意し、 +それぞれに作業中のパッチを含むリポジトリを複製します。 +別なアーキテクチャで変更内容の試験を行う際には、 +対応するカーネルソースのパッチリポジトリへ現時点のパッチを push し、 +全てのパッチを最適用(pop 後に push)した後に、 +そのカーネルのビルドおよび試験を行います。 + +リポジトリ形式の上でパッチを管理することで、 +適用対象のソースに対する制御の可否に関わり無く、 +開発者同士でお互いに衝突すること無しに、 +同じパッチ系列に対する作業を実施できます + +\subsection{MQ のパッチリポジトリサポート} + +MQ は \sdirname{.hg/patches} ディレクトリを自身のリポジトリとして、 +パッチ操作を補助しますが、 +\hgxcmd{mq}{qinit} での初期化の際に +\hgxopt{mq}{qinit}{-c} オプションを指定することで、 +\sdirname{.hg/patches} ディレクトリを +Mercurial リポジトリとして作成することが出来ます。 + +\begin{note} + \hgxopt{mq}{qinit}{-c} オプションの指定を忘れた場合、 + 任意の時点で \sdirname{.hg/patches} ディレクトリで + \hgcmd{init} を実行してください。 + \sfilename{status} を履歴管理しようと思うことは\emph{本当に}ありませんから、 + \sfilename{.hgignore} ファイルに + \sfilename{status} を追加するのを忘れないでください + (\hgcmdargs{qinit}{\hgxopt{mq}{qinit}{-c}} は、 + この作業を自動的に行います)。 +\end{note} + +利便性上、 +\dirname{.hg/patches} ディレクトリが +Mercurial リポジトリである場合、 +MQ は作成・取り込みを行ったパッチの全てを自動的に +\hgcmd{add} します。 + +最後になりますが、 +MQ は \sdirname{.hg/patches} において +\hgcmd{commit} を実行する短縮コマンド +\hgxcmd{mq}{qcommit} を提供していますので、 +(ディレクトリ移動等の)煩わしいキー入力が省略できます。 + +\subsection{幾つかの注意点} + +MQ によるパッチのリポジトリ管理のサポートは、限定的なものです。 + +MQ は、パッチディレクトリに対して行われた変更を、 +自動的に検出することはできません。 +\hgcmd{pull} の実行や、手動での編集、 +あるいは \hgcmd{update} の実行によるパッチや +\sfilename{series} の変更を行った場合、 +パッチ適用対象のリポジトリにおいて +\hgcmdargs{qpop}{\hgxopt{mq}{qpop}{-a}} の後に +\hgcmdargs{qpush}{\hgxopt{mq}{qpush}{-a}} を行って、 +それらの変更を有効にする必要があります。 +この作業を忘れた場合、 +MQ は適用されているパッチがどれなのか混乱してしまうでしょう。 + +\section{パッチ操作のためのサードパーティー製ツール} +\label{sec:mq:tools} + +暫くの間、パッチを使った作業をしていると、 +扱っているパッチの解釈や操作を補助するツールが、 +欲しくてたまらなくなっているに違いありません。 + +\command{diffstat} コマンド ~\cite{web:diffstat} は、 +パッチによって各ファイルがどれだけ変更されるかを表すヒストグラムを生成します。 +どのファイルが、どの程度の影響を受けるのか、 +といった全体的な``感覚を掴む''には良い方法です +(\command{diffstat} の +\cmdopt{diffstat}{-p} オプション利用は勿論良いのですが、 +ファイル名の前置詞に対して行う \cmdopt{diffstat}{-p} オプションの巧妙な処理は、 +少なくとも筆者にとってはわかりにくいです)。 + +\begin{figure}[ht] + \interaction{mq.tools.tools} + \label{ex:mq:tools} + \caption{\command{diffstat}、\command{filterdiff} および \command{lsdiff} コマンド} +\end{figure} + +\package{patchutils} パッケージ ~\cite{web:patchutils} は貴重な存在です。 +このパッケージは、 +``Unix の理念''に従って、 +それぞれがパッチに対して単一の処理を行う小さなツールの集まりです。 +\package{patchutils} の中で筆者が最も利用しているのは、 +パッチファイルから一部を展開する \command{filterdiff} です。 +例えば、 +あるパッチが数ダースのディレクトリに渡って数百のファイルを変更する場合、 +\command{filterdiff} を起動することで、 +指定したパターンに名前が合致するファイルにだけ変更を行う、 +小さなパッチを生成することが出来ます。 +それ以外の例については、 +~\ref{mq-collab:tips:interdiff} 節を参照してください。 + +\section{パッチを扱う良い方法} + +一連のパッチが、 +フリーソフトウェアやオープンソースプロジェクトへ送付するものであろうと、 +あなたの作業における定期的な変更手続きとみなされるものであろうとも、 +より良く作業するための、 +簡単に利用できる手法があります。 + +まずは、パッチに説明的な名前をつけましょう。 +例えば \filename{rework-device-alloc.patch} といった名前は、 +そのパッチが何を行うものかというヒントをすばやく与えてくれるので、 +良い名前と言えるでしょう。 +名前は長くても問題にはなりません。 +名前を入力することはそれほど多くはないでしょうが、 +\hgxcmd{mq}{qapplied} や \hgxcmd{mq}{qtop} といったコマンドは、 +何度も何度も実行するものですから。 +多くのパッチを扱う場合や、 +多くの異なるタスクに手一杯でパッチに多くの注意を割けないような場合、 +名前の適切さはとりわけ重要です。 + +次に、どのパッチに対して作業しているのかに注意しましょう。 +\hgxcmd{mq}{qtop} コマンドを +---例えば、\hgcmdargs{tip}{\hgopt{tip}{-p}} を指定しつつ--- +使用して頻繁にパッチの名前を見ることで、 +どんな作業をしているのかを確認しましょう。 +筆者は作業中に何度も意図しないパッチに対して +\hgxcmd{mq}{qrefresh} を実行してしまったことがありますが、 +間違ったパッチに取り込んでしまった変更を正しいパッチに移動させるのは、 +往々にして手のかかるものです。 + +上記の理由から、 +~\ref{sec:mq:tools} 節で紹介している +\command{diffstat} や \command{filterdiff} +のようなサードパーティー製ツールの学習に、 +少しでも良いので時間を費やすべきです。 +前者はパッチの及ぼす変更に関してすばやい見解を得ることが、 +後者はパッチ中の hunk +を選択的に継ぎ合わせて異なるパッチに組み上げることができます。 + +\section{MQ クックブック} + +\subsection{``些細な''パッチの管理} + +真新しい Mercurial リポジトリにファイルを投入するのは、 +非常にオーバーヘッドが低いので、 +単にダウンロードしたソース tarball に対して変更を加えるのだとしても、 +MQ によりパッチ管理を行うことは非常に理にかなっています。 + +まずはソース tarball のダウンロードと展開を行い、 +Mercurial リポジトリに投入します。 + +\interaction{mq.tarball.download} + +次にパッチスタックを作成し、変更を行います。 + +\interaction{mq.tarball.qinit} + +数週間から数ヵ月経ってから、 +そのパッケージの著者が新しい版をリリースしたとします。 +まずはリポジトリに変更を取り込みます。 + +\interaction{mq.tarball.newsource} + +上記手順で \hgcmd{locate} により始まるパイプラインは、 +作業領域ディレクトリ中の全てのファイルを削除しますので、 +\hgcmd{commit} の \hgopt{commit}{--addremove} オプションは、 +新しい版においてどのファイルが本当に追加/削除されたのかを判定できます。 + +最後に、新しくなったソースツリーの最上位でパッチを適用します。 + +\interaction{mq.tarball.repush} + +\subsection{パッチ全体の結合} +\label{sec:mq:combine} + +MQ はパッチ全体を結合する +\hgxcmd{mq}{qfold} コマンドを提供しています。 +このコマンドは、 +名前を指定したパッチを指定した順序で、 +最上位の適用済みパッチへと``結合''し、 +それらの説明文を最上位パッチの説明文末尾へ追加します。 +結合対象のパッチは、結合の時点で未適用でなければなりません。 + +パッチの結合順序は重要です。 +最上位の適用済みパッチが \texttt{foo} で、 +そこに \hgxcmd{mq}{qfold} と \texttt{quux} を +\hgxcmd{mq}{qfold} する場合、 +順に \texttt{foo}、\texttt{bar} そして \texttt{quux} +と適用するのと同じ効果を持つパッチができあがります。 + +\subsection{パッチの一部の他のパッチへの併合} + +パッチの\emph{一部}を他のパッへ併合するのは、 +パッチ全体を結合するよりも面倒です。 + +あるファイル(群)に対する変更全体を移動したい場合、 +\command{filterdiff} の \cmdopt{filterdiff}{-i} および +\cmdopt{filterdiff}{-x} オプションを用いることで、 +パッチから切り出す変更点を選択して、 +その結果を併合先パッチへと取り込むことでができます。 +通常は取り込み元となったパッチそのものは変更したくないものです。 +そこで、 +MQ は取り込み元パッチを \hgxcmd{mq}{qpush} する際に、 +取り込まれた分の hunk が拒否されたことが報告されますから、 +\hgxcmd{mq}{qrefresh} でパッチを更新することで、 +重複した hunk を取り除くことができます。 + +1つのファイルに対する複数の hunk を持つパッチの一部だけが欲しい場合、 +事態はもう少し厄介ですが、 +それでも部分的に自動化することができます。 +\cmdargs{lsdiff}{-nvv} を使うことで、 +パッチに関するメタデータを表示させます。 + +\interaction{mq.tools.lsdiff} + +このコマンドは、3つの異なる数値の類を表示します。 + +\begin{itemize} +\item (最初のカラムは)改変対象の個々のファイルをパッチ中で識別するための + \emph{ファイル番号}で、 + +\item (字下げされた次の行には)変更されるファイルでの hunk の開始行番号と、 + +\item (同じ行に)hunk を識別するための \emph{hunk 番号} + +\end{itemize} + +必要なファイル番号や hunk 番号を特定するためには、 +視覚的な精査やパッチの読解が必要とされますが、 +それらの数値を \command{filterdiff} の +\cmdopt{filterdiff}{--files} や +\cmdopt{filterdiff}{--hunks} といったオプションに指定することで、 +ファイルや hunk を正確に選択することができます。 + +一度 hunk を取り出してしまえば、 +結合先パッチの末尾に結合して +~\ref{sec:mq:combine} 節の残りの作業を再開することができます。 + +\section{quilt と MQ の違い} + +既に quilt を熟知しているのであれば、 +MQ は同様のコマンド群を持っていますが、 +その働きにはいくらかの違いがあります。 + +殆どの quilt コマンドに対して、 +``\texttt{q}'' で始まる対応する +MQ のコマンドがあることに気付くことでしょう。 +但し、 +quilt の \texttt{add} および \texttt{remove} コマンドに対応するのが、 +Mercurial の通常の \hgcmd{add} および \hgcmd{remove} であるのが例外です。 +また、MQ には quilt の \texttt{edit} に対応するコマンドはありません。 + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/preface.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/preface.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,86 @@ +\chapter*{Preface} +\addcontentsline{toc}{chapter}{Preface} +\label{chap:preface} + +分散構成管理は、比較的新しい領域であり、 +未開の地を切り開こうとする人々の意欲によって、 +発展著しいものがあります。 + +私が分散構成管理に関して筆を執っているのは、 +この分野が手引き書を書く価値のある重要なテーマであるという確信からです。 +執筆の題材として Mercurial を選択したのは、 +分散構成管理の概要を学習するのに適した容易さと、 +他の多くの構成管理ツールでは適用の難しい実践の場からの要望への適用性の、 +2つを併せ持っているためです。 + +\section{This book is a work in progress} + +本書は、 +読者の役に立つことを願って、 +執筆途中から公開しています。 +その一方で、 +読者が本書を利用することが、 +一種の査読として機能することも期待しています。 + +\section{About the examples in this book} +\label{sec:automated-example-running} + +本書では、 +コードのサンプルに関して、 +通例とは異なる手法を採用しています。 +全てのサンプルは``生きた''--- +シェルスクリプトにより実際に +Mercurial コマンドを実行した結果を使用した +---サンプルです。 +本書は常にソースファイルから「ビルド」され、 +全てのサンプルスクリプトの自動実行と、 +その結果と期待する結果との比較が行われます。 + +この手法の利点は、 +本書が冒頭で言及している +Mercurial の版における振る舞いを\emph{厳密に}記述していることになるため、 +サンプルが常に正確である点にあります。 +執筆対象となる Mercurial の版を変更し、 +その結果コマンドの出力が変化した場合、 +本書のビルドは失敗します。 + +この手法のわずかな欠点は、 +サンプルにおいて目にする日時情報が、 +同じコマンドを人手で入力した際とは異なる方法で、 +``押し潰され''がちな点です。 +複数のコマンドを毎秒入力し続けるのは人手では無理ですが、 +例示されている実行結果の日時情報によれば、 +本書のビルドに使用される自動化スクリプトは、 +1秒間に実に多くのコマンドを実行しています。 + +このため、 +本書のサンプルにおける連続した複数回のコミットは、 +まるで同一時刻に起きたことのように見えます。 +この現象は +\ref{sec:undo:bisect}~節における +\hgext{bisect} の例に見ることができます。 + +以上のことから、 +本書のサンプルを見る際には、 +コマンドの出力における日時情報に、 +必要以上の注意を払わないようにしてください。 +その代わり、 +サンプルにおいて目にする挙動や、その再現性に関しては、 +\emph{確信}を持っていただいて構いません。 + +\section{Colophon---this book is Free} + +本書は +Open Publication License 下における利用を許可し、 +もっぱら Free Software ツールを使用して生成されます。 +組版には \LaTeX{}、 +図版には \href{http://www.inkscape.org/}{Inkscape} を使用しています。 + +本書の全ソースコードは、 +\url{http://hg.serpentine.com/mercurial/book} +にある Mercurial リポジトリで公開されています。 + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/revlog.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/revlog.svg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,1155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + Second parent + 32bf9a5f22c0 + + + + Revision hash + 34b8b7a15ea1 + + + + ... + Revision data (delta or snapshot) + + + + + + + First parent + 000000000000 + + + + Second parent + 000000000000 + + + + + Revision hash + ff9dc8bc2a8b + + + + ... + Revision data (delta or snapshot) + + + + + + + First parent + 34b8b7a15ea1 + + + + Second parent + 000000000000 + + + + Revision hash + 1b67dc96f27a + + + + ... + Revision data (delta or snapshot) + + + + + + + + First parent + ff9dc8bc2a8b + + + + Second parent + 000000000000 + + + + Revision hash + 5b80c922ebdd + + + + ... + Revision data (delta or snapshot) + + + + + + + First parent + ecacb6b4c9fd + + + + Second parent + 000000000000 + + + + Revision hash + 32bf9a5f22c0 + + + + ... + Revision data (delta or snapshot) + + + + + + First parent + ff9dc8bc2a8b + + + + Second parent + 000000000000 + + + + Revision hash + ecacb6b4c9fd + + + + ... + Revision data (delta or snapshot) + + + + + + + Head revision(no children) + Merge revision(two parents) + Branches(two revisions,same parent) + + + First revision(both parents null) + + First parent + 5b80c922ebdd + + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/snapshot.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/snapshot.svg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,202 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + Index, rev 7 + + Revlog index (.i file) + Revlog data (.d file) + + + Snapshot, rev 4 + + Delta, rev 4 to 5 + + Delta, rev 5 to 6 + + Delta, rev 6 to 7 + + Delta, rev 2 to 3 + + diff -r a24b370a16ee -r d6ca1334a19d ja/srcinstall.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/srcinstall.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,75 @@ +\chapter{Installing Mercurial from source} +\label{chap:srcinstall} + +\section{On a Unix-like system} +\label{sec:srcinstall:unixlike} + +(2.3 ないしそれ以後の)新しい版の Python が利用可能な +Unix 的なシステムを利用している場合は、 +Mercurial をソースファイルからインストールするのは簡単です。 + +\begin{enumerate} +\item 最新版の tar アーカイブ(tarball)を + \url{http://www.selenic.com/mercurial/download} + からダウンロード。 + +\item tar アーカイブを展開: + \begin{codesample4} + gzip -dc mercurial-\emph{version}.tar.gz | tar xf - + \end{codesample4} + +\item ソースディレクトリに移動して、インストール用スクリプトを実行。 + 以下の手順は、 + ビルドした Mercurial をホームディレクトリ配下にインストールします。 + + \begin{codesample4} + cd mercurial-\emph{version} + python setup.py install --force --home=\$HOME + \end{codesample4} + +\end{enumerate} + +インストールが完了したなら、 +ホームディレクトリ直下の +\texttt{bin} ディレクトリに +Mercurial がインストールされます。 +シェルのコマンド検索パスへの +\texttt{bin} ディレクトリの追加を忘れないようにしてください。 + +Mercurial の実行に必要な Mercurial パッケージを探し出せるように、 +\envar{PYTHONPATH} 環境変数の設定も必要となるでしょう。 +例えば著者のラップトップでは、 +\envar{PYTHONPATH} 環境変数に +\texttt{/home/bos/lib/python} +を設定しています。 +実際に \envar{PYTHONPATH} 環境変数に設定する値は、 +各自の環境で Python がどのように設定されているかに依存しますが、 +設定すべき値を得るのは簡単です。 +設定値に確信が持てない場合、 +上記のインストール用スクリプトの出力を見て、 +\texttt{mercurial} +ディレクトリの内容がインストールされる先を確認してください。 + +\section{On Windows} + +Windows 上で Mercurial をソースからビルドするには、 +様々なツール、相当な技術的知識に加えて、 +少なからぬ忍耐が要求されます。 +``気軽に使ってみたい''場合には、 +ソースからのビルドは\emph{全くお薦めできません}。 +Mercurial そのものをハックするので無い限り、 +バイナリ版の利用をお薦めします\footnote{訳注: +どうしても最新の Mercurial ソースを利用したい場合、 +Windows ネイティブな振る舞いは期待できませんが、 +Cygwin 上で Mercurial をビルドするという手もあります。}。 + +Windows 上で Mercurial をソースからビルドする場合、 +多くの厄介事が起きることを覚悟した上で、 +Mercurial の Wiki 上にある +\url{http://www.selenic.com/mercurial/wiki/index.cgi/WindowsInstall} +に示されている``苦難の道''を辿ってください。 + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/svg2eps.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/svg2eps.sh Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +#!/bin/sh + +inkscape -E $1 $2 diff -r a24b370a16ee -r d6ca1334a19d ja/svg2eps_w32.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/svg2eps_w32.sh Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +#!/bin/sh + +inkscape -E `cygpath -w -a $1` `cygpath -w -a $2` diff -r a24b370a16ee -r d6ca1334a19d ja/svg2png.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/svg2png.sh Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +#!/bin/sh + +inkscape -D -e $1 $2 diff -r a24b370a16ee -r d6ca1334a19d ja/svg2png_w32.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/svg2png_w32.sh Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,3 @@ +#!/bin/sh + +inkscape -D -e `cygpath -w -a $1` `cygpath -w -a $2` diff -r a24b370a16ee -r d6ca1334a19d ja/template.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/template.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,600 @@ +\chapter{Customising the output of Mercurial} +\label{chap:template} + +Mercurial は、 +情報表示の体裁を制御する強力な仕組みを提供しています。 +この仕組みはテンプレートに基づいており、 +テンプレートを使用することで、 +単発のコマンド出力の固有化も、 +Mercurial 組み込みのウェブインタフェースの見かけ全体のカスタマイズもできます。 + +\section{Using precanned output styles} +\label{sec:style} + +Mercurial には即使用できる出力「様式」の幾つかが同梱されています。 +「様式」とは、 +誰かによって書かれて、 +Mercurial が探し出せる何処かにインストールされた、 +事前に用意されたテンプレートのことです。 + +Mercurial に同梱された「様式」を見る前に、 +Mercurial の標準的な出力を見てみましょう。 + +\interaction{template.simple.normal} + +この出力は有益ではありますが、 +チェンジセット毎に5行という多くの表示領域が必要です +\texttt{compact} 様式は、 +表題等を省くことで、 +この出力を3行に低減します。 + +\interaction{template.simple.compact} + +\texttt{changelog} 様式からは、 +Mercurial のテンプレートエンジンの持つ表現力を垣間見ることができます。 +この様式は、 +GNU プロジェクトの +changelog ガイドライン\cite{web:changelog}に沿った出力を行います。 + +\interaction{template.simple.changelog} + +Mercurial の既定出力様式が +\texttt{default} という名前であることを知っても驚くほどのことは無いでしょう。 + +\subsection{Setting a default style} + +好みの様式の名前を \hgrc\ ファイルで指定することで、 +Mercurial がコマンド実行の際に使用する出力様式を変える事ができます。 + +\begin{codesample2} + [ui] + style = compact +\end{codesample2} + +自分自身で様式を定義した場合、 +自分の様式ファイルへのパスを指定する方法と、 +自分の様式ファイルを Mercurial が探し出せる場所へコピーする方法 +(一般には Mercurial がインストールされたディレクトリ直下の +\texttt{templates} ディレクトリ)のどちらででも、 +自分の様式ファイルを使うことができます。 + +\section{Commands that support styles and templates} + +``\texttt{log}的な'' 全ての Mercurial コマンドに対して、 +様式やテンプレートを適用できます。 +例えば、\hgcmd{incoming}、\hgcmd{log}、\hgcmd{outgoing} +および \hgcmd{tip} がそうです\footnote{訳注: +Mercurial 0.9.5 版時点では、これ以外に +\hgcmd{heads} および \hgcmd{parents} +がテンプレートをサポートしています。}。 + +筆者がこのマニュアルを執筆している時点では、 +様式やテンプレートに対応しているコマンドは、 +それ程多くありません。 +しかし、対応済みのコマンドは、 +出力のカスタマイズが必要性が非常に高いコマンド群でしたので、 +Mercurial ユーザのコミュニティからは、 +他のコマンドにおける様式やテンプレートへの対応の要望は、 +今のところあまりありません。 + +\section{The basics of templating} + +Mercurial で言うテンプレートとは、 +大雑把に言うなら一片のテキストです。 +決して変更されない部分がある一方で、 +必要に応じて\emph{展開}や新たなテキストでの置換が実施されます。 + +詳細を説明する前に、 +Mercurial の通常出力の簡単な例をもう一度見てみましょう。 + +\interaction{template.simple.normal} + +それでは、 +出力を変えるためのテンプレートを指定して、 +同じコマンドを実行してみましょう。 + +\interaction{template.simple.simplest} + +上記の例は、可能な限り最も簡単なテンプレートとして、 +チェンジセット毎に表示される静的なテキストを指定するだけの例です。 +\hgcmd{log} コマンドに対する +\hgopt{log}{--template} オプション指定は、 +チェンジセット毎の表示の際に使用するテンプレートとして、 +指定されたテキストを使用することを Mercurial に指示します。 + +上記のテンプレート文字列は、``\Verb+\n+'' で終了している点に注意してください。 +これは\emph{エスケープシーケンス}と呼ばれるもので、 +個々のテンプレート要素の終端で改行を表示することを +Mercurial に指示します。 +この改行を省略した場合、 +Mercurial は個々の出力要素を単一行で出力します。 +エスケープシーケンスに関する詳細は、 +\ref{sec:template:escape}~節を参照してください。 + +常に固定された文字列を表示するテンプレートは、あまり有用とは言えませんので、 +もう少し複雑なものに挑戦してみましょう。 + +\interaction{template.simple.simplesub} + +ご覧の通り、 +テンプレート中の ``\Verb+{desc}+'' 文字列は、 +チェンジセット毎のログメッセージで置換されて出力されます。 +波括弧(``\texttt{\{}'' 及び ``\texttt{\}}'') +で囲まれたテキストが検出された際には、 +どんなテキストが囲まれていた場合でも常に、 +括弧およびテキスト部分の展開が Mercurial により試みられます。 +波括弧そのものを表示したい場合は、 +\ref{sec:template:escape}~節で述べる方法で、 +波括弧をエスケープしなければなりません。 + +\section{Common template keywords} +\label{sec:template:keyword} + +以下のキーワードを使用することで、 +すぐにでも簡単なテンプレートを書くことができます。 + +\begin{description} + +\item[\tplkword{author}] 文字列。 + チェンジセットの作成者。 + チェンジセット作成後は変更されません。 + +\item[\tplkword{branches}] 文字列。 + チェンジセットがコミットされたブランチの名前。 + ブランチ名が \texttt{default} の場合は空です。 + +\item[\tplkword{date}] 日付情報。 + チェンジセットがコミットされた日時。 + この値は可読性が\emph{ありません}ので、 + 適切に文字列化するフィルタに渡す必要があります。 + フィルタに関する詳細は\ref{sec:template:filter}~節を参照してください。 + 日時は数値の対として表されます。 + 最初の数値は Unix UTC タイムスタンプ(1970 年 1 月 1 日からの経過秒)で、 + 2つ目の数値はコミットの際の UTC からのタイムゾーンオフセット秒数です。 + +\item[\tplkword{desc}] 文字列。 + チェンジセットのログメッセージ。 + +\item[\tplkword{files}] 文字列リスト。 + 当該チェンジセットで変更・追加ないし削除された全てのファイル。 + +\item[\tplkword{file\_adds}] 文字列リスト。 + 当該チェンジセットで追加されたファイル。 + +\item[\tplkword{file\_dels}] 文字列リスト。 + 当該チェンジセットで削除されたファイル。 + +\item[\tplkword{node}] 文字列。 + チェンジセット識別用ハッシュ値を40文字の16進数文字列化したもの。 + +\item[\tplkword{parents}] 文字列リスト。 + チェンジセットの親。 + +\item[\tplkword{rev}] 整数値。 + リポジトリローカルなチェンジセットのリビジョン番号。 + +\item[\tplkword{tags}] 文字列リスト。 + 当該チェンジセットに関連付けられたタグ。 + +\end{description} + +幾つか実験してみることで、 +これらのキーワードを使用した際に期待される動作を見ることができます。 +図~\ref{fig:template:keywords}を参照してください。 + +\begin{figure} + \interaction{template.simple.keywords} + \caption{Template keywords in use} + \label{fig:template:keywords} +\end{figure} + +前述したように、 +\tplkword{date} キーワードは可読性のある出力を生成しませんので、 +特別扱いする必要があります。 +そのためには \emph{filter} を使う必要がありますが、 +詳細は \ref{sec:template:filter}~節を参照してください。 + +\interaction{template.simple.datekeyword} + +\section{Escape sequences} +\label{sec:template:escape} + +Mercurial のテンプレートエンジンは、 +最も広く使われている文字列エスケープシーケンスを認識します。 +バックスラッシュ(``\Verb+\+'')を検知した際には、 +それに続く文字を見て、 +それら2つの文字を以下に示すような単独の文字に置換します。 + +\begin{description} +\item[\Verb+\textbackslash\textbackslash+] バックスラッシュ(``\Verb+\+'') + /ASCII~134。 +\item[\Verb+\textbackslash n+] 改行/ASCII~12. +\item[\Verb+\textbackslash r+] 行頭/ASCII~15. +\item[\Verb+\textbackslash t+] タブ/ASCII~11. +\item[\Verb+\textbackslash v+] 垂直タブ/ASCII~13. +\item[\Verb+\textbackslash \{+] 開き波括弧(``\Verb+{+'')/ASCII~173. +\item[\Verb+\textbackslash \}+] 閉じ波括弧(``\Verb+}+'')/ASCII~175. +\end{description} + +上記のように、 +``\Verb+\+''、``\Verb+{+'' ないし ``\Verb+{+'' +そのものを含むテンプレートを使用したい場合、 +これらはエスケープされなければなりません。 + +\section{Filtering keywords to change their results} +\label{sec:template:filter} + +テンプレート展開における結果のうちの幾つかは、 +直ちに使えるほど簡便なものではありません。 +Mercurial は、 +キーワードの展開結果を変更するために、 +任意の\emph{フィルタ}の連鎖を指定することを求めてきます。 +上記の実行例において既に、 +一般的なフィルタである \tplkwfilt{date}{isodate} を、 +日付を読めるようにするために使用しています。 + +Mercurial がサポートする最も一般的に使用されるフィルタのリストを、 +以下に示します。 +任意のテキストに適用できるフィルタもあれば、 +特定の状況下でのみ適用可能なものもあります。 +個々のフィルタの説明は、名前に続いて利用可能な状況を提示し、 +それに効果の説明が続く形式となっています。 + +\begin{description} +\item[\tplfilter{addbreaks}] 任意のテキストに適用可能。 + XHTML の ``\Verb+
    +'' タグを、最終行を除く各行の末尾に付与します。 + 例えば ``\Verb+foo\nbar+'' は ``\Verb+foo
    \nbar+'' となります。 + +\item[\tplkwfilt{date}{age}] \tplkword{date} キーワードに適用可能。 + 現在時刻に対する日付の年齢を描画します。 + ``\Verb+10 minutes+'' のような文字列を生成します。 + +\item[\tplfilter{basename}] 任意のテキストに適用可能ですが、 + \tplkword{files} キーワードやその相対値に対して適用するのが最も有用です。 + テキストをパスとして扱い、そのベースネームを返します。 + 例えば ``\Verb+foo/bar/baz+'' は ``\Verb+baz+'' となります。 + +\item[\tplkwfilt{date}{date}] \tplkword{date} キーワードに適用可能。 + Unix の \tplkword{date} コマンドと同等のフォーマットで日付を描画しますが、 + タイムゾーンを含みます。 + ``\Verb+Mon Sep 04 15:13:13 2006 -0700+'' のような文字列を生成します。 + +\item[\tplkwfilt{author}{domain}] 任意のテキストに適用可能ですが、 + \tplkword{author} キーワードに対して適用するのが最も有用です。 + 電子メールアドレスと思しき最初の文字列を見つけ出し、 + ドメイン部分のみを取り出します。 + 例えば ``\Verb+Bryan O'Sullivan +'' は + ``\Verb+serpentine.com+'' となります。 + +\item[\tplkwfilt{author}{email}] 任意のテキストに適用可能ですが、 + \tplkword{author} キーワードに対して適用するのが最も有用です。 + 電子メールアドレスと思しき最初の文字列を見つけ出します。 + 例えば ``\Verb+Bryan O'Sullivan +'' は + ``\Verb+bos@serpentine.com+'' となります。 + +\item[\tplfilter{escape}] 任意のテキストに適用可能。 + XML/XHTML の特殊文字である + ``\Verb+&+''、``\Verb+<+'' および ``\Verb+>+'' を、 + XML の実体参照形式で置き換えます。 + +\item[\tplfilter{fill68}] 任意のテキストに適用可能。 + テキストを 68 桁に収まるように行を折り返します。 + \tplfilter{tabindent} フィルタ実施後も + 80 桁の固定フォント幅の画面に収めたい場合、 + \tplfilter{tabindent} フィルタに渡す前のテキストに適用するのが良いでしょう。 + +\item[\tplfilter{fill76}] 任意のテキストに適用可能。 + 76 桁に収まるように行を折り返します。 + +\item[\tplfilter{firstline}] 任意のテキストに適用可能。 + テキストの最初の行を、改行等を含まない形式で取り出します。 + +\item[\tplkwfilt{date}{hgdate}] \tplkword{date} キーワードに適用可能。 + 可読性のある数値の組として日付を描画します。 + ``\Verb+1157407993 25200+'' のような文字列を生成します。 + +\item[\tplkwfilt{date}{isodate}] \tplkword{date} キーワードに適用可能。 + ISO~8601 形式の文字列として日付を描画します。 + ``\Verb+2006-09-04 15:13:13 -0700+'' のような文字列を生成します。 + +\item[\tplfilter{obfuscate}] 任意のテキストに適用可能ですが、 + \tplkword{author} キーワードに対して適用するのが最も有用です。 + 入力テキストに対応する XML 実体参照シーケンスを生成します。 + 典型的な電子メールアドレス収集を行うスパムボット + (spambot)に対する対抗策の1つとして利用可能です。 + +\item[\tplkwfilt{author}{person}] 任意の文字列に適用可能ですが、 + \tplkword{author} キーワードに対して適用するのが最も有用です。 + 電子メールアドレスより前の部分を取り出します。 + 例えば ``\Verb+Bryan O'Sullivan +'' は + ``\Verb+Bryan O'Sullivan+'' となります。 + +\item[\tplkwfilt{date}{rfc822date}] \tplkword{date} キーワードに適用可能。 + 電子メールヘッダと同じ形式で日付を描画します。 + ``\Verb+Mon, 04 Sep 2006 15:13:13 -0700+'' のような文字列を生成します。 + +\item[\tplkwfilt{node}{short}] チェンジセットハッシュ値に適用可能です。 + チェンジセットハッシュの短縮形式、即ち 12 桁の 16 進文字列を生成します。 + +\item[\tplkwfilt{date}{shortdate}] \tplkword{date} キーワードに適用可能。 + 年月日形式で日付を描画します。 + ``\Verb+2006-09-04+'' のような文字列を生成します。 + +\item[\tplfilter{strip}] 任意のテキストに適用可能。 + 冒頭ならびに末尾の空白文字を全て除外します。 + +\item[\tplfilter{tabindent}] 任意のテキストに適用可能。 + 最初の行を除く全ての行がタブ文字で始まるようにします。 + +\item[\tplfilter{urlescape}] 任意のテキストに適用可能。 + URL 解析の際に``特殊文字''とされる文字をエスケープします。 + 例えば \Verb+foo bar+ は \Verb+foo%20bar+ になります。 + +\item[\tplkwfilt{author}{user}] 任意の文字列に適用可能ですが、 + \tplkword{author} キーワードに対して適用するのが最も有用です。 + 電子メールアドレスから``ユーザ''部分を取り出します。 + 例えば ``\Verb+Bryan O'Sullivan +'' は + ``\Verb+bos+'' となります。 + +\end{description} + +\begin{figure} + \interaction{template.simple.manyfilters} + \caption{Template filters in action} + \label{fig:template:filters} +\end{figure} + +\begin{note} + 適用対象外のデータに対してフィルタの適用を試みた場合、 + Mercurial は実行に失敗して Python の例外を表示します。 + 例えば、\tplkword{desc} キーワードに + \tplkwfilt{date}{isodate} フィルタを適用するのはよろしくありません。 +\end{note} + +\subsection{Combining filters} + +所定の形式での出力を得るために、 +簡単にフィルタを組み合わせることができます。 +以下の例では、ログメッセージの冒頭・末尾の空白を除外し、 +68 桁に収まるように改行した後で、 +さらに8文字分(タブ文字が慣習的に8文字として扱われる +Unix 的な環境では)の字下げが、 +フィルタ連鎖により実施されます。 + +\interaction{template.simple.combine} + +テンプレートにおける``\Verb+\t+''(タブ文字)の利用は、 +最初の行の強制的な字下げを行うためのものであることに注意してください。 +\tplkword{tabindent} が最初の行\emph{以外の}全ての行を字下げするために、 +このタブ文字が必要です。 + +連鎖におけるフィルタの順序が重要である点に留意してください。 +最初のフィルタがキーワードの置換結果に適用され、 +2つ目のフィルタが最初のフィルタの適用結果に適用される、 +という具合です。 +例えば、 +\Verb+fill68|tabindent+ という記述は +\Verb+tabindent|fill68+ とは全く違った結果となります。 + +\section{From templates to styles} + +コマンド行でのテンプレート指定は、 +手早く簡単に出力を整形する手段を提供します。 +しかし、テンプレートは冗長に成りがちですから、 +テンプレートに名前付けできれば便利になります。 +様式(sytle)ファイルは、名前が付けられ、 +ファイルに保存されたテンプレートのことです。 + +それ以上に、 +コマンド行での \hgopt{log}{--template} オプション使用では引き出せなかった +Mercurial のテンプレートエンジンの能力を、 +様式ファイルを用いることで引き出すことができます。 + +\subsection{The simplest of style files} + +以下に示す簡単な様式ファイルは、 +1行だけのものです。 + +\interaction{template.simple.rev} + +この様式記述は、 +``チェンジセットを表示する際には、 +右辺のテキストをテンプレートとして使用せよ'' +と Mercurial に指示します。 + +\subsection{Style file syntax} + +様式ファイルの文法は簡単です。 + +\begin{itemize} +\item ファイルは一行づつ処理されます。 + +\item 行頭および行末の空白は無視されます。 + +\item 空行は読み飛ばされます。 + +\item ``\texttt{\#}'' ないし ``\texttt{;}'' のいずれかで始まる行は、 + 行全体がコメントとみなされ、空行と同様に読み飛ばされます。 + +\item 行はキーワードで開始されます。 + キーワードは英字ないし下線(underscore)で開始され、 + 任意個数の英数字ないし下線が続きます + (正規表現で書くなら、 + キーワードは ``\Verb+[A-Za-z_][A-Za-z0-9_]*+.'' + に合致しなければなりません)。 + +\item キーワードに続く要素は文字 ``\texttt{=}'' でなければなりませんが、 + 前後に任意個の空白文字があっても構いません。 + +\item 行の残り部分が引用符(シングルクォートないしダブルクォート) + で囲まれている場合、 + その部分はテンプレートの本体とみなされます。 + +\item 行の乗り部分が引用符で囲まれて\emph{いない}場合、 + その部分は、 + テンプレート本体を内容として持つファイルのファイル名とみなされます。 + +\end{itemize} + +\section{Style files by example} + +様式ファイルの記述を説明するために、 +幾つかの例を示します。 +様式ファイル一式を通して読むよりも、 +非所に簡単な例から始めて、 +幾つかの複雑な例を通し読みすることで、 +通常の様式ファイル作成手順を示そうと思います。 + +\subsection{Identifying mistakes in style files} + +様式ファイル中に問題があった場合、 +Mercurial はそっけないエラーメッセージを表示しますが、 +意味するところがわかってしまえば、 +そのメッセージは非常に有用です。 + +\interaction{template.svnstyle.syntax.input} + +\filename{broken.style} は、 +\texttt{changeset} キーワードを定義しようとしているものの、 +その内容が記述されていない点に注目してください。 +このような様式ファイルが指定された場合、 +Mercurial は即座にメッセージを表示します。 + +\interaction{template.svnstyle.syntax.error} + +このメッセージは威圧的に見えますが、 +読み解くのはそれほど難しくありません。 + +\begin{itemize} +\item 最初の要素は、単に Mercurial が``実行をあきらめました'' + と通知しています。 + \begin{codesample4} + \textbf{abort:} broken.style:1: parse error + \end{codesample4} + +\item 次の要素は、エラーの要因が格納された様式ファイルの名前です。 + \begin{codesample4} + abort: \textbf{broken.style}:1: parse error + \end{codesample4} + +\item ファイル名の次は、エラーが発生した行番号になります。 + \begin{codesample4} + abort: broken.style:\textbf{1}: parse error + \end{codesample4} + +\item 最後に、問題の説明が記述されます。 + \begin{codesample4} + abort: broken.style:1: \textbf{parse error} + \end{codesample4} + 問題の説明は(この例のように)常に明確であるとは限りませんが、 + 暗号めいたものであったとしても、 + 様式ファイル中の問題となる行を目視確認して間違いを見つける上では、 + 殆どの場合は取るに足らない説明です。 + +\end{itemize} + +\subsection{Uniquely identifying a repository} + +短い文字列を識別子として +Mercurial リポジトリを``概ね一意に''識別\footnote{訳注: +ここで言う「リポジトリの識別」は、 +むしろ「プロジェクトの識別」に近いニュアンスと思われます。 +}したい場合、 +リポジトリの最初のリビジョンを使用するのが良いでしょう。 + +\interaction{template.svnstyle.id} + +この値は一意であることが保証されていませんが、 +それでも多くの場合において有用です。 + +\begin{itemize} +\item 完全に空のリポジトリではリビジョン~0が存在しないため、 + この方法は機能しません。 + +\item 以前は別々だった複数のリポジトリをマージしたものと、 + マージ前のリポジトリを併用している場合 + (このような事態は非常に稀ではありますが)、 + それらのリポジトリの間では、 + この方法による識別は機能しません。 + +\end{itemize} + +リポジトリ識別子の利用例を以下に示します。 + +\begin{itemize} +\item サーバ上のリポジトリを管理しているデータベースでの、 + テーブルにおけるキーとしての使用 + +\item \{\emph{リポジトリ識別子}, \emph{リビジョン識別子}\} + というタプルの一部としての使用。 + ビルドや他の自動化された処理を実施する際に、 + このタプル情報を保存しておくことで、 + 後に処理を``再現''することが可能です。 + +\end{itemize} + +\subsection{Mimicking Subversion's output} + +例えば Subversion のような、 +他の構成管理ツールのデフォルト出力形式をまねてみましょう。 + +\interaction{template.svnstyle.short} + +Subversion の出力様式はかなり単純ですので、 +出力内容をファイルに保存し、 +出力テキスト中で Subversion により(動的に)生成される部分を、 +展開されるテンプレート値\footnote{訳注: キーワードのこと? +}で置き換えるのは容易でしょう。 + +\interaction{template.svnstyle.template} + +このテンプレートによる出力が、 +Subversion により生成される出力様式から逸脱する場合\footnote{ +訳注: ``a few small ways'' よりは ``a few small point'' で、 +「逸脱する箇所」の方が良くないか?}が幾つかあります。 + +\begin{itemize} +\item Subversion は、``可読性のある''日付 + (上記の出力例における ``\texttt{Wed, 27 Sep 2006}'') + を丸括弧の中に表示します。 + Mercurial のテンプレートエンジンは、 + 時刻とタイムゾーンの無いこの形式で日付を表示する手段を提供していません。 + +\item テンプレート末尾に + ``\texttt{-}''文字を一杯に使った行の表示を配置することで + Subversion の``分離''線をまねています。 + Subversion の出力に似せるため、 + 出力の最初の分離線表示には、 + テンプレートエンジンの + \tplkword{header} キーワードを使用しています(後述します)。\footnote{ + 訳注:これは deviate な点ではない気が… } + +\item Subversion の出力は、 + ヘッダ部にコミットメッセージの行数が表示されます。 + Mercurial ではこれに相当する情報を表示することができません。 + 処理対象となるデータの行数を数え上げるフィルタを、 + テンプレートエンジンが現時点では提供していないためです。 + +\end{itemize} + +Subversion の出力例を元に、 +上記テンプレートのようなキーワード・フィルタへの置き換えを行う作業は、 +せいぜいが1〜2分で済む作業です。 +様式ファイルは、単にこのテンプレートを参照すれば良いのです。 + +\interaction{template.svnstyle.style} + +テンプレートファイルテキストを様式ファイルで直接設定するには、 +引用符で囲み、改行文字を ``\texttt{\\n}'' で置き換えれば良いのですが、 +様式ファイルを非常に読み難くしてしまいます。 +テンプレートを様式ファイルに直接記述するか、 +テンプレートファイルに記述したものを様式ファイルから参照するかを決める際には、 +可読性を基準とするのが良いでしょう。 +様式ファイルの大きさや複雑さが高まる場合は、 +テンプレートテキストを記述するのではなく、 +外部ファイルに出してしまいましょう。 + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/tour-basic.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/tour-basic.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,846 @@ +\chapter{A tour of Mercurial: the basics} +\label{chap:tour-basic} + +\section{Installing Mercurial on your system} +\label{sec:tour:install} + +一般的な全ての OS 向けに、 +ビルド済みの Mercurial バイナリ版が提供されています。 +バイナリ版を使用することで、 +簡単に Mercurial をセットアップすることができます。 + +\subsection{Linux} + +Linux ディストリビューションは、 +それぞれ固有のパッケージ管理ツール、 +パッケージ作成方針、ならびに開発ペースを持っていますので、 +全てのバイナリ版 Mercurial +のインストール手順に関する包括的な説明を行うのは困難です。 +また、 +バイナリ版のインストールによって利用可能な Mercurial のバージョンは、 +当該ディストリビューションのパッケージ保守担当者が、 +どの程度活発であるかによって異なります。 + +簡便化のため、 +著名な Linux ディストリビューションにおける、 +コマンドラインを用いた Mercurial のインストールに限定して説明します。 +殆どのディストリビューションでは、 +\texttt{mercurial} という名前のパッケージを探したならば、 +クリックひとつで Mercurial がインストールできるような、 +グラフィカルなパッケージ管理ツールが提供されています。 + +\begin{description} +\item[Debian] + \begin{codesample4} + apt-get install mercurial + \end{codesample4} + +\item[Fedora Core] + \begin{codesample4} + yum install mercurial + \end{codesample4} + +\item[Gentoo] + \begin{codesample4} + emerge mercurial + \end{codesample4} + +\item[OpenSUSE] + \begin{codesample4} + yum install mercurial + \end{codesample4} + +\item[Ubuntu] Ubuntu の Mercurial パッケージは非常に古いので、 + 使用すべきではありません。 + できれば、Debian パッケージをリビルドしてインストールしてください。 + おそらく Mercurial をソースからビルドする方が簡単でしょう。 + その場合の詳細は、\ref{sec:srcinstall:unixlike}~節を参照してください。 + +\end{description} + +\subsection{Mac OS X} + +Mac OS~X 向けの Mercurial インストーラは、 +Lee Cantey によって +\url{http://mercurial.berkwood.com} で公開されています。 +このパッケージは、Intel および Power の両 Mac で動作します。 +このインストーラを使用する前に、 +Universal MacPython~\cite{web:macpython} +と互換性のある Python をインストールする必要があります。 +Lee 氏のサイトにある手順を踏めば、簡単にインストールできます。 + +\subsection{Solaris} + +未校。XXX + +\subsection{Windows} + +Windows 向けの Mercurial インストーラは、 +Lee Cantey によって +\url{http://mercurial.berkwood.com} で公開されています。 +このパッケージは他のパッケージへの依存性がありませんので、 +単独で利用できます。 + +\begin{note} + 基底状態の Windows 版 Mercurial は、 + Windows と Unix の改行形式の自動変換は行いません。 + Unix 利用者と変更成果を共有したい場合は、 + 少々追加設定を行う必要があります。 + 詳細未校 XXX。 +\end{note} + +\section{Getting started} + +Mercurial を使い始めるにあたり、 +実際に利用可能な Mercurial コマンドのバージョンを確認するため、 +\hgcmd{version} コマンドを使ってみましょう。 +実際のバージョン情報にはそれほど重要性はありませんが、 +何も表示されない場合は対処が必要です。 + +\interaction{tour.version} + +\subsection{Built-in help} + +Mercurial は組み込みヘルプ機能を持っています。 +この機能は、 +コマンドの実行方法を思い出せない場合に有用です。 +何をして良いのか完全にわからなくなってしまった場合は、 +単に \hgcmd{help} を実行することで、 +それぞれがどのような機能を持っているかの説明が付いた、 +簡単なコマンド一覧が表示されます。 +以下に示すような形式で、 +特定のコマンドについて \hgcmd{help} を実行した場合、 +そのコマンドに関する詳細な情報が表示されます。 + +\interaction{tour.help} + +更に多くの詳細な(通常は必要としない)情報を表示するには、 +\hgcmdargs{help}{\hggopt{-v}} を実行します。 +\hggopt{-v} オプションは \hggopt{--verbose} の省略形で、 +通常よりも多くの情報を Mercurial に表示させます。 + +\section{Working with a repository} + +Mercurial では、 +全てが\emph{リポジトリ}に閉じています。 +例えば、あるプロジェクトのために作成したリポジトリには、 +プロジェクトに``属する''全てのファイルだけでなく、 +ファイルに関する履歴情報も格納されています。 + +リポジトリはファイルシステム上にある只のディレクトリツリーですので、 +Mercurial が特別扱いするということ以外には、 +通常のディレクトリやファイルと比較して特に変わっている点はありません。 +コマンド行やファイルブラウザを利用して、 +任意の時点で改名や削除することができます。 + +\subsection{Making a local copy of a repository} + +リポジトリの\emph{複製}は、少々特別です。 +通常のディレクトリ複製のコマンドでもリポジトリを複製できますが、 +Mercurial 組み込みの複製コマンドを使用した方が良いでしょ。 +このコマンドは、 +既存のリポジトリと同一の複製を生成するため、 +\hgcmd{clone} と呼ばれています。 + +\interaction{tour.clone} + +チュートリアル用のリポジトリからの複製に成功したなら、 +ローカルファイルシステム上に +\dirname{hello} という名前のディレクトリがある筈です。 +このディレクトリにはファイルが幾つか格納されていることでしょう。 +This directory will contain some files. + +\interaction{tour.ls} + +これらのファイルは、 +複製元になったリポジトリにおけるファイルと、 +全く同じ内容と履歴情報を持っています。 + +全ての Mercurial リポジトリは、 +機能提供に必要なものを全て格納しているため、 +それ自体で完結している、独立した存在です。 +リポジトリには、 +プロジェクトに属するファイルの私的な複製と履歴情報が格納されます。 +複製されたリポジトリは、 +複製元となったリポジトリの位置を記憶していますが、 +特に明示的な指示をしない限り、 +複製元リポジトリとの連携(および、それ以外のリポジトリとの連携も) +は行われません。 + +それぞれのリポジトリは、 +他のリポジトリに影響を及ぼすことの無い、 +私的な``箱庭''と言えますから、 +自身のリポジトリで自由に実験ができるわけです。 + +\subsection{What's in a repository?} + +リポジトリ内部を仔細に見てみると、 +\dirname{.hg} という名前のディレクトリがあることに気が付くことでしょう。 +このディレクトリは、 +Mercurial がリポジトリのメタデータを格納しているディレクトリです。 + +\interaction{tour.ls-a} + +\dirname{.hg} およびその配下のディレクトリの内容は、 +Mercurial が私的に使用するものです。 +リポジトリにおけるそれ以外のディレクトリ・ファイルは、 +自由に利用して構いません。 + +用語の定義をするにあたり、 +\dirname{.hg} ディレクトリを``本当の''リポジトリとするなら、 +それと共存する他のファイル・ディレクトリは +\emph{作業領域ディレクトリ}にあるもの、と呼ばれます。 +両者の区分を簡単に言うなら、 +\emph{リポジトリ}がプロジェクトの\emph{履歴}を保持する一方で、 +\emph{作業領域ディレクトリ}は、 +履歴上のとある時点におけるプロジェクトの\emph{スナップショット}を保持する、 +と言えます。 + +\section{A tour through history} + +馴染みの無い新しいリポジトリに対しては、 +まずはその履歴を参照してみようと思うことでしょう。 +\hgcmd{log} コマンドは、履歴情報を出力します。 + +\interaction{tour.log} + +このコマンドの基底動作では、 +プロジェクトに加えられた個々の変更の記録に対して簡単な出力を行います。 +Mercurial の用語では、 +複数のファイルに対する変更を保持し得ることから、 +記録されたこれらの出来事を\emph{チェンジセット}と呼称します。 + +\hgcmd{log} の出力形式における各欄は、 +以下のようになっています。 + +\begin{description} +\item[\texttt{changeset}] この欄は、10 進数、コロン(colon: \texttt{:}) + および 16 進数の連続形式となっています。 + 2つの数値は共にチェンジセットの\emph{識別子}です。 + 16 進数のものよりも、10 進数の方が短く、入力が容易であることから、 + 2つの識別氏が存在します。 + +\item[\texttt{user}] チェンジセットの作成者に関する識別情報です。 + この欄は自由形式ですが、殆どの場合、 + 人名と電子メールアドレスが格納されます。 + +\item[\texttt{date}] チェンジセットが作成された日時と、そのタイムゾーンです + (日時は当該タイムゾーンにおける値ですので、 + チェンジセットの作成者にとっての日時を表します)。 + +\item[\texttt{summary}] チェンジセット作成者が、 + 作成の際にチェンジセットの説明として入力したメッセージの最初の行です。 + +\end{description} + +基底動作における \hgcmd{log} の出力は、 +単純な要約ですので、 +多くの詳細データが欠けています。 + +図~\ref{fig:tour-basic:history} は、 +履歴の``動向''を把握し易くするために、 +\dirname{hello} リポジトリにおける履歴を図示したものです。 +本章および以降の章において、 +何度かこの図に立ち返ることになることでしょう。 + +\begin{figure}[ht] + \centering + \grafix{tour-history} + \label{fig:tour-basic:history} + \caption{Graphical history of the \dirname{hello} repository} +\end{figure} + +\subsection{Changesets, revisions, and talking to other people} + +英語が不正確さで悪名高い言語であり、 +計算機科学では用語の混乱はいつものことですので、 +構成管理の分野では、 +同じことを表す複数の用語や言い回しが存在します。 +Mercurial での履歴管理について話をする場合、 +``チェンジセット''(changeset)という用語が時には +``チェンジ''(change)や +(文書の場合は)``cset''などと省略されていたり、 +チェンジセットという言い回しが、 +``リビジョン''(revision)ないし``rev'' +を表すものとして使用されたりするのを目にするかもしれません。 + +``チェンジセット''の概念をどのような\emph{用語}で表そうが問題ではありませんが、 +``\emph{特定の}チェンジセット''を指すための\emph{識別子}は非常に重要です。 +\hgcmd{log} の出力における \texttt{changeset} 欄が、 +10 進数と 16 進数の両方の識別子を使ってチェンジセットを識別している、 +ということを思い出してください。 + +\begin{itemize} +\item 10 進数の識別子(= リビジョン番号)が、 + \emph{当該リポジトリでのみ有効な値}である一方で、 + +\item 16 進数の識別子は、\emph{全ての}複製リポジトリに渡って、 + 厳密にチェンジセットを識別可能な\emph{恒久普遍の識別子}です。 + +\end{itemize} + +この区別は重要です。 +電子メールで他の人と``リビジョン~33''の話をした場合、 +相手のリビジョン~33は、 +自分の意図するそれとは高い確率で\emph{異なります}。 +これは、 +リビジョン番号の割り付けが、 +当該チェンジセットがリポジトリに認識された順序に依存しており、 +チェンジセットの認識順序が同一であることを、 +異なるリポジトリの間では保障できないためです。 +3つのチェンジセット $a,b,c$ が、 +とあるリポジトリでは $0,1,2$ の順序で認識される一方で、 +別なリポジトリでは $1,0,2$ の順序で認識される、 +といったことは容易に起こり得ます。 + +Mercurial がリビジョン番号を使用しているのは、 +純粋に記述簡略化の利便性のためです。 +他の人とチェンジセットに関して話をする場合や、 +何らかの理由(例えば、障害報告における記録) +によってチェンジセットに関する記録を残す場合は、 +16 進数の識別子を使いましょう。 + +\subsection{Viewing specific revisions} + +\hgcmd{log} の出力を単一のリビジョンのものに限定する場合、 +\hgopt{log}{-r}(ないし \hgopt{log}{--rev})オプションを使用します。 +10 進数のリビジョン番号と、 +16 進数のチェンジセット識別子のどちらも使用できますし、 +必要に応じて複数のリビジョンを指定することもできます。 + +\interaction{tour.log-r} + +個別に列挙すること無しに複数のリビジョンの履歴を参照したい場合は、 +\emph{範囲記法}を使用します。 +この記法は、 +``$a$ から $b$ の間の全てのリビジョン'' +という意図を表現します。 + +\interaction{tour.log.range} + +Mercurial はりビジョンの記述順序に忠実に振舞いますので、 +\hgcmdargs{log}{-r 2:4} というコマンド起動が +$2,3,4$ の順序で表示する一方、 +\hgcmdargs{log}{-r 4:2} というコマンド起動は +$4,3,2$ の順序で表示します。 + +\subsection{More detailed information} + +目当てのチェンジセットが既に判明している場合は +\hgcmd{log} が出力する概要情報は有用ですが、 +あるチェンジセットが目当てのものか否かを判定しようとする場合には、 +変更についての完全な説明文や、 +変更されたファイルの一覧が必要になることでしょう。 +\hgcmd{log} コマンドの +\hggopt{-v}(ないし \hggopt{--verbose})オプションは、 +これら追加の詳細情報を表示します。 + +\interaction{tour.log-v} + +説明文と変更内容の両方を見たい場合は、 +\hgopt{log}{-p} (ないし \hgopt{log}{--patch}) +オプションを付加してください。 +このオプションにより、 +変更内容が \emph{unified diff} 形式 +(これまでに unified diff 形式を見たことが無いのでしたら、 +\ref{sec:mq:patch}~節に概要の説明があります)で出力されます。 + +\interaction{tour.log-vp} + +\section{All about command options} + +Mercurial のコマンド探検をここで少々中断して、 +Mercurial コマンドの動作パターンについて説明しましょう。 +本章におけるツアーを続けるにつれて、 +このことを覚えておいて良かったと思うことでしょう。 + +Mercurial は、 +コマンドに対して指定可能なオプションの取り扱いに関して、 +近年の Linux および Unix システムに共通のオプション記述慣習を踏襲した、 +一貫した素直な扱い方を採用しています。 + +\begin{itemize} +\item 全てのオプションはロングネーム(long name)を持っています。 + 例えば、既に見てきたように、 + \hgcmd{log} コマンドは \hgopt{log}{--rev} オプションを受け付けます。 + +\item 殆どのオプションがショートネーム(short name)も持っています。 + \hgopt{log}{--rev} オプションの代わりに + \hgopt{log}{-r} を使用できます + (ショートネームを持たないオプションがあるのは、 + それらのオプションが滅多に利用されないためです\footnote{訳注: + 訳者のコマンド開発経験では、 + ショートネームの候補となるアルファベットが複数のオプションの間で重なる場合、 + あえてショートネームを設定しない、 + という場合もあります。})。 + +\item ロングネームオプションは2つのマイナス記号\footnote{訳注: + 原文では ``dash(es)'' ですが、 + 「ダッシュ(ダーシ)」や「ハイフン」よりも、 + PC における入力では直接的な、 + 「マイナス記号」を訳語に当てました。}で始まります + (例: \hgopt{log}{--rev})が + ショートネームオプションは1つのマイナス記号で始まります + (例: \hgopt{log}{-r})。 + +\item オプションの命名と用法は、コマンド間で一貫性が取られています\footnote{ + 訳注: 訳者が以前、オプションを追加するパッチを提案した際には、 + パッチの機能的な話とは別に、 + 「○○のコマンドでは××というう命名になっているから、それに倣ってね」 + と指摘されたことがあり、 + 「一貫性がとられている」との主張は伊達ではありません。}。 + 例えば、チェンジセット識別子やりビジョン番号を指定可能なコマンドは、 + 全て \hgopt{log}{-r} および \hgopt{log}{--rev} オプションを受理します。 + +\end{itemize} + +本書の実行例では、 +ロングネームオプションの代わりにショートネームオプションを使用します。 +これは単に筆者の好みというだけのことですので、 +特に気にする必要はありません。 + +何らかの表示を行うコマンドの多くは、 +\hggopt{-v}(ないし \hggopt{--verbose}) +オプションを付与することでより多くの情報の表示を、 +\hggopt{-q}(ないし \hggopt{--quiet}) +オプションを付与することで表示を抑止することができます。 + +\section{Making and reviewing changes} + +この時点で、Mercurial における履歴を把握できていますので、 +変更の実施や、その検証を行ってみましょう。 + +まず始めにすべきことは、 +独自の実験を元々のリポジトリから隔離することです。 +リポジトリの複製に、先程は \hgcmd{clone} を使用しましたが、 +この時点での遠隔リポジトリからの複製は必要ありません。 +既に手元にある複製リポジトリから複製すれば良いのです。 +ローカルリポジトリの複製は、 +ネットワーク越しの複製よりも非常に高速ですし、 +多くの場合においてディスク領域消費も少なくて済みます\footnote{訳注: +詳細は ``Avoiding seeks'' にありますが、 +Mercurial はローカルリポジトリの複製の際に、 +ディスクヘッドのシーク回避のために、 +ファイルの複製ではなく所謂``ハードリンク''を実施します。}。 + +\interaction{tour.reclone} + +話は逸れますが、 +作業に着手しようとした際に、 +作業用サンドボックスとしての一時的な複製を何時でも作成できますので、 +遠隔リポジトリの複製を``まっさらな''状態で保つように心掛けるのが良いでしょ。 +こうすることで、 +複数の作業を平行に行うことができますし、 +作業完了後にそれらを統合するまでは、 +互いの作業を隔離された状態にすることができます。 +ローカルリポジトリの複製は低コストですから、 +リポジトリの複製および破棄にはオーバヘッドが殆どありません。 + +\dirname{my-hello} リポジトリには、 +典型的な ``hello, world'' プログラムが格納された +\filename{hello.c} ファイルがあります。 +ではここで、 +いにしえの \command{sed} コマンドを使用して、 +2行目を出力するように変更してみましょう。 +(変更のために \command{sed} を使用するのは、 +単にスクリプトによる自動化が簡単であるからです。 +自動化の必要が無ければ、 +おそらく \command{sed} を使用する必要は無いでしょう。 +好みのエディタで編集をしてください。)。 + +\interaction{tour.sed} + +\hgcmd{status} コマンドにより、 +リポジトリ配下のファイルの状況に関する +Mercurial の認識が表示されます。 + +\interaction{tour.status} + +幾つかのファイルに対しては、 +\hgcmd{status} コマンドは特に何も表示しませんが、 +\filename{hello.c} に対しては +``\texttt{M}'' で始まる行を表示します。 +明示的に指定しない限り、 +変更されていないファイルに対して +\hgcmd{status} は何も表示しません。 + +``\texttt{M}'' 表示は、 +Mercurial が \filename{hello.c} ファイルの変更を検知していることを表します。 +ファイルの変更に先立って(あるいは変更の後に)、 +Mercurial に対して\emph{通知}する必要はありません。 +Mercurial 自身で変更の実施を検知することができます。 + +\hgcmd{status} の表示は、 +\filename{hello.c} を変更したことを知るのに役立ちますが、 +\emph{どのような}変更を行ったのかを厳密に知りたい場合も有るでしょう。 +変更内容を知るためには、 +\hgcmd{diff} コマンドを使用します。 + +\interaction{tour.diff} + +\section{Recording changes in a new changeset} + +変更内容に満足して、 +新規チェンジセットに変更内容を記録するに足る状況に到達するまでは、 +ファイルの内容を変更し、 +ビルドと変更内容に対する試験を行い、 +\hgcmd{status} および \hgcmd{diff} による変更内容を確認する、 +という作業を繰り返します。 + +\hgcmd{commit} コマンドを用いることで、 +チェンジセットを新たに作成することができます。 +通常これを``コミットの実施''(``making a commit'')ないし +``コミットする''(``committing'')と言います。 + +\subsection{Setting up a username} + +最初に \hgcmd{commit} 実行を行う際には、 +必ずしも実行が成功\footnote{訳注: +ここで言う``成功''とは、 +コマンド実行そのものの成功というよりは、 +``思った通りのチェンジセットを生成''することに対する成功に近いニュアンスです。 +}するとは限りません。 +チェンジセットのコミットの際に Mercurial は、 +コミットしたユーザの名前と電子メールアドレスを、 +チェンジセット毎に記録しますので、 +誰もが後からチェンジセット作成者を知ることができます。 +Mercurial は以下の手順で、 +変更内容と共に記録する妥当なユーザ名を自動的に検出しようとします。 + +\begin{enumerate} +\item \hgcmd{commit} コマンド起動の際に + \hgopt{commit}{-u} オプションによってユーザ名を指定した場合、 + 常にその値が優先的に使用されます。 + +\item 次に \envar{HGUSER} 環境変数設定の有無が確認されます。 + +\item ホームディレクトリ直下に、 + \rcitem{ui}{username} 要素を持つ + \sfilename{.hgrc}\footnote{訳注: + Windows 向けバイナリ版の場合、 + \envar{HOME} 環境変数が指すディレクトリ、 + ないし \dirname{C:\\Documents and Settings\\USERNAME} + 配下の \sfilename{Mercurial.ini} が用いられます。} + がある場合、その値が使用されます。 + このファイルに書くべき内容に関しては、 + \ref{sec:tour-basic:username}節を参照してください。 + +\item \envar{EMAIL} 環境変数が設定されている場合は、 + その値が使用されます。 + +\item それ以外の場合、 + Mercurial は稼動しているシステムにユーザとホストの名前を問い合わせた上で、 + 電子メールアドレス形式のユーザ名を生成し、これを使用します。 + この方法で生成されたユーザ名は往々にして役に立たないため、 + Mercurial は警告を表示します。 + +\end{enumerate} + +上記の方法が全て失敗した場合、 +Mercurial によるコミットは失敗し、 +エラーメッセージを表示します。 +そのような場合では、明示的にユーザ名を指定しない限り、 +コミットは成功しないでしょう。 + +\envar{HGUSER} 環境変数と +\hgcmd{commit} コマンドへの +\hgopt{commit}{-u} オプション指定は、 +Mercurial 設定ファイル中の username 設定を +\emph{無効にする}点に注意してください。 +通常の使用において、 +自身のユーザ名を簡単且つ確実に指定するには、 +\sfilename{.hgrc} ファイルで指定するのが良いでしょ。 +記述方法に関する詳細は後述します。 + +\subsubsection{Creating a Mercurial configuration file} +\label{sec:tour-basic:username} + +ユーザ名を設定するには、 +まずは好みのエディタを使って、 +ホームディレクトリ直下に +\sfilename{.hgrc}ファイルを作成します。 +Mercurial はこのファイルから利用者の個人設定を参照します。 +\sfilename{.hgrc} の内容は、 +まずは以下のようになるでしょう。 + +\begin{codesample2} + # This is a Mercurial configuration file. + [ui] + username = Firstname Lastname +\end{codesample2} + +``\texttt{[ui]}'' 行は、 +設定ファイルの\emph{セクション}開始を意味し、 +``\texttt{username = ...}'' という記述行は +``\texttt{ui} セクションにおける \texttt{username} 項目への値の設定'' +とみなされます。 +一度セクションが開始されたなら、 +新たなセクションが開始されるか、 +ファイルの末尾に達するまで当該セクションが続きます。 +空の行と、 +``\texttt{\#}'' の次の文字から行末までは、 +Mercurial によってコメントとみなされ無視されます。 + +\subsubsection{Choosing a user name} + +\texttt{username} 設定項目は、 +Mercurial に与える値ではありますが、 +リポジトリを参照する他の利用者のための情報ですので、 +任意の文字を使用可能です。 +殆どの利用者は、 +名前と電子メールアドレスを用いた前述のような形式を用いています。 + +\begin{note} + Mercurial の組み込みウェブサーバ機能では、 + スパムメールの送付者が利用する電子メールアドレス自動収集ツールに対して、 + 電子メールアドレスを難読化することが可能です。 + この機能を用いることで、 + Mercurial リポジトリをウェブ上に公開した際に、 + 益体も無いメール受信の増加を抑止することができます。 +\end{note} + +\subsection{Writing a commit message} + +当該チェンジセットでの変更内容を説明するメッセージを入力するために、 +Mercurial はコミットの際にエディタを起動します。 +このメッセージを\emph{コミットメッセージ}と呼び、 +読み手に変更の内容と理由を伝えるために記録されるもので、 +コミット後の \hgcmd{log} コマンドにより表示されます。 + +\interaction{tour.commit} + +\hgcmd{commit} コマンドが起動するエディタは、 +``\texttt{HG:}''で始まる数行が後に続く空行を表示していることでしょう。 + +\begin{codesample2} + \emph{空行} + HG: changed hello.c +\end{codesample2} + +Mercurial は +``\texttt{HG:}'' で始まる行を無視します。 +これらの行は、 +チェンジセットへの変更記録対象となるファイルの一覧を、 +コミットしようとしているユーザに知らせるためだけのものです。 +そのため、これらの行の変更や削除は何も意味を持ちません。 + +\subsection{Writing a good commit message} + +\hgcmd{log} はコミットメッセージの最初の1行しか表示しませんので、 +最初の1行だけで意味の通じる内容にするのが良いでしょう。 +この方針から\emph{外れている}ために、 +読み難いコミットメッセージの実例を以下に示します。 + +\begin{codesample2} + changeset: 73:584af0e231be + user: Censored Person + date: Tue Sep 26 21:37:07 2006 -0700 + summary: include buildmeister/commondefs. Add an exports and install +\end{codesample2} + +コミットメッセージの2行目以降に関しては、 +特に厳密なルールは存在しません。 +コミットメッセージに対して、 +プロジェクト運用上の方針として何らかの形式を要求するかもしれませんが、 +Mercurial 自身が解釈や忖度をすることはありません。 + +筆者の個人的な好みは、 +\hgcmdargs{log}{--patch} を一瞥しただけでは判断できない事柄について、 +簡潔でありながら有益な情報をもたらすようなコミットメッセージです。 + +\subsection{Aborting a commit} + +コミットメッセージの記述中にコミットを取りやめを決意した場合には、 +編集中のファイルを保存せずにエディタを終了すれば良いのです。 +この場合、リポジトリと作業領域ディレクトリのいずれに対しても、 +何ら操作は加えられません。 + +引数無しで \hgcmd{commit} コマンドを実行した場合、 +\hgcmd{status} および \hgcmd{diff} +によって報告された全ての変更内容が記録されます。 + +\subsection{Admiring our new handiwork} + +コミットが完了したなら、 +今しがた新規作成したチェンジセットを +\hgcmd{tip} コマンドで表示することができます。 +このコマンドは \hgcmd{log} と同一の出力を行いますが、 +表示されるのはリポジトリにおける最新のリビジョンだけです。 + +\interaction{tour.tip} + +リポジトリにおける最新のリビジョンを tip リビジョン、 +あるいは単に tip と呼びます。 + +\section{Sharing changes} + +先の記述で、Mercurial におけるリポジトリは、 +それ自身で完結している旨述べました。 +これは即ち、 +たった今新規に作成したチェンジセットは、 +手元の \dirname{my-hello} リポジトリにしか存在しないことを意味します。 +この変更内容を他のリポジトリへと伝播する方法を、 +順に見てゆきましょう。 + +\subsection{Pulling changes from another repository} +\label{sec:tour:pull} + +まず始めに、 +元々の \dirname{hello} リポジトリを複製して、 +たった今新規に作成した変更のコミットされていないリポジトリを作成しましょう。 +この複製したリポジトリを、 +\dirname{hello-pull} と呼びます。 + +\interaction{tour.clone-pull} + +\hgcmd{pull} コマンドにより、 +\dirname{my-hello} から +\dirname{hello-pull} へと変更を取り込みます。 +しかしながら、未知の変更を闇雲にリポジトリに取り込むのは、 +あまりぞっとしません。 +Mercurial が提供する \hgcmd{incoming} コマンドは、 +実際に変更を取り込む事無く、 +\hgcmd{pull} +によってリポジトリに取り込まれる\emph{予定}のチェンジセットを表示します。 + +\interaction{tour.incoming} + +(勿論、 +\hgcmd{incoming} コマンドを実行したリポジトリに対して、 +\hgcmd{pull} による変更取り込みの機会よりも前に、 +より多くの変更を追加することは可能ですので、 +実際の変更取り込みは予定とは異なる可能性が有ります。) + +リポジトリへの変更の取り込みは、 +どのリポジトリから取り込むかを指示しつつ、 +\hgcmd{pull} コマンドを実行するという簡単なものです。 + +\interaction{tour.pull} + +実施前後の \hgcmd{tip} 出力から見て取れるように、 +手元のリポジトリへの変更内容の反映が成功しています。 +取り込んだ変更内容を作業領域ディレクトリにおいて参照するためには、 +もうひと手順必要です。 + +\subsection{Updating the working directory} + +リポジトリと作業領域ディレクトリの関係について、 +これまでは大雑把にしか説明してきませんでした。 +\ref{sec:tour:pull}~節で実行した +\hgcmd{pull} コマンドは、 +リポジトリへの変更の取り込みを行いますが、 +確認してみればわかるように、 +作業領域には何ら影響を及ぼしません。 +これは、 +\hgcmd{pull} の(基底の)挙動が、 +作業領域に影響を及ぼさないものであるためです。 +作業領域の更新には、 +\hgcmd{pull} ではなく \hgcmd{update} を用います。 + +\interaction{tour.update} + +\hgcmd{pull} 実行時に作業領域を自動的に更新しないことは、 +一見奇異に見えるかもしれませんが、 +実はそれには理由が有ります。 +\hgcmd{update} を用いることで、 +リポジトリに記録された\emph{任意の版}の状態へと、 +作業領域ディレクトリの内容を更新することができます。 +作業領域ディレクトリを +---例えば、バグの原因調査などのために--- +古い版にして作業していた場合などは、 +\hgcmd{pull} 実行が作業領域ディレクトリを最新の版に自動的に更新してしまうのは、 +あまりよろしくないでしょう。 + +しかし、\hgcmd{pull} 〜 \hgcmd{update} という流れは非常に頻繁な作業ですから、 +\hgcmd{pull} に \hgopt{pull}{-u} オプションを指定することで、 +Mercurial はこれら2つを組み合わせた機能を提供します。 + +\begin{codesample2} + hg pull -u +\end{codesample2} + +\ref{sec:tour:pull}~節での +\hgopt{pull}{-u} オプションを指定しない +\hgcmd{pull} 実行の出力には、 +作業領域ディレクトリの更新に明示的な手順が必要であることを示す、 +注意喚起のメッセージが表示されているのが見て取れます。 + +\begin{codesample2} + (run 'hg update' to get a working copy) +\end{codesample2} + +作業領域ディレクトリがどの版の内容に基づいているかを見るには、 +\hgcmd{parents} コマンドを使用します。 + +\interaction{tour.parents} + +図~\ref{fig:tour-basic:history} では、 +個々のチェンジセットを繋ぐ矢印が描かれています。 +矢印の\emph{根元}にあたるチェンジセットが親を、 +そして矢印の\emph{先}にあたるチェンジセットが子を表しています。 +同じように、作業領域ディレクトリも親を持っており、 +現時点で保持している作業領域ディレクトリの内容は、 +そのチェンジセットに基づいたものです。 + +作業領域ディレクトリの内容を特定の版のものにする場合、 +\hgcmd{update} コマンドにリビジョン番号ないしチェンジセット~IDを指定します。 + +\interaction{tour.older} + +明示的な版指定をしなかった場合、 +上記の例における2つ目の \hgcmd{update} 実行で見て取れるように、 +\hgcmd{update} は tip が指定されたものとして振舞います。 + +\subsection{Pushing changes to another repository} + +Mercurial では、 +現在作業を行っているリポジトリから他のリポジトリへの、 +変更内容の反映が可能です。 +先に示した \hgcmd{pull} の例と同様に、 +まずは変更反映先とするための一時的なリポジトリを作成します。 + +\interaction{tour.clone-push} + +\hgcmd{outgoing} コマンドは、 +他のリポジトリへの反映対象となるチェンジセットを一覧表示します。 + +\interaction{tour.outgoing} + +そして \hgcmd{push} コマンドが実際の反映作業を行います。 + +\interaction{tour.push} + +\hgcmd{pull} と同様に、 +\hgcmd{push} コマンドは変更反映先のリポジトリ側において、 +作業領域ディレクトリの更新は行いません +(\hgcmd{pull} と違い、 +\hgcmd{push} は変更反映先のリポジトリ側での作業領域ディレクトリを更新する +\texttt{-u} オプションを持ちません)。 + +当該リポジトリが既に相当するチェンジセットを持っている場合、 +変更の取り込みあるいは反映を行うとどうなるのでしょう? +驚くようなことは何も起こりません。 + +\interaction{tour.push.nothing} + +\subsection{Sharing changes over a network} + +先の幾つかの節で触れたコマンドの利用は、 +手元にあるリポジトリにのみ限定されているわけではありません。 +全く同様の形式で、 +ネットワーク接続経由でも機能します。 +ローカルファイルシステムのパスの代わりに、 +URL を指定すれば良いのです。 + +\interaction{tour.outgoing.net} + +この例では、 +遠隔リポジトリに対して反映可能な変更の一覧を見ることができますが、 +このリポジトリは匿名での変更反映を許すようには当然ですが設定されていません。 + +\interaction{tour.push.net} + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/tour-history.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/tour-history.svg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + 0: 0a04 + + 1: 82e5 + + 2: 057d + + 3: ff5d + + 4: b57f + + + + + + (newest) + (oldest) + + 4: b57f + + + revisionnumber + changesetidentifier + + diff -r a24b370a16ee -r d6ca1334a19d ja/tour-merge-conflict.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/tour-merge-conflict.svg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,210 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + Greetings!I am Mariam Abacha, the wife of former Nigerian dictator Sani Abacha. I am contacting you in confidence, and as a means of developing + + + + + + Greetings!I am Shehu Musa Abacha, cousin to the former Nigerian dictator Sani Abacha. I am contacting you in confidence, and as a means of developing + + + + + + Greetings!I am Alhaji Abba Abacha, son of the former Nigerian dictator Sani Abacha. I am contacting you in confidence, and as a means of developing + + + Base version + Our changes + Their changes + + diff -r a24b370a16ee -r d6ca1334a19d ja/tour-merge-merge.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/tour-merge-merge.svg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,380 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + 4: b57f + + + 5: ae13 + + + 6: d2b5 + + tip (and head) + head + + + + merge + working directoryduring merge + + 4: b57f + + + 5: ae13 + + + 6: d2b5 + + tip + + + 7: dba3 + Working directory during merge + Repository after merge committed + + diff -r a24b370a16ee -r d6ca1334a19d ja/tour-merge-pull.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/tour-merge-pull.svg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + 0: 0a04 + + 1: 82e5 + + 2: 057d + + 3: ff5d + + 4: b57f + + + + + + 5: ae13 + + + 6: d2b5 + + tip (and head) + head + + diff -r a24b370a16ee -r d6ca1334a19d ja/tour-merge-sep-repos.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/tour-merge-sep-repos.svg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,466 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + 0: 0a04 + + 1: 82e5 + + 2: 057d + + 3: ff5d + + 4: b57f + + + + + + 5: ae13 + + my-hello + + 0: 0a04 + + 1: 82e5 + + 2: 057d + + 3: ff5d + + 4: b57f + + + + + + 5: d2b5 + + my-new-hello + newest changesdiffer + common history + + + + + + + head revision(has no children) + + diff -r a24b370a16ee -r d6ca1334a19d ja/tour-merge.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/tour-merge.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,398 @@ +\chapter{A tour of Mercurial: merging work} +\label{chap:tour-merge} + +前章においては、 +リポジトリの複製、 +リポジトリでのチェンジセットの生成、 +ならびに \hgcmd{push} および \hgcmd{pull} +によるリポジトリ間でのチェンジセットの授受を見てきました。 +次の段階として、別々のリポジトリにおける変更の\emph{マージ} +(merge)について見てみましょう。 + +\section{Merging streams of work} + +分散構成管理ツールにおいて、マージは作業の基本です。 + +\begin{itemize} +\item Alice と Bob が、 + 共同作業しているプロジェクトのリポジトリから複製した、 + 個人的なリポジトリを持っているものとします。 + Alice は自分のリポジトリにおいてバグを修正しました。 + Bob は自分のリポジトリにおいて機能を追加しました。 + 二人は、 + バグフィックスと新機能の両方を含むリポジトリを共有したいと思うでしょう。 + +\item 筆者は、 + 個別のリポジトリによって、 + お互いが安全に隔離された複数の異なる作業を、 + 同一プロジェクトにおいて同時に実施することが頻繁にあります。 + この形式での作業では、 + あるリポジトリにおける成果を、 + 他のリポジトリに対して頻繁にマージする必要があります。 + +\end{itemize} + +マージは必要に応じて実施するありふれた作業ですので、 +Mercurial では簡単に行えるようになっています。 +それでは、マージ手順を見て行きましょう。 +もう一度リポジトリの複製を行い(もう何度も複製しましたよね?)、 +そのリポジトリにおいて変更を行います。 + +\interaction{tour.merge.clone} + +この時点で、 +内容の異なる2つの \filename{hello.c} のコピーが存在するはずです。 +2つのリポジトリの履歴は、 +図~\ref{fig:tour-merge:sep-repos} に示すように、 +枝分かれしています。 + +\interaction{tour.merge.cat} + +\begin{figure}[ht] + \centering + \grafix{tour-merge-sep-repos} + \caption{Divergent recent histories of the \dirname{my-hello} and + \dirname{my-new-hello} repositories} + \label{fig:tour-merge:sep-repos} +\end{figure} + +\hgcmd{pull} を行っても、 +作業領域ディレクトリには影響を及ぼさないことは既に説明したとおりですので、 +\dirname{my-hello} から \hgcmd{pull} してみましょう。 + +\interaction{tour.merge.pull} + +作業領域ディレクトリには影響を及ぼしていませんが、 +\hgcmd{pull} コマンドは ``heads'' について何か警告しています。 + +\subsection{Head changesets} + +``head'' とは、 +リポジトリ中において、 +子孫(ないし子供)となるチェンジセットが存在しないチェンジセットのことです。 +リポジトリにおける最も最新のリビジョンは、 +一切の子チェンジセットを持ちませんから、 +従って tip リビジョンは head となりますが、 +1つのリポジトリには複数の head が存在しえます。 + +\begin{figure}[ht] + \centering + \grafix{tour-merge-pull} + \caption{Repository contents after pulling from \dirname{my-hello} into + \dirname{my-new-hello}} + \label{fig:tour-merge:pull} +\end{figure} + +\dirname{my-hello} から \dirname{my-new-hello} への +\hgcmd{pull} による影響を、 +図~\ref{fig:tour-merge:pull} で見ることができます。 +既に \dirname{my-new-hello} に存在していた履歴には手が付けられていませんが、 +新しいリビジョンが追加されています。 +図~\ref{fig:tour-merge:pull} からは、 +新しいリポジトリ(\dirname{my-new-hello})において、 +\emph{チェンジセット識別子}は同じままでも、 +\emph{リビジョン番号}が異なる様が読み取れます +(そして、図らずも、チェンジセットについて話をする際に、 +リビジョン番号を使用するのが良くない、という好例になっています)。 +\hgcmd{heads} コマンドにより、 +リポジトリの head を見ることができます。 + +\interaction{tour.merge.heads} + +\subsection{Performing the merge} + +作業領域ディレクトリを、 +(\dirname{my-hello} から取り込んだ)新たな tip リビジョンに更新するために、 +いつものように \hgcmd{update} コマンドを実行すると、 +どうなるでしょう? + +\interaction{tour.merge.update} + +Mercurial から、 +\hgcmd{update} コマンドではマージが行われない旨が通達されます。 +マージの実施が必要と思われる場合、 +強制的な実行をしない限りは +\hgcmd{update} コマンドによる作業領域ディレクトリの更新は行われません。 +\hgcmd{update} コマンドの代わりに、 +\hgcmd{merge} コマンドを用いて2つの head をマージします。 + +\interaction{tour.merge.merge} + +\begin{figure}[ht] + \centering + \grafix{tour-merge-merge} + \caption{Working directory and repository during merge, and + following commit} + \label{fig:tour-merge:merge} +\end{figure} + +\hgcmd{merge} コマンドによって、 +\hgcmd{parents} コマンドの出力、 +および \filename{hello.c} の内容の変更という形で、 +\emph{両方}の head の変更内容が作業領域ディレクトリに反映されます。 + +\interaction{tour.merge.parents} + +\subsection{Committing the results of the merge} + +結果を \hgcmd{commit} するまでは、 +\hgcmd{parents} はマージの際には常に2つの親(チェンジセット)を表示します。 + +\interaction{tour.merge.commit} + +これで、新しい tip リビジョンが作成されました。 +先述した2つの head の\emph{両方}を親に持つ点に注意してください。 +これらは、先に \hgcmd{parents} で表示したリビジョンと一致します。 + +\interaction{tour.merge.tip} + +作業領域ディレクトリがマージの際にどのようになっているのか、 +そしてコミットによってどのようにリポジトリに作用するのかを、 +図~\ref{fig:tour-merge:merge} から読み取ることができます。 +マージの際に作業領域ディレクトリの親であった2つのチェンジセットは、 +コミットの際には新たなチェンジセットにとっての親チェンジセットとなります。 + +\section{Merging conflicting changes} + +殆どのマージ作業は簡単に済みますが、 +時にはマージ対象のチェンジセット同士が、 +同じファイルの同じ部位を変更している場合があります。 +両者の変更内容が同一で無ければ、 +マージは\emph{衝突}(conflict)を生じるため、 +両者の異なる変更内容を両立させて +何らかの一貫性の取れた状態にするための決断が必要です。 + +\begin{figure}[ht] + \centering + \grafix{tour-merge-conflict} + \caption{Conflicting changes to a document} + \label{fig:tour-merge:conflict} +\end{figure} + +文書に対する2つの変更の衝突の例を、 +図~\ref{fig:tour-merge:conflict} が図示しています。 +両者はファイルの同じ版を元にしていますが、 +一方が変更を行う傍ら、 +他方が同じ段落に対して異なる変更をしてしまいます。 +変更の衝突を解消する作業とは、 +そのファイルがどのようになっているべきかを決定することに他なりません。 + +Mercurial には衝突を扱う機能が組み込まれていません。 +その代わりに、 +\command{hgmerge} と呼ばれる外部プログラムを実行します。 +このプログラムは、 +Mercurial に添付されるシェルスクリプト\footnote{訳注: +\command{/bin/sh} 向けだから、ということなのでしょうが、 +Windows のバイナリ版には添付されていません。}ですが、 +別なプログラムを起動させることもできます。 +\command{hgmerge} の基底動作では、 +幾つかの著名なマージツールのうち、 +稼働環境においてインストールされていると思われるものを探します。 +まず始めに、 +非対話的マージツール\footnote{訳注: +\command{diff3} や \command{merge} など}を実行してみますが、 +(人手によって解決する必要性があるために)それが失敗した場合や、 +そもそもそれらのツールが提供されていない場合、 +他のグラフィカルなマージツールの起動を試みます +\footnote{訳注: 例えば、 +\command{diff3} によるマージを行い、 +衝突が検出された場合はそのファイルごとに、 +\envar{EDITOR} 環境変数で定義されるエディタ(ないし \command{vi}) +を起動して、 +それぞれのチェンジセットに由来する変更の間での調停を要求してきます。 +}。 + +\envar{HGMERGE} +環境変数に起動対象プログラムないしスクリプト名を設定することで、 +Mercurial に \command{hgmerge} 以外を起動させる事もできます + +\subsection{Using a graphical merge tool} + +著者のお薦めのグラフィカルなマージツールは \command{kdiff3} なので、 +グラフィカルなファイルマージツールに求められる機能について、 +これを題材に説明しようと思います。 +作業中の画面イメージが図~\ref{fig:tour-merge:kdiff3}にあります。 +着目している1つのファイルに対して、 +3つの異なるリビジョンが存在することから、 +マージ方法は\emph{3方向マージ}(three-way merge)と呼ばれています。 +それゆえ、マージツールはウィンドウ上部を3つの区画に分割しています。 + +\begin{itemize} +\item 左端に表示されているのは、 + ファイルの\emph{元}(base)の版、 + つまりマージ対象としている2つの版にとって、 + 最も新しい分岐元となっている版です。 + +\item 中央に表示されているのは、 + マージ``先''の版\footnote{訳注: 原文では「``our'' version」}ですので、 + 作業領域ディレクトリにおける変更内容が表示されます。 + +\item 右端に表示されているのは、 + マージ``元''\footnote{訳注: 原文では「``their'' version」}ですので、 + マージしようとしているチェンジセットに由来する内容が表示されます。 + +\end{itemize} + +これらの区画の下方に表示されているのは、 +現時点でのマージ\emph{結果}です。 +マージにおける作業とは、 +画面上に赤字で表示された\footnote{訳注: +\command{diff3} が行単位での衝突表示であるのに比べて、 +GUI である利点が生きています。}、 +慎重なファイルのマージが必要とされる未解決の衝突を、 +妥当な内容で置き換えることです。 + +これら4つの区画は\emph{互いに固定}されているので、 +いずれかの区画をスクロールさせた場合には、 +他の区画も相応の場所を表示するように更新されます。 + +\begin{figure}[ht] + \centering + \grafixL{kdiff3} + \label{fig:tour-merge:kdiff3} + \caption{Using \command{kdiff3} to merge versions of a file} +\end{figure} + +ファイル中の個々の衝突箇所において、 +衝突を解消するために、 +元版/マージ先版/マージ元版のテキストを +(それらの組み合わせも含めて)任意に選択することができます。 +また、更なる変更を行うために、 +マージ結果を直接手で入力することもできます。 + +ここで紹介し切れないほど\emph{多くの}ファイルマージツールが存在します。 +これらはそれぞれ、稼動可能プラットホームや、 +特徴的な得手不得手などの点で異なります。 +殆どのツールはテキストファイルのマージに特化していますが、 +中には特定のファイルフォーマット(一般には XML) +に特化したものもあります。 + +\subsection{A worked example} + +本節での例では、 +前述の図~\ref{fig:tour-merge:conflict} +におけるファイル更新の履歴を再現します。 +元となる版のファイルを格納したリポジトリを作成することから始めましょう +\footnote{訳注: 実行例では、 +新規のリポジトリである \dirname{scam} の \hgcmd{init} が抜けています。}。 + +\interaction{tour-merge-conflict.wife} + +次に、リポジトリを複製し、ファイルを変更します。 + +\interaction{tour-merge-conflict.cousin} + +もう一つリポジトリを複製し、 +他の利用者によるファイルへの変更を模擬的に再現します +(この模擬的な実行は、 +タスクごとに隔離したリポジトリの間でのマージどころか、 +それらのマージの際の衝突を解消することですら、 +決して珍しいことではない、 +ということを暗示しています)。 + +\interaction{tour-merge-conflict.son} + +同一ファイルに2つの異なる版ができたので、 +マージ実施の環境が整いました。 + +\interaction{tour-merge-conflict.pull} + +マージにおける対話的な処理の部分が、 +本書における実行例の自動実行機構~ref{sec:automated-example-running +}を損ねるため、 +この例では Mercurial の \command{hgmerge} を使用しません。 +その代わりに、 +\envar{HGMERGE} を設定することで、 +Mercurial に非対話的な \command{merge} コマンドを実行させます。 +このコマンドは多くの Unix 的なシステムに同梱されています。 +以下の例を実際に試す際には、 +\envar{HGMERGE} をわざわざ設定する必要はありません。 + +\interaction{tour-merge-conflict.merge} + +\command{merge} コマンドは衝突を解消せずに、 +どの行における変更が衝突していて、 +その変更がどのチェンジセットに由来するのかを示す\emph{マージマーク}を、 +衝突が検出されたファイルに書き込みます。 + +Mercurial は、 +\command{merge} の終了コードがマージ処理\footnote{訳注: +より正確には「マージにおける衝突の解消」}失敗を示す場合、 +マージ処理を再実行する手順を表示します。 +ここで提示される手順は、 +マージ作業の途中で混乱してしまったり、 +間違ってしまったことに気付いて、 +グラフィカルなマージツールを中途終了させた場合などに役立ちます。 + +自動ないし手動のマージが失敗した場合であっても、 +関連の有るファイルを直接``修正''した上で、 +マージ結果をコミットすることも可能です。 + +\interaction{tour-merge-conflict.commit} + +\section{Simplifying the pull-merge-commit sequence} +\label{sec:tour-merge:fetch} + +ここまでに述べてきた変更マージの手順は単純なものですが、 +3つのコマンドを順に実行する必要があります。 + +\begin{codesample2} + hg pull + hg merge + hg commit -m 'Merged remote changes' +\end{codesample2} + +最後のコミットの際には、 +通常は面白くも無い``決まりきった''内容にならざるを得ませんが、 +コミットメッセージを入力する必要があります。 + +可能であれば、必要とされる手順を低減させたいものです。 +実際に Mercurial は、これを可能とする \hgext{fetch} +と呼ばれるイクステンションが同梱されています。 + +Mercurial は、 +取り扱いの利便性上から中核機能を小さく簡潔に保つ一方で、 +機能追加を可能にするための柔軟な拡張(イクステンション)機構を提供しています。 +コマンドラインから利用できる +Mercurial コマンドを追加するイクステンションもあれば、 +例えばサーバ機能を拡張するような、 +``舞台裏''で機能するイクステンションもあります。 + +\hgext{fetch} イクステンションは、 +予想したこととは思いますが、 +\hgcmd{fetch} と呼ばれる新しいコマンドを追加します。 +\hgcmd{fetch} コマンドは、 +\hgcmd{pull}/\hgcmd{update}/\hgcmd{merge}/\hgcmd{commit} +の組み合わせのように振舞います。 +まずは他のリポジトリから作業中のリポジトリへ変更を取り込みます。 +取り込んだチェンジセットによる新たな head の追加が検知\footnote{訳注: +他のリポジトリからの取り込みにより、 +3つ以上の head がリポジトリに存在するようになった場合は、 +マージ対象の特定ができないため、 +取り込みのみで処理を中断します。}された場合、 +マージを開始し、 +自動的に生成されたコミットメッセージを使ってコミットを行います。 +新たな head の追加が無かった場合、 +\hgcmd{fetch} コマンドは作業領域ディレクトリを +tip リビジョンで更新します。 + +\hgext{fetch} イクステンションは簡単に有効化できます。 +\sfilename{.hgrc} ファイルを編集し、 +\rcsection{extensions} セクション +(無い場合は作成してください)に移動し、 +``\Verb+fetch +'' で始まる行を追加します。 + +\begin{codesample2} + [extensions] + fetch = +\end{codesample2} + +(通常は、 +``\texttt{=}'' の右辺にイクステンションの位置を指定しますが、 +\hgext{fetch} イクステンションは標準の配布物に同梱されているので、 +Mercurial は \hgext{fetch} を探し出すことができます) + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/undo-manual-merge.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/undo-manual-merge.dot Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,8 @@ +digraph undo_manual { + "first change" -> "second change"; + "second change" -> "third change"; + backout [label="back out\nsecond change", shape=box]; + "second change" -> backout; + "third change" -> "manual\nmerge"; + backout -> "manual\nmerge"; +} diff -r a24b370a16ee -r d6ca1334a19d ja/undo-manual.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/undo-manual.dot Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,6 @@ +digraph undo_manual { + "first change" -> "second change"; + "second change" -> "third change"; + backout [label="back out\nsecond change", shape=box]; + "second change" -> backout; +} diff -r a24b370a16ee -r d6ca1334a19d ja/undo-non-tip.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/undo-non-tip.dot Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,9 @@ +digraph undo_non_tip { + "first change" -> "second change"; + "second change" -> "third change"; + backout [label="back out\nsecond change", shape=box]; + "second change" -> backout; + merge [label="automated\nmerge", shape=box]; + "third change" -> merge; + backout -> merge; +} diff -r a24b370a16ee -r d6ca1334a19d ja/undo-simple.dot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/undo-simple.dot Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,4 @@ +digraph undo_simple { + "first change" -> "second change"; + "second change" -> "back out\nsecond change"; +} diff -r a24b370a16ee -r d6ca1334a19d ja/undo.tex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/undo.tex Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,1051 @@ +\chapter{Finding and fixing your mistakes} +\label{chap:undo} + +人は間違えるものですが、その結果をより上手に扱ってこそ、 +優れた構成管理システムと言えます。 +この章では、 +プロジェクトに忍び込んだ問題を発見した際に、 +使える手法について説明します。 +Mercurial は、 +問題の元を隔離し適切に処理するための優れた機能を持っています。 + +\section{Erasing local history} + +\subsection{The accidental commit} + +筆者は、時として考えるよりも先に入力してしまう、 +という根深い問題を抱えているため、 +不完全であったり、 +単純に間違った内容のチェンジセットをコミットしてしまうことがあります。 +筆者の場合、 +不完全なチェンジセットをコミットしてしまうのは、 +新しいソースファイルを作成したのに +\hgcmd{add} の実行を忘れている場合が殆どです。 +``単純に間違っている''チェンジセットをコミットしてしまうケースには、 +特に共通点はありませんが、but 非常に迷惑(no less annoying) XXXXX。 + +\subsection{Rolling back a transaction} +\label{sec:undo:rollback} + +Mercurial が、 +リポジトリへの個々の変更を\emph{トランザクション}として扱っていることを +\ref{sec:concepts:txn} 節で述べました。 +チェンジセットをコミットしたり、 +他のリポジトリから変更を pull する際に、 +Mercurial は常に処理したことを記録しています。 +\hgcmd{rollback} コマンドを使用することで、 +きっちり一回分の処理を元に戻す、 +別な言い方をするなら、\emph{巻き戻す}ことができます +(このコマンドを使用する際の重要な注意が述べられていますので、 +\ref{sec:undo:rollback-after-push} 節を参照してください)。 + +新しくファイルを作成したのに、 +そのファイルに対して \hgcmd{add} +コマンドを実行するのを忘れてコミットしてしまう、 +という筆者のよくやる間違いは、以下のようなものです。 + +\interaction{rollback.commit} + +コミット後の \hgcmd{status} 出力を見れば、 +すぐさま間違いを確証できます。 + +\interaction{rollback.status} + +先のコミットは、 +\filename{a} の変更は捉えていますが、 +新規のファイル \filename{b} は把握していません。 +同僚と共有しているリポジトリに、 +このチェンジセットを反映してしまったら、 +同僚がこのチェンジセットを取り込んだ際に、 +\filename{a} 中の何かが、 +同僚のリポジトリには存在しない \filename{b} を参照してしまいます。 +そうなれば、私は同僚の憤りの対象になってしまうでしょう。 + +しかし、幸いなことに、 +チェンジセットを共有リポジトリへと反映する前に、 +自分の間違いを見つけています。 +\hgcmd{rollback} コマンドを使うことで、 +Mercurial は最後のチェンジセットを消してくれます。 + +\interaction{rollback.rollback} + +リポジトリの履歴上、最早最前のチェンジセットは存在しませんので、 +作業領域ディレクトリは、 +再び \filename{a} ファイルが変更されている状態だとみなされます。 +コミット後のロールバックは、 +作業領域ディレクトリをコミット前の状態そのままに戻し、 +チェンジセットは完全に消去されます。 +そうなったなら、 +安全に \filename{b} ファイルを \hgcmd{add} し、 +再度コミットすることができます。 + +\interaction{rollback.add} + +\subsection{The erroneous pull} + +1つの プロジェクトで、 +別々に開発の進んでいるブランチを Mercurial で保守する場合、 +それぞれ異なるリポジトリで保守することが一般的な慣習となっています。 +開発チームは、 +プロジェクトの ``0.9'' リリース用に共有リポジトリを持つ一方で、 +異なる変更履歴を持つ ``1.0'' リリース用のリポジトリを別途持つかもしれません。 + +この場合、 +ローカルな ``0.9'' リポジトリがあって、 +そこに偶然 ``1.0'' 用共有リポジトリの成果を取り込んだ場合、 +面倒な事態になることが想像できます。 +最悪の場合、 +十分な注意を払わないために、 +``1.0'' のリポジトリから取り込んだ変更を +``0.9'' の共用リポジトリへと反映してしまった +チーム全体を混乱させてしまうでしょう +(この恐ろしいケースに関しては、 +後ほど解決方法を示しますので御安心を。)。 +しかし、 +Mercurial は成果取り込み先の URL を表示するか、 +Mercurial +が怪しげな大量の変更をリポジトリに取り込んだことが表示されますから、 +すぐに気付く方があり得ます +\footnote{訳注: ``display the URL it's pulling from'' +の関係がよくわからない}。 + +\hgcmd{rollback} コマンドは、 +今まさに取り込んだ全てのチェンジセットを、 +きちんと綺麗にします。 +Mercurial は、一回の \hgcmd{pull} 起動により取り込まれるチェンジセット全体を、 +単一のトランザクションに分類するので、 +一回の \hgcmd{rollback} 起動でこの失敗を取り消すことができます。 + +\subsection{Rolling back is useless once you've pushed} +\label{sec:undo:rollback-after-push} + +\hgcmd{rollback} は、 +一旦他のリポジトリに反映した変更でも、 +(手元のリポジトリにおいては)無かったことにできます。 +取り消しにより変更は完全に消されますが、 +それができるのは、 +\hgcmd{rollback} を実施したリポジトリにおける取り消し\emph{のみ}です。 +取り消しは履歴を削除しますので、 +変更の取り消しをリポジトリ間で伝播する手段が無いのです。 + +変更を他のリポジトリ--典型的な例では共有リポジトリ--に反映した場合、 +本質的には、その変更は``野生に逃げ出し''ており、 +取り消しとは別な方法で間違いを埋め合わせる必要があります。 +変更を他のリポジトリに反映し、 +(手元のリポジトリで)その変更を取り消した後で、 +変更を反映したリポジトリから変更を取り込んだ時には、 +取り消した変更が(手元のリポジトリに)再び現れます。 + +(取り消したい変更が、変更を反映したリポジトリにおける最新のもので、 +\emph{且つ}、 +誰もそれをそのリポジトリから取り込んでいないことが確実である場合、 +その変更を取り消すこともできますが、 +取り消しが機能することには依存しないようにしてください。 +遅かれ早かれ変更は直接触ることのできない +(あるいは存在を忘れていた)リポジトリへと反映され、 +回りまわって戻ってきた時に噛み付かれてしまいます。 +) + +\subsection{You can only roll back once} + +Mercurial は、 +当該リポジトリにおける最も最新のトランザクションを、 +1つだけトランザクションログに記録します。 +そのため、取り消せるトランザクションは1つ分だけです。 +トランザクションを1つ取り消した後で、 +その前のトランザクションも取り消せることを期待しても、 +期待通りの結果は得られません。 + +\interaction{rollback.twice} + +あるリポジトリでトランザクションの取り消しを行った場合、 +別な変更をコミットするなり取り込むなりしない限り、 +そのリポジトリで取り消しを行うことはできません。 + +\section{Reverting the mistaken change} + +ファイルを変更した後で、 +ファイルの変更が全く必要ないことに気付いた場合、 +変更をコミットする前であれば、 +\hgcmd{revert} コマンドが利用できます。 +このコマンドは、 +作業領域ディレクトリの親チェンジセットを参照し、 +ファイルの内容を元の状態に戻します。 +(説明すると長くなりますが、 +通常の場合、このコマンドは変更を取り消します。) + +\hgcmd{revert} コマンドの機能を、 +ちょっとしたサンプルで説明します。 +Mercurial により既に構成管理されているファイルを変更します。 + +\interaction{daily.revert.modify} + +変更が必要ない場合、 +単純に \hgcmd{revert} コマンドをファイルに適用します。 + +\interaction{daily.revert.unmodify} + +\hgcmd{revert} コマンドは、 +ある程度の安全性を確保するために、 +\filename{.orig} 拡張子付きのファイルに、 +変更されたファイルの内容を保存します。 + +\interaction{daily.revert.status} + +\hgcmd{revert} コマンドが扱うことのできる状況を以下にまとめます。 +個々の状況に関する詳細は、以後の節で説明します。 + +\begin{itemize} +\item ファイルが変更されていたなら、変更前の状態に戻します。 + +\item ファイルが \hgcmd{add} されていたなら、 + ファイルの``追加''されている状態を取り消しますが、 + ファイルそのものには何も変更を行いません。 + +\item Mercurial への指示無くファイルを削除していたなら、 + 変更前\footnote{訳注: ``削除前''ではない点に注意}の状態に戻します。 + +\item \hgcmd{remove} コマンドでファイルを削除していたなら、 + ファイルの``削除された''状態を取り消し、 + 変更前の状態に戻します。 + +\end{itemize} + +\subsection{File management errors} +\label{sec:undo:mgmt} + +\hgcmd{revert} は変更されたファイル以外に対しても有用なコマンドです。 +このコマンドは、 +Mercurial の全てのファイル管理コマンド +---\hgcmd{add} や \hgcmd{remove} など--- +の実施を反転させます。 + +ファイルに対して \hgcmd{add} を行った後で、 +そのファイルを Mercurial で構成管理する必要が無いことに気付いたなら、 +\hgcmd{revert} によりファイルの追加を取り消せます。 +Mercurial はファイル自体には何も変更を行いませんので安心してください。 +ファイル追加の取り消しは、 +ファイルに対して``印を消す''だけです。 + +\interaction{daily.revert.add} + +同様に、 +ファイルに対して \hgcmd{remove} を行った後でも、 +\hgcmd{revert} を使うことで、 +作業領域ディレクトリの親チェンジセットにおける状態に、 +ファイルの内容を復旧することができます。 + +\interaction{daily.revert.remove} + +これは、Mercurial を通さずに手動で削除したファイル +(Mercurial の用語ではこの種のファイルが``紛失''(missing) +と呼ばれることを思い出してください)であっても機能します。 + +\interaction{daily.revert.missing} + +\hgcmd{copy} されたファイルに取り消しを行った場合、 +複製先ファイルは作業領域ディレクトリに、 +構成管理されない状態でそのまま残ります。 +複製操作は複製元ファイルには何も作用しないので、 +取り消しの際に Mercurial は複製元ファイルに対して特に何もしません。 + +\interaction{daily.revert.copy} + +\subsubsection{A slightly special case: reverting a rename} + + +ファイルに対して \hgcmd{rename} を行った場合、 +覚えていて欲しいことがあります。 +\hgcmd{rename} 実行に対して \hgcmd{revert} を行う際には、 +以下に示すように、 +変更後のファイル名を指定しただけでは不十分です。 + +\interaction{daily.revert.rename} + +\hgcmd{status} コマンドの出力からもわかるように、 +変名後のファイルは既に未追加状態と認識されていますが、 +変名\emph{前}のファイルは未だに削除状態と認識されています! +これは(少なくとも著者にとっては)直感に反しますが、 +扱いは簡単です。 + +\interaction{daily.revert.rename-orig} + +\hgcmd{rename} の取り消しを行うには、 +変名前後のファイル名を\emph{両方}指定することを忘れないでください。 + +(ちなみに、 +ファイルの変名後に、変名後のファイルを変更し、 +それから変名前後のファイル名の両方を指定して取り消しを行った場合、 +Mercurial は変名の際に削除されたファイル\footnote{訳注: 変名前のファイル +}を何も変更されていない状態に戻します。 +変名後のファイルに対する変更を変名前ファイルに反映したい場合には、 +変名後ファイルから変名前ファイルへのコピーを忘れないでください。 +) + +変名の取り消しにおけるこれらの厄介な側面は、 +おそらく Mercurial の小さなバグに由来するものです。 + +\section{Dealing with committed changes} + +ある変更 $a$ をコミットし、その上で別の変更 $b$ をコミットした後で、 +変更 $a$ が間違っていたことに気付いたとします。 +Mercurial には、 +チェンジセットそのものを自動的に``無かったことにする''機能や、 +チェンジセットの一部を手動で無効にするための情報を提供する機能があります。 + +この節を読む前に、覚えておいて欲しいことが幾つかあります。 +\hgcmd{backout} コマンドによる変更の取り消しは、 +履歴を\emph{追加}することで行われるものであり、 +変更そのものを修正したり削除したりするものではありません。 +そのため、バグの修正をするのには向いていますが、 +破壊的な結果を伴う取り消しといった用途には向いていません。 +そのような取り消しに関しては、 +~\ref{sec:undo:aaaiiieee} 節を参照してください。 + +\subsection{Backing out a changeset} + +\hgcmd{backout} コマンドは、 +自動化された形式でチェンジセットの効果全体を``取り消し''ます。 +Mercurial の履歴は改変できないので、 +このコマンドは取り消したいチェンジセットを取り除いたりは\emph{しません}。 +その代わりにこのコマンドは、 +取り消したいチェンジセットによる改変内容を\emph{反転}させる、 +新たなチェンジセットを作成します。 + +\hgcmd{backout} コマンドの操作は少々複雑ですので、例を使って説明します。 +まずは単純なチェンジセットを幾つか持つリポジトリを作成します。 + +\interaction{backout.init} + +\hgcmd{backout} コマンドは、 +``bakc out''対象とする単一のチェンジセット識別子を引数に取ります。 +通常、 +\hgcmd{backout} +はコミットメッセージを書くためにテキストエディタを起動しますので、 +変更を back out する理由を記録することができます。 +この例では、 +\hgopt{backout}{-m} オプションを用いることで、 +コマンドラインからコミットメッセージを与えています。 + +\subsection{Backing out the tip changeset} + +以下の例では、 +最後にコミットしたチェンジセットを back out します。 + +\interaction{backout.simple} + +\filename{myfile} が既に2行目を持たないことがおわかりでしょう。 +\hgcmd{log} 出力を見れば、 +\hgcmd{backout} コマンドが何を行ったかを理解できます。 + +\interaction{backout.simple.log} + +\hgcmd{backout} が生成した新しいチェンジセットは、 +back out したチェンジセットの子チェンジセットとなる点に注意してください。 +変更履歴を図示した \ref{fig:undo:backout}~図を見れば、 +このことがわかるでしょう。 +ご覧の通り、履歴は見事に一直線です。 + +\begin{figure}[htb] + \centering + \grafix{undo-simple} + \label{fig:undo:backout} + \caption{Backing out a change using the \hgcmd{backout} command} +\end{figure} + +\subsection{Backing out a non-tip change} + +最後にコミットしたチェンジセット以外を back out したい場合、 +\hgcmd{backout} コマンドに +\hgopt{backout}{--merge} オプションを指定してください。 + +\interaction{backout.non-tip.clone} + +このコマンド実行は、 +任意のチェンジセットを、 +簡単で素早い``一回限りの''操作で back out できます。 + +\interaction{backout.non-tip.backout} + +back out 完了後の \filename{myfile} の内容には、 +1回目と3回目の変更に相当する内容は見ることができますが、 +2回目の変更に相当する内容は見ることができないでしょう。 + +\interaction{backout.non-tip.cat} + +履歴を図示した \ref{fig:undo:backout-non-tip}~図に見られるように、 +このような状況の場合、 +Mercurial は実際には\emph{2つ}のチェンジセットをコミットします +(Mercurial が自動的にコミットしたも\footnote{訳注: +実行例で Mercurial が出力するメッセージを見ればわかるように、 +マージされたチェンジセットのコミットは利用者責任となっているため、 +「自動的にコミット」したものではなく +「自動的に生成したもの」が正しい表現です。}のは矩形で示してあります)。 +Mercurial は back out 処理を始める前に、 +現時点での作業領域ディレクトリにおける親チェンジセットを覚えておきます。 +その上で対象チェンジセットを back out し、 +チェンジセットとしてコミットします。 +最後に、 +作業領域ディレクトリの親チェンジセットとマージした結果をコミットします +footnote{訳注: 前述のように、自動的にはコミットされません}。 + +\begin{figure}[htb] + \centering + \grafix{undo-non-tip} + \label{fig:undo:backout-non-tip} + \caption{Automated backout of a non-tip change using the \hgcmd{backout} command} +\end{figure} + +結果として、 +back out したいチェンジセットによる変更内容を取り消すための、 +幾つかの余分な履歴のみを伴って、 +``以前の状態への復旧''が行われます。 + +\subsubsection{Always use the \hgopt{backout}{--merge} option} + +実のところ、 +back out 対象のチェンジセットが tip か否かに関わらず、 +\hgopt{backout}{--merge} オプションは``正しく機能''します +(back out 対象が tip の場合は、必要が無いのでマージしようとはしません) +ので、 +\hgcmd{backout} コマンドを実行する際には\emph{常に} +\hgopt{backout}{--merge} オプションを指定するべきでしょう。 + +\subsection{Gaining more control of the backout process} + +先の記述では、変更の back out の際の +\hgopt{backout}{--merge} オプションの常用を推奨しましたが、 +その一方で、 +back out 対象となるチェンジセットのマージ方法を、 +\hgcmd{backout} コマンドの利用者が決定することもできます。 +back out 処理を手動で制御する必要は滅多にありませんが、 +手動制御の方法を知ることは、 +\hgcmd{backout} が自動的に行っていることの内情を理解する上で有用です。 +手動制御の説明のために、 +最初に作成したリポジトリを複製しますが、 +ここでは back out は行いません。 + +\interaction{backout.manual.clone} + +先の例と同様に、 +第3のチェンジセットをコミットし、 +その上でその親を back out した結果を見てみましょう。 + +\interaction{backout.manual.backout} + +新たなチェンジセットも第3のチェンジセット同様に、 +back out 対象のチェンジセットの子になりますので、 +それまで tip だったチェンジセット\footnote{訳注: 第3のチェンジセットのこと} +の子\emph{ではなく}、新たなヘッドになります。 +\hgcmd{backout} コマンドは、 +このことを告げる非常にはっきりとしたメッセージを表示しています。 + +\interaction{backout.manual.log} + +ここでも、 +履歴を図示した\ref{fig:undo:backout-manual}~図を見ることで、 +どういった状況にあるのかが理解し易いと思います。 +この図から、 +\hgcmd{backout} コマンドを tip 以外のチェンジセットに適用した際に、 +Mercurial が新しいヘッドをリポジトリに追加する +(Mercurial により追加されたチェンジセットは矩形で表しています) +ことがよくわかります。 + +\begin{figure}[htb] + \centering + \grafix{undo-manual} + \label{fig:undo:backout-manual} + \caption{Backing out a change using the \hgcmd{backout} command} +\end{figure} + +\hgcmd{backout} コマンドの実行が完了すると、 +作業領域ディレクトリの親チェンジセットが、 +新しい ``backout'' チェンジセットになります。 + +\interaction{backout.manual.parents} + +この時点で、2つの独立した変更のまとまり +\footnote{訳注: マージが必要な「複数ヘッド状態」のことを指していると思われます} +が存在します。 + +\interaction{backout.manual.heads} + +この時点で、\filename{myfile} +はどのような内容であることが期待されるかを考えてみましょう。 +第1の変更は back out していませんから、 +それに関する内容は存在していなければなりません。 +第2の変更は back out しましたので、 +それに関する内容は消失していなければなりません。 +履歴図で別個のヘッドとして図示されているように、 +第3の変更に関する内容が +\filename{myfile} に存在しては\emph{なりません}。 + +\interaction{backout.manual.cat} + +第3の変更の内容をファイルに取り込むには、 +2つのヘッドをいつものようにマージすれば良いのです。 + +\interaction{backout.manual.merge} + +マージすることで、 +リポジトリ中の履歴は +\ref{fig:undo:backout-manual-merge}~図に示すようになります。 + +\begin{figure}[htb] + \centering + \grafix{undo-manual-merge} + \caption{Manually merging a backout change} + \label{fig:undo:backout-manual-merge} +\end{figure} + +\subsection{Why \hgcmd{backout} works as it does} + +\hgcmd{backout} コマンドの振る舞いを簡単にまとめると以下のようになります。 + +\begin{enumerate} +\item 作業領域ディレクトリが``クリーン''な状態、 + 即ち \hgcmd{status} の出力が空であることを確認します。 + +\item その時点での作業領域ディレクトリの親チェンジセットを覚えておきます。 + 以下、このチェンジセットを \texttt{orig} と呼称します。 + +\item 作業領域ディレクトリを back out 対象チェンジセットに同期するために、 + \hgcmd{update} と同等の処理を行います。 + 以下、このチェンジセットを \texttt{backout} と呼称します。 + +\item \texttt{backout} の親チェンジセットを調べます。 + 以下、この親チェンジセットを \texttt{parent} と呼称します。 + +\item \texttt{backout} チェンジセットが影響する個々のファイルに対して、 + \hgcmdargs{revert}{-r parent} 相当の処理を行い、 + \texttt{backout} チェンジセットがコミットされる前の内容に復元します。 + +\item 復元結果を新しいチェンジセットとしてコミットします。 + このチェンジセットの親は \texttt{backout} です。 + +\item コマンドラインで \hgopt{backout}{--merge} が指定されていた場合、 + 新しいチェンジセットと \texttt{orig} をマージし、 + その結果をコミットします。 + +\end{enumerate} + +作業領域ディレクトリを弄繰り回すことなく +\hgcmd{backout} コマンド相当の効果を得るもう一つの方法は、 +back out されるチェンジセットに対して \hgcmd{export} +することで得た diff ファイルを、 +作用を反転させる +\cmdopt{patch}{--reverse} オプションを指定した +\command{patch} コマンドに用いることです。 +この方法は非常に簡単に感じるでしょうが、 +全く上手く機能しません。 + +\hgcmd{backout} が update、commit、merge および再度の commit を行うのは、 +back out 対象のチェンジセットと現在の tip +の\emph{間}の全てのチェンジセットを扱う際に、 +良好な結果を得るための最善の機会を Mercurial のマージ機構に与えるためです。 + +例えば、 +プロジェクトの履歴から、100 リビジョン分前のチェンジセットを +back out しようとした場合、 +\command{patch} がパッチの適用可否を判定するコンテキスト情報を、 +back out 対象との間にあるチェンジセットが``破壊''してしまうかもしれない +(この意味がわからない場合は、 +\ref{sec:mq:patch}~節の \command{patch} に関する説明を参照してください) +ので、 +\command{patch} コマンドが反転 diff を綺麗に適用できることは期待できません。 +Mercurial のマージ機構は、 +ファイルやディレクトリの変名、 +ファイル権限の変更や、 +バイナリファイルの変更といった +\command{patch} コマンドが扱うことのできないものも扱うことができます。 + +\section{Changes that should never have been} +\label{sec:undo:aaaiiieee} + +変更内容を取り消そうとした場合の殆どは、 +\hgcmd{backout} コマンドの利用が妥当です。 +\hgcmd{backout} コマンドは、 +元のチェンジセットのコミットと、 +後からそれを取り消した際の両方に関して、 +正確で永続的な記録を残します。 + +しかし、非常に稀な状況ですが、 +リポジトリ中に存在して欲しくない変更をコミットしてしまうかもしれません。 +例えば、 +ソースファイルと同様にオブジェクトファイルをコミットしてしまうような事態は、 +滅多に無いので通常は「間違い」とみなされます。 +オブジェクトファイルには本質的な価値はありませんし、 +非常に\emph{サイズが大きい}ですから、 +リポジトサイズや複製/変更取り込みに要する時間が増加してしまいます。 + +XXXXXXXXXX +Before I discuss the options that you have if you commit a ``brown +paper bag'' change (the kind that's so bad that you want to pull a +brown paper bag over your head), let me first discuss some approaches +that probably won't work. +XXXXXXXXXX + +Mercurial は履歴を「蓄積的なもの」--- +全ての変更が先行する変更の上に適用される---として扱いますので、 +破壊的な影響を持つチェンジセットに対してであっても、 +それを破棄することは通常はできません。 +\ref{sec:undo:rollback}~節で詳細を述べますが、 +例外的に \hgcmd{rollback} コマンドを安全に使用できるのは、 +変更をコミットした直後で、 +別なリポジトリへ \hgcmd{push} も \hgcmd{pull} もされていない場合だけです。 + +不適切なチェンジセットを他のリポジトリへ +\hgcmd{push} してしまった\emph{後でも}、 +\hgcmd{rollback} コマンドにより、 +ローカルなリポジトリでそのチェンジセットを破棄することはできますが、 +それはおそらく本来やりたかったことでは無い筈です。 +遠隔リポジトリ中には不適切なチェンジセットが存在し続けますので、 +次に変更の取り込みを行った際には、 +その変更が再びローカルリポジトリに現れるかもしれません。 + +このような状況が発生した場合、 +どのリポジトリが不適切なチェンジセットを保持しているかを把握しているなら、 +それら\emph{全ての}リポジトリからの不適切なチェンジセットの除去を、 +\emph{試みる}ことが可能です。 +勿論、これは申し分の無い解法ではありません。 +たった一つでも抹消し損ねたリポジトリがあれば、 +``野に放たれた''ままのチェンジセットは更に伝播してしまうでしょう。 + +除去したいチェンジセットの\emph{後に}、 +幾つかのチェンジセットをコミットしてしまった場合、 +取り得る選択肢は更に限られてしまいます。 +Mercurial は、 +チェンジセットに手をつけないままで、 +履歴に``穴を開ける''機能は提供していません。 + +XXX This needs filling out. +\texttt{examples} ディレクトリ配下の +\texttt{hg-replay} スクリプトは機能しますが、 +チェンジセットのマージを行いません。 +重大な手抜きです。 + +\subsection{Protect yourself from ``escaped'' changes} + +ローカルリポジトリにコミットした幾つかのチェンジセットが、 +\hgcmd{push} ないし \hgcmd{pull} +等によってそれらが他のリポジトリへと反映されたからといって、 +そのこと自体は必ずしも大失敗というわけではありません。 +ある種の不正なチェンジセットに対して、 +あらかじめ自己防衛することも可能です。 +開発チームが変更を中央のリポジトリから \hgcmd{pull} するような体制の場合、 +事故防衛は非常に簡単です。 + +中央のリポジトリの幾つかのフックを、 +追加されるチェンジセットの検証を行うように設定する +(\ref{chap:hook}~章を参照してください)ことで、 +ある種の不正なチェンジセットが、 +中央リポジトリに全く反映されないように自動化することができます。 +設定が適切であれば中央のリポジトリに反映できなくなるため、 +このようなチェンジセットは自然と``死に絶え''ます。 +なお良いことに、この手法は明示的な介入を必要としません。 + +例えば、当該チェンジセットが実際にコンパイル可能かどうかを検証する +incoming フックは、 +うっかり``ビルドできなくしてしまう''ことを防止できます。 + +\section{Finding the source of a bug} +\label{sec:undo:bisect} + +バグをもたらしたチェンジセットを back out できるのは非常に結構なのですが、 +どのチェンジセットを back out すべきかを知っている必要があります。 +Mercurial には、 +チェンジセット特定の自動化と非常に効率的な実施を補助する、 +\hgext{bisect} と呼ばれる重要な拡張があります。 + +チェンジセットによる変更は振る舞いに変化をもたらすので、 +その変化を簡単な2値テストによりそれを特定することができる、 +というのが \hgext{bisect} 拡張の原理です。 +どのコード片が変化をもたらしているのかはわからなくても、 +バグの有無を試験する方法はわかるでしょう。 +\hgext{bisect} 拡張は、 +バグの原因となったコードをもたらしたチェンジセットを探すのに、 +あなたのテストプログラムを直接使用します。 + +\hgext{bisect} 拡張の適用方法を理解しやすいように、 +幾つかのシナリオを例示します。 + +\begin{itemize} +\item 数週間前には見られなかったバグが、最新の版で発見されましたが、 + 何時それが混入されたのかがわかりません。 + この場合、binary test でバグの有無を調べることができます + \footnote{訳注: 「バグの有無」という2値を判定するテストを用いることで、 + バグの混入したチェンジセットを探します}。 + +\item 大急ぎでバグを修正し、 + 開発チームのバグデータベースの状態を「クローズ」にできるようになりました。 + 「クローズ」状態にする際に、 + バグデータベースがチェンジセットIDを求めてきましたが、 + どのチェンジセットでバグを修正したのか覚えていませんでした。 + ここで再び binary test でバグの有無を調べることができます。 + +\item ソフトウェアが正しく動作していますが、 + 以前計測した時よりも 15\% 遅くなってました。 + どのチェンジセットが性能低下の要因となっているのかを知りたいです。 + この場合、binary test はソフトウェアの性能を計測し、 + ``早い''のか``遅い''のかを判定します。 + +\item ここ最近、 + 出荷したプロジェクトの構成要素のサイズが爆発的に大きくなっていて、 + プロジェクトのビルド手順の何らかが変更されたのではないかと推測しています + \footnote{訳注: ビルド結果の「構成要素サイズの大小」 + という2値を判定するテストを用いることで、 + 変更が混入されたチェンジセットを探します}。 + +\end{itemize} + +これらの例から、 +\hgext{bisect} 拡張がバグの元を探すだけのものでないことは明らかでしょう。 +その特性に関する2値テストを書けるなら、 +リポジトリにおける +(ソースツリー中のファイルに対する単純な文字列検索では探し出せない) +任意の``特性の出現''を探し出すことができます。 + +利用者と Mercurial のそれぞれが、 +検索処理においてどの部分に責任を負うのかをはっきりとさせるために、 +ここでもう少し用語の説明をしましょう。 +\emph{テスト}(test)とは、 +\hgext{bisect} 拡張がチェンジセットを選択する際に、 +\emph{利用者}が実行するものです。 +\emph{調査}(probe)とは、 +あるリビジョンの良否を判定するために \hgext{bisect} が実行するものです。 +最後に、 +``bisect'' という言葉を、 +``\hgext{bisect} 拡張を用いた検索''の代用として、 +名詞および動詞として使用します。 + +検索処理を自動化する簡単な方法の一つが、 +全てのチェンジセットを調査する遣り方です。 +しかしながら、この遣り方には殆どスケーラビリティがありません。 +1つのチェンジセットのテストに10分必要で、 +リポジトリに1万のチェンジセットがあったとすると、 +徹底的に調査する遣り方では、 +バグをもたらしたチェンジセットを見つけるのに、 +平均で35~\emph{日}必要です。 +検索対象を最新の500チェンジセットに限定できるとしても、 +バグをもたらしたチェンジセットを見つけるのには、 +それでもなお40時間必要です。 + +\emph{bisect} 拡張は、 +確認するチェンジセット数に対して\emph{対数}のオーダーで検索 +(この種の検索は``二分探索''と呼ばれます)できるように、 +プロジェクト履歴の``形''に関する情報を利用します。 +この方法により、 +仮にテストあたりの所要時間が10分掛かるとしても、 +1万チェンジセットに対する検索は2時間以内で終わります。 +検索対象を最新の500チェンジセットに限定できるならば、 +1時間以内に検索できるでしょう。 + +\hgext{bisect} 拡張は、 +Mercurial で管理されているプロジェクトの履歴の持つ +``枝分かれ''の特質をわかっていますので、 +リポジトリにおける枝分かれ・マージ・複数ヘッドの扱いも問題ありません。 +単一の調査で履歴の枝分かれ全体を刈り取る\footnote{訳注: +「枝分かれ先全体を検索対象から除外する」の意}ことができるため、 +\hgext{bisect} 拡張は効率的に検索することができるのです。 + +\subsection{Using the \hgext{bisect} extension} + +ここでは \hgext{bisect} 拡張の実行例を示します。 +Mercurial 自体の簡便性を維持するために、 +\hgext{bisect} は拡張機能として提供されます。 +そのため、明示的に有効にしなければ、その機能は提供されません。 +\hgext{bisect} 拡張を有効にするには、 +(存在しない場合には) \hgrc\ に以下のセクションヘッダを追加し: + +\begin{codesample2} + [extensions] +\end{codesample2} + +続いて、\hgext{bisect} 拡張を有効化するための行をこのセクションに追加します +\footnote{1.0 版以降の Mercurial では、 +\hgext{bisect} 機能は基本機能に取り込まれていますので、 +「拡張機能の有効化」は不要です}。 + +\begin{codesample2} + hbisect = +\end{codesample2} + +\begin{note} + \hgext{bisect} 拡張の名前の先頭に``\texttt{h}''が付くのは間違っていません。 + この文字が付くのは、Mercurial が Python で実装されていて、 + Python の標準ライブラリの \texttt{bisect} を使用しているためです。 + 誤って ``\texttt{hbisect}''から``\texttt{h}''を省略した場合、 + \hgrc ファイルの記述のスペルを修正するまでは、 + Mercurial は Python 標準の \texttt{bisect} パッケージを見つけ出し、 + それを Mercurial 拡張として利用しようとしてクラッシュし続けることでしょう。 +\end{note} + +\hgext{bisect} 拡張を隔離して利用するために、 +リポジトリを作成しましょう。 + +\interaction{bisect.init} + +ループによって幾つかの些細な変更を行い、 +その中の特定の変更が``バグ''を持つようにする、 +という単純な方法で、 +バグを持ったプロジェクトのシミュレーションを行います。 +このループは 50 のチェンジセットを生成し、 +それぞれが1つのファイルをリポジトリに追加します。 +ここでは、 +ファイルが``i have a gub''というテキストを含んでいることをもって、 +``バグ''とみなします。 + +\interaction{bisect.commits} + +それでは、\hgext{bisect} 拡張の使用方法を理解しましょう。 +\hgext{bisect} 拡張に関しても、 +通常の Mercurial の組み込み help 機能が使用できます。 + +\interaction{bisect.help} + +\hgext{bisect} 拡張は段階を踏んで機能します。 +各段階は以下のように進みます。 + +\begin{enumerate} +\item 2値テストを実行します。 + \begin{itemize} + \item テストが成功した場合、 + \hgcmdargs{bisect}{good} コマンドにより + \hgext{bisect} 拡張にそのことを伝えます。 + \item テストが失敗した場合、 + \hgcmdargs{bisect}{bad} コマンドにより + \hgext{bisect} 拡張にそのことを伝えます。 + \end{itemize} +\item \hgext{bisect} 拡張は伝えられた情報を元に、 + 次にテストすべきチェンジセットを決定します。 + +\item \hgext{bisect} 拡張は、 + 作業領域ディレクトリをそのチェンジセットで更新しますので、 + 以上の手順を繰り返します。 + +\end{enumerate} + +2値テストの結果が``成功''から``失敗''に変化した点を示す、 +一意なチェンジセットを \hgext{bisect} 拡張が特定できた時点で、 +この手順は終了します。 + +検索の開始に当たっては、 +\hgcmdargs{bisect}{init} コマンドの実行が必要です。 + +\interaction{bisect.search.init} + +今回の実行例で使用する2値テストは簡単なもので、 +リポジトリ中の何れかのファイルが +``i have a gub'' 文字列を含んでいるか否かを判定します。 +含んでいる場合、そのチェンジセットは +``バグの要因となる''チェンジセットです。 +慣習上、 +検索対象となる特性を持っているチェンジセットを ``bad''、 +持っていないチェンジセットを ``good'' と呼びます。 +多くの場合、 +作業領域ディレクトリが同期しているリビジョン(通常は tip) +はバグを持つチェンジセットにより問題を抱えているものですから、 +これを``bad''とみなします。 + +\interaction{bisect.search.bad-init} + +次の作業は、 +バグが\emph{無い}チェンジセットを指定することです。 +\hgext{bisect} 拡張は +最初の ``good'' と ``bad'' のチェンジセット間の検査状況を +``括弧''で括って表示するでしょう。 +今回の事例では、 +リビジョン 10 にはバグがありません +(最初の ``good'' チェンジセットの選択に関しては、 +後ほど補足があります)。 + +\interaction{bisect.search.good-init} + +コマンド出力には以下の意味があります。 + +\begin{itemize} +\item バグをもたらしたチェンジセットの特定までに、 + どれだけのチェンジセットに対して考慮が必要であるか、 + また、どれだけのテストを要求するかを表示します。 + +\item \hgext{bisect} + 拡張は次にテストすべきチェンジセットへと作業領域ディレクトリを更新し、 + どのチェンジセットがテスト対象であるのかを表示します。 + +\end{itemize} + +早速作業領域ディレクトリでテストをしてみましょう。 +\command{grep} を使用して、 +作業領域ディレクトリの``bad''ファイルの有無を調べ、 +ファイルが無ければそのリビジョンは``good''です。 + +\interaction{bisect.search.step1} + +このテストは完全に自動化できそうですので、 +シェル関数にしてしまいましょう。 + +\interaction{bisect.search.mytest} + +これで、 +テスト手順全体を単一の +\texttt{mytest} コマンドで実行できます。 + +\interaction{bisect.search.step2} + +テスト手順が記録されたコマンドをあと数回起動することで、 +当初の目的が達成されます。 + +\interaction{bisect.search.rest} + +40 程のチェンジセット全体の検索にも関わらず、 +\hgext{bisect} +拡張はわずか5回のテストで``バグ''をもたらしたチェンジセットを特定できました。 +調査対象チェンジセット数に対して、 +\hgext{bisect} 拡張は対数のオーダーでテスト対象を選定するので、 +チェンジセットを追加しただけテスト回数が増加する +``力尽く''の手法よりも有利です。 + +\subsection{Cleaning up after your search} + +リポジトリにおける \hgext{bisect} 拡張の使用が終わったなら、 +検索に使用していた情報を +\hgcmdargs{bisect}{reset} コマンドにより破棄することができます。 +\hgext{bisect} 拡張はそれほど多くの領域を消費するわけではありませんので、 +この作業を忘れても問題にはなりません。 +しかし、\hgcmdargs{bisect}{reset} を実行するまでは、 +\hgext{bisect} はそのリポジトリで別の検索を開始させてくれません。 + +\interaction{bisect.search.reset} + +\section{Tips for finding bugs effectively} + +\subsection{Give consistent input} + +\hgext{bisect} 拡張には、 +実施した全てのテストの結果が正しく指定されなければなりません。 +本当はテストが成功していたにも関わらず、 +テストの失敗を \hgext{bisect} 拡張に伝えた場合、 +矛盾した結果を出す\emph{かも}しれません。 +テスト結果に対して矛盾が検知された場合、 +\hgext{bisect} は、 +特定のチェンジセットが``good''でも``bad''でもある、 +と言ってきます。 +しかし、この検知は完璧に行われるわけではないので、 +間違ったチェンジセットをバグの要因として報告するでしょう。 + +\subsection{Automate as much as possible} + +筆者が \hgext{bisect} 拡張を使い始めた頃は、 +検索のためのテストをコマンドラインで手動で実行していましたが、 +少なくとも私には、この手法は馴染みません。 +何度か \hgext{bisect} を使用した後で、 +最終的に正しい結果を得る前に、 +いつも手違いのために何度も検索をやり直していることに気付きました。 + +\hgext{bisect} 拡張を手動で駆動していた際には、 +小さなリポジトリにおける単純な検索であっても問題が発生していました。 +テストの内容が複雑であったり、 +\hgext{bisect} が要求するテスト実行回数が増えれば、 +それだけテスト実行における操作ミスの可能性は高まります。 +テストを自動化するようになって以来、 +非常に良好な結果を得られています。 + +テスト自動化のための鍵は2つあります。 + +\begin{itemize} +\item 常に同じ「症状」をテストすることと、 +\item 常に一貫した入力を \hgcmd{bisect} コマンドに与えること +\end{itemize} + +前述の実行例では、 +\command{grep} コマンドにより「症状」を調べていて、 +\texttt{if} ステートメントが「検査」の結果を受けて +\hgcmd{bisect} コマンドに同じ入力を与えることを保証していました。 +\texttt{mytest} 関数が、 +これらを再現しやすい形式に統合したことで、 +全てのテストが均一で整合性の取れたものになっています。 + +\subsection{Check your results} + +\hgext{bisect} による検索の出力結果は与えた情報程度にしか正しくないので、 +\hgext{bisect} により ``good'' と報告されたチェンジセットを、 +絶対的に正しいものとみなさないでください。 +報告内容をクロスチェックする簡単な方法は、 +以下のようなチェンジセットのそれぞれに対して、 +手動で自身のテストを実行してみることです。 + +\begin{itemize} +\item 最初の ``bad'' リビジョンであると報告されたもの + (以下、「障害チェンジセット」と呼称)。 + あなたのテストはこれに関して ``bad'' と報告しなければなりません。 + +\item 上記チェンジセットの親チェンジセット + (マージされた場合は両方の親)。 + あなたのテストはこれ(これら)に関して ``good'' と報告しなければなりません。 + +\item 障害チェンジセットの子チェンジセット。 + あなたのテストはこれに関して ``bad'' と報告しなければなりません。 + +\end{itemize} + +\subsection{Beware interference between bugs} + +あるバグを探す際に、 +他のバグの存在により混乱させられる可能性もあります。 +例えば、 +リビジョン 100 でソフトウェアがクラッシュし、 +リビジョン 50 では正しく動作していたとします。 +あなたの知らない間に、 +ソフトウェアをクラッシュさせる別のバグを、 +他の人がリビジョン 60 で入れてしまい、 +それをリビジョン 80 で修正した場合、 +なんらかの方法で検索結果を混乱させるかもしれません。 + +他のバグの存在によって、 +探しているバグが完全に``覆い隠される''かもしれず、 +探しているバグがその存在を示す機会を得る前に他のバグが発生している、 +と言えます。 +他のバグを回避したテストが +(例えば、そのバグがプロジェクトのビルドを阻害するなどの理由で) +できないために、 +特定のチェンジセットにおける検索対象のバグの有無を明言できない場合、 +\hgext{bisect} 拡張の助けを直接受けることはできません。 +その替わり、 +他のバグが存在するチェンジセットを手動で取り除くことで、 +``周辺''での別な検索を行いましょう。 + +バグの存在に関するテストが十分明確でない場合には、 +別な問題が発生し得ます。 +``プログラムのクラッシュ''でバグの有無を確認している場合、 +ソフトウェアをクラッシュさせる全然関係ないバグにより、 +検索対象であるバグが覆い隠されてしまい、 +両方とも同じものとみなされるために、 +\hgext{bisect} が惑わされてしまいます。 + +\subsection{Bracket your search lazily} + +検索における終端の印となる +``good'' および ``bad'' なチェンジセットの最初の選択は、 +通常は簡単なことですが、 +そうであっても多少は議論の余地があります。 +\hgext{bisect} の立場から見た場合、 +``最新''のチェンジセットは通例では``bad''で、 +最古のチェンジセットは``good''です。 + +\hgext{bisect} の使用に当たって +``good''にふさわしいチェンジセットがどれかを思い出すのが難しい場合には、 +でたらめにテストするのも悪くはないでしょう。 +どうあってもバグの兆候が見出せない +(例えば、バグの発生に関連する機能がまだ提供されていない)ものや、 +他の問題が(前述したように)バグを覆い隠してしまうようなものを、 +テスト候補のチェンジセットから除外するのを忘れないようにしましょう。 + +数千のチェンジセット、 +ないし数ヶ月の履歴の``初期''のものが最終結果だったとしても、 +対数オーダーの振る舞いのお陰で、 +\hgext{bisect} が実施しなければならない総回数が数回増えるだけです。 + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "00book" +%%% End: diff -r a24b370a16ee -r d6ca1334a19d ja/variant.el --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/variant.el Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,54 @@ +(setq wellknown + '(("write" "実装") + ("writing" "実装") + + ("tarball" "tarball") + + ("patch queue" "パッチキュー(?)") + ("patch" "パッチ") + ("patches" "パッチ") + + ("extension" "イクステンション") + ("daemon" "デーモン") + + ("configuration item" "設定項目") + ("by default" "基底動作") ;; !既定 + + ("revision control" "構成管理") + ("revision" "リビジョン") + ("revisions" "リビジョン") + ("working directory" "作業領域ディレクトリ") + ("refresh" "refresh") + ("refreshes" "refresh") + ("refreshing" "refresh") + ("stack" "積み重ね" "スタック") + ("popped" "取り除かれた") + ("branch" "ブランチ") + + ("hunk" "hunk") + ("context" "コンテキスト") + ("offset" "オフセット") + + ("fuzz" "あいまい") + ("rejection" "却下") + ("reject" "却下") + + ("rebase" "リベース") + + ("commit" "コミット") + ("merge" "マージ") + ("head" "head? ヘッド?" ) + + ("backport" "バックポート") + ("distribution" "ディストリビューション") + ("platform" "プラットホーム") + )) + +(setq japanese-variant + '(("できる" "出来") + ("いる" "居る") + ("いくつ" "幾つ") + ("新しい" "新規") + ("ほとんど" "殆ど") + ("あいまい" "曖昧") + )) \ No newline at end of file diff -r a24b370a16ee -r d6ca1334a19d ja/wdir-after-commit.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/wdir-after-commit.svg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,394 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + dfbbb33f3fa3 + + + e7639888bb2f + + 7b064d8bac5e + + + + 000000000000 + + History in repository + + + + dfbbb33f3fa3 + + + + 000000000000 + + First parent + Second parent + Parents of working directory + + + Newchangeset + + diff -r a24b370a16ee -r d6ca1334a19d ja/wdir-branch.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/wdir-branch.svg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,418 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + e7639888bb2f + + + + 7b064d8bac5e + + + + 000000000000 + + + + + ffb20e1701ea + + + + 000000000000 + + First parent + Second parent + Parents of working directory + + + + + ffb20e1701ea + + + Pre-existing head + Newly created head (and tip) + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/wdir-merge.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/wdir-merge.svg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,425 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + 7b064d8bac5e + + + + 000000000000 + + + + + ffb20e1701ea + + + + e7639888bb2f + + First parent (unchanged) + Second parent + Parents of working directory + + + + + ffb20e1701ea + + + Pre-existing head + Newly created head (and tip) + + + + + + e7639888bb2f + + + diff -r a24b370a16ee -r d6ca1334a19d ja/wdir-pre-branch.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/wdir-pre-branch.svg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,364 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + e7639888bb2f + + + 7b064d8bac5e + + + + 000000000000 + + History in repository + + + + 7b064d8bac5e + + + + 000000000000 + + First parent + Second parent + Parents of working directory + + + + diff -r a24b370a16ee -r d6ca1334a19d ja/wdir.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ja/wdir.svg Fri Jul 31 19:49:16 2009 +0900 @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + e7639888bb2f + + 7b064d8bac5e + + + 000000000000 + + + History in repository + + + + + e7639888bb2f + + + + 000000000000 + + First parent + Second parent + + Parents of working directory + + + +