hgweb: side-by-side comparison functionality
authorwujek srujek
Sun, 08 Jul 2012 17:17:02 +0200
changeset 17202 1ae119269ddc
parent 17201 afd75476939e
child 17203 0cb55b5c19a3
hgweb: side-by-side comparison functionality Adds new web command to the core, ``comparison``, which enables colorful side-by-side change display, which for some might be much easier to work with than the standard line diff output. The idea how to implement comes from the SonicHq extension. The web interface gets a new link to call the comparison functionality. It lets users configure the amount of context lines around change blocks, or to show full files - check help (also in this changeset) for details and defaults. The setting in hgrc can be overridden by adding ``context=<value>`` to the request query string. The comparison creates addressable lines, so as to enable sharing links to specific lines, just as standard diff does. Incorporates updates to all web related styles. Known limitations: * the column diff is done against the first parent, just as the standard diff * this change allows examining diffs for single files only (as I am not sure if examining the whole changeset in this way would be helpful) * syntax highlighting of the output changes is not performed (enabling the highlight extension has no influence on it)
mercurial/help/config.txt
mercurial/hgweb/webcommands.py
mercurial/hgweb/webutil.py
mercurial/templates/coal/map
mercurial/templates/gitweb/fileannotate.tmpl
mercurial/templates/gitweb/filecomparison.tmpl
mercurial/templates/gitweb/filediff.tmpl
mercurial/templates/gitweb/filelog.tmpl
mercurial/templates/gitweb/filerevision.tmpl
mercurial/templates/gitweb/map
mercurial/templates/monoblue/fileannotate.tmpl
mercurial/templates/monoblue/filecomparison.tmpl
mercurial/templates/monoblue/filediff.tmpl
mercurial/templates/monoblue/filelog.tmpl
mercurial/templates/monoblue/filerevision.tmpl
mercurial/templates/monoblue/map
mercurial/templates/paper/fileannotate.tmpl
mercurial/templates/paper/filecomparison.tmpl
mercurial/templates/paper/filediff.tmpl
mercurial/templates/paper/filelog.tmpl
mercurial/templates/paper/filerevision.tmpl
mercurial/templates/paper/map
mercurial/templates/static/style-coal.css
mercurial/templates/static/style-gitweb.css
mercurial/templates/static/style-monoblue.css
mercurial/templates/static/style-paper.css
tests/test-hgweb-commands.t
tests/test-hgweb-diffs.t
tests/test-hgweb-filelog.t
tests/test-hgweb-removed.t
tests/test-hgweb.t
tests/test-highlight.t
--- a/mercurial/help/config.txt	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/help/config.txt	Sun Jul 08 17:17:02 2012 +0200
@@ -1377,6 +1377,12 @@
 ``errorlog``
     Where to output the error log. Default is stderr.
 
+``comparisoncontext``
+    Number of lines of context to show in side-by-side file comparison. If
+    negative or the value ``full``, whole files are shown. Default is 5.
+    This setting can be overridden by a ``context`` request parameter to the
+    ``comparison`` command, taking the same values.
+
 ``hidden``
     Whether to hide the repository in the hgwebdir index.
     Default is False.
--- a/mercurial/hgweb/webcommands.py	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/hgweb/webcommands.py	Sun Jul 08 17:17:02 2012 +0200
@@ -22,7 +22,7 @@
 __all__ = [
    'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev',
    'manifest', 'tags', 'bookmarks', 'branches', 'summary', 'filediff', 'diff',
-   'annotate', 'filelog', 'archive', 'static', 'graph', 'help',
+   'comparison', 'annotate', 'filelog', 'archive', 'static', 'graph', 'help',
 ]
 
 def log(web, req, tmpl):
@@ -586,6 +586,31 @@
 
 diff = filediff
 
+def comparison(web, req, tmpl):
+    ctx = webutil.changectx(web.repo, req)
+    path = webutil.cleanpath(web.repo, req.form['file'][0])
+    rename = path in ctx and webutil.renamelink(ctx[path]) or []
+
+    parsecontext = lambda v: v == 'full' and -1 or int(v)
+    if 'context' in req.form:
+        context = parsecontext(req.form['context'][0])
+    else:
+        context = parsecontext(web.config('web', 'comparisoncontext', '5'))
+
+    comparison = webutil.compare(tmpl, ctx, path, context)
+    return tmpl('filecomparison',
+                file=path,
+                node=hex(ctx.node()),
+                rev=ctx.rev(),
+                date=ctx.date(),
+                desc=ctx.description(),
+                author=ctx.user(),
+                rename=rename,
+                branch=webutil.nodebranchnodefault(ctx),
+                parent=webutil.parents(ctx),
+                child=webutil.children(ctx),
+                comparison=comparison)
+
 def annotate(web, req, tmpl):
     fctx = webutil.filectx(web.repo, req)
     f = fctx.path()
--- a/mercurial/hgweb/webutil.py	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/hgweb/webutil.py	Sun Jul 08 17:17:02 2012 +0200
@@ -6,10 +6,11 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
-import os, copy
+import os, mimetypes, copy
 from mercurial import match, patch, scmutil, error, ui, util
 from mercurial.i18n import _
 from mercurial.node import hex, nullid
+import difflib
 
 def up(p):
     if p[0] != "/":
@@ -220,6 +221,92 @@
     yield tmpl('diffblock', parity=parity.next(), blockno=blockno,
                lines=prettyprintlines(''.join(block), blockno))
 
