changeset 4488:649dd2492ae5

Merge with crew.
author Bryan O'Sullivan <bos@serpentine.com>
date Sat, 02 Jun 2007 09:04:23 -0700
parents ead2fa544cbf (current diff) b2b55acbacdd (diff)
children fc20fa9f2dfd
files mercurial/commands.py
diffstat 29 files changed, 271 insertions(+), 156 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -1594,7 +1594,7 @@ def clone(ui, source, dest=None, **opts)
                 destrev = heads.keys()
                 destrev.append(sr.changelog.parents(qbase)[0])
     ui.note(_('cloning main repo\n'))
-    sr, dr = hg.clone(ui, sr, dest,
+    sr, dr = hg.clone(ui, sr.url(), dest,
                       pull=opts['pull'],
                       rev=destrev,
                       update=False,
--- a/hgext/purge.py
+++ b/hgext/purge.py
@@ -32,7 +32,7 @@ from mercurial.i18n import _
 import os
 
 def dopurge(ui, repo, dirs=None, act=True, abort_on_err=False, eol='\n',
-            force=False):
+            force=False, include=None, exclude=None):
     def error(msg):
         if abort_on_err:
             raise util.Abort(msg)
@@ -51,7 +51,8 @@ def dopurge(ui, repo, dirs=None, act=Tru
     directories = []
     files = []
     missing = []
-    roots, match, anypats = util.cmdmatcher(repo.root, repo.getcwd(), dirs)
+    roots, match, anypats = util.cmdmatcher(repo.root, repo.getcwd(), dirs,
+                                            include, exclude)
     for src, f, st in repo.dirstate.statwalk(files=roots, match=match,
                                              ignored=True, directories=True):
         if src == 'd':
@@ -71,7 +72,7 @@ def dopurge(ui, repo, dirs=None, act=Tru
             remove(os.remove, f)
 
     for f in directories[::-1]:
-        if not os.listdir(repo.wjoin(f)):
+        if match(f) and not os.listdir(repo.wjoin(f)):
             ui.note(_('Removing directory %s\n') % f)
             remove(os.rmdir, f)
 
@@ -144,7 +145,9 @@ def purge(ui, repo, *dirs, **opts):
         # --print0 implies --print
         act = False
     force = bool(opts['force'])
-    dopurge(ui, repo, dirs, act, abort_on_err, eol, force)
+    include = opts['include']
+    exclude = opts['exclude']
+    dopurge(ui, repo, dirs, act, abort_on_err, eol, force, include, exclude)
 
 
 cmdtable = {
@@ -154,6 +157,8 @@ cmdtable = {
           ('f', 'force', None, _('purge even when missing files are detected')),
           ('p', 'print', None, _('print the file names instead of deleting them')),
           ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
-                                  ' (implies -p)'))],
+                                  ' (implies -p)')),
+          ('I', 'include', [], _('include names matching the given patterns')),
+          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
          _('hg purge [OPTION]... [DIR]...'))
 }
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -11,6 +11,15 @@ import os, sys, mdiff, bdiff, util, temp
 
 revrangesep = ':'
 
+def parseurl(url, revs):
+    '''parse url#branch, returning url, branch + revs'''
+
+    if '#' not in url:
+        return url, (revs or None)
+
+    url, rev = url.split('#', 1)
+    return url, revs + [rev]
+
 def revpair(repo, revs):
     '''return pair of nodes, given list of revisions. second item can
     be None, meaning use working dir.'''
