Bryan O'Sullivan  committed 187702d

Piles of new content for MQ chapter - cookbook stuff.

  • Participants
  • Parent commits e6f4088
  • Branches default

Comments (0)

Files changed (10)

File en/99defs.tex

 \newcommand{\hgext}[1]{\index{\texttt{#1} extension}\texttt{#1}}
 \newcommand{\hgcmd}[1]{\index{\texttt{#1} command}``\texttt{hg #1}''}
 \newcommand{\command}[1]{\index{\texttt{#1} command}\texttt{#1}}
+\newcommand{\cmdargs}[2]{\index{\texttt{#1} command}\texttt{#1 #2}}
 \newcommand{\hgcmdargs}[2]{\index{\texttt{#1} command}``\texttt{hg #1 #2}''}
 \newcommand{\hgopt}[2]{\index{\texttt{#1} command!\texttt{#2} option}\texttt{#2}}
+\newcommand{\cmdopt}[2]{\index{\texttt{#1} command!\texttt{#2} option}\texttt{#2}}
 \newcommand{\package}[1]{\index{\texttt{#1} package}\texttt{#1}}
 example-sources := \
 	examples/run-example \
 	examples/mq.qinit-help \
+	examples/mq.diff \
+	examples/mq.tarball \
+	examples/ \
 latex-options = \

File en/examples/data/netplug-1.2.5.tar.bz2

Binary file added.

File en/examples/data/netplug-1.2.8.tar.bz2

Binary file added.

File en/examples/data/remove-redundant-null-checks.patch

+From: Jesper Juhl <>
+Remove redundant NULL chck before kfree + tiny CodingStyle cleanup for
+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;
+ 	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);

File en/examples/mq.diff

+#$ 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 newfile

File en/examples/mq.tarball

+cp $EXAMPLE_DIR/data/netplug-*.tar.bz2 .
+ln -s /bin/true download
+#$ 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 ' =' >> $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

File en/examples/

+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

File en/examples/run-example

         rcfp = open(rcfile, 'w')
         print >> rcfp, 'PS1="%s"' % self.prompt
         print >> rcfp, 'unset HISTFILE'
+        print >> rcfp, 'export EXAMPLE_DIR="%s"' % os.getcwd()
         print >> rcfp, 'export LANG=C'
         print >> rcfp, 'export LC_ALL=C'
         print >> rcfp, 'export TZ=GMT'
                         if nl: hunk += '\n'
                     # then its output
-                    ofp.write(output)
+                    ofp.write(tex_escape(output))
     for name in os.listdir(path):
         if name == 'run-example' or name.startswith('.'): continue
         if name.endswith('.out') or name.endswith('~'): continue
-        example(os.path.join(path, name)).run()
+        pathname = os.path.join(path, name)
+        if os.path.isfile(pathname):
+            example(pathname).run()
     print >> open(os.path.join(path, '.run'), 'w'), time.asctime()
 if __name__ == '__main__':
 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.
+  \interaction{mq.diff.diff}
+  \caption{Simple uses of the \command{diff} and \command{patch} commands}
+  \label{ex:mq:diff}
+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}
 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
 \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}
+\section{More about patches}
-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.
-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.
 confuse MQ's idea of which patches are applied.
 \section{Commands for working with patches}
 Once you've been working with patches for a while, you'll find
 yourself hungry for tools that will help you to understand and
 do clever things with prefixes of file names that inevitably confuse
 at least me.)
+  \interaction{}
+  \caption{The \command{diffstat}, \command{filterdiff}, and \command{lsdiff} commands}
+  \label{ex:mq:tools}
 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
 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
+Begin by downloading and unpacking the source tarball,
+and turning it into a Mercurial repository.
+Continue by creating a patch stack and making your changes.
+Let's say a few weeks or months pass, and your package author releases
+a new version.  First, bring their changes into the repository.
+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.
+\subsection{Combining entire patches}
+It's easy to combine entire patches.
+\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.
+\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.
+This command prints three different kinds of number:
+\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.
+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
 %%% Local Variables: 
 %%% mode: latex
 %%% TeX-master: "00book"