+def compare(tmpl, ctx, path, context):
+    '''Generator function that provides side-by-side comparison data.'''
+
+    def filelines(f):
+        if util.binary(f.data()):
+            mt = mimetypes.guess_type(f.path())[0]
+            if not mt:
+                mt = 'application/octet-stream'
+            return [_('(binary file %s, hash: %s)') % (mt, hex(f.filenode()))]
+        return f.data().splitlines()
+
+    def compline(type, leftlineno, leftline, rightlineno, rightline):
+        lineid = leftlineno and ("l%s" % leftlineno) or ''
+        lineid += rightlineno and ("r%s" % rightlineno) or ''
+        return tmpl('comparisonline',
+                    type=type,
+                    lineid=lineid,
+                    leftlinenumber="% 6s" % (leftlineno or ''),
+                    leftline=leftline or '',
+                    rightlinenumber="% 6s" % (rightlineno or ''),
+                    rightline=rightline or '')
+
+    def getblock(opcodes):
+        for type, llo, lhi, rlo, rhi in opcodes:
+            len1 = lhi - llo
+            len2 = rhi - rlo
+            count = min(len1, len2)
+            for i in xrange(count):
+                yield compline(type=type,
+                               leftlineno=llo + i + 1,
+                               leftline=leftlines[llo + i],
+                               rightlineno=rlo + i + 1,
+                               rightline=rightlines[rlo + i])
+            if len1 > len2:
+                for i in xrange(llo + count, lhi):
+                    yield compline(type=type,
+                                   leftlineno=i + 1,
+                                   leftline=leftlines[i],
+                                   rightlineno=None,
+                                   rightline=None)
+            elif len2 > len1:
+                for i in xrange(rlo + count, rhi):
+                    yield compline(type=type,
+                                   leftlineno=None,
+                                   leftline=None,
+                                   rightlineno=i + 1,
+                                   rightline=rightlines[i])
+
+    if path in ctx:
+        fctx = ctx[path]
+        rightrev = fctx.filerev()
+        rightnode = fctx.filenode()
+        rightlines = filelines(fctx)
+        parents = fctx.parents()
+        if not parents:
+            leftrev = -1
+            leftnode = nullid
+            leftlines = ()
+        else:
+            pfctx = parents[0]
+            leftrev = pfctx.filerev()
+            leftnode = pfctx.filenode()
+            leftlines = filelines(pfctx)
+    else:
+        rightrev = -1
+        rightnode = nullid
+        rightlines = ()
+        fctx = ctx.parents()[0][path]
+        leftrev = fctx.filerev()
+        leftnode = fctx.filenode()
+        leftlines = filelines(fctx)
+
+    s = difflib.SequenceMatcher(None, leftlines, rightlines)
+    if context < 0:
+        blocks = [tmpl('comparisonblock', lines=getblock(s.get_opcodes()))]
+    else:
+        blocks = (tmpl('comparisonblock', lines=getblock(oc))
+                     for oc in s.get_grouped_opcodes(n=context))
+
+    yield tmpl('comparison',
+               leftrev=leftrev,
+               leftnode=hex(leftnode),
+               rightrev=rightrev,
+               rightnode=hex(rightnode),
+               blocks=blocks)
+
 def diffstatgen(ctx):
     '''Generator function that provides the diffstat data.'''
 
--- a/mercurial/templates/coal/map	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/coal/map	Sun Jul 08 17:17:02 2012 +0200
@@ -63,6 +63,7 @@
 filerevision = ../paper/filerevision.tmpl
 fileannotate = ../paper/fileannotate.tmpl
 filediff = ../paper/filediff.tmpl
+filecomparison = ../paper/filecomparison.tmpl
 filelog = ../paper/filelog.tmpl
 fileline = '
   <div class="parity{parity} source"><a href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</div>'
@@ -83,6 +84,26 @@
 difflineat = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> <span class="atline">{line|escape}</span>'
 diffline = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}'
 
+comparison = '
+  <table class="bigtable">
+    <thead class="header">
+      <tr>
+        <th>{leftrev}:{leftnode|short}</th>
+        <th>{rightrev}:{rightnode|short}</th>
+      </tr>
+    </thead>
+    {blocks}
+  </table>'
+comparisonblock ='
+  <tbody class="block">
+  {lines}
+  </tbody>'
+comparisonline = '
+  <tr>
+    <td class="source {type}"><a href="#{lineid}" id="{lineid}">{leftlinenumber}</a> {leftline|escape}</td>
+    <td class="source {type}"><a href="#{lineid}" id="{lineid}">{rightlinenumber}</a> {rightline|escape}</td>
+  </tr>'
+
 changelogparent = '
   <tr>
     <th class="parent">parent {rev}:</th>
--- a/mercurial/templates/gitweb/fileannotate.tmpl	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/gitweb/fileannotate.tmpl	Sun Jul 08 17:17:02 2012 +0200
@@ -26,6 +26,7 @@
 <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
 annotate |
 <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
+<a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
 <a href="{url}raw-annotate/{node|short}/{file|urlescape}">raw</a> |
 <a href="{url}help{sessionvars%urlparameter}">help</a>
 <br/>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/templates/gitweb/filecomparison.tmpl	Sun Jul 08 17:17:02 2012 +0200
@@ -0,0 +1,63 @@
+{header}
+<title>{repo|escape}: comparison {file|escape}</title>
+<link rel="alternate" type="application/atom+xml"
+   href="{url}atom-log" title="Atom feed for {repo|escape}"/>
+<link rel="alternate" type="application/rss+xml"
+   href="{url}rss-log" title="RSS feed for {repo|escape}"/>
+</head>
+<body>
+
+<div class="page_header">
+<a href="{logourl}" title="Mercurial" style="float: right;">Mercurial</a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / comparison
+</div>
+
+<div class="page_nav">
+<a href="{url}summary{sessionvars%urlparameter}">summary</a> |
+<a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> |
+<a href="{url}log{sessionvars%urlparameter}">changelog</a> |
+<a href="{url}graph{sessionvars%urlparameter}">graph</a> |
+<a href="{url}tags{sessionvars%urlparameter}">tags</a> |
+<a href="{url}bookmarks{sessionvars%urlparameter}">bookmarks</a> |
+<a href="{url}branches{sessionvars%urlparameter}">branches</a> |
+<a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a> |
+<a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> |
+<a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
+<a href="{url}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a> |
+<a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
+<a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
+<a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
+comparison |
+<a href="{url}raw-diff/{node|short}/{file|urlescape}">raw</a> |
+<a href="{url}help{sessionvars%urlparameter}">help</a>
+<br/>
+</div>
+
+<div class="title">{file|escape}</div>
+
+<table>
+{branch%filerevbranch}
+<tr>
+ <td>changeset {rev}</td>
+ <td style="font-family:monospace"><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>
+{parent%filecompparent}
+{child%filecompchild}
+</table>
+
+<div class="list_head"></div>
+
+<div class="page_body">
+
+<div class="legend">
+  <span class="legendinfo equal">equal</span>
+  <span class="legendinfo delete">deleted</span>
+  <span class="legendinfo insert">inserted</span>
+  <span class="legendinfo replace">replaced</span>
+</div>
+
+<div class="comparison">
+{comparison}
+</div>
+
+</div>
+
+{footer}
--- a/mercurial/templates/gitweb/filediff.tmpl	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/gitweb/filediff.tmpl	Sun Jul 08 17:17:02 2012 +0200
@@ -26,6 +26,7 @@
 <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
 diff |