@@ -160,9 +169,11 @@ def findrenames(repo, added=None, remove
                 for line in alines[x1:x2]:
                     equal += len(line)
 
-            myscore = equal*2.0 / (len(aa)+len(rr))
-            if myscore >= bestscore:
-                bestname, bestscore = r, myscore
+            lengths = len(aa) + len(rr)
+            if lengths:
+                myscore = equal*2.0 / lengths
+                if myscore >= bestscore:
+                    bestname, bestscore = r, myscore
         if bestname:
             yield bestname, a, bestscore
 
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -336,7 +336,8 @@ def bundle(ui, repo, fname, dest=None, *
                         visit.append(p)
     else:
         setremoteconfig(ui, opts)
-        dest = ui.expandpath(dest or 'default-push', dest or 'default')
+        dest, revs = cmdutil.parseurl(
+            ui.expandpath(dest or 'default-push', dest or 'default'), revs)
         other = hg.repository(ui, dest)
         o = repo.findoutgoing(other, force=opts['force'])
 
@@ -407,7 +408,7 @@ def clone(ui, source, dest=None, **opts)
     about ssh:// URLs.
     """
     setremoteconfig(ui, opts)
-    hg.clone(ui, ui.expandpath(source), dest,
+    hg.clone(ui, source, dest,
              pull=opts['pull'],
              stream=opts['uncompressed'],
              rev=opts['rev'],
@@ -1583,12 +1584,18 @@ def incoming(ui, repo, source="default",
 
     See pull for valid source format details.
     """
-    source = ui.expandpath(source)
+    source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
     setremoteconfig(ui, opts)
 
     other = hg.repository(ui, source)
     ui.status(_('comparing with %s\n') % source)
-    incoming = repo.findincoming(other, force=opts["force"])
+    if revs:
+        if 'lookup' in other.capabilities:
+            revs = [other.lookup(rev) for rev in revs]
+        else:
+            error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
+            raise util.Abort(error)
+    incoming = repo.findincoming(other, heads=revs, force=opts["force"])
     if not incoming:
         try:
             os.unlink(opts["bundle"])
@@ -1602,7 +1609,12 @@ def incoming(ui, repo, source="default",
         fname = opts["bundle"]
         if fname or not other.local():
             # create a bundle (uncompressed if other repo is not local)
-            cg = other.changegroup(incoming, "incoming")
+            if revs is None:
+                cg = other.changegroup(incoming, "incoming")
+            else:
+                if 'changegroupsubset' not in other.capabilities:
+                    raise util.Abort(_("Partial incoming cannot be done because other repository doesn't support changegroupsubset."))
+                cg = other.changegroupsubset(incoming, revs, 'incoming')
             bundletype = other.local() and "HG10BZ" or "HG10UN"
             fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
             # keep written bundle?
@@ -1612,9 +1624,6 @@ def incoming(ui, repo, source="default",
                 # use the created uncompressed bundlerepo
                 other = bundlerepo.bundlerepository(ui, repo.root, fname)
 
-        revs = None
-        if opts['rev']:
-            revs = [other.lookup(rev) for rev in opts['rev']]
         o = other.changelog.nodesbetween(incoming, revs)[0]
         if opts['newest_first']:
             o.reverse()
@@ -1877,11 +1886,11 @@ def outgoing(ui, repo, dest=None, **opts
 
     See pull for valid destination format details.
     """
-    dest = ui.expandpath(dest or 'default-push', dest or 'default')
+    dest, revs = cmdutil.parseurl(
+        ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
     setremoteconfig(ui, opts)
-    revs = None
-    if opts['rev']:
-        revs = [repo.lookup(rev) for rev in opts['rev']]
+    if revs:
+        revs = [repo.lookup(rev) for rev in revs]
 
     other = hg.repository(ui, dest)
     ui.status(_('comparing with %s\n') % dest)
@@ -1975,6 +1984,9 @@ def pull(ui, repo, source="default", **o
     allows access to a Mercurial repository where you simply use a web
     server to publish the .hg directory as static content.
 
+    An optional identifier after # indicates a particular branch, tag,
+    or changeset to pull.
+
     Some notes about using SSH with Mercurial:
     - SSH requires an accessible shell account on the destination machine
       and a copy of hg in the remote path or specified with as remotecmd.
@@ -1990,18 +2002,18 @@ def pull(ui, repo, source="default", **o
       Alternatively specify "ssh -C" as your ssh command in your hgrc or
       with the --ssh command line option.
     """
-    source = ui.expandpath(source)
+    source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
     setremoteconfig(ui, opts)
 
     other = hg.repository(ui, source)
     ui.status(_('pulling from %s\n') % (source))
-    revs = None
-    if opts['rev']:
+    if revs:
         if 'lookup' in other.capabilities:
-            revs = [other.lookup(rev) for rev in opts['rev']]
+            revs = [other.lookup(rev) for rev in revs]
         else:
             error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
             raise util.Abort(error)
+
     modheads = repo.pull(other, heads=revs, force=opts['force'])
     return postincoming(ui, repo, modheads, opts['update'])
 
@@ -2026,20 +2038,23 @@ def push(ui, repo, dest=None, **opts):
       http://[user@]host[:port]/[path]
       https://[user@]host[:port]/[path]
 
+    An optional identifier after # indicates a particular branch, tag,
+    or changeset to push.
+
     Look at the help text for the pull command for important details
     about ssh:// URLs.
 
     Pushing to http:// and https:// URLs is only possible, if this
     feature is explicitly enabled on the remote Mercurial server.
     """
-    dest = ui.expandpath(dest or 'default-push', dest or 'default')
+    dest, revs = cmdutil.parseurl(
+        ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
     setremoteconfig(ui, opts)
 
     other = hg.repository(ui, dest)
     ui.status('pushing to %s\n' % (dest))
-    revs = None
-    if opts['rev']:
-        revs = [repo.lookup(rev) for rev in opts['rev']]
+    if revs:
+        revs = [repo.lookup(rev) for rev in revs]
     r = repo.push(other, opts['force'], revs=revs)
     return r == 0
 
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -10,7 +10,7 @@ from node import *
 from repo import *
 from i18n import _
 import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo
-import errno, lock, os, shutil, util
+import errno, lock, os, shutil, util, cmdutil
 import merge as _merge
 import verify as _verify
 
@@ -97,6 +97,10 @@ def clone(ui, source, dest=None, pull=Fa
     update: update working directory after clone completes, if
     destination is local repository
     """
+
+    origsource = source
+    source, rev = cmdutil.parseurl(ui.expandpath(source), rev)
+
     if isinstance(source, str):
         src_repo = repository(ui, source)
     else:
@@ -134,10 +138,10 @@ def clone(ui, source, dest=None, pull=Fa
     if islocal(dest):
         dir_cleanup = DirCleanup(dest)
 
-    abspath = source
+    abspath = origsource
     copy = False
     if src_repo.local() and islocal(dest):
-        abspath = os.path.abspath(source)
+        abspath = os.path.abspath(origsource)
         copy = not pull and not rev
 
     src_lock, dest_lock = None, None
@@ -218,7 +222,11 @@ def clone(ui, source, dest=None, pull=Fa
             dest_lock.release()
 
         if update:
-            _update(dest_repo, dest_repo.changelog.tip())
+            try:
+                checkout = dest_repo.lookup("default")
+            except:
+                checkout = dest_repo.changelog.tip()
+            _update(dest_repo, checkout)
     if dir_cleanup:
         dir_cleanup.close()
 
--- a/mercurial/hgweb/common.py
+++ b/mercurial/hgweb/common.py
@@ -60,3 +60,19 @@ def style_map(templatepath, style):
             return mapfile
     raise RuntimeError("No hgweb templates found in %r" % templatepath)
 
+def paritygen(stripecount, offset=0):
+    """count parity of horizontal stripes for easier reading"""
+    if stripecount and offset:
+        # account for offset, e.g. due to building the list in reverse
+        count = (stripecount + offset) % stripecount
+        parity = (stripecount + offset) / stripecount & 1
+    else:
+        count = 0
+        parity = 0
+    while True:
+        yield parity
+        count += 1
+        if stripecount and count >= stripecount:
+            parity = 1 - parity
+            count = 0
+
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -12,7 +12,7 @@ from mercurial.node import *
 from mercurial.i18n import gettext as _
 from mercurial import mdiff, ui, hg, util, archival, streamclone, patch
 from mercurial import revlog, templater
-from common import get_mtime, staticfile, style_map
+from common import get_mtime, staticfile, style_map, paritygen
 
 def _up(p):
     if p[0] != "/":
@@ -147,14 +147,13 @@ class hgweb(object):
                 l += [x for x in files if x.startswith(t)]
             return l
 
-        parity = [0]
+        parity = paritygen(self.stripecount)
         def diffblock(diff, f, fn):
             yield self.t("diffblock",
                          lines=prettyprintlines(diff),
-                         parity=parity[0],
+                         parity=parity.next(),
                          file=f,
                          filenode=hex(fn or nullid))
-            parity[0] = 1 - parity[0]
 
         def prettyprintlines(diff):
             for l in diff.splitlines(1):
@@ -197,14 +196,13 @@ class hgweb(object):
 
     def changelog(self, ctx, shortlog=False):
         def changelist(**map):
-            parity = (start - end) & 1
             cl = self.repo.changelog
             l = [] # build a list in forward order for efficiency
             for i in xrange(start, end):
                 ctx = self.repo.changectx(i)
                 n = ctx.node()
 
-                l.insert(0, {"parity": parity,
+                l.insert(0, {"parity": parity.next(),
                              "author": ctx.user(),
                              "parent": self.siblings(ctx.parents(), i - 1),
                              "child": self.siblings(ctx.children(), i + 1),
@@ -214,7 +212,6 @@ class hgweb(object):
                              "files": self.listfilediffs(ctx.files(), n),
                              "rev": i,
                              "node": hex(n)})
-                parity = 1 - parity
 
             for e in l:
                 yield e
@@ -226,6 +223,7 @@ class hgweb(object):
         start = max(0, pos - maxchanges + 1)
         end = min(count, start + maxchanges)
         pos = end - 1
+        parity = paritygen(self.stripecount, offset=start-end)
 
         changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
 
@@ -267,7 +265,7 @@ class hgweb(object):
                 n = ctx.node()
 
                 yield self.t('searchentry',
-                             parity=self.stripes(count),
+                             parity=parity.next(),
                              author=ctx.user(),
                              parent=self.siblings(ctx.parents()),
                              child=self.siblings(ctx.children()),
@@ -282,11 +280,13 @@ class hgweb(object):
                     break
 
         cl = self.repo.changelog
+        parity = paritygen(self.stripecount)
 
         yield self.t('search',
                      query=query,
                      node=hex(cl.tip()),
-                     entries=changelist)
+                     entries=changelist,
+                     archives=self.archivelist("tip"))
 
     def changeset(self, ctx):
         n = ctx.node()
@@ -294,12 +294,11 @@ class hgweb(object):
         p1 = parents[0].node()
 
         files = []
-        parity = 0
+        parity = paritygen(self.stripecount)
         for f in ctx.files():
             files.append(self.t("filenodelink",
                                 node=hex(n), file=f,
-                                parity=parity))
-            parity = 1 - parity
+                                parity=parity.next()))
 
         def diff(**map):
             yield self.diff(p1, n, None)
@@ -326,16 +325,16 @@ class hgweb(object):
         start = max(0, pos - pagelen + 1)
         end = min(count, start + pagelen)
         pos = end - 1
+        parity = paritygen(self.stripecount, offset=start-end)
 
         def entries(**map):
             l = []
-            parity = (count - 1) & 1
 
             for i in xrange(start, end):
                 ctx = fctx.filectx(i)
                 n = fl.node(i)
 
-                l.insert(0, {"parity": parity,
+                l.insert(0, {"parity": parity.next(),
                              "filerev": i,
                              "file": f,
                              "node": hex(ctx.node()),
@@ -345,7 +344,6 @@ class hgweb(object):
                              "parent": self.siblings(fctx.parents()),
                              "child": self.siblings(fctx.children()),
                              "desc": ctx.description()})
-                parity = 1 - parity
 
             for e in l:
                 yield e
@@ -360,6 +358,7 @@ class hgweb(object):
         text = fctx.data()
         fl = fctx.filelog()
         n = fctx.filenode()
+        parity = paritygen(self.stripecount)
 
         mt = mimetypes.guess_type(f)[0]
         rawtext = text
@@ -372,7 +371,7 @@ class hgweb(object):
             for l, t in enumerate(text.splitlines(1)):
                 yield {"line": t,
                        "linenumber": "% 6d" % (l + 1),
-                       "parity": self.stripes(l)}
+                       "parity": parity.next()}
 
         yield self.t("filerevision",
                      file=f,
@@ -394,19 +393,18 @@ class hgweb(object):
         f = fctx.path()
         n = fctx.filenode()
         fl = fctx.filelog()
+        parity = paritygen(self.stripecount)
 
         def annotate(**map):
-            parity = 0
             last = None
             for f, l in fctx.annotate(follow=True):
                 fnode = f.filenode()
                 name = self.repo.ui.shortuser(f.user())
 
                 if last != fnode:
-                    parity = 1 - parity
                     last = fnode
 
-                yield {"parity": parity,
+                yield {"parity": parity.next(),
                        "node": hex(f.node()),
                        "rev": f.rev(),
                        "author": name,
@@ -432,6 +430,7 @@ class hgweb(object):
         node = ctx.node()
 
         files = {}
+        parity = paritygen(self.stripecount)
 
         if path and path[-1] != "/":
             path += "/"
@@ -450,7 +449,6 @@ class hgweb(object):
                 files[short] = (f, n)
 
         def filelist(**map):
-            parity = 0
             fl = files.keys()
             fl.sort()
             for f in fl:
@@ -459,14 +457,12 @@ class hgweb(object):
                     continue
 
                 yield {"file": full,
-                       "parity": self.stripes(parity),
+                       "parity": parity.next(),
                        "basename": f,
                        "size": ctx.filectx(full).size(),
                        "permissions": mf.execf(full)}
-                parity += 1
 
         def dirlist(**map):
-            parity = 0
             fl = files.keys()
             fl.sort()
             for f in fl:
@@ -474,16 +470,16 @@ class hgweb(object):
                 if fnode:
                     continue
 
-                yield {"parity": self.stripes(parity),
+                yield {"parity": parity.next(),
                        "path": os.path.join(abspath, f),
                        "basename": f[:-1]}
-                parity += 1
 
         yield self.t("manifest",
                      rev=ctx.rev(),
                      node=hex(node),
                      path=abspath,
                      up=_up(abspath),
+                     upparity=parity.next(),
                      fentries=filelist,
                      dentries=dirlist,
                      archives=self.archivelist(hex(node)))
@@ -491,17 +487,16 @@ class hgweb(object):
     def tags(self):
         i = self.repo.tagslist()
         i.reverse()
+        parity = paritygen(self.stripecount)
 
         def entries(notip=False, **map):
-            parity = 0
             for k, n in i:
                 if notip and k == "tip":
                     continue
-                yield {"parity": self.stripes(parity),
+                yield {"parity": parity.next(),
                        "tag": k,
                        "date": self.repo.changectx(n).date(),
                        "node": hex(n)}
-                parity += 1
 
         yield self.t("tags",
                      node=hex(self.repo.changelog.tip()),
@@ -513,7 +508,7 @@ class hgweb(object):
         i.reverse()
 
         def tagentries(**map):
-            parity = 0
+            parity = paritygen(self.stripecount)
             count = 0
             for k, n in i:
                 if k == "tip": # skip tip
@@ -524,15 +519,14 @@ class hgweb(object):
                     break;
 
                 yield self.t("tagentry",
-                             parity=self.stripes(parity),
+                             parity=parity.next(),
                              tag=k,
                              node=hex(n),
                              date=self.repo.changectx(n).date())
-                parity += 1
 
 
         def branches(**map):
-            parity = 0
+            parity = paritygen(self.stripecount)
 
             b = self.repo.branchtags()
             l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
@@ -541,14 +535,13 @@ class hgweb(object):
             for r,n,t in l:
                 ctx = self.repo.changectx(n)
 
-                yield {'parity': self.stripes(parity),
+                yield {'parity': parity.next(),
                        'branch': t,
                        'node': hex(n),
                        'date': ctx.date()}
-                parity += 1
 
         def changelist(**map):
-            parity = 0
+            parity = paritygen(self.stripecount, offset=start-end)
             l = [] # build a list in forward order for efficiency
             for i in xrange(start, end):
                 ctx = self.repo.changectx(i)
@@ -556,13 +549,12 @@ class hgweb(object):
 
                 l.insert(0, self.t(
                     'shortlogentry',
-                    parity=parity,
+                    parity=parity.next(),
                     author=ctx.user(),
                     desc=ctx.description(),
                     date=ctx.date(),
                     rev=i,
                     node=hn))
-                parity = 1 - parity
 
             yield l
 
@@ -845,13 +837,6 @@ class hgweb(object):
 
         return fctx
 
-    def stripes(self, parity):
-        "make horizontal stripes for easier reading"
-        if self.stripecount:
-            return (1 + parity / self.stripecount) & 1
-        else:
-            return 0
-
     def do_log(self, req):
         if req.form.has_key('file') and req.form['file'][0]:
             self.do_filelog(req)
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -10,7 +10,7 @@ from mercurial import demandimport; dema
 import os, mimetools, cStringIO
 from mercurial.i18n import gettext as _
 from mercurial import ui, hg, util, templater
-from common import get_mtime, staticfile, style_map
+from common import get_mtime, staticfile, style_map, paritygen
 from hgweb_mod import hgweb
 
 # This is a stopgap
@@ -22,6 +22,7 @@ class hgwebdir(object):
         self.parentui = parentui
         self.motd = None
         self.style = None
+        self.stripecount = None
         self.repos_sorted = ('name', False)
         if isinstance(config, (list, tuple)):
             self.repos = cleannames(config)
@@ -41,6 +42,8 @@ class hgwebdir(object):
                     self.motd = cp.get('web', 'motd')
                 if cp.has_option('web', 'style'):
                     self.style = cp.get('web', 'style')
+                if cp.has_option('web', 'stripes'):
+                    self.stripecount = int(cp.get('web', 'stripes'))
             if cp.has_section('paths'):
                 self.repos.extend(cleannames(cp.items('paths')))
             if cp.has_section('collections'):
@@ -97,6 +100,8 @@ class hgwebdir(object):
             style = config('web', 'style', '')
         if req.form.has_key('style'):
             style = req.form['style'][0]
+        if self.stripecount is None:
+            self.stripecount = int(config('web', 'stripes', 1))
         mapfile = style_map(templater.templatepath(), style)
         tmpl = templater.templater(mapfile, templater.common_filters,
                                    defaults={"header": header,
@@ -127,7 +132,7 @@ class hgwebdir(object):
                     separator = ';'
 
             rows = []
-            parity = 0
+            parity = paritygen(self.stripecount)
             for name, path in self.repos:
                 u = ui.ui(parentui=parentui)
                 try:
@@ -165,8 +170,7 @@ class hgwebdir(object):
                 if (not sortcolumn
                     or (sortcolumn, descending) == self.repos_sorted):
                     # fast path for unsorted output
-                    row['parity'] = parity
-                    parity = 1 - parity
+                    row['parity'] = parity.next()
                     yield row
                 else:
                     rows.append((row["%s_sort" % sortcolumn], row))
@@ -175,8 +179,7 @@ class hgwebdir(object):
                 if descending:
                     rows.reverse()
                 for key, row in rows:
-                    row['parity'] = parity
-                    parity = 1 - parity
+                    row['parity'] = parity.next()
                     yield row
 
         try:
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -1006,6 +1006,12 @@ class localrepository(repo.repository):
         for f in list:
             p = self.wjoin(f)
             islink = os.path.islink(p)
+            size = os.lstat(p).st_size
+            if size > 10000000:
+                self.ui.warn(_("%s: files over 10MB may cause memory and"
+                               " performance problems\n"
+                               "(use 'hg revert %s' to unadd the file)\n")
+                               % (f, f))
             if not islink and not os.path.exists(p):
                 self.ui.warn(_("%s does not exist!\n") % f)
             elif not islink and not os.path.isfile(p):
--- a/templates/gitweb/changelog.tmpl
+++ b/templates/gitweb/changelog.tmpl
@@ -18,7 +18,7 @@
 </div>
 
 <div class="page_nav">
-<a href="{url}summary{sessionvars%urlparameter}">summary</a> | <a href="{url}shortlog/#rev#{sessionvars%urlparameter}">shortlog</a> | changelog | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a>#archives%archiveentry#<br/>
+<a href="{url}summary{sessionvars%urlparameter}">summary</a> | <a href="{url}shortlog/#rev#{sessionvars%urlparameter}">shortlog</a> | changelog | <a href="{url}tags{sessionvars%urlparameter}">tags</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a>#archives%archiveentry#
 <br/>
 #changenav%naventry#<br/>
 </div>
--- a/templates/gitweb/changeset.tmpl
+++ b/templates/gitweb/changeset.tmpl
@@ -21,7 +21,6 @@
 <tr><td>author</td><td>#author|obfuscate#</td></tr>
 <tr><td></td><td>#date|date# (#date|age# ago)</td></tr>
 <tr><td>changeset {rev}</td><td style="font-family:monospace">{node|short}</td></tr>
-<tr><td>manifest</td><td style="font-family:monospace"><a class="list" href="{url}file/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>
 #parent%changesetparent#
 #child%changesetchild#
 #changesettag#
--- a/templates/gitweb/fileannotate.tmpl
+++ b/templates/gitweb/fileannotate.tmpl
@@ -26,24 +26,21 @@ annotate |
 <div class="title">#file|escape#</div>
 
 <div class="title_text">
-<table>
+<table cellspacing="0">
+<tr>
+ <td>author</td>
+ <td>#author|obfuscate#</td></tr>
 <tr>
- <td class="metatag">changeset #rev#:</td>
- <td><a href="{url}rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>
+ <td></td>
+ <td>#date|date# (#date|age# ago)</td></tr>
+<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%fileannotateparent#
 #child%fileannotatechild#
 <tr>
- <td class="metatag">manifest:</td>
- <td><a href="{url}file/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>
-<tr>
- <td class="metatag">author:</td>
- <td>#author|obfuscate#</td></tr>
-<tr>
- <td class="metatag">date:</td>
- <td>#date|date# (#date|age# ago)</td></tr>
-<tr>
- <td class="metatag">permissions:</td>
- <td>#permissions|permissions#</td></tr>
+ <td>permissions</td>
+ <td style="font-family:monospace">#permissions|permissions#</td></tr>
 </table>
 </div>
 
--- a/templates/gitweb/filediff.tmpl
+++ b/templates/gitweb/filediff.tmpl
@@ -6,7 +6,7 @@
 <body>
 
 <div class="page_header">
-<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / annotate
+<a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="{url}summary{sessionvars%urlparameter}">{repo|escape}</a> / diff
 </div>
 
 <div class="page_nav">
@@ -27,21 +27,16 @@ diff |
 
 <table>
 <tr>
- <td class="metatag">changeset {rev}:</td>
- <td><a href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
-</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%filediffparent}
 {child%filediffchild}
-<tr>
- <td class="metatag">manifest:</td>
- <td><a href="{url}file/{node|short}{sessionvars%urlparameter}">{node|short}</a></td>
-</tr>
 </table>
 
+<div class="list_head"></div>
+
 <div class="page_body">
-<table>
 {diff}
-</table>
 </div>
 
 {footer}
--- a/templates/gitweb/filelog.tmpl
+++ b/templates/gitweb/filelog.tmpl
@@ -18,10 +18,9 @@
 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}rss-log/#node|short#/#file|urlescape#">rss</a><br/>
-
+<a href="{url}rss-log/#node|short#/#file|urlescape#">rss</a>
 <br/>
-{nav%filenaventry}<br/>
+{nav%filenaventry}
 </div>
 
 <div class="title" >#file|urlescape#</div>
@@ -30,4 +29,8 @@ revisions |
 #entries%filelogentry#
 </table>
 
+<div class="page_nav">
+{nav%filenaventry}
+</div>
+
 #footer#
--- a/templates/gitweb/filerevision.tmpl
+++ b/templates/gitweb/filerevision.tmpl
@@ -26,24 +26,21 @@ file |
 <div class="title">#file|escape#</div>
 
 <div class="title_text">
-<table>
+<table cellspacing="0">
+<tr>
+ <td>author</td>
+ <td>#author|obfuscate#</td></tr>
 <tr>
- <td class="metatag">changeset #rev#:</td>
- <td><a href="{url}rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>
+ <td></td>
+ <td>#date|date# (#date|age# ago)</td></tr>
+<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%filerevparent#
 #child%filerevchild#
 <tr>
- <td class="metatag">manifest:</td>
- <td><a href="{url}file/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>
-<tr>
- <td class="metatag">author:</td>
- <td>#author|obfuscate#</td></tr>
-<tr>
- <td class="metatag">date:</td>
- <td>#date|date# (#date|age# ago)</td></tr>
-<tr>
- <td class="metatag">permissions:</td>
- <td>#permissions|permissions#</td></tr>
+ <td>permissions</td>
+ <td style="font-family:monospace">#permissions|permissions#</td></tr>
 </table>
 </div>
 
--- a/templates/gitweb/manifest.tmpl
+++ b/templates/gitweb/manifest.tmpl
@@ -19,9 +19,8 @@ manifest |
 </div>
 
 <div class="title" >#path|escape#</div>
-<div class="page_body">
 <table cellspacing="0">
-<tr class="light">
+<tr class="parity#upparity#">
 <td style="font-family:monospace">drwxr-xr-x</td>
 <td style="font-family:monospace"></td>
 <td><a href="{url}file/#node|short##up|urlescape#{sessionvars%urlparameter}">[up]</a></td>
@@ -30,4 +29,5 @@ manifest |
 #dentries%manifestdirentry#
 #fentries%manifestfileentry#
 </table>
+
 #footer#
--- a/templates/gitweb/map
+++ b/templates/gitweb/map
@@ -15,7 +15,7 @@ changelogentry = changelogentry.tmpl
 searchentry = changelogentry.tmpl
 changeset = changeset.tmpl
 manifest = manifest.tmpl
-manifestdirentry = '<tr class="parity#parity#"><td style="font-family:monospace">drwxr-xr-x</td><td style="font-family:monospace"></td><td><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">#basename|escape#/</a></td><td class="link"><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">manifest</a></td></tr>'
+manifestdirentry = '<tr class="parity#parity#"><td style="font-family:monospace">drwxr-xr-x</td><td style="font-family:monospace"></td><td><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">#basename|escape#</a></td><td class="link"><a href="#url#file/#node|short##path|urlescape#{sessionvars%urlparameter}">manifest</a></td></tr>'
 manifestfileentry = '<tr class="parity#parity#"><td style="font-family:monospace">#permissions|permissions#</td><td style="font-family:monospace" align=right>#size#</td><td class="list"><a class="list" href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">#basename|escape#</a></td><td class="link"><a href="#url#file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">file</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></td></tr>'
 filerevision = filerevision.tmpl
 fileannotate = fileannotate.tmpl
@@ -29,23 +29,23 @@ difflineat = '<div style="color:#990099;
 diffline = '<div>#line|escape#</div>'
 changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="#url#rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
 changesetparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
-filerevparent = '<tr><td class="metatag">parent {rev}:</td><td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>'
+filerevparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>'
 filerename = '{file|escape}@'
 filelogrename = '| <a href="{url}file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">base</a>'
-fileannotateparent = '<tr><td class="metatag">parent {rev}:</td><td><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>'
+fileannotateparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{rename%filerename}{node|short}</a></td></tr>'
 changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="{url}rev/#node|short#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
 changesetchild = '<tr><td>child {rev}</td><td style="font-family:monospace"><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
-filerevchild = '<tr><td class="metatag">child {rev}:</td><td><a href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
-fileannotatechild = '<tr><td class="metatag">child {rev}:</td><td><a href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
+filerevchild = '<tr><td>child {rev}</td><td style="font-family:monospace"><a class="list" href="{url}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
+fileannotatechild = '<tr><td>child {rev}</td><td style="font-family:monospace"><a class="list" href="{url}annotate/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
 tags = tags.tmpl
 tagentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>#tag|escape#</b></a></td><td class="link"><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}log/#node|short#{sessionvars%urlparameter}">changelog</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a></td></tr>'
 branchentry = '<tr class="parity{parity}"><td class="age"><i>{date|age} ago</i></td><td><a class="list" href="{url}rev/{node|short}{sessionvars%urlparameter}"><b>{node|short}</b></td><td>{branch|escape}</td><td class="link"><a href="{url}rev/{node|short}{sessionvars%urlparameter}">changeset</a> | <a href="{url}log/{node|short}{sessionvars%urlparameter}">changelog</a> | <a href="{url}file/{node|short}{sessionvars%urlparameter}">manifest</a></td></tr>'
 diffblock = '<pre>#lines#</pre>'
 changelogtag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
 changesettag = '<tr><td>tag</td><td>#tag|escape#</td></tr>'
-filediffparent = '<tr><th class="parent">parent {rev}:</th><td class="parent"><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
+filediffparent = '<tr><td>parent {rev}</td><td style="font-family:monospace"><a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
 filelogparent = '<tr><td align="right">parent #rev#:&nbsp;</td><td><a href="{url}file/{node|short}/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
-filediffchild = '<tr><th class="child">child {rev}:</th><td class="child"><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
+filediffchild = '<tr><td>child {rev}</td><td style="font-family:monospace"><a class="list" href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>'
 filelogchild = '<tr><td align="right">child #rev#:&nbsp;</td><td><a href="{url}file{node|short}/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>'
 shortlog = shortlog.tmpl
 shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author#</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b></a></td><td class="link" nowrap><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a></td></tr>'
--- a/templates/gitweb/search.tmpl
+++ b/templates/gitweb/search.tmpl
@@ -1,27 +1,32 @@
 #header#
+<title>#repo|escape#: Search</title>
+<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="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / search
+
+<form action="{url}log">
+{sessionvars%hiddenformentry}
+<div class="search">
+<input type="text" name="rev" value="#query|escape#" />
+</div>
+</form>
+</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}tags{sessionvars%urlparameter}">tags</a> |
-<a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a><br/>
+<a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a>#archives%archiveentry#
+<br/>
 </div>
 
-<h2>searching for #query|escape#</h2>
-
-<form action="{url}log">
-{sessionvars%hiddenformentry}
-search:
-<input name="rev" type="text" width="30" value="#query|escape#">
-</form>
+<div class="title">searching for #query|escape#</div>
 
 #entries#
 
-<form action="{url}log">
-{sessionvars%hiddenformentry}
-search:
-<input type="hidden" name="style" value="gitweb">
-<input name="rev" type="text" width="30">
-</form>
-
 #footer#
--- a/templates/gitweb/shortlog.tmpl
+++ b/templates/gitweb/shortlog.tmpl
@@ -21,14 +21,18 @@
 shortlog |
 <a href="{url}log/#rev#{sessionvars%urlparameter}">changelog</a> |
 <a href="{url}tags{sessionvars%urlparameter}">tags</a> |
-<a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a>#archives%archiveentry#<br/>
+<a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a>#archives%archiveentry#
 <br/>
-
 #changenav%navshortentry#<br/>
 </div>
 
+<div class="title">&nbsp;</div>
 <table cellspacing="0">
 #entries%shortlogentry#
 </table>
 
+<div class="page_nav">
+#changenav%navshortentry#
+</div>
+
 #footer#
--- a/templates/gitweb/summary.tmpl
+++ b/templates/gitweb/summary.tmpl
@@ -7,7 +7,15 @@
 
 <div class="page_header">
 <a href="http://www.selenic.com/mercurial/" title="Mercurial"><div style="float:right;">Mercurial</div></a><a href="{url}summary{sessionvars%urlparameter}">#repo|escape#</a> / summary
+
+<form action="{url}log">
+{sessionvars%hiddenformentry}
+<div class="search">
+<input type="text" name="rev"  />
 </div>
+</form>
+</div>
+
 <div class="page_nav">
 summary |
 <a href="{url}shortlog{sessionvars%urlparameter}">shortlog</a> |
@@ -27,7 +35,7 @@ summary |
 <div><a  class="title" href="{url}log{sessionvars%urlparameter}">changes</a></div>
 <table cellspacing="0">
 #shortlog#
-<tr class="light"><td colspan="3"><a class="list" href="{url}log{sessionvars%urlparameter}">...</a></td></tr>
+<tr class="light"><td colspan="4"><a class="list" href="{url}log{sessionvars%urlparameter}">...</a></td></tr>
 </table>
 
 <div><a class="title" href="{url}tags{sessionvars%urlparameter}">tags</a></div>
@@ -40,7 +48,7 @@ summary |
 <table cellspacing="0">
 {branches%branchentry}
 <tr class="light">
-  <td colspan="3"><a class="list"  href="#">...</a></td>
+  <td colspan="4"><a class="list"  href="#">...</a></td>
 </tr>
 </table>
 #footer#
--- a/templates/gitweb/tags.tmpl
+++ b/templates/gitweb/tags.tmpl
@@ -18,6 +18,7 @@ tags |
 <br/>
 </div>
 
+<div class="title">&nbsp;</div>
 <table cellspacing="0">
 #entries%tagentry#
 </table>
--- a/templates/manifest.tmpl
+++ b/templates/manifest.tmpl
@@ -14,7 +14,7 @@
 <h2>manifest for changeset #node|short#: #path|escape#</h2>
 
 <table cellpadding="0" cellspacing="0">
-<tr class="parity1">
+<tr class="parity#upparity#">
   <td><tt>drwxr-xr-x</tt>&nbsp;
   <td>&nbsp;
   <td><a href="#url#file/#node|short##up|urlescape#{sessionvars%urlparameter}">[up]</a>
--- a/templates/search.tmpl
+++ b/templates/search.tmpl
@@ -8,6 +8,7 @@
 <a href="#url#shortlog{sessionvars%urlparameter}">shortlog</a>
 <a href="#url#tags{sessionvars%urlparameter}">tags</a>
 <a href="#url#file/#node|short#{sessionvars%urlparameter}">manifest</a>
+#archives%archiveentry#
 </div>
 
 <h2>searching for #query|escape#</h2>
--- a/tests/test-addremove
+++ b/tests/test-addremove
@@ -22,5 +22,5 @@ hg commit -Ama
 mv a b
 rm c
 echo d > d
-hg addremove -s 0.5
+hg addremove -s 50
 hg commit -mb
--- a/tests/test-addremove-similar
+++ b/tests/test-addremove-similar
@@ -16,6 +16,12 @@ hg addremove -s50
 
 hg commit -m B
 
+echo % comparing two empty files caused ZeroDivisionError in the past
+hg update -C 0
+rm empty-file
+touch another-empty-file
+hg addremove -s50
+
 cd ..
 
 hg init rep2; cd rep2
--- a/tests/test-addremove-similar.out
+++ b/tests/test-addremove-similar.out
@@ -4,6 +4,10 @@ adding another-file
 removing empty-file
 removing large-file
 recording removal of large-file as rename to another-file (99% similar)
+% comparing two empty files caused ZeroDivisionError in the past
+2 files updated, 0 files merged, 1 files removed, 0 files unresolved
+adding another-empty-file
+removing empty-file
 adding large-file
 adding tiny-file
 adding small-file
--- a/tests/test-incoming-outgoing.out
+++ b/tests/test-incoming-outgoing.out
@@ -73,6 +73,7 @@ date:        Mon Jan 12 13:46:40 1970 +0
 summary:     3
 
 changeset:   4:1f3a964b6022
+tag:         tip
 user:        test
 date:        Mon Jan 12 13:46:40 1970 +0000
 summary:     4
--- a/tests/test-purge
+++ b/tests/test-purge
@@ -97,3 +97,34 @@ fi
 hg purge -v --force
 hg revert --all --quiet
 ls
+
+echo % skip excluded files
+touch excluded_file
+hg purge -p -X excluded_file
+hg purge -v -X excluded_file
+ls
+rm excluded_file
+
+echo % skip files in excluded dirs
+mkdir excluded_dir
+touch excluded_dir/file
+hg purge -p -X excluded_dir
+hg purge -v -X excluded_dir
+ls
+ls excluded_dir
+rm -R excluded_dir
+
+echo % skip excluded empty dirs
+mkdir excluded_dir
+hg purge -p -X excluded_dir
+hg purge -v -X excluded_dir
+ls
+rmdir excluded_dir
+
+echo % skip patterns
+mkdir .svn
+touch .svn/foo
+mkdir directory/.svn
+touch directory/.svn/foo
+hg purge -p -X .svn -X '*/.svn'
+hg purge -p -X re:.*.svn
--- a/tests/test-purge.out
+++ b/tests/test-purge.out
@@ -56,3 +56,17 @@ untracked_file still around
 Removing file untracked_file
 directory
 r1
+% skip excluded files
+directory
+excluded_file
+r1
+% skip files in excluded dirs
+directory
+excluded_dir
+r1
+file
+% skip excluded empty dirs
+directory
+excluded_dir
+r1
+% skip patterns