Mercurial > hgbook
diff en/mq.tex @ 19:187702df428b
Piles of new content for MQ chapter - cookbook stuff.
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Fri, 07 Jul 2006 19:56:53 -0700 |
parents | e6f4088ebe52 |
children | 9d5b6d303ef5 |
line wrap: on
line diff
--- a/en/mq.tex Tue Jul 04 16:41:31 2006 -0700 +++ b/en/mq.tex Fri Jul 07 19:56:53 2006 -0700 @@ -126,6 +126,62 @@ Because quilt does not care about revision control tools, it is still a tremendously useful piece of software to know about for situations where you cannot use Mercurial and MQ. + +\section{Understanding patches} + +Because MQ doesn't hide its patch-oriented nature, it is helpful to +understand what patches are, and a little about the tools that work +with them. + +The traditional Unix \command{diff} command compares two files, and +prints a list of differences between them. The \command{patch} command +understands these differences as \emph{modifications} to make to a +file. Take a look at figure~\ref{ex:mq:diff} for a simple example of +these commands in action. + +\begin{figure}[ht] + \interaction{mq.diff.diff} + \caption{Simple uses of the \command{diff} and \command{patch} commands} + \label{ex:mq:diff} +\end{figure} + +The type of file that \command{diff} generates (and \command{patch} +takes as input) is called a ``patch'' or a ``diff''; there is no +difference between a patch and a diff. (We'll use the term ``patch'', +since it's more commonly used.) + +A patch file can start with arbitrary text; the \command{patch} +command ignores this text, but MQ uses it as the commit message when +creating changesets. To find the beginning of the patch content, +\command{patch} searches for the first line that starts with the +string ``\texttt{diff~-}''. + +MQ works with \emph{unified} diffs (\command{patch} can accept several +other diff formats, but MQ doesn't). A unified diff contains two +kinds of header. The \emph{file header} describes the file being +modified; it contains the name of the file to modify. When +\command{patch} sees a new file header, it looks for a file with that +name to start modifying. + +After the file header comes a series of \emph{hunks}. Each hunk +starts with a header; this identifies the range of line numbers within +the file that the hunk should modify. Following the header, a hunk +starts and ends with a few (usually three) lines of text from the +unmodified file; these are called the \emph{context} for the hunk. If +there's only a small amount of context between successive hunks, +\command{diff} doesn't print a new hunk header; it just runs the hunks +together, with a few lines of context between modifications. + +Each line of context begins with a space character. Within the hunk, +a line that begins with ``\texttt{-}'' means ``remove this line,'' +while a line that begins with ``\texttt{+}'' means ``insert this +line.'' For example, a line that is modified is represented by one +deletion and one insertion. + +We will return to ome of the more subtle aspects of patches later (in +section~\ref{ex:mq:adv-patch}), but you should have enough information +now to use MQ. + \section{Getting started with Mercurial Queues} \label{sec:mq:start} @@ -200,6 +256,7 @@ working directory as you usually would. All of the normal Mercurial commands, such as \hgcmd{diff} and \hgcmd{annotate}, work exactly as they did before. + \subsection{Refreshing a patch} When you reach a point where you want to save your work, use the @@ -319,45 +376,12 @@ \hgcmd{qrefresh} the core patch, and \hgcmd{qpush} back to the UI patch to continue where you left off. -\section{Mercurial Queues and GNU patch} -\label{sec:mq:patch} - -MQ uses the GNU \command{patch} command to apply patches. Because MQ -doesn't hide its patch-oriented nature, it is helpful to understand -the data that MQ and \command{patch} work with, and a few aspects of -how \command{patch} operates. - -The \command{diff} command generates a list of modifications by -comparing two files. The \command{patch} command applies a list of -modifications to a file. The kinds of files that \command{diff} and -\command{patch} work with are referred to as both ``diffs'' and -``patches;'' there is no difference between a diff and a patch. - -A patch file can start with arbitrary text; MQ uses this text as the -commit message when creating changesets. It treats the first line -that starts with the string ``\texttt{diff~-}'' as the separator -between header and content. +\section{More about patches} +\label{sec:mq:adv-patch} -MQ works with \emph{unified} diffs (\command{patch} can accept several -other diff formats, but MQ doesn't). A unified diff contains two -kinds of header. The \emph{file header} describes the file being -modified; it contains the name of the file to modify. When -\command{patch} sees a new file header, it looks for a file with that -name to start modifying. - -After the file header comes a series of \emph{hunks}. Each hunk -starts with a header; this identifies the range of line numbers within -the file that the hunk should modify. Following the header, a hunk -starts and ends with a few (usually three) lines of text from the -unmodified file; these are called the \emph{context} for the hunk. -Each unmodified line begins with a space characters. Within the hunk, -a line that begins with ``\texttt{-}'' means ``remove this line,'' -while a line that begins with ``\texttt{+}'' means ``insert this -line.'' For example, a line that is modified is represented by one -deletion and one insertion. - -The \command{diff} command runs hunks together when there's not enough -context between modifications to justify +MQ uses the GNU \command{patch} command to apply patches, so it's +helpful to know about a few more detailed aspects of how +\command{patch} works. When \command{patch} applies a hunk, it tries a handful of successively less accurate strategies to try to make the hunk apply. @@ -622,6 +646,7 @@ confuse MQ's idea of which patches are applied. \section{Commands for working with patches} +\label{sec:mq:tools} Once you've been working with patches for a while, you'll find yourself hungry for tools that will help you to understand and @@ -636,6 +661,12 @@ do clever things with prefixes of file names that inevitably confuse at least me.) +\begin{figure}[ht] + \interaction{mq.tools.tools} + \caption{The \command{diffstat}, \command{filterdiff}, and \command{lsdiff} commands} + \label{ex:mq:tools} +\end{figure} + The \package{patchutils} package~\cite{web:patchutils} is invaluable. It provides a set of small utilities that follow the ``Unix philosophy;'' each does one useful thing with a patch. The @@ -645,6 +676,122 @@ invocation of \command{filterdiff} can generate a smaller patch that only touches files whose names match a particular glob pattern. +\section{Good ways to work with patches} + +Whether you are working on a patch series to submit to a free software +or open source project, or a series that you intend to treat as a +sequence of regular changesets when you're done, you can use some +simple techniques to keep your work well organised. + +Give your patches descriptive names. A good name for a patch might be +\filename{rework-device-alloc.patch}, because it will immediately give +you a hint what the purpose of the patch is. Long names shouldn't be +a problem; you won't be typing the names often, but you \emph{will} be +running commands like \hgcmd{qapplied} and \hgcmd{qtop} over and over. +Good naming becomes especially important when you have a number of +patches to work with, or if you are juggling a number of different +tasks and your patches only get a fraction of your attention. + +Be aware of what patch you're working on. Use the \hgcmd{qtop} +command and skim over the text of your patches frequently---for +example, using \hgcmdargs{tip}{\hgopt{tip}{-p}})---to be sure of where +you stand. I have several times worked on and \hgcmd{qrefresh}ed a +patch other than the one I intended, and it's often tricky to migrate +changes into the right patch after making them in the wrong one. + +For this reason, it is very much worth investing a little time to +learn how to use some of the third-party tools I described in +section~\ref{sec:mq:tools}, particularly \command{diffstat} and +\command{filterdiff}. The former will give you a quick idea of what +changes your patch is making, while the latter makes it easy to splice +hunks selectively out of one patch and into another. + +\section{MQ cookbook} + +\subsection{Manage ``trivial'' patches} + +Because the overhead of dropping files into a new Mercurial repository +is so low, it makes a lot of sense to manage patches this way even if +you simply want to make a few changes to a source tarball that you +downloaded. + +Begin by downloading and unpacking the source tarball, +and turning it into a Mercurial repository. +\interaction{mq.tarball.download} + +Continue by creating a patch stack and making your changes. +\interaction{mq.tarball.qinit} + +Let's say a few weeks or months pass, and your package author releases +a new version. First, bring their changes into the repository. +\interaction{mq.tarball.newsource} +The pipeline starting with \hgcmd{locate} above deletes all files in +the working directory, so that \hgcmd{commit}'s +\hgopt{commit}{--addremove} option can actually tell which files have +really been removed in the newer version of the source. + +Finally, you can apply your patches on top of the new tree. +\interaction{mq.tarball.repush} + +\subsection{Combining entire patches} +\label{sec:mq:combine} + +It's easy to combine entire patches. + +\begin{enumerate} +\item \hgcmd{qpop} your applied patches until neither patch is + applied. +\item Concatenate the patches that you want to combine together: + \begin{codesample4} + cat patch-to-drop.patch >> patch-to-augment.patch + \end{codesample4} + The description from the first patch (if you have one) will be used + as the commit comment when you \hgcmd{qpush} the combined patch. + Edit the patch description if you need to. +\item Use the \hgcmd{qdel} command to delete the patch you're dropping + from the \sfilename{series} file. +\item \hgcmd{qpush} the combined patch. Fix up any rejects. +\item \hgcmd{qrefresh} the combined patch to tidy it up. +\end{enumerate} + +\subsection{Merging part of one patch into another} + +Merging \emph{part} of one patch into another is more difficult than +combining entire patches. + +If you want to move changes to entire files, you can use +\command{filterdiff}'s \cmdopt{filterdiff}{-i} and +\cmdopt{filterdiff}{-x} options to choose the modifications to snip +out of one patch, concatenating its output onto the end of the patch +you want to merge into. You usually won't need to modify the patch +you've merged the changes from. Instead, MQ will report some rejected +hunks when you \hgcmd{qpush} it (from the hunks you moved into the +other patch), and you can simply \hgcmd{qrefresh} the patch to drop +the duplicate hunks. + +If you have a patch that has multiple hunks modifying a file, and you +only want to move a few of those hunks, the job becomes more messy, +but you can still partly automate it. Use \cmdargs{lsdiff}{-nvv} to +print some metadata about the patch. +\interaction{mq.tools.lsdiff} + +This command prints three different kinds of number: +\begin{itemize} +\item a \emph{file number} to identify each file modified in the patch; +\item the line number within a modified file that a hunk starts at; and +\item a \emph{hunk number} to identify that hunk. +\end{itemize} + +You'll have to use some visual inspection, and reading of the patch, +to identify the file and hunk numbers you'll want, but you can then +pass them to to \command{filterdiff}'s \cmdopt{filterdiff}{--files} +and \cmdopt{filterdiff}{--hunks} options, to select exactly the file +and hunk you want to extract. + +Once you have this hunk, you can concatenate it onto the end of your +destination patch and continue with the remainder of +section~\ref{sec:mq:combine}. + %%% Local Variables: %%% mode: latex %%% TeX-master: "00book"