+<a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
 <a href="{url}raw-diff/{node|short}/{file|urlescape}">raw</a> |
 <a href="{url}help{sessionvars%urlparameter}">help</a>
 <br/>
--- a/mercurial/templates/gitweb/filelog.tmpl	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/gitweb/filelog.tmpl	Sun Jul 08 17:17:02 2012 +0200
@@ -23,6 +23,7 @@
 revisions |
 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
 <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
+<a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
 <a href="{url}rss-log/tip/{file|urlescape}">rss</a> |
 <a href="{url}help{sessionvars%urlparameter}">help</a>
 <br/>
--- a/mercurial/templates/gitweb/filerevision.tmpl	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/gitweb/filerevision.tmpl	Sun Jul 08 17:17:02 2012 +0200
@@ -26,6 +26,7 @@
 <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a> |
 <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
 <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
+<a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
 <a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a> |
 <a href="{url}help{sessionvars%urlparameter}">help</a>
 <br/>
--- a/mercurial/templates/gitweb/map	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/gitweb/map	Sun Jul 08 17:17:02 2012 +0200
@@ -26,6 +26,7 @@
       <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
       <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
       <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
+      <a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
       <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
     </td>
   </tr>'
@@ -37,6 +38,7 @@
       file |
       annotate |
       <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
+      <a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
       <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
     </td>
   </tr>'
@@ -81,6 +83,7 @@
 filerevision = filerevision.tmpl
 fileannotate = fileannotate.tmpl
 filediff = filediff.tmpl
+filecomparison = filecomparison.tmpl
 filelog = filelog.tmpl
 fileline = '
   <div style="font-family:monospace" class="parity{parity}">
@@ -99,6 +102,27 @@
 difflineminus = '<span style="color:#cc0000;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
 difflineat = '<span style="color:#990099;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
 diffline = '<span><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
+
+comparison = '
+  <table style="border-collapse:collapse;">
+    <thead class="header">
+      <tr>
+        <th>{leftrev}:{leftnode|short}</th>
+        <th>{rightrev}:{rightnode|short}</th>
+      </tr>
+    </thead>
+    {blocks}
+  </table>'
+comparisonblock ='
+  <tbody class="block">
+  {lines}
+  </tbody>'
+comparisonline = '
+  <tr style="font-family:monospace">
+    <td class="{type}"><pre><a class="linenr" href="#{lineid}" id="{lineid}">{leftlinenumber}</a> {leftline|escape}</pre></td>
+    <td class="{type}"><pre><a class="linenr" href="#{lineid}" id="{lineid}">{rightlinenumber}</a> {rightline|escape}</pre></td>
+  </tr>'
+
 changelogparent = '
   <tr>
     <th class="parent">parent {rev}:</th>
@@ -203,6 +227,15 @@
       </a>
     </td>
   </tr>'
+filecompparent = '
+  <tr>
+    <td>parent {rev}</td>
+    <td style="font-family:monospace">
+      <a class="list" href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">
+        {node|short}
+      </a>
+    </td>
+  </tr>'
 filelogparent = '
   <tr>
     <td align="right">parent {rev}:&nbsp;</td>
@@ -215,6 +248,13 @@
       <a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
     </td>
   </tr>'
+filecompchild = '
+  <tr>
+    <td>child {rev}</td>
+    <td style="font-family:monospace">
+      <a class="list" href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a>
+    </td>
+  </tr>'
 filelogchild = '
   <tr>
     <td align="right">child {rev}:&nbsp;</td>
--- a/mercurial/templates/monoblue/fileannotate.tmpl	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/monoblue/fileannotate.tmpl	Sun Jul 08 17:17:02 2012 +0200
@@ -35,6 +35,7 @@
         <li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a></li>
         <li class="current">annotate</li>
         <li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
+        <li><a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
         <li><a href="{url}raw-annotate/{node|short}/{file|urlescape}">raw</a></li>
     </ul>
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/templates/monoblue/filecomparison.tmpl	Sun Jul 08 17:17:02 2012 +0200
@@ -0,0 +1,64 @@
+{header}
+<title>{repo|escape}: comparison {file|escape}</title>
+    <link rel="alternate" type="application/atom+xml" href="{url}atom-log" title="Atom feed for {repo|escape}"/>
+    <link rel="alternate" type="application/rss+xml" href="{url}rss-log" title="RSS feed for {repo|escape}"/>
+</head>
+
+<body>
+<div id="container">
+    <div class="page-header">
+        <h1><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / file comparison</h1>
+
+        <form action="{url}log">
+            {sessionvars%hiddenformentry}
+            <dl class="search">
+                <dt><label>Search: </label></dt>
+                <dd><input type="text" name="rev" /></dd>
+            </dl>
+        </form>
+
+        <ul class="page-nav">
+            <li><a href="{url}summary{sessionvars%urlparameter}">summary</a></li>
+            <li><a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a></li>
+            <li><a href="{url}log{sessionvars%urlparameter}">changelog</a></li>
+            <li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
+            <li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
+            <li><a href="{url}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
+            <li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
+            <li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">files</a></li>
+            <li><a href="{url}help{sessionvars%urlparameter}">help</a></li>
+        </ul>
+    </div>
+
+    <ul class="submenu">
+        <li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
+        <li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a></li>
+        <li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
+        <li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
+        <li class="current">comparison</li>
+        <li><a href="{url}raw-diff/{node|short}/{file|urlescape}">raw</a></li>
+    </ul>
+
+    <h2 class="no-link no-border">comparison: {file|escape}</h2>
+    <h3 class="changeset">{file|escape}</h3>
+
+    <dl class="overview">
+        {branch%filerevbranch}
+        <dt>changeset {rev}</dt>
+        <dd><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>
+        {parent%filecompparent}
+        {child%filecompchild}
+    </dl>
+
+    <div class="legend">
+      <span class="legendinfo equal">equal</span>
+      <span class="legendinfo delete">deleted</span>
+      <span class="legendinfo insert">inserted</span>
+      <span class="legendinfo replace">replaced</span>
+    </div>
+
+    <div class="comparison">
+    {comparison}
+    </div>
+
+{footer}
--- a/mercurial/templates/monoblue/filediff.tmpl	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/monoblue/filediff.tmpl	Sun Jul 08 17:17:02 2012 +0200
@@ -35,6 +35,7 @@
         <li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a></li>
         <li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
         <li class="current">diff</li>
