# HG changeset patch # User Bryan O'Sullivan # Date 1161296287 25200 # Node ID 5b80c922ebddb38c2287b6b032b072c5efc24182 # Parent ff9dc8bc2a8b28491dc3137e940f3f11d6694543 More merge content. diff -r ff9dc8bc2a8b -r 5b80c922ebdd en/99defs.tex --- a/en/99defs.tex Wed Oct 18 15:47:04 2006 -0700 +++ b/en/99defs.tex Thu Oct 19 15:18:07 2006 -0700 @@ -108,7 +108,7 @@ % Graphics inclusion. \ifpdf - \newcommand{\grafix}[1]{\includegraphics{#1.pdf}} + \newcommand{\grafix}[1]{\includegraphics{#1}} \else \newcommand{\grafix}[1]{\includegraphics{#1.png}} \fi diff -r ff9dc8bc2a8b -r 5b80c922ebdd en/Makefile --- a/en/Makefile Wed Oct 18 15:47:04 2006 -0700 +++ b/en/Makefile Thu Oct 19 15:18:07 2006 -0700 @@ -19,11 +19,15 @@ tour-merge.tex image-sources := \ + kdiff3.png \ mq-stack.svg \ tour-history.svg \ - tour-merge-sep-repos.svg \ + tour-merge-conflict.svg \ + tour-merge-merge.svg \ tour-merge-pull.svg \ - tour-merge-merge.svg + tour-merge-sep-repos.svg + +image-svg := $(filter %.svg,$(image-sources)) example-sources := \ daily.files \ @@ -38,7 +42,8 @@ mq.tutorial \ template.simple \ template.svnstyle \ - tour + tour \ + tour-merge-conflict latex-options = \ -interaction batchmode \ @@ -134,5 +139,7 @@ echo -n $(hg_id) > build_id.tex clean: - rm -rf beta html pdf *.eps *.pdf *.png *.aux *.dvi *.log *.out \ + rm -rf beta html pdf \ + $(image-svg:%.svg=%.pdf) \ + $(image-svg:%.svg=%.png) \ examples/*.{out,run} examples/.run build_id.tex diff -r ff9dc8bc2a8b -r 5b80c922ebdd en/examples/run-example --- a/en/examples/run-example Wed Oct 18 15:47:04 2006 -0700 +++ b/en/examples/run-example Thu Oct 19 15:18:07 2006 -0700 @@ -39,7 +39,8 @@ class example: shell = '/usr/bin/env bash' - prompt = '__run_example_prompt__ ' + ps1 = '__run_example_ps1__ ' + ps2 = '__run_example_ps2__ ' pi_re = re.compile(r'#\$\s*(name):\s*(.*)$') timeout = 5 @@ -99,21 +100,23 @@ s = self.read() except OSError, err: if err.errno == errno.EIO: - return '' + return '', '' raise if self.verbose: print >> sys.stderr, self.debugrepr(s) out.write(s) s = out.getvalue() - if s.endswith(self.prompt): - return s.replace('\r\n', '\n')[:-len(self.prompt)] + 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): self.send(s) - r = self.receive() + ps, r = self.receive() if r.startswith(s): r = r[len(s):] - return r + return ps, r def run(self): ofp = None @@ -128,8 +131,8 @@ rcfile = os.path.join(tmpdir, '.bashrc') rcfp = open(rcfile, 'w') - print >> rcfp, 'PS1="%s"' % self.prompt - print >> rcfp, 'PS2="%s"' % self.prompt + print >> rcfp, 'PS1="%s"' % self.ps1 + print >> rcfp, 'PS2="%s"' % self.ps2 print >> rcfp, 'unset HISTFILE' print >> rcfp, 'export EXAMPLE_DIR="%s"' % os.getcwd() print >> rcfp, 'export LANG=C' @@ -153,12 +156,19 @@ os._exit(0) self.poll.register(self.cfd, select.POLLIN | select.POLLERR | select.POLLHUP) + + prompts = { + '': '', + self.ps1: '$', + self.ps2: '>', + } + try: try: # eat first prompt string from shell self.read() # setup env and prompt - self.sendreceive('source %s\n' % rcfile) + ps, output = self.sendreceive('source %s\n' % rcfile) for hunk in self.parse(): # is this line a processing instruction? m = self.pi_re.match(hunk) @@ -174,18 +184,20 @@ ofp = None elif hunk.strip(): # it's something we should execute - output = self.sendreceive(hunk) + newps, output = self.sendreceive(hunk) if not ofp: continue # first, print the command we ran if not hunk.startswith('#'): nl = hunk.endswith('\n') - hunk = ('$ \\textbf{%s}' % - tex_escape(hunk.rstrip('\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') open(self.name + '.run', 'w') except: @@ -195,7 +207,7 @@ raise else: try: - output = self.sendreceive('exit\n') + ps, output = self.sendreceive('exit\n') if ofp: ofp.write(output) os.close(self.cfd) diff -r ff9dc8bc2a8b -r 5b80c922ebdd en/examples/tour-merge-conflict --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/examples/tour-merge-conflict Thu Oct 19 15:18:07 2006 -0700 @@ -0,0 +1,71 @@ +#!/bin/bash + +hg init scam +cd scam + +#$ name: wife + +cat > letter.txt < letter.txt < letter.txt < letter.txt < + + + + + + + + + + + + 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 ff9dc8bc2a8b -r 5b80c922ebdd en/tour-merge.tex --- a/en/tour-merge.tex Wed Oct 18 15:47:04 2006 -0700 +++ b/en/tour-merge.tex Thu Oct 19 15:18:07 2006 -0700 @@ -113,12 +113,126 @@ \section{Merging conflicting changes} Most merges are simple affairs, but sometimes you'll find yourself -merging a change that you made with another, where both modify the -same portions of the same files. Unless both modifications are -identical, this results in a \emph{conflict}, where you have to decide -how to reconcile the different changes into something coherent. +merging changes where each modifies the same portions of the same +files. Unless both modifications are identical, this results in a +\emph{conflict}, where you have to decide how to reconcile the +different changes into something coherent. + +\begin{figure}[ht] + \centering + \grafix{tour-merge-conflict} + \caption{Conflicting changes to a document} + \label{fig:tour-merge:conflict} +\end{figure} + +Figure~\ref{fig:tour-merge:conflict} illustrates an instance of two +conflicting changes to a document. We started with a single version +of the file; then we made some changes; while someone else made +different changes to the same text. Our task in resolving the +conflicting changes is to decide what the file should look like. + +Mercurial doesn't have a built-in facility for handling conflicts. +Instead, it runs an external program called \command{hgmerge}. This +is a shell script that is bundled with Mercurial; you can change it to +behave however you please. What it does by default is try to find one +of several different merging tools that are likely to be installed on +your system. It first tries a few fully automatic merging tools; if +these don't succeed (because the resolution process requires human +guidance) or aren't present, the script tries a few different +graphical merging tools. + +It's also possible to get Mercurial to run another program or script +instead of \command{hgmerge}, by setting the \envar{HGMERGE} +environment variable to the name of your preferred program. + +\subsection{Using a graphical merge tool} + +My preferred graphical merge tool is \command{kdiff3}, which I'll use +to describe the features that are common to graphical file merging +tools. You can see a screenshot of \command{kdiff3} in action in +figure~\ref{fig:tour-merge:kdiff3}. The kind of merge it is +performing is called a \emph{three-way merge}, because there are three +different versions of the file of interest to us. The tool thus +splits the upper portion of the window into three panes: +\begin{itemize} +\item At the left is the \emph{base} version of the file, i.e.~the + most recent version from which the two versions we're trying to + merge are descended. +\item In the middle is ``our'' version of the file, with the contents + that we modified. +\item On the right is ``their'' version of the file, the one that + from the changeset that we're trying to merge with. +\end{itemize} +In the pane below these is the current \emph{result} of the merge. +Our task is to replace all of the red text, which indicates unresolved +conflicts, with some sensible merger of the ``ours'' and ``theirs'' +versions of the file. + +All four of these panes are \emph{locked together}; if we scroll +vertically or horizontally in any of them, the others are updated to +display the corresponding sections of their respective files. -\section{Using an extension to simplify merging} +\begin{figure}[ht] + \centering + \grafix{kdiff3} + \caption{Using \command{kdiff3} to merge versions of a file} + \label{fig:tour-merge:kdiff3} +\end{figure} + +For each conflicting portion of the file, we can choose to resolve +thhe conflict using some combination of text from the base version, +ours, or theirs. We can also manually edit the merged file at any +time, in case we need to make further modifications. + +There are \emph{many} file merging tools available, too many to cover +here. They vary in which platforms they are available for, and in +their particular strengths and weaknesses. Most are tuned for merging +files containing plain text, while a few are aimed at specialised file +formats (generally XML). + +\subsection{A worked example} + +In this example, we will reproduce the file modification history of +figure~\ref{fig:tour-merge:conflict} above. Let's begin by creating a +repository with a base version of our document. +\interaction{tour-merge-conflict.wife} +We'll clone the repository and make a change to the file. +\interaction{tour-merge-conflict.cousin} +And another clone, to simulate someone else making a change to the +file. (This hints at the idea that it's not all that unusual to merge +with yourself when you isolate tasks in separate repositories, and +indeed to find and resolve conflicts while doing so.) +\interaction{tour-merge-conflict.son} +Having created two different versions of the file, we'll set up an +environment suitable for running our merge. +\interaction{tour-merge-conflict.pull} + +In this example, I won't use Mercurial's normal \command{hgmerge} +program to do the merge, because it would drop my nice automated +example-running tool into a graphical user interface. Instead, I'll +set \envar{HGMERGE} to tell Mercurial to use the non-interactive +\command{merge} command. This is bundled with many Unix-like systems. +If you're following this example on your computer, don't bother +setting \envar{HGMERGE}. +\interaction{tour-merge-conflict.merge} +Because \command{merge} can't resolve the conflicting changes, it +leaves \emph{merge markers} inside the file that has conflicts, +indicating which lines have conflicts, and whether they came from our +version of the file or theirs. + +Mercurial can tell from the way \command{merge} exits that it wasn't +able to merge successfully, so it tells us what commands we'll need to +run if we want to redo the merging operation. This could be useful +if, for example, we were running a graphical merge tool and quit +because we were confused or realised we had made a mistake. + +If automatic or manual merges fail, there's nothing to prevent us from +``fixing up'' the affected files ourselves, and committing the results +of our merge: +\interaction{tour-merge-conflict.commit} + +\section{Simplifying the pull-merge-commit + sequence} The process of merging changes as outlined above is straightforward, but requires running three commands in sequence. @@ -127,9 +241,9 @@ hg merge hg commit -m 'Merged remote changes' \end{codesample2} -In the case of the final commit, you also need to come up with a -commit message, which is almost always going to be a piece of -uninteresting ``boilerplate'' text. +In the case of the final commit, you also need to enter a commit +message, which is almost always going to be a piece of uninteresting +``boilerplate'' text. It would be nice to reduce the number of steps needed, if this were possible. Indeed, Mercurial is distributed with an extension called