Merge with upstream
authorThomas Arendsen Hein <thomas@intevation.de>
Sat, 30 Sep 2006 09:46:03 +0200
changeset 3203 14792adabf80
parent 3195 f8e67b7c57a4 (diff)
parent 3202 9e002614f2eb (current diff)
child 3204 a7377a238cec
Merge with upstream
mercurial/context.py
--- a/hgext/hgk.py
+++ b/hgext/hgk.py
@@ -279,8 +279,10 @@ def revlist(ui, repo, *revs, **opts):
 def view(ui, repo, *etc, **opts):
     "start interactive history viewer"
     os.chdir(repo.root)
-    optstr = ' '.join(['--%s %s' % (k, v) for k, v in opts.iteritems()])
-    os.system(ui.config("hgk", "path", "hgk") + " %s %s" % (optstr, " ".join(etc)))
+    optstr = ' '.join(['--%s %s' % (k, v) for k, v in opts.iteritems() if v])
+    cmd = ui.config("hgk", "path", "hgk") + " %s %s" % (optstr, " ".join(etc))
+    ui.debug("running %s\n" % cmd)
+    os.system(cmd)
 
 cmdtable = {
     "view": (view,
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -114,6 +114,9 @@ class queue:
                 comment = l[h:]
             patch = patch.strip()
             if patch:
+                if patch in self.series:
+                    raise util.Abort(_('%s appears more than once in %s') %
+                                     (patch, self.join(self.series_path)))
                 self.series.append(patch)
                 self.series_guards.append(self.guard_re.findall(comment))
 
@@ -800,11 +803,9 @@ class queue:
             wlock = repo.wlock()
         patch = self.lookup(patch)
         if patch and self.isapplied(patch):
-            self.ui.warn(_("patch %s is already applied\n") % patch)
-            sys.exit(1)
+            raise util.Abort(_("patch %s is already applied") % patch)
         if self.series_end() == len(self.series):
-            self.ui.warn(_("patch series fully applied\n"))
-            sys.exit(1)
+            raise util.Abort(_("patch series fully applied"))
         if not force:
             self.check_localchanges(repo)
 
@@ -854,8 +855,7 @@ class queue:
             if not info:
                 raise util.Abort(_("patch %s is not applied") % patch)
         if len(self.applied) == 0:
-            self.ui.warn(_("no patches applied\n"))
-            sys.exit(1)
+            raise util.Abort(_("no patches applied"))
 
         if not update:
             parents = repo.dirstate.parents()
@@ -1071,25 +1071,38 @@ class queue:
             self.explain_pushable(i)
         return unapplied
 
-    def qseries(self, repo, missing=None, summary=False):
-        start = self.series_end(all_patches=True)
+    def qseries(self, repo, missing=None, start=0, length=0, status=None,
+                summary=False):
+        def displayname(patchname):
+            if summary:
+                msg = self.readheaders(patchname)[0]
+                msg = msg and ': ' + msg[0] or ': '
+            else:
+                msg = ''
+            return '%s%s' % (patchname, msg)
+
+        def pname(i):
+            if status == 'A':
+                return self.applied[i].name
+            else:
+                return self.series[i]
+
+        unapplied = self.series_end(all_patches=True)
+        if not length:
+            length = len(self.series) - start
         if not missing:
-            for i in range(len(self.series)):
-                patch = self.series[i]
+            for i in range(start, start+length):
+                pfx = ''
+                patch = pname(i)
                 if self.ui.verbose:
-                    if i < start:
+                    if i < unapplied:
                         status = 'A'
                     elif self.pushable(i)[0]:
                         status = 'U'
                     else:
                         status = 'G'
-                    self.ui.write('%d %s ' % (i, status))
-                if summary:
-                    msg = self.readheaders(patch)[0]
-                    msg = msg and ': ' + msg[0] or ': '
-                else:
-                    msg = ''
-                self.ui.write('%s%s\n' % (patch, msg))
+                    pfx = '%d %s ' % (i, status)
+                self.ui.write('%s%s\n' % (pfx, displayname(patch)))
         else:
             msng_list = []
             for root, dirs, files in os.walk(self.path):
@@ -1102,9 +1115,8 @@ class queue:
                         msng_list.append(fl)
             msng_list.sort()
             for x in msng_list:
-                if self.ui.verbose:
-                    self.ui.write("D ")
-                self.ui.write("%s\n" % x)
+                pfx = self.ui.verbose and ('D ') or ''
+                self.ui.write("%s%s\n" % (pfx, displayname(x)))
 
     def issaveline(self, l):
         if l.name == '.hg.patches.save.line':
@@ -1136,7 +1148,8 @@ class queue:
                 file_ = se.name
                 if se.rev:
                     applied.append(se)
-                series.append(file_)
+                else:
+                    series.append(file_)
         if datastart == None:
             self.ui.warn("No saved patch data found\n")
             return 1
@@ -1227,17 +1240,6 @@ class queue:
             return next(end + 1)
         return next(end)
 
-    def qapplied(self, repo, patch=None):
-        if patch and patch not in self.series:
-            raise util.Abort(_("patch %s is not in series file") % patch)
-        if not patch:
-            end = len(self.applied)
-        else:
-            end = self.series.index(patch) + 1
-        for x in xrange(end):
-            p = self.appliedname(x)
-            self.ui.write("%s\n" % p)
-
     def appliedname(self, index):
         pname = self.applied[index].name
         if not self.ui.verbose:
@@ -1246,36 +1248,6 @@ class queue:
             p = str(self.series.index(pname)) + " " + pname
         return p
 
-    def top(self, repo):
-        if len(self.applied):
-            p = self.appliedname(-1)
-            self.ui.write(p + '\n')
-        else:
-            self.ui.write("No patches applied\n")
-            return 1
-
-    def next(self, repo):
-        end = self.series_end()
-        if end == len(self.series):
-            self.ui.write("All patches applied\n")
-            return 1
-        else:
-            p = self.series[end]
-            if self.ui.verbose:
-                self.ui.write("%d " % self.series.index(p))
-            self.ui.write(p + '\n')
-
-    def prev(self, repo):
-        if len(self.applied) > 1:
-            p = self.appliedname(-2)
-            self.ui.write(p + '\n')
-        elif len(self.applied) == 1:
-            self.ui.write("Only one patch applied\n")
-            return 1
-        else:
-            self.ui.write("No patches applied\n")
-            return 1
-
     def qimport(self, repo, files, patchname=None, rev=None, existing=None,
                 force=None):
         def checkseries(patchname):
@@ -1396,15 +1368,28 @@ def delete(ui, repo, patch, *patches, **
 
 def applied(ui, repo, patch=None, **opts):
     """print the patches already applied"""
-    repo.mq.qapplied(repo, patch)
-    return 0
+    q = repo.mq
+    if patch:
+        if patch not in q.series:
+            raise util.Abort(_("patch %s is not in series file") % patch)
+        end = q.series.index(patch) + 1
+    else:
+        end = len(q.applied)
+    if not end:
+        return
+
+    return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
 
 def unapplied(ui, repo, patch=None, **opts):
     """print the patches not yet applied"""
-    for i, p in repo.mq.unapplied(repo, patch):
-        if ui.verbose:
-            ui.write("%d " % i)
-        ui.write("%s\n" % p)
+    q = repo.mq
+    if patch:
+        if patch not in q.series:
+            raise util.Abort(_("patch %s is not in series file") % patch)
+        start = q.series.index(patch) + 1
+    else:
+        start = q.series_end()
+    q.qseries(repo, start=start, summary=opts.get('summary'))
 
 def qimport(ui, repo, *filename, **opts):
     """import a patch
@@ -1503,15 +1488,36 @@ def series(ui, repo, **opts):
 
 def top(ui, repo, **opts):
     """print the name of the current patch"""
-    return repo.mq.top(repo)
+    q = repo.mq
+    t = len(q.applied)
+    if t:
+        return q.qseries(repo, start=t-1, length=1, status='A',
+                         summary=opts.get('summary'))
+    else:
+        ui.write("No patches applied\n")
+        return 1
 
 def next(ui, repo, **opts):
     """print the name of the next patch"""
-    return repo.mq.next(repo)
+    q = repo.mq
+    end = q.series_end()
+    if end == len(q.series):
+        ui.write("All patches applied\n")
+        return 1
+    return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
 
 def prev(ui, repo, **opts):
     """print the name of the previous patch"""
-    return repo.mq.prev(repo)
+    q = repo.mq
+    l = len(q.applied)
+    if l == 1:
+        ui.write("Only one patch applied\n")
+        return 1
+    if not l:
+        ui.write("No patches applied\n")
+        return 1
+    return q.qseries(repo, start=l-2, length=1, status='A',
+                     summary=opts.get('summary'))
 
 def new(ui, repo, patch, **opts):
     """create a new patch
@@ -1988,8 +1994,10 @@ def reposetup(ui, repo):
         repo.__class__ = mqrepo
         repo.mq = queue(ui, repo.join(""))
 
+seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
+
 cmdtable = {
-    "qapplied": (applied, [], 'hg qapplied [PATCH]'),
+    "qapplied": (applied, [] + seriesopts, 'hg qapplied [-s] [PATCH]'),
     "qclone": (clone,
                [('', 'pull', None, _('use pull protocol to copy metadata')),
                 ('U', 'noupdate', None, _('do not update the new working directories')),
@@ -2043,8 +2051,8 @@ cmdtable = {
           ('l', 'logfile', '', _('read the commit message from <file>')),
           ('f', 'force', None, _('import uncommitted changes into patch'))],
          'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
-    "qnext": (next, [], 'hg qnext'),
-    "qprev": (prev, [], 'hg qprev'),
+    "qnext": (next, [] + seriesopts, 'hg qnext [-s]'),
+    "qprev": (prev, [] + seriesopts, 'hg qprev [-s]'),
     "^qpop":
         (pop,
          [('a', 'all', None, 'pop all patches'),
@@ -2094,8 +2102,7 @@ cmdtable = {
                 'hg qselect [OPTION...] [GUARD...]'),
     "qseries":
         (series,
-         [('m', 'missing', None, 'print patches not in series'),
-          ('s', 'summary', None, _('print first line of patch header'))],
+         [('m', 'missing', None, 'print patches not in series')] + seriesopts,
          'hg qseries [-ms]'),
     "^strip":
         (strip,
@@ -2103,6 +2110,6 @@ cmdtable = {
           ('b', 'backup', None, 'bundle unrelated changesets'),
           ('n', 'nobackup', None, 'no backups')],
          'hg strip [-f] [-b] [-n] REV'),
-    "qtop": (top, [], 'hg qtop'),
-    "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
+    "qtop": (top, [] + seriesopts, 'hg qtop [-s]'),
+    "qunapplied": (unapplied, [] + seriesopts, 'hg qunapplied [-s] [PATCH]'),
 }
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -312,7 +312,7 @@ class changeset_printer(object):
         self.ui = ui
         self.repo = repo
 
-    def show(self, rev=0, changenode=None, brinfo=None):
+    def show(self, rev=0, changenode=None, brinfo=None, copies=None):
         '''show a single changeset or file revision'''
         log = self.repo.changelog
         if changenode is None:
@@ -359,6 +359,9 @@ class changeset_printer(object):
                     self.ui.note("%-12s %s\n" % (key, " ".join(value)))
         else:
             self.ui.note(_("files:       %s\n") % " ".join(changes[3]))
+        if copies:
+            copies = ['%s (%s)' % c for c in copies]
+            self.ui.note(_("copies:      %s\n") % ' '.join(copies))
 
         description = changes[4].strip()
         if description:
@@ -372,21 +375,36 @@ class changeset_printer(object):
         self.ui.status("\n")
 
 def show_changeset(ui, repo, opts):
-    '''show one changeset.  uses template or regular display.  caller
-    can pass in 'style' and 'template' options in opts.'''
-
+    """show one changeset using template or regular display.
+
+    Display format will be the first non-empty hit of:
+    1. option 'template'
+    2. option 'style'
+    3. [ui] setting 'logtemplate'
+    4. [ui] setting 'style'
+    If all of these values are either the unset or the empty string,
+    regular display via changeset_printer() is done.
+    """
+    # options
     tmpl = opts.get('template')
+    mapfile = None
     if tmpl:
         tmpl = templater.parsestring(tmpl, quoted=False)
     else:
-        tmpl = ui.config('ui', 'logtemplate')
-        if tmpl: tmpl = templater.parsestring(tmpl)
-    mapfile = opts.get('style') or ui.config('ui', 'style')
+        mapfile = opts.get('style')
+        # ui settings
+        if not mapfile:
+            tmpl = ui.config('ui', 'logtemplate')
+            if tmpl:
+                tmpl = templater.parsestring(tmpl)
+            else:
+                mapfile = ui.config('ui', 'style')
+
     if tmpl or mapfile:
         if mapfile:
-            if not os.path.isfile(mapfile):
-                mapname = templater.templatepath('map-cmdline.' + mapfile)
-                if not mapname: mapname = templater.templatepath(mapfile)
+            if not os.path.split(mapfile)[0]:
+                mapname = (templater.templatepath('map-cmdline.' + mapfile)
+                           or templater.templatepath(mapfile))
                 if mapname: mapfile = mapname
         try:
             t = templater.changeset_templater(ui, repo, mapfile)
@@ -574,7 +592,7 @@ def add(ui, repo, *pats, **opts):
         repo.add(names)
 
 def addremove(ui, repo, *pats, **opts):
-    """add all new files, delete all missing files (DEPRECATED)
+    """add all new files, delete all missing files
 
     Add all new files and remove all missing files from the repository.
 
@@ -612,8 +630,9 @@ def annotate(ui, repo, *pats, **opts):
     opmap = [['user', lambda x: ui.shortuser(x.user())],
              ['number', lambda x: str(x.rev())],
              ['changeset', lambda x: short(x.node())],
-             ['date', getdate]]
-    if not opts['user'] and not opts['changeset'] and not opts['date']:
+             ['date', getdate], ['follow', lambda x: x.path()]]
+    if (not opts['user'] and not opts['changeset'] and not opts['date']
+        and not opts['follow']):
         opts['number'] = 1
 
     ctx = repo.changectx(opts['rev'])
@@ -625,7 +644,7 @@ def annotate(ui, repo, *pats, **opts):
             ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
             continue
 
-        lines = fctx.annotate()
+        lines = fctx.annotate(follow=opts.get('follow'))
         pieces = []
 
         for o, f in opmap:
@@ -760,6 +779,7 @@ def bundle(ui, repo, fname, dest=None, *
     contents including permissions, rename data, and revision history.
     """
     dest = ui.expandpath(dest or 'default-push', dest or 'default')
+    setremoteconfig(ui, opts)
     other = hg.repository(ui, dest)
     o = repo.findoutgoing(other, force=opts['force'])
     cg = repo.changegroup(o, 'bundle')
@@ -1757,6 +1777,40 @@ def log(ui, repo, *pats, **opts):
         limit = sys.maxint
     count = 0
 
+    if opts['copies'] and opts['rev']:
+        endrev = max([int(i)
+                      for i in cmdutil.revrange(ui, repo, opts['rev'])]) + 1
+    else:
+        endrev = repo.changelog.count()
+    rcache = {}
+    ncache = {}
+    dcache = []
+    def getrenamed(fn, rev, man):
+        '''looks up all renames for a file (up to endrev) the first
+        time the file is given. It indexes on the changerev and only
+        parses the manifest if linkrev != changerev.
+        Returns rename info for fn at changerev rev.'''
+        if fn not in rcache:
+            rcache[fn] = {}
+            ncache[fn] = {}
+            fl = repo.file(fn)
+            for i in xrange(fl.count()):
+                node = fl.node(i)
+                lr = fl.linkrev(node)
+                renamed = fl.renamed(node)
+                rcache[fn][lr] = renamed
+                if renamed:
+                    ncache[fn][node] = renamed
+                if lr >= endrev:
+                    break
+        if rev in rcache[fn]:
+            return rcache[fn][rev]
+        if not dcache or dcache[0] != man:
+            dcache[:] = [man, repo.manifest.readdelta(man)]
+        if fn in dcache[1]:
+            return ncache[fn].get(dcache[1][fn])
+        return None
+
     displayer = show_changeset(ui, repo, opts)
     for st, rev, fns in changeiter:
         if st == 'window':
@@ -1788,7 +1842,14 @@ def log(ui, repo, *pats, **opts):
             if opts['branches']:
                 br = repo.branchlookup([repo.changelog.node(rev)])
 
-            displayer.show(rev, brinfo=br)
+            copies = []
+            if opts.get('copies') and rev:
+                mf = getchange(rev)[0]
+                for fn in getchange(rev)[3]:
+                    rename = getrenamed(fn, rev, mf)
+                    if rename:
+                        copies.append((fn, rename[0]))
+            displayer.show(rev, brinfo=br, copies=copies)
             if opts['patch']:
                 prev = (parents and parents[0]) or nullid
                 patch.diff(repo, prev, changenode, match=matchfn, fp=du)
@@ -2652,32 +2713,60 @@ def verify(ui, repo):
 
 # Command options and aliases are listed here, alphabetically
 
+globalopts = [
+    ('R', 'repository', '',
+     _('repository root directory or symbolic path name')),
+    ('', 'cwd', '', _('change working directory')),
+    ('y', 'noninteractive', None,
+     _('do not prompt, assume \'yes\' for any required answers')),
+    ('q', 'quiet', None, _('suppress output')),
+    ('v', 'verbose', None, _('enable additional output')),
+    ('', 'config', [], _('set/override config option')),
+    ('', 'debug', None, _('enable debugging output')),
+    ('', 'debugger', None, _('start debugger')),
+    ('', 'lsprof', None, _('print improved command execution profile')),
+    ('', 'traceback', None, _('print traceback on exception')),
+    ('', 'time', None, _('time how long the command takes')),
+    ('', 'profile', None, _('print command execution profile')),
+    ('', 'version', None, _('output version information and exit')),
+    ('h', 'help', None, _('display help and exit')),
+]
+
+dryrunopts = [('n', 'dry-run', None,
+               _('do not perform actions, just print output'))]
+
+remoteopts = [
+    ('e', 'ssh', '', _('specify ssh command to use')),
+    ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
+]
+
+walkopts = [
+    ('I', 'include', [], _('include names matching the given patterns')),
+    ('X', 'exclude', [], _('exclude names matching the given patterns')),
+]
+
 table = {
     "^add":
         (add,
-         [('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns')),
-          ('n', 'dry-run', None, _('do not perform actions, just print output'))],
+         walkopts + dryrunopts,
          _('hg add [OPTION]... [FILE]...')),
     "addremove":
         (addremove,
-         [('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns')),
-          ('n', 'dry-run', None,
-           _('do not perform actions, just print output')),
-          ('s', 'similarity', '',
-           _('guess renamed files by similarity (0<=s<=1)'))],
+         walkopts + dryrunopts +
+         [('s', 'similarity', '',
+           _('guess renamed files by similarity (0<=s<=100)')),
+         ] + walkopts + dryrunopts,
          _('hg addremove [OPTION]... [FILE]...')),
     "^annotate":
         (annotate,
          [('r', 'rev', '', _('annotate the specified revision')),
+          ('f', 'follow', None, _('follow file copies and renames')),
           ('a', 'text', None, _('treat all files as text')),
           ('u', 'user', None, _('list the author')),
           ('d', 'date', None, _('list the date')),
           ('n', 'number', None, _('list the revision number (default)')),
           ('c', 'changeset', None, _('list the changeset')),
-          ('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
+         ] + walkopts,
          _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
     "archive":
         (archive,
@@ -2685,8 +2774,7 @@ table = {
           ('p', 'prefix', '', _('directory prefix for files in archive')),
           ('r', 'rev', '', _('revision to distribute')),
           ('t', 'type', '', _('type of distribution to create')),
-          ('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
+         ] + walkopts,
          _('hg archive [OPTION]... DEST')),
     "backout":
         (backout,
@@ -2697,20 +2785,19 @@ table = {
           ('d', 'date', '', _('record datecode as commit date')),
           ('', 'parent', '', _('parent to choose when backing out merge')),
           ('u', 'user', '', _('record user as committer')),
-          ('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
+         ] + walkopts,
          _('hg backout [OPTION]... REV')),
     "bundle":
         (bundle,
          [('f', 'force', None,
-           _('run even when remote repository is unrelated'))],
+           _('run even when remote repository is unrelated')),
+         ] + remoteopts,
          _('hg bundle FILE DEST')),
     "cat":
         (cat,
          [('o', 'output', '', _('print output to file with formatted name')),
           ('r', 'rev', '', _('print the given revision')),
-          ('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
+         ] + walkopts,
          _('hg cat [OPTION]... FILE...')),
     "^clone":
         (clone,
@@ -2720,9 +2807,7 @@ table = {
           ('', 'pull', None, _('use pull protocol to copy metadata')),
           ('', 'uncompressed', None,
            _('use uncompressed transfer (fast over LAN)')),
-          ('e', 'ssh', '', _('specify ssh command to use')),
-          ('', 'remotecmd', '',
-           _('specify hg command to run on the remote side'))],
+         ] + remoteopts,
          _('hg clone [OPTION]... SOURCE [DEST]')),
     "^commit|ci":
         (commit,
@@ -2732,17 +2817,14 @@ table = {
           ('l', 'logfile', '', _('read the commit message from <file>')),
           ('d', 'date', '', _('record datecode as commit date')),
           ('u', 'user', '', _('record user as commiter')),
-          ('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
+         ] + walkopts,
          _('hg commit [OPTION]... [FILE]...')),
     "copy|cp":
         (copy,
          [('A', 'after', None, _('record a copy that has already occurred')),
           ('f', 'force', None,
            _('forcibly copy over an existing managed file')),
-          ('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns')),
-          ('n', 'dry-run', None, _('do not perform actions, just print output'))],
+         ] + walkopts + dryrunopts,
          _('hg copy [OPTION]... [SOURCE]... DEST')),
     "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
     "debugcomplete":
@@ -2762,10 +2844,7 @@ table = {
     "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
     "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
     "debugwalk":
-        (debugwalk,
-         [('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
-         _('debugwalk [OPTION]... [FILE]...')),
+        (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
     "^diff":
         (diff,
          [('r', 'rev', [], _('revision')),
@@ -2773,27 +2852,25 @@ table = {
           ('p', 'show-function', None,
            _('show which function each change is in')),
           ('g', 'git', None, _('use git extended diff format')),
+          ('', 'nodates', None, _("don't include dates in diff headers")),
           ('w', 'ignore-all-space', None,
            _('ignore white space when comparing lines')),
           ('b', 'ignore-space-change', None,
            _('ignore changes in the amount of white space')),
           ('B', 'ignore-blank-lines', None,
            _('ignore changes whose lines are all blank')),
-          ('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
+         ] + walkopts,
          _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
     "^export":
         (export,
          [('o', 'output', '', _('print output to file with formatted name')),
           ('a', 'text', None, _('treat all files as text')),
           ('g', 'git', None, _('use git extended diff format')),
+          ('', 'nodates', None, _("don't include dates in diff headers")),
           ('', 'switch-parent', None, _('diff against the second parent'))],
          _('hg export [-a] [-o OUTFILESPEC] REV...')),
     "debugforget|forget":
-        (forget,
-         [('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
-         _('hg forget [OPTION]... FILE...')),
+        (forget, walkopts, _('hg forget [OPTION]... FILE...')),
     "grep":
         (grep,
          [('0', 'print0', None, _('end fields with NUL')),
@@ -2806,8 +2883,7 @@ table = {
           ('n', 'line-number', None, _('print matching line numbers')),
           ('r', 'rev', [], _('search in given revision range')),
           ('u', 'user', None, _('print user who committed change')),
-          ('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
+         ] + walkopts,
          _('hg grep [OPTION]... PATTERN [FILE]...')),
     "heads":
         (heads,
@@ -2838,17 +2914,11 @@ table = {
           ('p', 'patch', None, _('show patch')),
           ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
           ('', 'template', '', _('display with template')),
-          ('e', 'ssh', '', _('specify ssh command to use')),
-          ('', 'remotecmd', '',
-           _('specify hg command to run on the remote side'))],
+         ] + remoteopts,
          _('hg incoming [-p] [-n] [-M] [-r REV]...'
            ' [--bundle FILENAME] [SOURCE]')),
     "^init":
-        (init,
-         [('e', 'ssh', '', _('specify ssh command to use')),
-          ('', 'remotecmd', '',
-           _('specify hg command to run on the remote side'))],
-         _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
+        (init, remoteopts, _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
     "locate":
         (locate,
          [('r', 'rev', '', _('search the repository as it stood at rev')),
@@ -2856,8 +2926,7 @@ table = {
            _('end filenames with NUL, for use with xargs')),
           ('f', 'fullpath', None,
            _('print complete paths from the filesystem root')),
-          ('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
+         ] + walkopts,
          _('hg locate [OPTION]... [PATTERN]...')),
     "^log|history":
         (log,
@@ -2866,6 +2935,7 @@ table = {
            _('follow changeset history, or file history across copies and renames')),
           ('', 'follow-first', None,
            _('only follow the first parent of merge changesets')),
+          ('C', 'copies', None, _('show copied files')),
           ('k', 'keyword', [], _('search for a keyword')),
           ('l', 'limit', '', _('limit number of changes displayed')),
           ('r', 'rev', [], _('show the specified revision or range')),
@@ -2875,8 +2945,7 @@ table = {
           ('p', 'patch', None, _('show patch')),
           ('P', 'prune', [], _('do not display revision or any of its ancestors')),
           ('', 'template', '', _('display with template')),
-          ('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
+         ] + walkopts,
          _('hg log [OPTION]... [FILE]')),
     "manifest": (manifest, [], _('hg manifest [REV]')),
     "merge":
@@ -2893,9 +2962,7 @@ table = {
           ('r', 'rev', [], _('a specific revision you would like to push')),
           ('n', 'newest-first', None, _('show newest record first')),
           ('', 'template', '', _('display with template')),
-          ('e', 'ssh', '', _('specify ssh command to use')),
-          ('', 'remotecmd', '',
-           _('specify hg command to run on the remote side'))],
+         ] + remoteopts,
          _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
     "^parents":
         (parents,
@@ -2908,21 +2975,17 @@ table = {
     "^pull":
         (pull,
          [('u', 'update', None,
-           _('update the working directory to tip after pull')),
-          ('e', 'ssh', '', _('specify ssh command to use')),
+           _('update to new tip if changesets were pulled')),
           ('f', 'force', None,
            _('run even when remote repository is unrelated')),
           ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
-          ('', 'remotecmd', '',
-           _('specify hg command to run on the remote side'))],
+         ] + remoteopts,
          _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
     "^push":
         (push,
          [('f', 'force', None, _('force push')),
-          ('e', 'ssh', '', _('specify ssh command to use')),
           ('r', 'rev', [], _('a specific revision you would like to push')),
-          ('', 'remotecmd', '',
-           _('specify hg command to run on the remote side'))],
+         ] + remoteopts,
          _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
     "debugrawcommit|rawcommit":
         (rawcommit,
@@ -2938,26 +3001,21 @@ table = {
         (remove,
          [('A', 'after', None, _('record remove that has already occurred')),
           ('f', 'force', None, _('remove file even if modified')),
-          ('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
+         ] + walkopts,
          _('hg remove [OPTION]... FILE...')),
     "rename|mv":
         (rename,
          [('A', 'after', None, _('record a rename that has already occurred')),
           ('f', 'force', None,
            _('forcibly copy over an existing managed file')),
-          ('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns')),
-          ('n', 'dry-run', None, _('do not perform actions, just print output'))],
+         ] + walkopts + dryrunopts,
          _('hg rename [OPTION]... SOURCE... DEST')),
     "^revert":
         (revert,
          [('a', 'all', None, _('revert all changes when no arguments given')),
           ('r', 'rev', '', _('revision to revert to')),
           ('', 'no-backup', None, _('do not save backup copies of files')),
-          ('I', 'include', [], _('include names matching given patterns')),
-          ('X', 'exclude', [], _('exclude names matching given patterns')),
-          ('n', 'dry-run', None, _('do not perform actions, just print output'))],
+         ] + walkopts + dryrunopts,
          _('hg revert [-r REV] [NAME]...')),
     "rollback": (rollback, [], _('hg rollback')),
     "root": (root, [], _('hg root')),
@@ -2993,8 +3051,7 @@ table = {
           ('C', 'copies', None, _('show source of copied files')),
           ('0', 'print0', None,
            _('end filenames with NUL, for use with xargs')),
-          ('I', 'include', [], _('include names matching the given patterns')),
-          ('X', 'exclude', [], _('exclude names matching the given patterns'))],
+         ] + walkopts,
          _('hg status [OPTION]... [FILE]...')),
     "tag":
         (tag,
@@ -3015,7 +3072,7 @@ table = {
     "unbundle":
         (unbundle,
          [('u', 'update', None,
-           _('update the working directory to tip after unbundle'))],
+           _('update to new tip if changesets were unbundled'))],
          _('hg unbundle [-u] FILE')),
     "debugundo|undo": (undo, [], _('hg undo')),
     "^update|up|checkout|co":
@@ -3029,25 +3086,6 @@ table = {
     "version": (show_version, [], _('hg version')),
 }
 
-globalopts = [
-    ('R', 'repository', '',
-     _('repository root directory or symbolic path name')),
-    ('', 'cwd', '', _('change working directory')),
-    ('y', 'noninteractive', None,
-     _('do not prompt, assume \'yes\' for any required answers')),
-    ('q', 'quiet', None, _('suppress output')),
-    ('v', 'verbose', None, _('enable additional output')),
-    ('', 'config', [], _('set/override config option')),
-    ('', 'debug', None, _('enable debugging output')),
-    ('', 'debugger', None, _('start debugger')),
-    ('', 'lsprof', None, _('print improved command execution profile')),
-    ('', 'traceback', None, _('print traceback on exception')),
-    ('', 'time', None, _('time how long the command takes')),
-    ('', 'profile', None, _('print command execution profile')),
-    ('', 'version', None, _('output version information and exit')),
-    ('h', 'help', None, _('display help and exit')),
-]
-
 norepo = ("clone init version help debugancestor debugcomplete debugdata"
           " debugindex debugindexdot")
 optionalrepo = ("paths serve debugconfig")
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -5,6 +5,10 @@
 # This software may be used and distributed according to the terms
 # of the GNU General Public License, incorporated herein by reference.
 
+from demandload import *
+from node import *
+demandload(globals(), 'bdiff')
+
 from node import *
 from demandload import demandload
 demandload(globals(), "ancestor util")
@@ -183,13 +187,74 @@ class filectx(object):
         return [ filectx(self._repo, self._path, fileid=x,
                          filelog=self._filelog) for x in c ]
 
-    def annotate(self):
-        getctx = util.cachefunc(lambda x: filectx(self._repo, self._path,
-                                                  changeid=x,
-                                                  filelog=self._filelog))
-        hist = self._filelog.annotate(self._filenode)
+    def annotate(self, follow=False):
+        '''returns a list of tuples of (ctx, line) for each line
+        in the file, where ctx is the filectx of the node where
+        that line was last changed'''
+
+        def decorate(text, rev):
+            return ([rev] * len(text.splitlines()), text)
+
+        def pair(parent, child):
+            for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
+                child[0][b1:b2] = parent[0][a1:a2]
+            return child
+
+        getlog = util.cachefunc(lambda x: self._repo.file(x))
+        def getctx(path, fileid):
+            log = path == self._path and self._filelog or getlog(path)
+            return filectx(self._repo, path, fileid=fileid, filelog=log)
+        getctx = util.cachefunc(getctx)
+
+        def parents(f):
+            # we want to reuse filectx objects as much as possible
+            p = f._path
+            pl = [ (p, f._filelog.rev(n)) for n in f._filelog.parents(f._filenode) ]
+
+            if follow:
+                r = f.renamed()
+                if r:
+                    pl[0] = (r[0], getlog(r[0]).rev(r[1]))
 
-        return [(getctx(rev), line) for rev, line in hist]
+            return [ getctx(p, n) for p, n in pl if n != -1 ]
+                
+        # find all ancestors
+        needed = {self: 1}
+        visit = [self]
+        files = [self._path]
+        while visit:
+            f = visit.pop(0)
+            for p in parents(f):
+                if p not in needed:
+                    needed[p] = 1
+                    visit.append(p)
+                    if p._path not in files:
+                        files.append(p._path)
+                else:
+                    # count how many times we'll use this
+                    needed[p] += 1
+
+        # sort by revision (per file) which is a topological order
+        visit = []
+        files.reverse()
+        for f in files:
+            fn = [(n._filerev, n) for n in needed.keys() if n._path == f]
+            fn.sort()
+            visit.extend(fn)
+        hist = {}
+
+        for r, f in visit:
+            curr = decorate(f.data(), f)
+            for p in parents(f):
+                if p != nullid:
+                    curr = pair(hist[p], curr)
+                    # trim the history of unneeded revs
+                    needed[p] -= 1
+                    if not needed[p]:
+                        del hist[p]
+            hist[f] = curr
+
+        return zip(hist[f][0], hist[f][1].splitlines(1))
 
     def ancestor(self, fc2):
         """
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -14,7 +14,7 @@ demandload(globals(), "localrepo bundler
 demandload(globals(), "errno lock os shutil util merge@_merge verify@_verify")
 
 def _local(path):
-    return (os.path.isfile(path and util.drop_scheme('file', path)) and
+    return (os.path.isfile(util.drop_scheme('file', path)) and
             bundlerepo or localrepo)
 
 schemes = {
@@ -51,7 +51,7 @@ def islocal(repo):
 
 repo_setup_hooks = []
 
-def repository(ui, path=None, create=False):
+def repository(ui, path='', create=False):
     """return a repository object for the specified path"""
     repo = _lookup(path).instance(ui, path, create)
     for hook in repo_setup_hooks:
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -292,9 +292,12 @@ class hgweb(object):
 
         files = []
         mf = self.repo.manifest.read(changes[0])
+        parity = 0
         for f in changes[3]:
             files.append(self.t("filenodelink",
-                                filenode=hex(mf.get(f, nullid)), file=f))
+                                filenode=hex(mf.get(f, nullid)), file=f,
+                                parity=parity))
+            parity = 1 - parity
 
         def diff(**map):
             yield self.diff(p1, n, None)
@@ -391,42 +394,27 @@ class hgweb(object):
                      permissions=self.repo.manifest.read(mfn).execf(f))
 
     def fileannotate(self, f, node):
-        bcache = {}
-        ncache = {}
-        fl = self.repo.file(f)
-        n = fl.lookup(node)
-        node = hex(n)
-        changerev = fl.linkrev(n)
-
-        cl = self.repo.changelog
-        cn = cl.node(changerev)
-        cs = cl.read(cn)
-        mfn = cs[0]
+        fctx = self.repo.filectx(f, fileid=node)
+        n = fctx.filenode()
+        fl = fctx.filelog()
 
         def annotate(**map):
             parity = 0
             last = None
-            for r, l in fl.annotate(n):
-                try:
-                    cnode = ncache[r]
-                except KeyError:
-                    cnode = ncache[r] = self.repo.changelog.node(r)
+            for f, l in fctx.annotate(follow=True):
+                fnode = f.filenode()
+                name = self.repo.ui.shortuser(f.user())
 
-                try:
-                    name = bcache[r]
-                except KeyError:
-                    cl = self.repo.changelog.read(cnode)
-                    bcache[r] = name = self.repo.ui.shortuser(cl[1])
-
-                if last != cnode:
+                if last != fnode:
                     parity = 1 - parity
-                    last = cnode
+                    last = fnode
 
                 yield {"parity": parity,
-                       "node": hex(cnode),
-                       "rev": r,
+                       "node": hex(f.node()),
+                       "filenode": hex(fnode),
+                       "rev": f.rev(),
                        "author": name,
-                       "file": f,
+                       "file": f.path(),
                        "line": l}
 
         yield self.t("fileannotate",
@@ -434,15 +422,15 @@ class hgweb(object):
                      filenode=node,
                      annotate=annotate,
                      path=_up(f),
-                     rev=changerev,
-                     node=hex(cn),
-                     manifest=hex(mfn),
-                     author=cs[1],
-                     date=cs[2],
+                     rev=fctx.rev(),
+                     node=hex(fctx.node()),
+                     manifest=hex(fctx.changectx().changeset()[0]),
+                     author=fctx.user(),
+                     date=fctx.date(),
                      rename=self.renamelink(fl, n),
                      parent=self.siblings(fl.parents(n), fl.rev, file=f),
                      child=self.siblings(fl.children(n), fl.rev, file=f),
-                     permissions=self.repo.manifest.read(mfn).execf(f))
+                     permissions=fctx.manifest().execf(f))
 
     def manifest(self, mnode, path):
         man = self.repo.manifest
--- a/mercurial/httprepo.py
+++ b/mercurial/httprepo.py
@@ -165,7 +165,8 @@ class httprepository(remoterepository):
                                                proxyuser, proxypasswd or ''),
                     proxypath, proxyquery, proxyfrag))
                 handler = urllib2.ProxyHandler({scheme: proxyurl})
-                ui.debug(_('proxying through %s\n') % proxyurl)
+                ui.debug(_('proxying through http://%s:%s\n') % 
+                          (proxyhost, proxyport))
 
         # urllib2 takes proxy values from the environment and those
         # will take precedence if found, so drop them
--- a/mercurial/manifest.py
+++ b/mercurial/manifest.py
@@ -9,6 +9,7 @@ from revlog import *
 from i18n import gettext as _
 from demandload import *
 demandload(globals(), "array bisect struct")
+demandload(globals(), "mdiff")
 
 class manifestdict(dict):
     def __init__(self, mapping=None, flags=None):
@@ -42,16 +43,25 @@ class manifest(revlog):
         revlog.__init__(self, opener, "00manifest.i", "00manifest.d",
                         defversion)
 
+    def parselines(self, lines):
+        for l in lines.splitlines(1):
+            yield l.split('\0')
+
+    def readdelta(self, node):
+        delta = mdiff.patchtext(self.delta(node))
+        deltamap = manifestdict()
+        for f, n in self.parselines(delta):
+            deltamap.rawset(f, n)
+        return deltamap
+            
     def read(self, node):
         if node == nullid: return manifestdict() # don't upset local cache
         if self.mapcache and self.mapcache[0] == node:
             return self.mapcache[1]
         text = self.revision(node)
         self.listcache = array.array('c', text)
-        lines = text.splitlines(1)
         mapping = manifestdict()
-        for l in lines:
-            (f, n) = l.split('\0')
+        for f, n in self.parselines(text):
             mapping.rawset(f, n)
         self.mapcache = (node, mapping)
         return mapping
--- a/mercurial/mdiff.py
+++ b/mercurial/mdiff.py
@@ -24,6 +24,7 @@ class diffopts(object):
     text treats all files as text
     showfunc enables diff -p output
     git enables the git extended patch format
+    nodates removes dates from diff headers
     ignorews ignores all whitespace changes in the diff
     ignorewsamount ignores changes in the amount of whitespace
     ignoreblanklines ignores changes whose lines are all blank'''
@@ -33,6 +34,7 @@ class diffopts(object):
         'text': False,
         'showfunc': True,
         'git': False,
+        'nodates': False,
         'ignorews': False,
         'ignorewsamount': False,
         'ignoreblanklines': False,
@@ -51,7 +53,7 @@ defaultopts = diffopts()
 
 def unidiff(a, ad, b, bd, fn, r=None, opts=defaultopts):
     def datetag(date):
-        return opts.git and '\n' or '\t%s\n' % date
+        return (opts.git or opts.nodates) and '\n' or '\t%s\n' % date
 
     if not a and not b: return ""
     epoch = util.datestr((0, 0))
--- a/mercurial/patch.py
+++ b/mercurial/patch.py
@@ -291,6 +291,8 @@ def diffopts(ui, opts={}):
         text=opts.get('text'),
         git=(opts.get('git') or
                   ui.configbool('diff', 'git', None)),
+        nodates=(opts.get('nodates') or
+                  ui.configbool('diff', 'nodates', None)),
         showfunc=(opts.get('show_function') or
                   ui.configbool('diff', 'showfunc', None)),
         ignorews=(opts.get('ignore_all_space') or
--- a/mercurial/templater.py
+++ b/mercurial/templater.py
@@ -330,7 +330,8 @@ class changeset_templater(object):
     def __init__(self, ui, repo, mapfile, dest=None):
         self.t = templater(mapfile, common_filters,
                            cache={'parent': '{rev}:{node|short} ',
-                                  'manifest': '{rev}:{node|short}'})
+                                  'manifest': '{rev}:{node|short}',
+                                  'filecopy': '{name} ({source})'})
         self.ui = ui
         self.dest = dest
         self.repo = repo
@@ -355,7 +356,7 @@ class changeset_templater(object):
         self.write(thing, header=True)
 
     def show(self, rev=0, changenode=None, brinfo=None, changes=None,
-             **props):
+             copies=[], **props):
         '''show a single changeset or file revision'''
         log = self.repo.changelog
         if changenode is None:
@@ -472,6 +473,13 @@ class changeset_templater(object):
             showadds = ''
             showdels = ''
 
+        copies = [{'name': x[0], 'source': x[1]}
+                  for x in copies]
+        def showcopies(**args):
+            for x in showlist('file_copy', copies, plural='file_copies',
+                              **args):
+                yield x
+
         defprops = {
             'author': changes[1],
             'branches': showbranches,
@@ -480,6 +488,7 @@ class changeset_templater(object):
             'file_adds': showadds,
             'file_dels': showdels,
             'files': showfiles,
+            'file_copies': showcopies,
             'manifest': showmanifest,
             'node': hex(changenode),
             'parents': showparents,
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -948,6 +948,9 @@ def shortuser(user):
     f = user.find('<')
     if f >= 0:
         user = user[f+1:]
+    f = user.find(' ')
+    if f >= 0:
+        user = user[:f]
     return user
 
 def walkrepos(path):
--- a/mercurial/verify.py
+++ b/mercurial/verify.py
@@ -102,21 +102,15 @@ def verify(repo):
                     (short(n), short(p)))
 
         try:
-            delta = mdiff.patchtext(repo.manifest.delta(n))
+            for f, fn in repo.manifest.readdelta(n).iteritems():
+                filenodes.setdefault(f, {})[fn] = 1
         except KeyboardInterrupt:
             repo.ui.warn(_("interrupted"))
             raise
         except Exception, inst:
-            err(_("unpacking manifest %s: %s") % (short(n), inst))
+            err(_("reading delta for manifest %s: %s") % (short(n), inst))
             continue
 
-        try:
-            ff = [ l.split('\0') for l in delta.splitlines() ]
-            for f, fn in ff:
-                filenodes.setdefault(f, {})[bin(fn[:40])] = 1
-        except (ValueError, TypeError), inst:
-            err(_("broken delta in manifest %s: %s") % (short(n), inst))
-
     repo.ui.status(_("crosschecking files in changesets and manifests\n"))
 
     for m, c in neededmanifests.items():
--- a/templates/changeset-gitweb.tmpl
+++ b/templates/changeset-gitweb.tmpl
@@ -27,10 +27,10 @@
 #changesettag#
 </table></div>
 
-<div class="title_text">
+<div class="page_body">
 #desc|strip|escape|addbreaks#
 </div>
-
+<div class="list_head"></div>
 <div class="title_text">
 <table cellspacing="0">
 #files#
--- a/templates/map
+++ b/templates/map
@@ -22,7 +22,7 @@ filediff = filediff.tmpl
 filelog = filelog.tmpl
 fileline = '<div class="parity#parity#"><span class="lineno">#linenumber#</span>#line|escape#</div>'
 filelogentry = filelogentry.tmpl
-annotateline = '<tr class="parity#parity#"><td class="annotate"><a href="?cs=#node|short#">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
+annotateline = '<tr class="parity#parity#"><td class="annotate"><a href="?fa=#filenode|short#;file=#file|urlescape#">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
 difflineplus = '<span class="plusline">#line|escape#</span>'
 difflineminus = '<span class="minusline">#line|escape#</span>'
 difflineat = '<span class="atline">#line|escape#</span>'
@@ -32,11 +32,11 @@ changesetparent = '<tr><th class="parent
 filerevparent = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
 filerename = '<tr><td class="metatag">parent:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#file|escape#@#node|short#</a></td></tr>'
 filelogrename = '<tr><th>base:</th><td><a href="?f=#node|short#;file=#file|urlescape#">#file|escape#@#node|short#</a></td></tr>'
-fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="?fa=#filenode|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
+fileannotateparent = '<tr><td class="metatag">parent:</td><td><a href="?fa=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
 changesetchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
 changelogchild = '<tr><th class="child">child #rev#:</th><td class="child"><a href="?cs=#node|short#">#node|short#</a></td></tr>'
 filerevchild = '<tr><td class="metatag">child:</td><td><a href="?f=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
-fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="?fa=#filenode|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
+fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="?fa=#node|short#;file=#file|urlescape#">#node|short#</a></td></tr>'
 tags = tags.tmpl
 tagentry = '<li class="tagEntry parity#parity#"><tt class="node">#node#</tt> <a href="?cs=#node|short#">#tag|escape#</a></li>'
 diffblock = '<pre class="parity#parity#">#lines#</pre>'
--- a/templates/map-cmdline.default
+++ b/templates/map-cmdline.default
@@ -1,12 +1,15 @@
 changeset = 'changeset:   {rev}:{node|short}\n{tags}{short_parents}user:        {author}\ndate:        {date|date}\nsummary:     {desc|firstline}\n\n'
 changeset_quiet = '{rev}:{node|short}\n'
-changeset_verbose = 'changeset:   {rev}:{node}\n{tags}{parents}{manifest}user:        {author}\ndate:        {date|date}\nfiles:       {files}\n{file_adds}{file_dels}description:\n{desc|strip}\n\n\n'
+changeset_verbose = 'changeset:   {rev}:{node}\n{tags}{parents}{manifest}user:        {author}\ndate:        {date|date}\nfiles:       {files}\n{file_adds}{file_dels}{file_copies}description:\n{desc|strip}\n\n\n'
 start_file_adds = 'files+:     '
 file_add = ' {file_add}'
 end_file_adds = '\n'
 start_file_dels = 'files-:     '
 file_del = ' {file_del}'
 end_file_dels = '\n'
+start_file_copies = 'copies:     '
+file_copy = ' {name} ({source})'
+end_file_copies = '\n'
 short_parent = 'parent:      {rev}:{node|short}\n'
 parent = 'parent:      {rev}:{node}\n'
 manifest = 'manifest:    {rev}:{node}\n'
--- a/templates/map-gitweb
+++ b/templates/map-gitweb
@@ -8,7 +8,7 @@ error = error-gitweb.tmpl
 naventry = '<a href="?cmd=changelog;rev=#rev#;style=gitweb">#label|escape#</a> '
 navshortentry = '<a href="?cmd=shortlog;rev=#rev#;style=gitweb">#label|escape#</a> '
 filedifflink = '<a href="?cmd=filediff;node=#node#;file=#file|urlescape#;style=gitweb">#file|escape#</a> '
-filenodelink = '<tr class="light"><td><a class="list" href="">#file|escape#</a></td><td></td><td class="link"><a href="?cmd=file;filenode=#filenode#;file=#file|urlescape#;style=gitweb">file</a> | <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a> | <!-- FIXME: <a href="?fd=#filenode|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?cmd=filelog;filenode=#filenode|short#;file=#file|urlescape#;style=gitweb">revisions</a></td></tr>'
+filenodelink = '<tr class="parity#parity#"><td><a class="list" href="">#file|escape#</a></td><td></td><td class="link"><a href="?cmd=file;filenode=#filenode#;file=#file|urlescape#;style=gitweb">file</a> | <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a> | <!-- FIXME: <a href="?fd=#filenode|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?cmd=filelog;filenode=#filenode|short#;file=#file|urlescape#;style=gitweb">revisions</a></td></tr>'
 fileellipses = '...'
 changelogentry = changelogentry-gitweb.tmpl
 searchentry = changelogentry-gitweb.tmpl
@@ -20,7 +20,7 @@ filerevision = filerevision-gitweb.tmpl
 fileannotate = fileannotate-gitweb.tmpl
 filelog = filelog-gitweb.tmpl
 fileline = '<div style="font-family:monospace" class="parity#parity#"><pre><span class="linenr">   #linenumber#</span> #line|escape#</pre></div>'
-annotateline = '<tr style="font-family:monospace" class="parity#parity#"><td class="linenr" style="text-align: right;"><a href="?cs=#node|short#;style=gitweb">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
+annotateline = '<tr style="font-family:monospace" class="parity#parity#"><td class="linenr" style="text-align: right;"><a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
 difflineplus = '<div style="color:#008800;">#line|escape#</div>'
 difflineminus = '<div style="color:#cc0000;">#line|escape#</div>'
 difflineat = '<div style="color:#990099;">#line|escape#</div>'
--- a/templates/template-vars.txt
+++ b/templates/template-vars.txt
@@ -25,6 +25,7 @@ header        the global page header
 footer        the global page footer
 
 files         a list of file links
+file_copies   a list of pairs of name, source filenames
 dirs          a set of directory links
 diff          a diff of one or more files
 annotate      an annotated file
@@ -36,4 +37,4 @@ Templates and commands:
     filenodelink - jump to file diff
     fileellipses - printed after maxfiles
     changelogentry - an entry in the log
-  manifest - browse a manifest as a directory tree
\ No newline at end of file
+  manifest - browse a manifest as a directory tree
--- a/tests/README
+++ b/tests/README
@@ -26,11 +26,9 @@ writing tests:
 
   use commit -m "test" -u test -d "1000000 0"
 
-- diff will show the current time
+- diff and export may show the current time
 
-  use hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-                    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
-  to strip dates
+  use -D/--nodates to strip the dates
 
 - You can append your own hgrc settings to the file that the environment
   variable HGRCPATH points to. This file is cleared before running a test.
--- a/tests/test-annotate
+++ b/tests/test-annotate
@@ -21,3 +21,53 @@ hg annotate -u a
 
 echo % annotate -cdnu
 hg annotate -cdnu a
+
+cat <<EOF >>a
+a
+a
+EOF
+hg ci -ma1 -d '1 0'
+hg cp a b
+hg ci -mb -d '1 0'
+cat <<EOF >> b
+b
+b
+b
+EOF
+hg ci -mb2 -d '2 0'
+
+echo % annotate b
+hg annotate b
+echo % annotate -nf b
+hg annotate -nf b
+
+hg up -C 2
+cat <<EOF >> b
+b
+c
+b
+EOF
+hg ci -mb2.1 -d '2 0'
+hg merge
+hg ci -mmergeb -d '3 0'
+echo % annotate after merge
+hg annotate -nf b
+
+hg up -C 1
+hg cp a b
+cat <<EOF > b
+a
+z
+a
+EOF
+hg ci -mc -d '3 0'
+hg merge
+cat <<EOF >> b
+b
+c
+b
+EOF
+echo d >> b
+hg ci -mmerge2 -d '4 0'
+echo % annotate after rename merge
+hg annotate -nf b
--- a/tests/test-annotate.out
+++ b/tests/test-annotate.out
@@ -11,3 +11,40 @@ 0: a
 nobody: a
 % annotate -cdnu
 nobody 0 8435f90966e4 Thu Jan 01 00:00:01 1970 +0000: a
+% annotate b
+2: a
+2: a
+2: a
+3: b
+3: b
+3: b
+% annotate -nf b
+0 a: a
+1 a: a
+1 a: a
+3 b: b
+3 b: b
+3 b: b
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+merging b
+0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+(branch merge, don't forget to commit)
+% annotate after merge
+0 a: a
+1 a: a
+1 a: a
+3 b: b
+4 b: c
+3 b: b
+0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+merging b
+0 files updated, 1 files merged, 0 files removed, 0 files unresolved
+(branch merge, don't forget to commit)
+% annotate after rename merge
+0 a: a
+6 b: z
+1 a: a
+3 b: b
+4 b: c
+3 b: b
+7 b: d
--- a/tests/test-command-template
+++ b/tests/test-command-template
@@ -65,6 +65,10 @@ echo '[ui]' > .hg/hgrc
 echo 'style = t' >> .hg/hgrc
 hg log
 
+echo '# issue338'
+hg log --style=changelog > changelog
+cat changelog
+
 echo "# keys work"
 for key in author branches date desc file_adds file_dels files \
         manifest node parents rev tags; do
--- a/tests/test-command-template.out
+++ b/tests/test-command-template.out
@@ -76,6 +76,33 @@ 3
 2
 1
 0
+# issue338
+1970-01-16  person  <person>
+
+	* c:
+	no user, no domain
+	[10e46f2dcbf4] [tip]
+
+1970-01-14  other  <other@place>
+
+	* c:
+	no person
+	[97054abb4ab8]
+
+1970-01-13  A. N. Other  <other@place>
+
+	* b:
+	other 1 other 2
+
+	other 3
+	[b608e9d1a3f0]
+
+1970-01-12  User Name  <user@hostname>
+
+	* a:
+	line 1 line 2
+	[1e4e1b8f71e0]
+
 # keys work
 author: person
 author: other@place
--- a/tests/test-diff-ignore-whitespace
+++ b/tests/test-diff-ignore-whitespace
@@ -3,8 +3,7 @@
 # GNU diff is the reference for all of these results.
 
 hgdiff() {
-    hg diff "$@" | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-                  -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+    hg diff --nodates "$@"
 }
 
 test_added_blank_lines() {
--- a/tests/test-diff-subdir
+++ b/tests/test-diff-subdir
@@ -14,14 +14,11 @@ echo 1 > alpha/one
 echo 2 > beta/two
 
 echo EVERYTHING
-hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-              -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --nodates
 
 echo BETA ONLY
-hg diff beta | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-                -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --nodates beta
 
 echo INSIDE BETA
 cd beta
-hg diff . | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-                -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --nodates .
--- a/tests/test-diffdir
+++ b/tests/test-diffdir
@@ -7,12 +7,9 @@ hg ci -m "a" -d "1000000 0"
 
 echo 123 > b
 hg add b
-hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-              -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --nodates
 
-hg diff -r tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-                     -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --nodates -r tip
 
 echo foo > a
-hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-              -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --nodates
--- a/tests/test-git-export
+++ b/tests/test-git-export
@@ -8,26 +8,22 @@ hg ci -Amstart -d '0 0'
 echo new > new
 hg ci -Amnew -d '0 0'
 echo '% new file'
-hg diff --git -r 0 | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --git -r 0
 
 hg cp new copy
 hg ci -mcopy -d '0 0'
 echo '% copy'
-hg diff --git -r 1:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --git -r 1:tip
 
 hg mv copy rename
 hg ci -mrename -d '0 0'
 echo '% rename'
-hg diff --git -r 2:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --git -r 2:tip
 
 hg rm rename
 hg ci -mdelete -d '0 0'
 echo '% delete'
-hg diff --git -r 3:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --git -r 3:tip
 
 cat > src <<EOF
 1
@@ -40,17 +36,14 @@ hg ci -Amsrc -d '0 0'
 chmod +x src
 hg ci -munexec -d '0 0'
 echo '% chmod 644'
-hg diff --git -r 5:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --git -r 5:tip
 
 hg mv src dst
 chmod -x dst
 echo a >> dst
 hg ci -mrenamemod -d '0 0'
 echo '% rename+mod+chmod'
-hg diff --git -r 6:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --git -r 6:tip
 
 echo '% nonexistent in tip+chmod'
-hg diff --git -r 5:6 | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-    -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --git -r 5:6
--- a/tests/test-globalopts.out
+++ b/tests/test-globalopts.out
@@ -115,7 +115,7 @@ Mercurial Distributed SCM
 list of commands (use "hg help -v" to show aliases and global options):
 
  add         add the specified files on the next commit
- addremove   add all new files, delete all missing files (DEPRECATED)
+ addremove   add all new files, delete all missing files
  annotate    show changeset information per file line
  archive     create unversioned archive of a repository revision
  backout     reverse effect of earlier changeset
@@ -162,7 +162,7 @@ Mercurial Distributed SCM
 list of commands (use "hg help -v" to show aliases and global options):
 
  add         add the specified files on the next commit
- addremove   add all new files, delete all missing files (DEPRECATED)
+ addremove   add all new files, delete all missing files
  annotate    show changeset information per file line
  archive     create unversioned archive of a repository revision
  backout     reverse effect of earlier changeset
--- a/tests/test-help.out
+++ b/tests/test-help.out
@@ -39,7 +39,7 @@ Mercurial Distributed SCM
 list of commands (use "hg help -v" to show aliases and global options):
 
  add         add the specified files on the next commit
- addremove   add all new files, delete all missing files (DEPRECATED)
+ addremove   add all new files, delete all missing files
  annotate    show changeset information per file line
  archive     create unversioned archive of a repository revision
  backout     reverse effect of earlier changeset
@@ -82,7 +82,7 @@ list of commands (use "hg help -v" to sh
  verify      verify the integrity of the repository
  version     output version and copyright information
  add         add the specified files on the next commit
- addremove   add all new files, delete all missing files (DEPRECATED)
+ addremove   add all new files, delete all missing files
  annotate    show changeset information per file line
  archive     create unversioned archive of a repository revision
  backout     reverse effect of earlier changeset
@@ -179,6 +179,7 @@ options:
  -a --text                 treat all files as text
  -p --show-function        show which function each change is in
  -g --git                  use git extended diff format
+    --nodates              don't include dates in diff headers
  -w --ignore-all-space     ignore white space when comparing lines
  -b --ignore-space-change  ignore changes in the amount of white space
  -B --ignore-blank-lines   ignore changes whose lines are all blank
--- a/tests/test-log
+++ b/tests/test-log
@@ -29,6 +29,9 @@ hg log -vf a
 echo % many renames
 hg log -vf e
 
+echo % log copies
+hg log -vC --template '{rev} {file_copies%filecopy}\n'
+
 # log --follow tests
 hg init ../follow
 cd ../follow
--- a/tests/test-log.out
+++ b/tests/test-log.out
@@ -76,6 +76,12 @@ description:
 a
 
 
+% log copies
+4 e (dir/b)
+3 b (a)
+2 dir/b (b)
+1 b (a)
+0 
 adding base
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 adding b1
--- a/tests/test-merge-revert2
+++ b/tests/test-merge-revert2
@@ -27,8 +27,8 @@ hg id
 hg update -C 0
 echo "changed file1 different" >> file1
 HGMERGE=merge hg update
-hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" -e "s/\(<<<<<<<\) .*/\1/" \
-              -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" -e "s/\(>>>>>>>\) .*/\1/"
+#hg diff --nodates | sed -e "s/\(<<<<<<<\) .*/\1/" -e "s/\(>>>>>>>\) .*/\1/"
+hg diff --nodates | sed -e "s/\(<<<<<<<\|>>>>>>>\) .*/\1/"
 hg status
 hg id
 hg revert --no-backup --all
--- a/tests/test-remove
+++ b/tests/test-remove
@@ -13,8 +13,8 @@ hg revert --all
 rm foo
 hg remove --after
 hg commit -m 2 -d "1000000 0"
-hg export 0
-hg export 1
+hg export --nodates 0
+hg export --nodates 1
 hg log -p -r 0
 hg log -p -r 1
 
--- a/tests/test-remove.out
+++ b/tests/test-remove.out
@@ -10,8 +10,8 @@ removing foo
 1
 
 diff -r 000000000000 -r 8ba83d44753d foo
---- /dev/null	Thu Jan 01 00:00:00 1970 +0000
-+++ b/foo	Mon Jan 12 13:46:40 1970 +0000
+--- /dev/null
++++ b/foo
 @@ -0,0 +1,1 @@
 +a
 # HG changeset patch
@@ -22,8 +22,8 @@ diff -r 000000000000 -r 8ba83d44753d foo
 2
 
 diff -r 8ba83d44753d -r a1fce69c50d9 foo
---- a/foo	Mon Jan 12 13:46:40 1970 +0000
-+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
+--- a/foo
++++ /dev/null
 @@ -1,1 +0,0 @@
 -a
 changeset:   0:8ba83d44753d
--- a/tests/test-up-local-change
+++ b/tests/test-up-local-change
@@ -12,8 +12,7 @@ hg clone . ../r2
 cd ../r2
 hg up
 echo abc > a
-hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-              -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --nodates
 
 cd ../r1
 echo b > b
@@ -34,8 +33,7 @@ hg parents
 hg --debug up
 hg parents
 hg -v history
-hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-              -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --nodates
 
 # create a second head
 cd ../r1
@@ -53,8 +51,7 @@ hg --debug up || echo failed
 hg --debug merge || echo failed
 hg --debug merge -f
 hg parents
-hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
-              -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
+hg diff --nodates
 
 # test a local add
 cd ..