+        <li><a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
         <li><a href="{url}raw-diff/{node|short}/{file|urlescape}">raw</a></li>
     </ul>
 
--- a/mercurial/templates/monoblue/filelog.tmpl	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/monoblue/filelog.tmpl	Sun Jul 08 17:17:02 2012 +0200
@@ -35,6 +35,7 @@
         <li class="current">revisions</li>
         <li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
         <li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
+        <li><a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
         <li><a href="{url}rss-log/tip/{file|urlescape}">rss</a></li>
     </ul>
 
--- a/mercurial/templates/monoblue/filerevision.tmpl	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/monoblue/filerevision.tmpl	Sun Jul 08 17:17:02 2012 +0200
@@ -35,6 +35,7 @@
         <li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a></li>
         <li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
         <li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
+        <li><a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
         <li><a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a></li>
     </ul>
 
--- a/mercurial/templates/monoblue/map	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/monoblue/map	Sun Jul 08 17:17:02 2012 +0200
@@ -26,6 +26,7 @@
       <a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a> |
       <a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a> |
       <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
+      <a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
       <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
     </td>
   </tr>'
@@ -37,6 +38,7 @@
       file |
       annotate |
       <a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a> |
+      <a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a> |
       <a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">revisions</a>
     </td>
   </tr>'
@@ -74,6 +76,7 @@
 filerevision = filerevision.tmpl
 fileannotate = fileannotate.tmpl
 filediff = filediff.tmpl
+filecomparison = filecomparison.tmpl
 filelog = filelog.tmpl
 fileline = '
   <div style="font-family:monospace" class="parity{parity}">
@@ -94,6 +97,27 @@
 difflineminus = '<span style="color:#cc0000;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
 difflineat = '<span style="color:#990099;"><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
 diffline = '<span><a class="linenr" href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</span>'
+
+comparison = '
+  <table class="bigtable">
+    <thead class="header">
+      <tr>
+        <th>{leftrev}:{leftnode|short}</th>
+        <th>{rightrev}:{rightnode|short}</th>
+      </tr>
+    </thead>
+    {blocks}
+  </table>'
+comparisonblock ='
+  <tbody class="block">
+  {lines}
+  </tbody>'
+comparisonline = '
+  <tr>
+    <td class="source {type}"><a class="linenr" href="#{lineid}" id="{lineid}">{leftlinenumber}</a> {leftline|escape}</td>
+    <td class="source {type}"><a class="linenr" href="#{lineid}" id="{lineid}">{rightlinenumber}</a> {rightline|escape}</td>
+  </tr>'
+
 changelogparent = '
   <tr>
     <th class="parent">parent {rev}:</th>
@@ -176,6 +200,9 @@
 filediffparent = '
   <dt>parent {rev}</dt>
   <dd><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>'
+filecompparent = '
+  <dt>parent {rev}</dt>
+  <dd><a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>'
 filelogparent = '
   <tr>
     <td align="right">parent {rev}:&nbsp;</td>
@@ -184,6 +211,9 @@
 filediffchild = '
   <dt>child {rev}</dt>
   <dd><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>'
+filecompchild = '
+  <dt>child {rev}</dt>
+  <dd><a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>'
 filelogchild = '
   <tr>
     <td align="right">child {rev}:&nbsp;</td>
--- a/mercurial/templates/paper/fileannotate.tmpl	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/paper/fileannotate.tmpl	Sun Jul 08 17:17:02 2012 +0200
@@ -25,6 +25,7 @@
 <li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
 <li><a href="{url}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
 <li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
+<li><a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
 <li class="active">annotate</li>
 <li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
 <li><a href="{url}raw-annotate/{node|short}/{file|urlescape}">raw</a></li>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/templates/paper/filecomparison.tmpl	Sun Jul 08 17:17:02 2012 +0200
@@ -0,0 +1,85 @@
+{header}
+<title>{repo|escape}: {file|escape} comparison</title>
+</head>
+<body>
+
+<div class="container">
+<div class="menu">
+<div class="logo">
+<a href="{logourl}">
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
+</div>
+<ul>
+<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
+<li><a href="{url}graph/{node|short}{sessionvars%urlparameter}">graph</a></li>
+<li><a href="{url}tags{sessionvars%urlparameter}">tags</a></li>
+<li><a href="{url}bookmarks{sessionvars%urlparameter}">bookmarks</a></li>
+<li><a href="{url}branches{sessionvars%urlparameter}">branches</a></li>
+</ul>
+<ul>
+<li><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a></li>
+<li><a href="{url}file/{node|short}{path|urlescape}{sessionvars%urlparameter}">browse</a></li>
+</ul>
+<ul>
+<li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
+<li><a href="{url}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
+<li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
+<li class="active">comparison</li>
+<li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
+<li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
+<li><a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a></li>
+</ul>
+<ul>
+<li><a href="{url}help{sessionvars%urlparameter}">help</a></li>
+</ul>
+</div>
+
+<div class="main">
+<h2><a href="{url}{sessionvars%urlparameter}">{repo|escape}</a></h2>
+<h3>comparison {file|escape} @ {rev}:{node|short}</h3>
+
+<form class="search" action="{url}log">
+<p>{sessionvars%hiddenformentry}</p>
+<p><input name="rev" id="search1" type="text" size="30" /></p>
+<div id="hint">find changesets by author, revision,
+files, or words in the commit message</div>
+</form>
+
+<div class="description">{desc|strip|escape|nonempty}</div>
+
+<table id="changesetEntry">
+<tr>
+ <th>author</th>
+ <td>{author|obfuscate}</td>
+</tr>
+<tr>
+ <th>date</th>
+ <td class="date age">{date|rfc822date}</td>
+</tr>
+<tr>
+ <th>parents</th>
+ <td>{parent%filerevparent}</td>
+</tr>
+<tr>
+ <th>children</th>
+ <td>{child%filerevchild}</td>
+</tr>
+{changesettag}
+</table>
+
+<div class="overflow">
+<div class="sourcefirst">   comparison</div>
+<div class="legend">
+  <span class="legendinfo equal">equal</span>
+  <span class="legendinfo delete">deleted</span>
+  <span class="legendinfo insert">inserted</span>
+  <span class="legendinfo replace">replaced</span>
+</div>
+
+{comparison}
+
+</div>
+</div>
+</div>
+
+{footer}
--- a/mercurial/templates/paper/filediff.tmpl	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/paper/filediff.tmpl	Sun Jul 08 17:17:02 2012 +0200
@@ -24,6 +24,7 @@
 <li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
 <li><a href="{url}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
 <li class="active">diff</li>
