Mercurial > hgbook
changeset 106:9cbc5d0db542
Finish off advanced MQ chapter (maybe).
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Mon, 23 Oct 2006 15:43:04 -0700 |
parents | ecacb6b4c9fd |
children | a0d7e11db169 |
files | en/99defs.tex en/mq-collab.tex en/mq.tex examples/hg-interdiff |
diffstat | 4 files changed, 172 insertions(+), 12 deletions(-) [+] |
line wrap: on
line diff
--- a/en/99defs.tex Sat Oct 21 11:05:51 2006 -0700 +++ b/en/99defs.tex Mon Oct 23 15:43:04 2006 -0700 @@ -106,6 +106,9 @@ % Interaction from the examples directory. \newcommand{\interaction}[1]{\VerbatimInput[frame=single,numbers=left,commandchars=\\\{\}]{examples/#1.out}} +% Example code from the examples directory. +\newcommand{\excode}[1]{\VerbatimInput[frame=single,numbers=left,commandchars=\\\{\}]{../examples/#1}} + % Graphics inclusion. \ifpdf \newcommand{\grafix}[1]{\includegraphics{#1}}
--- a/en/mq-collab.tex Sat Oct 21 11:05:51 2006 -0700 +++ b/en/mq-collab.tex Mon Oct 23 15:43:04 2006 -0700 @@ -154,15 +154,12 @@ \interaction{mq.guards.qselect.qpush} A guard cannot start with a ``\texttt{+}'' or ``\texttt{-}'' -character. The name of a guard must start with an alphabetic -character (upper or lower case) or an underscore. The rest of the -guard's name can contain any of these characters, or a digit. These -rules are similar to those used for variable naming in most popular -programming languages. If you try to use a guard with an invalid -name, MQ will complain: -\interaction{mq.guards.qselect.error} +character. The name of a guard must not contain white space, but most +othter characters are acceptable. If you try to use a guard with an +invalid name, MQ will complain: +\interaction{mq.guards.qselect.error} Changing the selected guards changes the patches that are applied. -\interaction{mq.guards.qselect.quux} +\interaction{mq.guards.qselect.quux} You can see in the example below that negative guards take precedence over positive guards. \interaction{mq.guards.qselect.foobar} @@ -250,9 +247,132 @@ a while. The ``backport'' and ``do not ship'' patches float at the end of the -\sfilename{series} file in part because they'll never be shipped -upstream. Additionally, the backport patches must be applied on top -of all other patches. +\sfilename{series} file. The backport patches must be applied on top +of all other patches, and the ``do not ship'' patches might as well +stay out of harm's way. + +\section{Maintaining the patch series} + +In my work, I use a number of guards to control which patches are to +be applied. + +\begin{itemize} +\item ``Accepted'' patches are guarded with \texttt{accepted}. I + enable this guard most of the time. When I'm applying the patches + on top of a tree where the patches are already present, I can turn + this patch off, and the paptches that follow it will apply cleanly. +\item Patches that are ``finished'', but not yet submitted, have no + guards. If I'm applying the patch stack to a copy of the upstream + tree, I don't need to enable any guards in order to get a reasonably + safe source tree. +\item Those patches that need reworking before being resubmitted are + guarded with \texttt{rework}. +\item For those patches that are still under development, I use + \texttt{devel}. +\item A backport patch may have several guards, one for each version + of the kernel to which it applies. For example, a patch that + backports a piece of code to~2.6.9 will have a~\texttt{2.6.9} guard. +\end{itemize} +This variety of guards gives me considerable flexibility in +qdetermining what kind of source tree I want to end up with. For most +situations, the selection of appropriate guards is automated during +the build process, but I can manually tune the guards to use for less +common circumstances. + +\subsection{The art of writing backport patches} + +Using MQ, writing a backport patch is a simple process. All such a +patch has to do is modify a piece of code that uses a kernel feature +not present in the older version of the kernel, so that the driver +continues to work correctly under that older version. + +A useful goal when writing a good backport patch is to make your code +look as if it was written for the older version of the kernel you're +targeting. The less obtrusive the patch, the easier it will be to +understand and maintain. If you're writing a collection of backport +patches to avoid the ``rat's nest'' effect of lots of +\texttt{\#ifdef}s (hunks of source code that are only used +conditionally) in your code, don't introduce version-dependent +\texttt{\#ifdef}s into the patches. Instead, write several patches, +each of which makes unconditional changes, and control their +application using guards. + +There are two reasons to divide backport patches into a distinct +group, away from the ``regular'' patches whose effects they modify. +The first is that intermingling the two makes it more difficult to use +a tool like the \hgext{patchbomb} extension to automate the process of +submitting the patches to an upstream maintainer. The second is that +a backport patch could perturb the context in which a subsequent +regular patch is applied, making it impossible to apply the regular +patch cleanly \emph{without} the earlier backport patch already being +applied. + +\section{Useful tips for developing with MQ} + +\subsection{Organising patches in directories} + +If you're working on a substantial project with MQ, it's not difficult +to accumulate a large number of patches. For example, I have one +patch repository that contains over 250 patches. + +If you can group these patches into separate logical categories, you +can if you like store them in different directories; MQ has no +problems with patch names that contain path separators. + +\subsection{Viewing the history of a patch} +\label{mq-collab:tips:interdiff} + +If you're developing a set of patches over a long time, it's a good +idea to maintain them in a repository, as discussed in +section~\ref{sec:mq:repo}. If you do so, you'll quickly discover that +using the \hgcmd{diff} command to look at the history of changes to a +patch is unworkable. This is in part because you're looking at the +second derivative of the real code (a diff of a diff), but also +because MQ adds noise to the process by modifying time stamps and +directory names when it updates a patch. + +However, you can use the \hgext{extdiff} extension, which is bundled +with Mercurial, to turn a diff of two versions of a patch into +something readable. To do this, you will need a third-party package +called \package{patchutils}~\cite{web:patchutils}. This provides a +command named \command{interdiff}, which shows the differences between +two diffs as a diff. Used on two versions of the same diff, it +generates a diff that represents the diff from the first to the second +version. + +You can enable the \hgext{extdiff} extension in the usual way, by +adding a line to the \rcsection{extensions} section of your \hgrc. +\begin{codesample2} + [extensions] + extdiff = +\end{codesample2} +The \command{interdiff} command expects to be passed the names of two +files, but the \hgext{extdiff} extension passes the program it runs a +pair of directories, each of which can contain an arbitrary number of +files. We thus need a small program that will run \command{interdiff} +on each pair of files in these two directories. This program is +available as \sfilename{hg-interdiff} in the \dirname{examples} +directory of the source code repository that accompanies this book. +\excode{hg-interdiff} + +With the \sfilename{hg-interdiff} program in your shell's search path, +you can run it as follows, from inside an MQ patch directory: +\begin{codesample2} + hg extdiff -p hg-interdiff -r A:B my-change.patch +\end{codesample2} +Since you'll probably want to use this long-winded command a lot, you +can get \hgext{hgext} to make it available as a normal Mercurial +command, again by editing your \hgrc. +\begin{codesample2} + [extdiff] + cmd.interdiff = hg-interdiff +\end{codesample2} +This directs \hgext{hgext} to make an \texttt{interdiff} command +available, so you can now shorten the previous invocation of +\hgcmd{extdiff} to something a little more wieldy. +\begin{codesample2} + hg interdiff -r A:B my-change.patch +\end{codesample2} %%% Local Variables: %%% mode: latex
--- a/en/mq.tex Sat Oct 21 11:05:51 2006 -0700 +++ b/en/mq.tex Mon Oct 23 15:43:04 2006 -0700 @@ -800,6 +800,7 @@ \end{itemize} \section{Managing patches in a repository} +\label{sec:mq:repo} Because MQ's \sdirname{.hg/patches} directory resides outside a Mercurial repository's working directory, the ``underlying'' Mercurial @@ -894,7 +895,8 @@ extracts subsets from a patch file. For example, given a patch that modifies hundreds of files across dozens of directories, a single invocation of \command{filterdiff} can generate a smaller patch that -only touches files whose names match a particular glob pattern. +only touches files whose names match a particular glob pattern. See +section~\ref{mq-collab:tips:interdiff} for another example. \section{Good ways to work with patches}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/hg-interdiff Mon Oct 23 15:43:04 2006 -0700 @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# +# Adapter for using interdiff with mercurial's extdiff extension. +# Copyright 2006 Bryan O'Sullivan <bos@serpentine.com> + +import os, sys + +def walk(base): + # yield all non-directories below the base path. + for root, dirs, files in os.walk(base): + for f in files: + path = os.path.join(root, f) + yield path[len(base)+1:], path + +# create list of unique file names under both directories. +files = dict(walk(sys.argv[1])) +files.update(walk(sys.argv[2])) +files = files.keys() +files.sort() + +def name(base, f): + # interdiff requires two files; use /dev/null if one is missing. + path = os.path.join(base, f) + if os.path.exists(path): + return path + return '/dev/null' + +ret = 0 + +for f in files: + if os.system('interdiff "%s" "%s"' % (name(sys.argv[1], f), + name(sys.argv[2], f))): + ret = 1 + +sys.exit(ret)