Mercurial > hgbook
changeset 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 | 2888fe6176b3 |
files | en/99defs.tex en/Makefile en/examples/data/netplug-1.2.5.tar.bz2 en/examples/data/netplug-1.2.8.tar.bz2 en/examples/data/remove-redundant-null-checks.patch en/examples/mq.diff en/examples/mq.tarball en/examples/mq.tools en/examples/run-example en/mq.tex |
diffstat | 10 files changed, 454 insertions(+), 40 deletions(-) [+] |
line wrap: on
line diff
--- a/en/99defs.tex Tue Jul 04 16:41:31 2006 -0700 +++ b/en/99defs.tex Fri Jul 07 19:56:53 2006 -0700 @@ -6,8 +6,10 @@ \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}} \newsavebox{\notebox}
--- a/en/Makefile Tue Jul 04 16:41:31 2006 -0700 +++ b/en/Makefile Fri Jul 07 19:56:53 2006 -0700 @@ -16,6 +16,9 @@ example-sources := \ examples/run-example \ examples/mq.qinit-help \ + examples/mq.diff \ + examples/mq.tarball \ + examples/mq.tools \ examples/mq.tutorial latex-options = \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/examples/data/remove-redundant-null-checks.patch Fri Jul 07 19:56:53 2006 -0700 @@ -0,0 +1,190 @@ + +From: Jesper Juhl <jesper.juhl@gmail.com> + +Remove redundant NULL chck before kfree + tiny CodingStyle cleanup for +drivers/ + +Signed-off-by: Jesper Juhl <jesper.juhl@gmail.com> +Signed-off-by: Andrew Morton <akpm@osdl.org> +--- + + 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); +_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/examples/mq.diff Fri Jul 07 19:56:53 2006 -0700 @@ -0,0 +1,12 @@ +#$ 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/examples/mq.tarball Fri Jul 07 19:56:53 2006 -0700 @@ -0,0 +1,48 @@ +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 '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 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/examples/mq.tools Fri Jul 07 19:56:53 2006 -0700 @@ -0,0 +1,9 @@ +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
--- a/en/examples/run-example Tue Jul 04 16:41:31 2006 -0700 +++ b/en/examples/run-example Fri Jul 07 19:56:53 2006 -0700 @@ -76,6 +76,7 @@ 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' @@ -117,7 +118,7 @@ if nl: hunk += '\n' ofp.write(hunk) # then its output - ofp.write(output) + ofp.write(tex_escape(output)) self.status('\n') finally: try: @@ -140,7 +141,9 @@ 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__':
--- 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"