+<li><a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
 <li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
 <li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
 <li><a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a></li>
--- a/mercurial/templates/paper/filelog.tmpl	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/paper/filelog.tmpl	Sun Jul 08 17:17:02 2012 +0200
@@ -27,6 +27,7 @@
 <ul>
 <li><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file</a></li>
 <li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
+<li><a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
 <li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
 <li class="active">file log</li>
 <li><a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a></li>
--- a/mercurial/templates/paper/filerevision.tmpl	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/paper/filerevision.tmpl	Sun Jul 08 17:17:02 2012 +0200
@@ -23,6 +23,7 @@
 <li class="active">file</li>
 <li><a href="{url}file/tip/{file|urlescape}{sessionvars%urlparameter}">latest</a></li>
 <li><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">diff</a></li>
+<li><a href="{url}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">comparison</a></li>
 <li><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">annotate</a></li>
 <li><a href="{url}log/{node|short}/{file|urlescape}{sessionvars%urlparameter}">file log</a></li>
 <li><a href="{url}raw-file/{node|short}/{file|urlescape}">raw</a></li>
--- a/mercurial/templates/paper/map	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/paper/map	Sun Jul 08 17:17:02 2012 +0200
@@ -62,6 +62,7 @@
 filerevision = filerevision.tmpl
 fileannotate = fileannotate.tmpl
 filediff = filediff.tmpl
+filecomparison = filecomparison.tmpl
 filelog = filelog.tmpl
 fileline = '
   <div class="parity{parity} source"><a href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}</div>'
@@ -82,6 +83,26 @@
 difflineat = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> <span class="atline">{line|escape}</span>'
 diffline = '<a href="#{lineid}" id="{lineid}">{linenumber}</a> {line|escape}'
 
+comparison = '
+  <table class="bigtable">
+    <thead class="header">
+      <tr>
+        <th>{leftrev}:{leftnode|short}</th>
+        <th>{rightrev}:{rightnode|short}</th>
+      </tr>
+    </thead>
+    {blocks}
+  </table>'
+comparisonblock ='
+  <tbody class="block">
+  {lines}
+  </tbody>'
+comparisonline = '
+  <tr>
+    <td class="source {type}"><a href="#{lineid}" id="{lineid}">{leftlinenumber}</a> {leftline|escape}</td>
+    <td class="source {type}"><a href="#{lineid}" id="{lineid}">{rightlinenumber}</a> {rightline|escape}</td>
+  </tr>'
+
 changelogparent = '
   <tr>
     <th class="parent">parent {rev}:</th>
--- a/mercurial/templates/static/style-coal.css	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/static/style-coal.css	Sun Jul 08 17:17:02 2012 +0200
@@ -286,3 +286,40 @@
 	position: relative;
 	top: -3px;
 }
+
+/* Comparison */
+.legend {
+    padding: 1.5% 0 1.5% 0;
+}
+
+.legendinfo {
+    border: 1px solid #999;
+    font-size: 80%;
+    text-align: center;
+    padding: 0.5%;
+}
+
+.equal {
+    background-color: #ffffff;
+}
+
+.delete {
+    background-color: black;
+    color: white;
+}
+
+.insert {
+    background-color: #d0d0d0;
+}
+
+.replace {
+    background-color: #f9f9f9;
+}
+
+.header {
+    text-align: center;
+}
+
+.block {
+    border-top: 1px solid #999;
+}
--- a/mercurial/templates/static/style-gitweb.css	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/static/style-gitweb.css	Sun Jul 08 17:17:02 2012 +0200
@@ -126,3 +126,43 @@
 	top: -3px;
 	font-style: italic;
 }
+
+/* Comparison */
+.legend {
+    padding: 1.5% 0 1.5% 0;
+}
+
+.legendinfo {
+    border: 1px solid #d9d8d1;
+    font-size: 80%;
+    text-align: center;
+    padding: 0.5%;
+}
+
+.equal {
+    background-color: #ffffff;
+}
+
+.delete {
+    background-color: #ffc5ce;
+}
+
+.insert {
+    background-color: #c5ffc4;
+}
+
+.replace {
+    background-color: #ffff99;
+}
+
+.comparison {
+    overflow-x: auto;
+}
+
+.header th {
+    text-align: center;
+}
+
+.block {
+    border-top: 1px solid #d9d8d1;
+}
--- a/mercurial/templates/static/style-monoblue.css	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/static/style-monoblue.css	Sun Jul 08 17:17:02 2012 +0200
@@ -477,3 +477,49 @@
 	position: relative;
 }
 /** end of canvas **/
+
+/** comparison **/
+.legend {
+    margin-left: 20px;
+    padding: 1.5% 0 1.5% 0;
+}
+
+.legendinfo {
+    border: 1px solid #999;
+    font-size: 80%;
+    text-align: center;
+    padding: 0.5%;
+}
+
+.equal {
+    background-color: #ffffff;
+}
+
+.delete {
+    background-color: #ffc5ce;
+}
+
+.insert {
+    background-color: #c5ffc4;
+}
+
+.replace {
+    background-color: #ffff99;
+}
+
+.comparison {
+    overflow-x: auto;
+}
+
+.comparison table td {
+    padding: 0px 5px;
+}
+
+.header th {
+    font-weight: bold;
+}
+
+.block {
+    border-top: 1px solid #999;
+}
+/** end of comparison **/
--- a/mercurial/templates/static/style-paper.css	Fri Jul 06 13:56:40 2012 -0700
+++ b/mercurial/templates/static/style-paper.css	Sun Jul 08 17:17:02 2012 +0200
@@ -275,3 +275,39 @@
 	position: relative;
 	top: -3px;
 }
+
+/* Comparison */
+.legend {
+    padding: 1.5% 0 1.5% 0;
+}
+
+.legendinfo {
+    border: 1px solid #999;
+    font-size: 80%;
+    text-align: center;
+    padding: 0.5%;
+}
+
+.equal {
+    background-color: #ffffff;
+}
+
+.delete {
+    background-color: #ffc5ce;
+}
+
+.insert {
+    background-color: #c5ffc4;
+}
+
+.replace {
+    background-color: #ffff99;
+}
+
+.header {
+    text-align: center;
+}
+
+.block {
+    border-top: 1px solid #999;
+}
--- a/tests/test-hgweb-commands.t	Fri Jul 06 13:56:40 2012 -0700
+++ b/tests/test-hgweb-commands.t	Sun Jul 08 17:17:02 2012 +0200
@@ -605,6 +605,7 @@
   <li class="active">file</li>
   <li><a href="/file/tip/foo">latest</a></li>
   <li><a href="/diff/a4f92ed23982/foo">diff</a></li>
+  <li><a href="/comparison/a4f92ed23982/foo">comparison</a></li>
   <li><a href="/annotate/a4f92ed23982/foo">annotate</a></li>
   <li><a href="/log/a4f92ed23982/foo">file log</a></li>
   <li><a href="/raw-file/a4f92ed23982/foo">raw</a></li>
--- a/tests/test-hgweb-diffs.t	Fri Jul 06 13:56:40 2012 -0700
+++ b/tests/test-hgweb-diffs.t	Sun Jul 08 17:17:02 2012 +0200
@@ -227,6 +227,7 @@
   <li><a href="/file/559edbd9ed20/b">file</a></li>
   <li><a href="/file/tip/b">latest</a></li>
   <li class="active">diff</li>
+  <li><a href="/comparison/559edbd9ed20/b">comparison</a></li>
   <li><a href="/annotate/559edbd9ed20/b">annotate</a></li>
   <li><a href="/log/559edbd9ed20/b">file log</a></li>
   <li><a href="/raw-file/559edbd9ed20/b">raw</a></li>
@@ -491,6 +492,7 @@
   <li><a href="/file/559edbd9ed20/a">file</a></li>
   <li><a href="/file/tip/a">latest</a></li>
   <li class="active">diff</li>
+  <li><a href="/comparison/559edbd9ed20/a">comparison</a></li>
   <li><a href="/annotate/559edbd9ed20/a">annotate</a></li>
   <li><a href="/log/559edbd9ed20/a">file log</a></li>
   <li><a href="/raw-file/559edbd9ed20/a">raw</a></li>
@@ -549,6 +551,378 @@
   </body>
   </html>
   
+
+comparison new file
+
+  $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'comparison/0/a'
+  200 Script output follows
+  
+  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
+  <head>
+  <link rel="icon" href="/static/hgicon.png" type="image/png" />
+  <meta name="robots" content="index, nofollow" />
+  <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
+  <script type="text/javascript" src="/static/mercurial.js"></script>
+  
+  <title>test: a comparison</title>
+  </head>
+  <body>
+  
+  <div class="container">
+  <div class="menu">
+  <div class="logo">
+  <a href="http://mercurial.selenic.com/">
+  <img src="/static/hglogo.png" alt="mercurial" /></a>
+  </div>
+  <ul>
+  <li><a href="/shortlog/0cd96de13884">log</a></li>
+  <li><a href="/graph/0cd96de13884">graph</a></li>
+  <li><a href="/tags">tags</a></li>
+  <li><a href="/bookmarks">bookmarks</a></li>
+  <li><a href="/branches">branches</a></li>
+  </ul>
+  <ul>
+  <li><a href="/rev/0cd96de13884">changeset</a></li>
+  <li><a href="/file/0cd96de13884">browse</a></li>
+  </ul>
+  <ul>
+  <li><a href="/file/0cd96de13884/a">file</a></li>
+  <li><a href="/file/tip/a">latest</a></li>
+  <li><a href="/diff/0cd96de13884/a">diff</a></li>
+  <li class="active">comparison</li>
+  <li><a href="/annotate/0cd96de13884/a">annotate</a></li>
+  <li><a href="/log/0cd96de13884/a">file log</a></li>
+  <li><a href="/raw-file/0cd96de13884/a">raw</a></li>
+  </ul>
+  <ul>
+  <li><a href="/help">help</a></li>
+  </ul>
+  </div>
+  
+  <div class="main">
+  <h2><a href="/">test</a></h2>
+  <h3>comparison a @ 0:0cd96de13884</h3>
+  
+  <form class="search" action="/log">
+  <p></p>
+  <p><input name="rev" id="search1" type="text" size="30" /></p>
+  <div id="hint">find changesets by author, revision,
+  files, or words in the commit message</div>
+  </form>
+  
+  <div class="description">a</div>
+  
+  <table id="changesetEntry">
+  <tr>
+   <th>author</th>
+   <td>&#116;&#101;&#115;&#116;</td>
+  </tr>
+  <tr>
+   <th>date</th>
+   <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
+  </tr>
+  <tr>
+   <th>parents</th>
+   <td></td>
+  </tr>
+  <tr>
+   <th>children</th>
+   <td><a href="/file/559edbd9ed20/a">559edbd9ed20</a> </td>
+  </tr>
+  
+  </table>
+  
+  <div class="overflow">
+  <div class="sourcefirst">   comparison</div>
+  <div class="legend">
+    <span class="legendinfo equal">equal</span>
+    <span class="legendinfo delete">deleted</span>
+    <span class="legendinfo insert">inserted</span>
+    <span class="legendinfo replace">replaced</span>
+  </div>
+  
+  
+  <table class="bigtable">
+  <thead class="header">
+  <tr>
+  <th>-1:000000000000</th>
+  <th>0:b789fdd96dc2</th>
+  </tr>
+  </thead>
+  
+  <tbody class="block">
+  
+  <tr>
+  <td class="source insert"><a href="#r1" id="r1">      </a> </td>
+  <td class="source insert"><a href="#r1" id="r1">     1</a> a</td>
+  </tr>
+  </tbody>
+  </table>
+  
+  </div>
+  </div>
+  </div>
+  
+  <script type="text/javascript">process_dates()</script>
+  
+  
+  </body>
+  </html>
+  
+
+comparison existing file
+
+  $ hg up
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo a >> a
+  $ hg ci -mc
+  $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'comparison/tip/a'
+  200 Script output follows
+  
+  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
+  <head>
+  <link rel="icon" href="/static/hgicon.png" type="image/png" />
+  <meta name="robots" content="index, nofollow" />
+  <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
+  <script type="text/javascript" src="/static/mercurial.js"></script>
+  
+  <title>test: a comparison</title>
+  </head>
+  <body>
+  
+  <div class="container">
+  <div class="menu">
+  <div class="logo">
+  <a href="http://mercurial.selenic.com/">
+  <img src="/static/hglogo.png" alt="mercurial" /></a>
+  </div>
+  <ul>
+  <li><a href="/shortlog/d73db4d812ff">log</a></li>
+  <li><a href="/graph/d73db4d812ff">graph</a></li>
+  <li><a href="/tags">tags</a></li>
+  <li><a href="/bookmarks">bookmarks</a></li>
+  <li><a href="/branches">branches</a></li>
+  </ul>
+  <ul>
+  <li><a href="/rev/d73db4d812ff">changeset</a></li>
+  <li><a href="/file/d73db4d812ff">browse</a></li>
+  </ul>
+  <ul>
+  <li><a href="/file/d73db4d812ff/a">file</a></li>
+  <li><a href="/file/tip/a">latest</a></li>
+  <li><a href="/diff/d73db4d812ff/a">diff</a></li>
+  <li class="active">comparison</li>
+  <li><a href="/annotate/d73db4d812ff/a">annotate</a></li>
+  <li><a href="/log/d73db4d812ff/a">file log</a></li>
+  <li><a href="/raw-file/d73db4d812ff/a">raw</a></li>
+  </ul>
+  <ul>
+  <li><a href="/help">help</a></li>
+  </ul>
+  </div>
+  
+  <div class="main">
+  <h2><a href="/">test</a></h2>
+  <h3>comparison a @ 2:d73db4d812ff</h3>
+  
+  <form class="search" action="/log">
+  <p></p>
+  <p><input name="rev" id="search1" type="text" size="30" /></p>
+  <div id="hint">find changesets by author, revision,
+  files, or words in the commit message</div>
+  </form>
+  
+  <div class="description">c</div>
+  
+  <table id="changesetEntry">
+  <tr>
+   <th>author</th>
+   <td>&#116;&#101;&#115;&#116;</td>
+  </tr>
+  <tr>
+   <th>date</th>
+   <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
+  </tr>
+  <tr>
+   <th>parents</th>
+   <td><a href="/file/559edbd9ed20/a">559edbd9ed20</a> </td>
+  </tr>
+  <tr>
+   <th>children</th>
+   <td></td>
+  </tr>
+  
+  </table>
+  
+  <div class="overflow">
+  <div class="sourcefirst">   comparison</div>
+  <div class="legend">
+    <span class="legendinfo equal">equal</span>
+    <span class="legendinfo delete">deleted</span>
+    <span class="legendinfo insert">inserted</span>
+    <span class="legendinfo replace">replaced</span>
+  </div>
+  
+  
+  <table class="bigtable">
+  <thead class="header">
+  <tr>
+  <th>0:b789fdd96dc2</th>
+  <th>1:a80d06849b33</th>
+  </tr>
+  </thead>
+  
+  <tbody class="block">
+  
+  <tr>
+  <td class="source equal"><a href="#l1r1" id="l1r1">     1</a> a</td>
+  <td class="source equal"><a href="#l1r1" id="l1r1">     1</a> a</td>
+  </tr>
+  <tr>
+  <td class="source insert"><a href="#r2" id="r2">      </a> </td>
+  <td class="source insert"><a href="#r2" id="r2">     2</a> a</td>
+  </tr>
+  </tbody>
+  </table>
+  
+  </div>
+  </div>
+  </div>
+  
+  <script type="text/javascript">process_dates()</script>
+  
+  
+  </body>
+  </html>
+  
+
+comparison removed file
+
+  $ hg rm a
+  $ hg ci -md
+  $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT 'comparison/tip/a'
+  200 Script output follows
+  
+  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
+  <head>
+  <link rel="icon" href="/static/hgicon.png" type="image/png" />
+  <meta name="robots" content="index, nofollow" />
+  <link rel="stylesheet" href="/static/style-paper.css" type="text/css" />
+  <script type="text/javascript" src="/static/mercurial.js"></script>
+  
+  <title>test: a comparison</title>
+  </head>
+  <body>
+  
+  <div class="container">
+  <div class="menu">
+  <div class="logo">
+  <a href="http://mercurial.selenic.com/">
+  <img src="/static/hglogo.png" alt="mercurial" /></a>
+  </div>
+  <ul>
+  <li><a href="/shortlog/20e80271eb7a">log</a></li>
+  <li><a href="/graph/20e80271eb7a">graph</a></li>
+  <li><a href="/tags">tags</a></li>
+  <li><a href="/bookmarks">bookmarks</a></li>
+  <li><a href="/branches">branches</a></li>
+  </ul>
+  <ul>
+  <li><a href="/rev/20e80271eb7a">changeset</a></li>
+  <li><a href="/file/20e80271eb7a">browse</a></li>
+  </ul>
+  <ul>
+  <li><a href="/file/20e80271eb7a/a">file</a></li>
+  <li><a href="/file/tip/a">latest</a></li>
+  <li><a href="/diff/20e80271eb7a/a">diff</a></li>
+  <li class="active">comparison</li>
+  <li><a href="/annotate/20e80271eb7a/a">annotate</a></li>
+  <li><a href="/log/20e80271eb7a/a">file log</a></li>
+  <li><a href="/raw-file/20e80271eb7a/a">raw</a></li>
+  </ul>
+  <ul>
+  <li><a href="/help">help</a></li>
+  </ul>
+  </div>
+  
+  <div class="main">
+  <h2><a href="/">test</a></h2>
+  <h3>comparison a @ 3:20e80271eb7a</h3>
+  
+  <form class="search" action="/log">
+  <p></p>
+  <p><input name="rev" id="search1" type="text" size="30" /></p>
+  <div id="hint">find changesets by author, revision,
+  files, or words in the commit message</div>
+  </form>
+  
+  <div class="description">d</div>
+  
+  <table id="changesetEntry">
+  <tr>
+   <th>author</th>
+   <td>&#116;&#101;&#115;&#116;</td>
+  </tr>
+  <tr>
+   <th>date</th>
+   <td class="date age">Thu, 01 Jan 1970 00:00:00 +0000</td>
+  </tr>
+  <tr>
+   <th>parents</th>
+   <td><a href="/file/d73db4d812ff/a">d73db4d812ff</a> </td>
+  </tr>
+  <tr>
+   <th>children</th>
+   <td></td>
+  </tr>
+  
+  </table>
+  
+  <div class="overflow">
+  <div class="sourcefirst">   comparison</div>
+  <div class="legend">
+    <span class="legendinfo equal">equal</span>
+    <span class="legendinfo delete">deleted</span>
+    <span class="legendinfo insert">inserted</span>
+    <span class="legendinfo replace">replaced</span>
+  </div>
+  
+  
+  <table class="bigtable">
+  <thead class="header">
+  <tr>
+  <th>1:a80d06849b33</th>
+  <th>-1:000000000000</th>
+  </tr>
+  </thead>
+  
+  <tbody class="block">
+  
+  <tr>
+  <td class="source delete"><a href="#l1" id="l1">     1</a> a</td>
+  <td class="source delete"><a href="#l1" id="l1">      </a> </td>
+  </tr>
+  <tr>
+  <td class="source delete"><a href="#l2" id="l2">     2</a> a</td>
+  <td class="source delete"><a href="#l2" id="l2">      </a> </td>
+  </tr>
+  </tbody>
+  </table>
+  
+  </div>
+  </div>
+  </div>
+  
+  <script type="text/javascript">process_dates()</script>
+  
+  
+  </body>
+  </html>
+  
+
   $ cd ..
 
 test import rev as raw-rev
--- a/tests/test-hgweb-filelog.t	Fri Jul 06 13:56:40 2012 -0700
+++ b/tests/test-hgweb-filelog.t	Sun Jul 08 17:17:02 2012 +0200
@@ -148,6 +148,7 @@
   <ul>
   <li><a href="/file/01de2d66a28d/a">file</a></li>
   <li><a href="/diff/01de2d66a28d/a">diff</a></li>
+  <li><a href="/comparison/01de2d66a28d/a">comparison</a></li>
   <li><a href="/annotate/01de2d66a28d/a">annotate</a></li>
   <li class="active">file log</li>
   <li><a href="/raw-file/01de2d66a28d/a">raw</a></li>
@@ -249,6 +250,7 @@
   <ul>
   <li><a href="/file/01de2d66a28d/a">file</a></li>
   <li><a href="/diff/01de2d66a28d/a">diff</a></li>
+  <li><a href="/comparison/01de2d66a28d/a">comparison</a></li>
   <li><a href="/annotate/01de2d66a28d/a">annotate</a></li>
   <li class="active">file log</li>
   <li><a href="/raw-file/01de2d66a28d/a">raw</a></li>
@@ -350,6 +352,7 @@
   <ul>
   <li><a href="/file/5ed941583260/a">file</a></li>
   <li><a href="/diff/5ed941583260/a">diff</a></li>
+  <li><a href="/comparison/5ed941583260/a">comparison</a></li>
   <li><a href="/annotate/5ed941583260/a">annotate</a></li>
   <li class="active">file log</li>
   <li><a href="/raw-file/5ed941583260/a">raw</a></li>
@@ -446,6 +449,7 @@
   <ul>
   <li><a href="/file/5ed941583260/a">file</a></li>
   <li><a href="/diff/5ed941583260/a">diff</a></li>
+  <li><a href="/comparison/5ed941583260/a">comparison</a></li>
   <li><a href="/annotate/5ed941583260/a">annotate</a></li>
   <li class="active">file log</li>
   <li><a href="/raw-file/5ed941583260/a">raw</a></li>
--- a/tests/test-hgweb-removed.t	Fri Jul 06 13:56:40 2012 -0700
+++ b/tests/test-hgweb-removed.t	Sun Jul 08 17:17:02 2012 +0200
@@ -171,6 +171,7 @@
   <li><a href="/file/c78f6c5cbea9/a">file</a></li>
   <li><a href="/file/tip/a">latest</a></li>
   <li class="active">diff</li>
+  <li><a href="/comparison/c78f6c5cbea9/a">comparison</a></li>
   <li><a href="/annotate/c78f6c5cbea9/a">annotate</a></li>
   <li><a href="/log/c78f6c5cbea9/a">file log</a></li>
   <li><a href="/raw-file/c78f6c5cbea9/a">raw</a></li>
--- a/tests/test-hgweb.t	Fri Jul 06 13:56:40 2012 -0700
+++ b/tests/test-hgweb.t	Sun Jul 08 17:17:02 2012 +0200
@@ -437,6 +437,46 @@
   	top: -3px;
   	font-style: italic;
   }
+  
+  /* Comparison */
+  .legend {
+      padding: 1.5% 0 1.5% 0;
+  }
+  
+  .legendinfo {
+      border: 1px solid #d9d8d1;
+      font-size: 80%;
+      text-align: center;
+      padding: 0.5%;
+  }
+  
+  .equal {
+      background-color: #ffffff;
+  }
+  
+  .delete {
+      background-color: #ffc5ce;
+  }
+  
+  .insert {
+      background-color: #c5ffc4;
+  }
+  
+  .replace {
+      background-color: #ffff99;
+  }
+  
+  .comparison {
+      overflow-x: auto;
+  }
+  
+  .header th {
+      text-align: center;
+  }
+  
+  .block {
+      border-top: 1px solid #d9d8d1;
+  }
   304 Not Modified
   
 
--- a/tests/test-highlight.t	Fri Jul 06 13:56:40 2012 -0700
+++ b/tests/test-highlight.t	Sun Jul 08 17:17:02 2012 +0200
@@ -92,6 +92,7 @@
   <li class="active">file</li>
   <li><a href="/file/tip/primes.py">latest</a></li>
   <li><a href="/diff/853dcd4de2a6/primes.py">diff</a></li>
+  <li><a href="/comparison/853dcd4de2a6/primes.py">comparison</a></li>
   <li><a href="/annotate/853dcd4de2a6/primes.py">annotate</a></li>
   <li><a href="/log/853dcd4de2a6/primes.py">file log</a></li>
   <li><a href="/raw-file/853dcd4de2a6/primes.py">raw</a></li>
@@ -222,6 +223,7 @@
   <li><a href="/file/853dcd4de2a6/primes.py">file</a></li>
   <li><a href="/file/tip/primes.py">latest</a></li>
   <li><a href="/diff/853dcd4de2a6/primes.py">diff</a></li>
+  <li><a href="/comparison/853dcd4de2a6/primes.py">comparison</a></li>
   <li class="active">annotate</li>
   <li><a href="/log/853dcd4de2a6/primes.py">file log</a></li>
   <li><a href="/raw-annotate/853dcd4de2a6/primes.py">raw</a></li>