# HG changeset patch # User Patrick Mezard # Date 1231101846 -3600 # Node ID 81deb1ba440258ecb2172ce8eaf5addfa2ca1fad # Parent a204547790bc83b1443ff6f3d93c15107d6fbd2f# Parent 5f7e3f17aececd6c4e19e7488e90c382057eee11 Merge with crew-stable diff -r 5f7e3f17aece -r 81deb1ba4402 contrib/zsh_completion --- a/contrib/zsh_completion Sun Jan 04 21:32:40 2009 +0100 +++ b/contrib/zsh_completion Sun Jan 04 21:44:06 2009 +0100 @@ -4,14 +4,13 @@ # it into your zsh function path (/usr/share/zsh/site-functions for # instance) # -# Copyright (C) 2005 Steve Borho +# Copyright (C) 2005-6 Steve Borho # Copyright (C) 2006-8 Brendan Cully # # This is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free # Software Foundation; either version 2 of the License, or (at your # option) any later version. -# emulate -LR zsh setopt extendedglob @@ -118,27 +117,17 @@ typeset -ga _hg_cmd_list typeset -gA _hg_alias_list local hline cmd cmdalias - _call_program help hg --verbose help | while read -A hline + + _call_program hg hg debugcomplete -v 2>/dev/null | while read -A hline do - cmd="$hline[1]" - case $cmd in - *:) - cmd=${cmd%:} - _hg_cmd_list+=($cmd) - ;; - *,) - cmd=${cmd%,} - _hg_cmd_list+=($cmd) - integer i=2 - while (( i <= $#hline )) - do - cmdalias=${hline[$i]%(:|,)} - _hg_cmd_list+=($cmdalias) - _hg_alias_list+=($cmdalias $cmd) - (( i++ )) - done - ;; - esac + cmd=$hline[1] + _hg_cmd_list+=($cmd) + + for cmdalias in $hline[2,-1] + do + _hg_cmd_list+=($cmdalias) + _hg_alias_list+=($cmdalias $cmd) + done done } diff -r 5f7e3f17aece -r 81deb1ba4402 hgext/bookmarks.py --- a/hgext/bookmarks.py Sun Jan 04 21:32:40 2009 +0100 +++ b/hgext/bookmarks.py Sun Jan 04 21:44:06 2009 +0100 @@ -14,9 +14,19 @@ It is possible to use bookmark names in every revision lookup (e.g. hg merge, hg update). + +The bookmark extension offers the possiblity to have a more git-like experience +by adding the following configuration option to your .hgrc: + +[bookmarks] +track.current = True + +This will cause bookmarks to track the bookmark that you are currently on, and +just updates it. This is similar to git's approach of branching. ''' from mercurial.commands import templateopts, hex, short +from mercurial import extensions from mercurial.i18n import _ from mercurial import cmdutil, util, commands, changelog from mercurial.node import nullid, nullrev @@ -54,11 +64,54 @@ ''' if os.path.exists(repo.join('bookmarks')): util.copyfile(repo.join('bookmarks'), repo.join('undo.bookmarks')) + if current(repo) not in refs: + setcurrent(repo, None) file = repo.opener('bookmarks', 'w+') for refspec, node in refs.items(): file.write("%s %s\n" % (hex(node), refspec)) file.close() +def current(repo): + '''Get the current bookmark + + If we use gittishsh branches we have a current bookmark that + we are on. This function returns the name of the bookmark. It + is stored in .hg/bookmarks.current + ''' + if repo._bookmarkcurrent: + return repo._bookmarkcurrent + mark = None + if os.path.exists(repo.join('bookmarks.current')): + file = repo.opener('bookmarks.current') + mark = file.readline() + if mark == '': + mark = None + file.close() + repo._bookmarkcurrent = mark + return mark + +def setcurrent(repo, mark): + '''Set the name of the bookmark that we are currently on + + Set the name of the bookmark that we are on (hg update ). + The name is recoreded in .hg/bookmarks.current + ''' + if current(repo) == mark: + return + + refs = parse(repo) + + # do not update if we do update to a rev equal to the current bookmark + if (mark not in refs and + current(repo) and refs[current(repo)] == repo.changectx('.').node()): + return + if mark not in refs: + mark = '' + file = repo.opener('bookmarks.current', 'w+') + file.write(mark) + file.close() + repo._bookmarkcurrent = mark + def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False, rename=None): '''mercurial bookmarks @@ -85,6 +138,8 @@ raise util.Abort(_("new bookmark name required")) marks[mark] = marks[rename] del marks[rename] + if current(repo) == rename: + setcurrent(repo, mark) write(repo, marks) return @@ -121,7 +176,11 @@ ui.status("no bookmarks set\n") else: for bmark, n in marks.iteritems(): - prefix = (n == cur) and '*' or ' ' + if ui.configbool('bookmarks', 'track.current'): + prefix = (bmark == current(repo) and n == cur) and '*' or ' ' + else: + prefix = (n == cur) and '*' or ' ' + ui.write(" %s %-25s %d:%s\n" % ( prefix, bmark, repo.changelog.rev(n), hexfn(n))) return @@ -166,6 +225,7 @@ # init a bookmark cache as otherwise we would get a infinite reading # in lookup() repo._bookmarks = None + repo._bookmarkcurrent = None class bookmark_repo(repo.__class__): def rollback(self): @@ -192,9 +252,14 @@ marks = parse(repo) update = False for mark, n in marks.items(): - if n in parents: - marks[mark] = node - update = True + if ui.configbool('bookmarks', 'track.current'): + if mark == current(repo) and n in parents: + marks[mark] = node + update = True + else: + if n in parents: + marks[mark] = node + update = True if update: write(repo, marks) return node @@ -218,8 +283,35 @@ write(repo, marks) return result + def tags(self): + """Merge bookmarks with normal tags""" + if self.tagscache: + return self.tagscache + + tagscache = super(bookmark_repo, self).tags() + tagscache.update(parse(repo)) + return tagscache + repo.__class__ = bookmark_repo +def updatecurbookmark(orig, ui, repo, *args, **opts): + '''Set the current bookmark + + If the user updates to a bookmark we update the .hg/bookmarks.current + file. + ''' + res = orig(ui, repo, *args, **opts) + rev = opts['rev'] + if not rev and len(args) > 0: + rev = args[0] + setcurrent(repo, rev) + return res + +def uisetup(ui): + 'Replace push with a decorator to provide --non-bookmarked option' + if ui.configbool('bookmarks', 'track.current'): + extensions.wrapcommand(commands.table, 'update', updatecurbookmark) + cmdtable = { "bookmarks": (bookmark, diff -r 5f7e3f17aece -r 81deb1ba4402 hgext/bugzilla.py --- a/hgext/bugzilla.py Sun Jan 04 21:32:40 2009 +0100 +++ b/hgext/bugzilla.py Sun Jan 04 21:44:06 2009 +0100 @@ -4,53 +4,107 @@ # # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -# -# hook extension to update comments of bugzilla bugs when changesets -# that refer to bugs by id are seen. this hook does not change bug -# status, only comments. -# -# to configure, add items to '[bugzilla]' section of hgrc. -# -# to use, configure bugzilla extension and enable like this: -# -# [extensions] -# hgext.bugzilla = -# -# [hooks] -# # run bugzilla hook on every change pulled or pushed in here -# incoming.bugzilla = python:hgext.bugzilla.hook -# -# config items: -# -# section name is 'bugzilla'. -# [bugzilla] -# -# REQUIRED: -# host = bugzilla # mysql server where bugzilla database lives -# password = ** # user's password -# version = 2.16 # version of bugzilla installed -# -# OPTIONAL: -# bzuser = ... # fallback bugzilla user name to record comments with -# db = bugs # database to connect to -# notify = ... # command to run to get bugzilla to send mail -# regexp = ... # regexp to match bug ids (must contain one "()" group) -# strip = 0 # number of slashes to strip for url paths -# style = ... # style file to use when formatting comments -# template = ... # template to use when formatting comments -# timeout = 5 # database connection timeout (seconds) -# user = bugs # user to connect to database as -# [web] -# baseurl = http://hgserver/... # root of hg web site for browsing commits -# -# if hg committer names are not same as bugzilla user names, use -# "usermap" feature to map from committer email to bugzilla user name. -# usermap can be in hgrc or separate config file. -# -# [bugzilla] -# usermap = filename # cfg file with "committer"="bugzilla user" info -# [usermap] -# committer_email = bugzilla_user_name + +'''Bugzilla integration + +This hook extension adds comments on bugs in Bugzilla when changesets +that refer to bugs by Bugzilla ID are seen. The hook does not change bug +status. + +The hook updates the Bugzilla database directly. Only Bugzilla installations +using MySQL are supported. + +The hook relies on a Bugzilla script to send bug change notification emails. +That script changes between Bugzilla versions; the 'processmail' script used +prior to 2.18 is replaced in 2.18 and subsequent versions by +'config/sendbugmail.pl'. Note that these will be run by Mercurial as the user +pushing the change; you will need to ensure the Bugzilla install file +permissions are set appropriately. + +Configuring the extension: + + [bugzilla] + host Hostname of the MySQL server holding the Bugzilla database. + db Name of the Bugzilla database in MySQL. Default 'bugs'. + user Username to use to access MySQL server. Default 'bugs'. + password Password to use to access MySQL server. + timeout Database connection timeout (seconds). Default 5. + version Bugzilla version. Specify '3.0' for Bugzilla versions from + 3.0 onwards, and '2.16' for versions prior to 3.0. + bzuser Fallback Bugzilla user name to record comments with, if + changeset committer cannot be found as a Bugzilla user. + notify The command to run to get Bugzilla to send bug change + notification emails. Substitutes one string parameter, + the bug ID. Default 'cd /var/www/html/bugzilla && ' + './processmail %s nobody@nowhere.com'. + regexp Regular expression to match bug IDs in changeset commit message. + Must contain one "()" group. The default expression matches + 'Bug 1234', 'Bug no. 1234', 'Bug number 1234', + 'Bugs 1234,5678', 'Bug 1234 and 5678' and variations thereof. + Matching is case insensitive. + style The style file to use when formatting comments. + template Template to use when formatting comments. Overrides + style if specified. In addition to the usual Mercurial + keywords, the extension specifies: + {bug} The Bugzilla bug ID. + {root} The full pathname of the Mercurial repository. + {webroot} Stripped pathname of the Mercurial repository. + {hgweb} Base URL for browsing Mercurial repositories. + Default 'changeset {node|short} in repo {root} refers ' + 'to bug {bug}.\\ndetails:\\n\\t{desc|tabindent}' + strip The number of slashes to strip from the front of {root} + to produce {webroot}. Default 0. + usermap Path of file containing Mercurial committer ID to Bugzilla user + ID mappings. If specified, the file should contain one mapping + per line, "committer"="Bugzilla user". See also the + [usermap] section. + + [usermap] + Any entries in this section specify mappings of Mercurial committer ID + to Bugzilla user ID. See also [bugzilla].usermap. + "committer"="Bugzilla user" + + [web] + baseurl Base URL for browsing Mercurial repositories. Reference from + templates as {hgweb}. + +Activating the extension: + + [extensions] + hgext.bugzilla = + + [hooks] + # run bugzilla hook on every change pulled or pushed in here + incoming.bugzilla = python:hgext.bugzilla.hook + +Example configuration: + +This example configuration is for a collection of Mercurial repositories +in /var/local/hg/repos/ used with a local Bugzilla 3.2 installation in +/opt/bugzilla-3.2. + + [bugzilla] + host=localhost + password=XYZZY + version=3.0 + bzuser=unknown@domain.com + notify=cd /opt/bugzilla-3.2 && perl -T contrib/sendbugmail.pl %%s bugmail@domain.com + template=Changeset {node|short} in {root|basename}.\\n{hgweb}/{webroot}/rev/{node|short}\\n\\n{desc}\\n + strip=5 + + [web] + baseurl=http://dev.domain.com/hg + + [usermap] + user@emaildomain.com=user.name@bugzilladomain.com + +Commits add a comment to the Bugzilla bug record of the form: + + Changeset 3b16791d6642 in repository-name. + http://dev.domain.com/hg/repository-name/rev/3b16791d6642 + + Changeset commit comment. Bug 1234. +''' from mercurial.i18n import _ from mercurial.node import short @@ -185,6 +239,7 @@ self.run('''insert into bugs_activity (bug_id, who, bug_when, fieldid) values (%s, %s, %s, %s)''', (bugid, userid, now, self.longdesc_id)) + self.conn.commit() class bugzilla_3_0(bugzilla_2_16): '''support for bugzilla 3.0 series.''' diff -r 5f7e3f17aece -r 81deb1ba4402 hgext/churn.py --- a/hgext/churn.py Sun Jan 04 21:32:40 2009 +0100 +++ b/hgext/churn.py Sun Jan 04 21:44:06 2009 +0100 @@ -12,27 +12,6 @@ import os, sys import time, datetime -def get_tty_width(): - if 'COLUMNS' in os.environ: - try: - return int(os.environ['COLUMNS']) - except ValueError: - pass - try: - import termios, array, fcntl - for dev in (sys.stdout, sys.stdin): - try: - fd = dev.fileno() - if not os.isatty(fd): - continue - arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8) - return array.array('h', arri)[1] - except ValueError: - pass - except ImportError: - pass - return 80 - def maketemplater(ui, repo, tmpl): tmpl = templater.parsestring(tmpl, quoted=False) try: @@ -157,7 +136,7 @@ maxcount = float(max([v for k, v in rate])) maxname = max([len(k) for k, v in rate]) - ttywidth = get_tty_width() + ttywidth = util.termwidth() ui.debug(_("assuming %i character terminal\n") % ttywidth) width = ttywidth - maxname - 2 - 6 - 2 - 2 diff -r 5f7e3f17aece -r 81deb1ba4402 hgext/color.py --- a/hgext/color.py Sun Jan 04 21:32:40 2009 +0100 +++ b/hgext/color.py Sun Jan 04 21:44:06 2009 +0100 @@ -204,6 +204,7 @@ _diff_prefixes = [('diff', 'diffline'), ('copy', 'extended'), ('rename', 'extended'), + ('old', 'extended'), ('new', 'extended'), ('deleted', 'extended'), ('---', 'file_a'), diff -r 5f7e3f17aece -r 81deb1ba4402 hgext/convert/__init__.py --- a/hgext/convert/__init__.py Sun Jan 04 21:32:40 2009 +0100 +++ b/hgext/convert/__init__.py Sun Jan 04 21:44:06 2009 +0100 @@ -7,6 +7,7 @@ '''converting foreign VCS repositories to Mercurial''' import convcmd +import cvsps from mercurial import commands from mercurial.i18n import _ @@ -183,7 +184,18 @@ def debugsvnlog(ui, **opts): return convcmd.debugsvnlog(ui, **opts) -commands.norepo += " convert debugsvnlog" +def debugcvsps(ui, *args, **opts): + '''Create changeset information from CVS + + This command is intended as a debugging tool for the CVS to Mercurial + converter, and can be used as a direct replacement for cvsps. + + Hg debugcvsps reads the CVS rlog for current directory (or any named + directory) in the CVS repository, and converts the log to a series of + changesets based on matching commit log entries and dates.''' + return cvsps.debugcvsps(ui, *args, **opts) + +commands.norepo += " convert debugsvnlog debugcvsps" cmdtable = { "convert": @@ -200,4 +212,22 @@ (debugsvnlog, [], 'hg debugsvnlog'), + "debugcvsps": + (debugcvsps, + [ + # Main options shared with cvsps-2.1 + ('b', 'branches', [], _('Only return changes on specified branches')), + ('p', 'prefix', '', _('Prefix to remove from file names')), + ('r', 'revisions', [], _('Only return changes after or between specified tags')), + ('u', 'update-cache', None, _("Update cvs log cache")), + ('x', 'new-cache', None, _("Create new cvs log cache")), + ('z', 'fuzz', 60, _('Set commit time fuzz in seconds')), + ('', 'root', '', _('Specify cvsroot')), + # Options specific to builtin cvsps + ('', 'parents', '', _('Show parent changesets')), + ('', 'ancestors', '', _('Show current changeset in ancestor branches')), + # Options that are ignored for compatibility with cvsps-2.1 + ('A', 'cvs-direct', None, 'Ignored for compatibility'), + ], + 'hg debugcvsps [OPTION]... [PATH]...'), } diff -r 5f7e3f17aece -r 81deb1ba4402 hgext/convert/cvs.py --- a/hgext/convert/cvs.py Sun Jan 04 21:32:40 2009 +0100 +++ b/hgext/convert/cvs.py Sun Jan 04 21:44:06 2009 +0100 @@ -144,11 +144,11 @@ if branch == "HEAD": branch = "" if branch: - latest = None + latest = 0 # the last changeset that contains a base # file is our parent for r in oldrevs: - latest = max(filerevids.get(r, None), latest) + latest = max(filerevids.get(r, 0), latest) if latest: p = [latest] diff -r 5f7e3f17aece -r 81deb1ba4402 hgext/convert/cvsps --- a/hgext/convert/cvsps Sun Jan 04 21:32:40 2009 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,154 +0,0 @@ -#!/usr/bin/env python -# -# Commandline front-end for cvsps.py -# -# Copyright 2008, Frank Kingswood -# -# This software may be used and distributed according to the terms -# of the GNU General Public License, incorporated herein by reference. - -import sys -from mercurial import util -from mercurial.i18n import _ -from optparse import OptionParser, SUPPRESS_HELP -from hgext.convert.cvsps import createlog, createchangeset, logerror - -def main(): - '''Main program to mimic cvsps.''' - - op = OptionParser(usage='%prog [-bpruvxz] path', - description='Read CVS rlog for current directory or named ' - 'path in repository, and convert the log to changesets ' - 'based on matching commit log entries and dates.') - - # Options that are ignored for compatibility with cvsps-2.1 - op.add_option('-A', dest='Ignore', action='store_true', help=SUPPRESS_HELP) - op.add_option('--cvs-direct', dest='Ignore', action='store_true', help=SUPPRESS_HELP) - op.add_option('-q', dest='Ignore', action='store_true', help=SUPPRESS_HELP) - - # Main options shared with cvsps-2.1 - op.add_option('-b', dest='Branches', action='append', default=[], - help='Only return changes on specified branches') - op.add_option('-p', dest='Prefix', action='store', default='', - help='Prefix to remove from file names') - op.add_option('-r', dest='Revisions', action='append', default=[], - help='Only return changes after or between specified tags') - op.add_option('-u', dest='Cache', action='store_const', const='update', - help="Update cvs log cache") - op.add_option('-v', dest='Verbose', action='count', default=0, - help='Be verbose') - op.add_option('-x', dest='Cache', action='store_const', const='write', - help="Create new cvs log cache") - op.add_option('-z', dest='Fuzz', action='store', type='int', default=60, - help='Set commit time fuzz', metavar='seconds') - op.add_option('--root', dest='Root', action='store', default='', - help='Specify cvsroot', metavar='cvsroot') - - # Options specific to this version - op.add_option('--parents', dest='Parents', action='store_true', - help='Show parent changesets') - op.add_option('--ancestors', dest='Ancestors', action='store_true', - help='Show current changeset in ancestor branches') - - options, args = op.parse_args() - - # Create a ui object for printing progress messages - class UI: - def __init__(self, verbose): - if verbose: - self.status = self.message - if verbose>1: - self.note = self.message - if verbose>2: - self.debug = self.message - def message(self, msg): - sys.stderr.write(msg) - def nomessage(self, msg): - pass - status = nomessage - note = nomessage - debug = nomessage - ui = UI(options.Verbose) - - try: - if args: - log = [] - for d in args: - log += createlog(ui, d, root=options.Root, cache=options.Cache) - else: - log = createlog(ui, root=options.Root, cache=options.Cache) - except logerror, e: - print e - return - - changesets = createchangeset(ui, log, options.Fuzz) - del log - - # Print changesets (optionally filtered) - - off = len(options.Revisions) - branches = {} # latest version number in each branch - ancestors = {} # parent branch - for cs in changesets: - - if options.Ancestors: - if cs.branch not in branches and cs.parents and cs.parents[0].id: - ancestors[cs.branch] = changesets[cs.parents[0].id-1].branch, cs.parents[0].id - branches[cs.branch] = cs.id - - # limit by branches - if options.Branches and (cs.branch or 'HEAD') not in options.Branches: - continue - - if not off: - # Note: trailing spaces on several lines here are needed to have - # bug-for-bug compatibility with cvsps. - print '---------------------' - print 'PatchSet %d ' % cs.id - print 'Date: %s' % util.datestr(cs.date, '%Y/%m/%d %H:%M:%S %1%2') - print 'Author: %s' % cs.author - print 'Branch: %s' % (cs.branch or 'HEAD') - print 'Tag%s: %s ' % (['', 's'][len(cs.tags)>1], - ','.join(cs.tags) or '(none)') - if options.Parents and cs.parents: - if len(cs.parents)>1: - print 'Parents: %s' % (','.join([str(p.id) for p in cs.parents])) - else: - print 'Parent: %d' % cs.parents[0].id - - if options.Ancestors: - b = cs.branch - r = [] - while b: - b, c = ancestors[b] - r.append('%s:%d:%d' % (b or "HEAD", c, branches[b])) - if r: - print 'Ancestors: %s' % (','.join(r)) - - print 'Log:' - print cs.comment - print - print 'Members: ' - for f in cs.entries: - fn = f.file - if fn.startswith(options.Prefix): - fn = fn[len(options.Prefix):] - print '\t%s:%s->%s%s ' % (fn, '.'.join([str(x) for x in f.parent]) or 'INITIAL', - '.'.join([str(x) for x in f.revision]), ['', '(DEAD)'][f.dead]) - print - - # have we seen the start tag? - if options.Revisions and off: - if options.Revisions[0] == str(cs.id) or \ - options.Revisions[0] in cs.tags: - off = False - - # see if we reached the end tag - if len(options.Revisions)>1 and not off: - if options.Revisions[1] == str(cs.id) or \ - options.Revisions[1] in cs.tags: - break - - -if __name__ == '__main__': - main() diff -r 5f7e3f17aece -r 81deb1ba4402 hgext/convert/cvsps.py --- a/hgext/convert/cvsps.py Sun Jan 04 21:32:40 2009 +0100 +++ b/hgext/convert/cvsps.py Sun Jan 04 21:44:06 2009 +0100 @@ -191,7 +191,13 @@ ui.note(_("running %s\n") % (' '.join(cmd))) ui.debug(_("prefix=%r directory=%r root=%r\n") % (prefix, directory, root)) - for line in util.popen(' '.join(cmd)): + pfp = util.popen(' '.join(cmd)) + peek = pfp.readline() + while True: + line = peek + if line == '': + break + peek = pfp.readline() if line.endswith('\n'): line = line[:-1] #ui.debug('state=%d line=%r\n' % (state, line)) @@ -312,7 +318,7 @@ e.branches = [tuple([int(y) for y in x.strip().split('.')]) for x in m.group(1).split(';')] state = 8 - elif re_31.match(line): + elif re_31.match(line) and re_50.match(peek): state = 5 store = True elif re_32.match(line): @@ -584,3 +590,95 @@ ui.status(_('%d changeset entries\n') % len(changesets)) return changesets + + +def debugcvsps(ui, *args, **opts): + '''Read CVS rlog for current directory or named path in repository, and + convert the log to changesets based on matching commit log entries and dates.''' + + if opts["new_cache"]: + cache = "write" + elif opts["update_cache"]: + cache = "update" + else: + cache = None + + revisions = opts["revisions"] + + try: + if args: + log = [] + for d in args: + log += createlog(ui, d, root=opts["root"], cache=cache) + else: + log = createlog(ui, root=opts["root"], cache=cache) + except logerror, e: + ui.write("%r\n"%e) + return + + changesets = createchangeset(ui, log, opts["fuzz"]) + del log + + # Print changesets (optionally filtered) + + off = len(revisions) + branches = {} # latest version number in each branch + ancestors = {} # parent branch + for cs in changesets: + + if opts["ancestors"]: + if cs.branch not in branches and cs.parents and cs.parents[0].id: + ancestors[cs.branch] = changesets[cs.parents[0].id-1].branch, cs.parents[0].id + branches[cs.branch] = cs.id + + # limit by branches + if opts["branches"] and (cs.branch or 'HEAD') not in opts["branches"]: + continue + + if not off: + # Note: trailing spaces on several lines here are needed to have + # bug-for-bug compatibility with cvsps. + ui.write('---------------------\n') + ui.write('PatchSet %d \n' % cs.id) + ui.write('Date: %s\n' % util.datestr(cs.date, '%Y/%m/%d %H:%M:%S %1%2')) + ui.write('Author: %s\n' % cs.author) + ui.write('Branch: %s\n' % (cs.branch or 'HEAD')) + ui.write('Tag%s: %s \n' % (['', 's'][len(cs.tags)>1], + ','.join(cs.tags) or '(none)')) + if opts["parents"] and cs.parents: + if len(cs.parents)>1: + ui.write('Parents: %s\n' % (','.join([str(p.id) for p in cs.parents]))) + else: + ui.write('Parent: %d\n' % cs.parents[0].id) + + if opts["ancestors"]: + b = cs.branch + r = [] + while b: + b, c = ancestors[b] + r.append('%s:%d:%d' % (b or "HEAD", c, branches[b])) + if r: + ui.write('Ancestors: %s\n' % (','.join(r))) + + ui.write('Log:\n') + ui.write('%s\n\n' % cs.comment) + ui.write('Members: \n') + for f in cs.entries: + fn = f.file + if fn.startswith(opts["prefix"]): + fn = fn[len(opts["prefix"]):] + ui.write('\t%s:%s->%s%s \n' % (fn, '.'.join([str(x) for x in f.parent]) or 'INITIAL', + '.'.join([str(x) for x in f.revision]), ['', '(DEAD)'][f.dead])) + ui.write('\n') + + # have we seen the start tag? + if revisions and off: + if revisions[0] == str(cs.id) or \ + revisions[0] in cs.tags: + off = False + + # see if we reached the end tag + if len(revisions)>1 and not off: + if revisions[1] == str(cs.id) or \ + revisions[1] in cs.tags: + break diff -r 5f7e3f17aece -r 81deb1ba4402 hgext/convert/gnuarch.py --- a/hgext/convert/gnuarch.py Sun Jan 04 21:32:40 2009 +0100 +++ b/hgext/convert/gnuarch.py Sun Jan 04 21:44:06 2009 +0100 @@ -3,7 +3,8 @@ from common import NoRepo, commandline, commit, converter_source from mercurial.i18n import _ from mercurial import util -import os, shutil, tempfile, stat +import os, shutil, tempfile, stat, locale +from email.Parser import Parser class gnuarch_source(converter_source, commandline): @@ -13,6 +14,7 @@ self.summary = '' self.date = None self.author = '' + self.continuationof = None self.add_files = [] self.mod_files = [] self.del_files = [] @@ -46,38 +48,74 @@ self.parents = {} self.tags = {} self.modecache = {} + self.catlogparser = Parser() + self.locale = locale.getpreferredencoding() + self.archives = [] def before(self): + # Get registered archives + self.archives = [i.rstrip('\n') + for i in self.runlines0('archives', '-n')] + if self.execmd == 'tla': output = self.run0('tree-version', self.path) else: output = self.run0('tree-version', '-d', self.path) self.treeversion = output.strip() - self.ui.status(_('analyzing tree version %s...\n') % self.treeversion) - # Get name of temporary directory version = self.treeversion.split('/') self.tmppath = os.path.join(tempfile.gettempdir(), 'hg-%s' % version[1]) # Generate parents dictionary - child = [] - output, status = self.runlines('revisions', self.treeversion) - self.checkexit(status, 'archive registered?') - for l in output: - rev = l.strip() - self.changes[rev] = self.gnuarch_rev(rev) + self.parents[None] = [] + treeversion = self.treeversion + child = None + while treeversion: + self.ui.status(_('analyzing tree version %s...\n') % treeversion) + + archive = treeversion.split('/')[0] + if archive not in self.archives: + self.ui.status(_('tree analysis stopped because it points to an unregistered archive %s...\n') % archive) + break + + # Get the complete list of revisions for that tree version + output, status = self.runlines('revisions', '-r', '-f', treeversion) + self.checkexit(status, 'failed retrieveing revisions for %s' % treeversion) + + # No new iteration unless a revision has a continuation-of header + treeversion = None + + for l in output: + rev = l.strip() + self.changes[rev] = self.gnuarch_rev(rev) + self.parents[rev] = [] - # Read author, date and summary - catlog = self.runlines0('cat-log', '-d', self.path, rev) - self._parsecatlog(catlog, rev) + # Read author, date and summary + catlog, status = self.run('cat-log', '-d', self.path, rev) + if status: + catlog = self.run0('cat-archive-log', rev) + self._parsecatlog(catlog, rev) + + # Populate the parents map + self.parents[child].append(rev) - self.parents[rev] = child - child = [rev] - if rev == self.rev: - break - self.parents[None] = child + # Keep track of the current revision as the child of the next + # revision scanned + child = rev + + # Check if we have to follow the usual incremental history + # or if we have to 'jump' to a different treeversion given + # by the continuation-of header. + if self.changes[rev].continuationof: + treeversion = '--'.join(self.changes[rev].continuationof.split('--')[:-1]) + break + + # If we reached a base-0 revision w/o any continuation-of + # header, it means the tree history ends here. + if rev[-6:] == 'base-0': + break def after(self): self.ui.debug(_('cleaning up %s\n') % self.tmppath) @@ -136,7 +174,7 @@ def getcommit(self, rev): changes = self.changes[rev] return commit(author = changes.author, date = changes.date, - desc = changes.summary, parents = self.parents[rev]) + desc = changes.summary, parents = self.parents[rev], rev=rev) def gettags(self): return self.tags @@ -151,26 +189,19 @@ return os.system(cmdline) def _update(self, rev): - if rev == 'base-0': - # Initialise 'base-0' revision + self.ui.debug(_('applying revision %s...\n') % rev) + changeset, status = self.runlines('replay', '-d', self.tmppath, + rev) + if status: + # Something went wrong while merging (baz or tla + # issue?), get latest revision and try from there + shutil.rmtree(self.tmppath, ignore_errors=True) self._obtainrevision(rev) else: - self.ui.debug(_('applying revision %s...\n') % rev) - revision = '%s--%s' % (self.treeversion, rev) - changeset, status = self.runlines('replay', '-d', self.tmppath, - revision) - if status: - # Something went wrong while merging (baz or tla - # issue?), get latest revision and try from there - shutil.rmtree(self.tmppath, ignore_errors=True) - self._obtainrevision(rev) - else: - old_rev = self.parents[rev][0] - self.ui.debug(_('computing changeset between %s and %s...\n') - % (old_rev, rev)) - rev_a = '%s--%s' % (self.treeversion, old_rev) - rev_b = '%s--%s' % (self.treeversion, rev) - self._parsechangeset(changeset, rev) + old_rev = self.parents[rev][0] + self.ui.debug(_('computing changeset between %s and %s...\n') + % (old_rev, rev)) + self._parsechangeset(changeset, rev) def _getfile(self, name, rev): mode = os.lstat(os.path.join(self.tmppath, name)).st_mode @@ -218,8 +249,7 @@ def _obtainrevision(self, rev): self.ui.debug(_('obtaining revision %s...\n') % rev) - revision = '%s--%s' % (self.treeversion, rev) - output = self._execute('get', revision, self.tmppath) + output = self._execute('get', rev, self.tmppath) self.checkexit(output) self.ui.debug(_('analysing revision %s...\n') % rev) files = self._readcontents(self.tmppath) @@ -231,20 +261,27 @@ return path def _parsecatlog(self, data, rev): - summary = [] - for l in data: - l = l.strip() - if summary: - summary.append(l) - elif l.startswith('Summary:'): - summary.append(l[len('Summary: '):]) - elif l.startswith('Standard-date:'): - date = l[len('Standard-date: '):] - strdate = util.strdate(date, '%Y-%m-%d %H:%M:%S') - self.changes[rev].date = util.datestr(strdate) - elif l.startswith('Creator:'): - self.changes[rev].author = l[len('Creator: '):] - self.changes[rev].summary = '\n'.join(summary) + try: + catlog = self.catlogparser.parsestr(data) + + # Commit date + self.changes[rev].date = util.datestr( + util.strdate(catlog['Standard-date'], + '%Y-%m-%d %H:%M:%S')) + + # Commit author + self.changes[rev].author = self.recode(catlog['Creator']) + + # Commit description + self.changes[rev].summary = '\n\n'.join((catlog['Summary'], + catlog.get_payload())) + self.changes[rev].summary = self.recode(self.changes[rev].summary) + + # Commit revision origin when dealing with a branch or tag + if catlog.has_key('Continuation-of'): + self.changes[rev].continuationof = self.recode(catlog['Continuation-of']) + except Exception, err: + raise util.Abort(_('could not parse cat-log of %s') % rev) def _parsechangeset(self, data, rev): for l in data: diff -r 5f7e3f17aece -r 81deb1ba4402 hgext/extdiff.py --- a/hgext/extdiff.py Sun Jan 04 21:32:40 2009 +0100 +++ b/hgext/extdiff.py Sun Jan 04 21:44:06 2009 +0100 @@ -80,9 +80,7 @@ '''snapshot files from working directory. if not using snapshot, -I/-X does not work and recursive diff in tools like kdiff3 and meld displays too many files.''' - repo_root = repo.root - - dirname = os.path.basename(repo_root) + dirname = os.path.basename(repo.root) if dirname == "": dirname = "root" base = os.path.join(tmproot, dirname) @@ -105,8 +103,7 @@ fp.write(chunk) fp.close() - fns_and_mtime.append((dest, os.path.join(repo_root, fn), - os.path.getmtime(dest))) + fns_and_mtime.append((dest, repo.wjoin(fn), os.path.getmtime(dest))) return dirname, fns_and_mtime diff -r 5f7e3f17aece -r 81deb1ba4402 hgext/keyword.py --- a/hgext/keyword.py Sun Jan 04 21:32:40 2009 +0100 +++ b/hgext/keyword.py Sun Jan 04 21:44:06 2009 +0100 @@ -425,14 +425,10 @@ keyword substitutions. Monkeypatches patch and webcommands.''' - try: - if (not repo.local() or not kwtools['inc'] - or kwtools['hgcmd'] in nokwcommands.split() - or '.hg' in util.splitpath(repo.root) - or repo._url.startswith('bundle:')): - return - except AttributeError: - pass + if (not hasattr(repo, 'dirstate') or not kwtools['inc'] + or kwtools['hgcmd'] in nokwcommands.split() + or '.hg' in util.splitpath(repo.root)): + return kwtools['templater'] = kwt = kwtemplater(ui, repo) diff -r 5f7e3f17aece -r 81deb1ba4402 hgext/patchbomb.py --- a/hgext/patchbomb.py Sun Jan 04 21:32:40 2009 +0100 +++ b/hgext/patchbomb.py Sun Jan 04 21:44:06 2009 +0100 @@ -9,8 +9,7 @@ The remainder of the changeset description. - [Optional] If the diffstat program is installed, the result of - running diffstat on the patch. + [Optional] The result of running diffstat on the patch. The patch itself, as generated by "hg export". @@ -99,16 +98,12 @@ def cdiffstat(ui, summary, patchlines): s = patch.diffstat(patchlines) - if s: - if summary: - ui.write(summary, '\n') - ui.write(s, '\n') - ans = prompt(ui, _('Does the diffstat above look okay? '), 'y') - if not ans.lower().startswith('y'): - raise util.Abort(_('diffstat rejected')) - elif s is None: - ui.warn(_('no diffstat information available\n')) - s = '' + if summary: + ui.write(summary, '\n') + ui.write(s, '\n') + ans = prompt(ui, _('Does the diffstat above look okay? '), 'y') + if not ans.lower().startswith('y'): + raise util.Abort(_('diffstat rejected')) return s def makepatch(ui, repo, patch, opts, _charsets, idx, total, patchname=None): diff -r 5f7e3f17aece -r 81deb1ba4402 hgext/purge.py --- a/hgext/purge.py Sun Jan 04 21:32:40 2009 +0100 +++ b/hgext/purge.py Sun Jan 04 21:44:06 2009 +0100 @@ -64,7 +64,7 @@ def remove(remove_func, name): if act: try: - remove_func(os.path.join(repo.root, name)) + remove_func(repo.wjoin(name)) except OSError: m = _('%s cannot be removed') % name if opts['abort_on_err']: diff -r 5f7e3f17aece -r 81deb1ba4402 mercurial/changelog.py --- a/mercurial/changelog.py Sun Jan 04 21:32:40 2009 +0100 +++ b/mercurial/changelog.py Sun Jan 04 21:44:06 2009 +0100 @@ -179,7 +179,7 @@ user = user.strip() if "\n" in user: - raise RevlogError(_("username %s contains a newline") % `user`) + raise RevlogError(_("username %s contains a newline") % repr(user)) user, desc = util.fromlocal(user), util.fromlocal(desc) if date: diff -r 5f7e3f17aece -r 81deb1ba4402 mercurial/commands.py --- a/mercurial/commands.py Sun Jan 04 21:32:40 2009 +0100 +++ b/mercurial/commands.py Sun Jan 04 21:44:06 2009 +0100 @@ -328,29 +328,34 @@ state = hbisect.load_state(repo) if command: + commandpath = util.find_exe(command) changesets = 1 - while changesets: - # update state - status = os.spawnlp(os.P_WAIT, command) - node = repo.lookup(rev or '.') - if status == 125: - transition = "skip" - elif status == 0: - transition = "good" - # status < 0 means process was killed - elif status == 127 or status < 0: - break - else: - transition = "bad" - state[transition].append(node) - ui.note(_('Changeset %s: %s\n') % (short(node), transition)) - check_state(state, interactive=False) - # bisect - nodes, changesets, good = hbisect.bisect(repo.changelog, state) - # update to next check - cmdutil.bail_if_changed(repo) - hg.clean(repo, nodes[0], show_stats=False) - hbisect.save_state(repo, state) + try: + while changesets: + # update state + status = os.spawnl(os.P_WAIT, commandpath) + if status == 125: + transition = "skip" + elif status == 0: + transition = "good" + # status < 0 means process was killed + elif status == 127: + raise util.Abort(_("failed to execute %s") % command) + elif status < 0: + raise util.Abort(_("%s killed") % command) + else: + transition = "bad" + node = repo.lookup(rev or '.') + state[transition].append(node) + ui.note(_('Changeset %s: %s\n') % (short(node), transition)) + check_state(state, interactive=False) + # bisect + nodes, changesets, good = hbisect.bisect(repo.changelog, state) + # update to next check + cmdutil.bail_if_changed(repo) + hg.clean(repo, nodes[0], show_stats=False) + finally: + hbisect.save_state(repo, state) return print_result(nodes, not status) # update state @@ -688,7 +693,10 @@ ui.write("%s\n" % "\n".join(options)) return - ui.write("%s\n" % "\n".join(util.sort(cmdutil.findpossible(cmd, table)))) + cmdlist = cmdutil.findpossible(cmd, table) + if ui.verbose: + cmdlist = [' '.join(c[0]) for c in cmdlist.values()] + ui.write("%s\n" % "\n".join(util.sort(cmdlist))) def debugfsinfo(ui, path = "."): file('.debugfsinfo', 'w').write('') @@ -1794,7 +1802,7 @@ if not rev and abs not in repo.dirstate: continue if opts.get('fullpath'): - ui.write(os.path.join(repo.root, abs), end) + ui.write(repo.wjoin(abs), end) else: ui.write(((pats and m.rel(abs)) or abs), end) ret = 0 diff -r 5f7e3f17aece -r 81deb1ba4402 mercurial/dispatch.py --- a/mercurial/dispatch.py Sun Jan 04 21:32:40 2009 +0100 +++ b/mercurial/dispatch.py Sun Jan 04 21:44:06 2009 +0100 @@ -90,7 +90,7 @@ else: raise except socket.error, inst: - ui.warn(_("abort: %s\n") % inst[-1]) + ui.warn(_("abort: %s\n") % inst.args[-1]) except IOError, inst: if hasattr(inst, "code"): ui.warn(_("abort: %s\n") % inst) @@ -100,7 +100,7 @@ except: # it might be anything, for example a string reason = inst.reason ui.warn(_("abort: error: %s\n") % reason) - elif hasattr(inst, "args") and inst[0] == errno.EPIPE: + elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE: if ui.debugflag: ui.warn(_("broken pipe\n")) elif getattr(inst, "strerror", None): @@ -116,13 +116,13 @@ else: ui.warn(_("abort: %s\n") % inst.strerror) except util.UnexpectedOutput, inst: - ui.warn(_("abort: %s") % inst[0]) - if not isinstance(inst[1], basestring): - ui.warn(" %r\n" % (inst[1],)) - elif not inst[1]: + ui.warn(_("abort: %s") % inst.args[0]) + if not isinstance(inst.args[1], basestring): + ui.warn(" %r\n" % (inst.args[1],)) + elif not inst.args[1]: ui.warn(_(" empty string\n")) else: - ui.warn("\n%r\n" % util.ellipsis(inst[1])) + ui.warn("\n%r\n" % util.ellipsis(inst.args[1])) except ImportError, inst: m = str(inst).split()[-1] ui.warn(_("abort: could not import module %s!\n") % m) diff -r 5f7e3f17aece -r 81deb1ba4402 mercurial/hbisect.py --- a/mercurial/hbisect.py Sun Jan 04 21:32:40 2009 +0100 +++ b/mercurial/hbisect.py Sun Jan 04 21:44:06 2009 +0100 @@ -102,7 +102,7 @@ if value == perfect: # found a perfect candidate? quit early break - if y < perfect: # all downhill from here? + if y < perfect and rev not in skip: # all downhill from here? for c in children.get(rev, []): poison[c] = True # poison children continue diff -r 5f7e3f17aece -r 81deb1ba4402 mercurial/hg.py --- a/mercurial/hg.py Sun Jan 04 21:32:40 2009 +0100 +++ b/mercurial/hg.py Sun Jan 04 21:44:06 2009 +0100 @@ -252,8 +252,6 @@ note = ", ".join([_("%d files %s") % s for s in stats]) repo.ui.status("%s\n" % note) -def _update(repo, node): return update(repo, node) - def update(repo, node): """update the working directory to node, merging linear changes""" stats = _merge.update(repo, node, False, False, None) @@ -262,6 +260,9 @@ repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n")) return stats[3] > 0 +# naming conflict in clone() +_update = update + def clean(repo, node, show_stats=True): """forcibly switch the working directory to node, clobbering changes""" stats = _merge.update(repo, node, False, True, None) diff -r 5f7e3f17aece -r 81deb1ba4402 mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py Sun Jan 04 21:32:40 2009 +0100 +++ b/mercurial/hgweb/hgweb_mod.py Sun Jan 04 21:44:06 2009 +0100 @@ -284,14 +284,13 @@ raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized') allow_read = self.configlist('web', 'allow_read') - result = (not allow_read) or (allow_read == ['*']) or (user in allow_read) - if not result: + result = (not allow_read) or (allow_read == ['*']) + if not result or user in allow_read: raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized') if op == 'pull' and not self.allowpull: raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized') - # op is None when checking allow/deny_read permissions for a web-browser request - elif op == 'pull' or op is None: + elif op == 'pull' or op is None: # op is None for interface requests return # enforce that you can only push using POST requests diff -r 5f7e3f17aece -r 81deb1ba4402 mercurial/hgweb/hgwebdir_mod.py --- a/mercurial/hgweb/hgwebdir_mod.py Sun Jan 04 21:32:40 2009 +0100 +++ b/mercurial/hgweb/hgwebdir_mod.py Sun Jan 04 21:44:06 2009 +0100 @@ -102,11 +102,11 @@ user = req.env.get('REMOTE_USER') - deny_read = ui.configlist('web', 'deny_read', default=None, untrusted=True) + deny_read = ui.configlist('web', 'deny_read', untrusted=True) if deny_read and (not user or deny_read == ['*'] or user in deny_read): return False - allow_read = ui.configlist('web', 'allow_read', default=None, untrusted=True) + allow_read = ui.configlist('web', 'allow_read', untrusted=True) # by default, allow reading if no allow_read option has been set if (not allow_read) or (allow_read == ['*']) or (user in allow_read): return True diff -r 5f7e3f17aece -r 81deb1ba4402 mercurial/patch.py --- a/mercurial/patch.py Sun Jan 04 21:32:40 2009 +0100 +++ b/mercurial/patch.py Sun Jan 04 21:44:06 2009 +0100 @@ -9,7 +9,7 @@ from i18n import _ from node import hex, nullid, short import base85, cmdutil, mdiff, util, revlog, diffhelpers, copies -import cStringIO, email.Parser, os, re, errno +import cStringIO, email.Parser, os, re, errno, math import sys, tempfile, zlib gitre = re.compile('diff --git a/(.*) b/(.*)') @@ -794,9 +794,7 @@ def readline(self): if self.buf: - l = self.buf[0] - del self.buf[0] - return l + return self.buf.pop(0) return self.fp.readline() def __iter__(self): @@ -1059,7 +1057,7 @@ gp = patches[f] if gp and gp.mode: islink, isexec = gp.mode - dst = os.path.join(repo.root, gp.path) + dst = repo.wjoin(gp.path) # patch won't create empty files if gp.op == 'ADD' and not os.path.exists(dst): flags = (isexec and 'x' or '') + (islink and 'l' or '') @@ -1346,13 +1344,57 @@ for seqno, rev in enumerate(revs): single(rev, seqno+1, fp) -def diffstat(patchlines): - if not util.find_exe('diffstat'): - return - output = util.filter('\n'.join(patchlines), - 'diffstat -p1 -w79 2>%s' % util.nulldev) - stat = [l.lstrip() for l in output.splitlines(True)] - last = stat.pop() - stat.insert(0, last) - stat = ''.join(stat) - return stat +def diffstatdata(lines): + filename = None + for line in lines: + if line.startswith('diff'): + if filename: + yield (filename, adds, removes) + # set numbers to 0 anyway when starting new file + adds = 0 + removes = 0 + if line.startswith('diff --git'): + filename = gitre.search(line).group(1) + else: + # format: "diff -r ... -r ... file name" + filename = line.split(None, 5)[-1] + elif line.startswith('+') and not line.startswith('+++'): + adds += 1 + elif line.startswith('-') and not line.startswith('---'): + removes += 1 + yield (filename, adds, removes) + +def diffstat(lines): + output = [] + stats = list(diffstatdata(lines)) + width = util.termwidth() - 2 + + maxtotal, maxname = 0, 0 + totaladds, totalremoves = 0, 0 + for filename, adds, removes in stats: + totaladds += adds + totalremoves += removes + maxname = max(maxname, len(filename)) + maxtotal = max(maxtotal, adds+removes) + + countwidth = len(str(maxtotal)) + graphwidth = width - countwidth - maxname + if graphwidth < 10: + graphwidth = 10 + + factor = int(math.ceil(float(maxtotal) / graphwidth)) + + for filename, adds, removes in stats: + # If diffstat runs out of room it doesn't print anything, which + # isn't very useful, so always print at least one + or - if there + # were at least some changes + pluses = '+' * max(adds/factor, int(bool(adds))) + minuses = '-' * max(removes/factor, int(bool(removes))) + output.append(' %-*s | %*.d %s%s\n' % (maxname, filename, countwidth, + adds+removes, pluses, minuses)) + + if stats: + output.append(' %d files changed, %d insertions(+), %d deletions(-)\n' % + (len(stats), totaladds, totalremoves)) + + return ''.join(output) diff -r 5f7e3f17aece -r 81deb1ba4402 mercurial/ui.py --- a/mercurial/ui.py Sun Jan 04 21:32:40 2009 +0100 +++ b/mercurial/ui.py Sun Jan 04 21:44:06 2009 +0100 @@ -350,7 +350,7 @@ if not user: raise util.Abort(_("Please specify a username.")) if "\n" in user: - raise util.Abort(_("username %s contains a newline\n") % `user`) + raise util.Abort(_("username %s contains a newline\n") % repr(user)) return user def shortuser(self, user): diff -r 5f7e3f17aece -r 81deb1ba4402 mercurial/util.py --- a/mercurial/util.py Sun Jan 04 21:32:40 2009 +0100 +++ b/mercurial/util.py Sun Jan 04 21:44:06 2009 +0100 @@ -705,7 +705,7 @@ if cwd is not None and oldcwd != cwd: os.chdir(oldcwd) -class SignatureError: +class SignatureError(Exception): pass def checksignature(func): @@ -1991,3 +1991,24 @@ def uirepr(s): # Avoid double backslash in Windows path repr() return repr(s).replace('\\\\', '\\') + +def termwidth(): + if 'COLUMNS' in os.environ: + try: + return int(os.environ['COLUMNS']) + except ValueError: + pass + try: + import termios, array, fcntl + for dev in (sys.stdout, sys.stdin): + try: + fd = dev.fileno() + if not os.isatty(fd): + continue + arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8) + return array.array('h', arri)[1] + except ValueError: + pass + except ImportError: + pass + return 80 diff -r 5f7e3f17aece -r 81deb1ba4402 mercurial/util_win32.py --- a/mercurial/util_win32.py Sun Jan 04 21:32:40 2009 +0100 +++ b/mercurial/util_win32.py Sun Jan 04 21:44:06 2009 +0100 @@ -19,7 +19,7 @@ import util from win32com.shell import shell,shellcon -class WinError: +class WinError(Exception): winerror_map = { winerror.ERROR_ACCESS_DENIED: errno.EACCES, winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES, diff -r 5f7e3f17aece -r 81deb1ba4402 setup.py --- a/setup.py Sun Jan 04 21:32:40 2009 +0100 +++ b/setup.py Sun Jan 04 21:44:06 2009 +0100 @@ -9,6 +9,23 @@ if not hasattr(sys, 'version_info') or sys.version_info < (2, 3, 0, 'final'): raise SystemExit("Mercurial requires python 2.3 or later.") +# Solaris Python packaging brain damage +try: + import hashlib + sha = hashlib.sha1() +except: + try: + import sha + except: + raise SystemExit( + "Couldn't import standard hashlib (incomplete Python install).") + +try: + import zlib +except: + raise SystemExit( + "Couldn't import standard zlib (incomplete Python install).") + import os import shutil import tempfile diff -r 5f7e3f17aece -r 81deb1ba4402 tests/tampered.hg Binary file tests/tampered.hg has changed diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-audit-path --- a/tests/test-audit-path Sun Jan 04 21:32:40 2009 +0100 +++ b/tests/test-audit-path Sun Jan 04 21:44:06 2009 +0100 @@ -41,4 +41,8 @@ hg manifest -r3 hg update -Cr3 +echo % attack /tmp/test +hg manifest -r4 +hg update -Cr4 2>&1 | sed -e "s|$HGTMP|[HGTMP]|" + exit 0 diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-audit-path.out --- a/tests/test-audit-path.out Sun Jan 04 21:32:40 2009 +0100 +++ b/tests/test-audit-path.out Sun Jan 04 21:44:06 2009 +0100 @@ -10,7 +10,7 @@ adding changesets adding manifests adding file changes -added 4 changesets with 5 changes to 5 files (+3 heads) +added 5 changesets with 6 changes to 6 files (+4 heads) (run 'hg heads' to see heads, 'hg merge' to merge) % attack .hg/test .hg/test @@ -25,3 +25,6 @@ % attack ../test ../test abort: path contains illegal component: ../test +% attack /tmp/test +/tmp/test +abort: No such file or directory: [HGTMP]/test-audit-path/target//tmp/test diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-bdiff --- a/tests/test-bdiff Sun Jan 04 21:32:40 2009 +0100 +++ b/tests/test-bdiff Sun Jan 04 21:44:06 2009 +0100 @@ -9,13 +9,13 @@ if d: c = mpatch.patches(a, [d]) if c != b: - print "***", `a`, `b` + print "***", repr(a), repr(b) print "bad:" - print `c`[:200] - print `d` + print repr(c)[:200] + print repr(d) def test(a, b): - print "***", `a`, `b` + print "***", repr(a), repr(b) test1(a, b) test1(b, a) diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-bisect --- a/tests/test-bisect Sun Jan 04 21:32:40 2009 +0100 +++ b/tests/test-bisect Sun Jan 04 21:44:06 2009 +0100 @@ -72,3 +72,13 @@ echo % test no action hg bisect -r hg bisect || echo failure + +echo % reproduce AssertionError, issue1445 +hg bisect -r +hg bisect -b 6 +hg bisect -g 0 +hg bisect -s +hg bisect -s +hg bisect -s +hg bisect -s +hg bisect -g diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-bisect.out --- a/tests/test-bisect.out Sun Jan 04 21:32:40 2009 +0100 +++ b/tests/test-bisect.out Sun Jan 04 21:44:06 2009 +0100 @@ -286,3 +286,20 @@ % test no action abort: cannot bisect (no known good revisions) failure +% reproduce AssertionError, issue1445 +Testing changeset 3:b53bea5e2fcb (6 changesets remaining, ~2 tests) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +Testing changeset 2:db07c04beaca (6 changesets remaining, ~2 tests) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +Testing changeset 4:9b2ba8336a65 (6 changesets remaining, ~2 tests) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +Testing changeset 1:5cd978ea5149 (6 changesets remaining, ~2 tests) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +Testing changeset 5:7874a09ea728 (6 changesets remaining, ~2 tests) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +The first bad revision is: +changeset: 6:a3d5c6fdf0d3 +user: test +date: Thu Jan 01 00:00:06 1970 +0000 +summary: msg 6 + diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-bookmarks-current --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-bookmarks-current Sun Jan 04 21:44:06 2009 +0100 @@ -0,0 +1,41 @@ +#!/bin/sh + +echo "[extensions]" >> $HGRCPATH +echo "bookmarks=" >> $HGRCPATH + +echo "[bookmarks]" >> $HGRCPATH +echo "track.current = True" >> $HGRCPATH + +hg init + +echo % no bookmarks +hg bookmarks + +echo % set bookmark X +hg bookmark X + +echo % update to bookmark X +hg update X + +echo % list bookmarks +hg bookmarks + +echo % rename +hg bookmark -m X Z + +echo % list bookmarks +hg bookmarks + +echo % new bookmark Y +hg bookmark Y + +echo % list bookmarks +hg bookmark + +echo % commit +echo 'b' > b +hg add b +hg commit -m'test' + +echo % list bookmarks +hg bookmark diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-bookmarks-current.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-bookmarks-current.out Sun Jan 04 21:44:06 2009 +0100 @@ -0,0 +1,18 @@ +% no bookmarks +no bookmarks set +% set bookmark X +% update to bookmark X +0 files updated, 0 files merged, 0 files removed, 0 files unresolved +% list bookmarks + * X -1:000000000000 +% rename +% list bookmarks + * Z -1:000000000000 +% new bookmark Y +% list bookmarks + Y -1:000000000000 + * Z -1:000000000000 +% commit +% list bookmarks + Y -1:000000000000 + * Z 0:719295282060 diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-bookmarks-rebase.out --- a/tests/test-bookmarks-rebase.out Sun Jan 04 21:32:40 2009 +0100 +++ b/tests/test-bookmarks-rebase.out Sun Jan 04 21:44:06 2009 +0100 @@ -18,6 +18,8 @@ rebase completed changeset: 3:9163974d1cb5 tag: tip +tag: two +tag: one parent: 1:925d80f479bb parent: 2:db815d6d32e6 user: test diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-bookmarks.out --- a/tests/test-bookmarks.out Sun Jan 04 21:32:40 2009 +0100 +++ b/tests/test-bookmarks.out Sun Jan 04 21:44:06 2009 +0100 @@ -7,6 +7,7 @@ * X 0:f7b1eb17ad24 % look up bookmark changeset: 0:f7b1eb17ad24 +tag: X tag: tip user: test date: Thu Jan 01 00:00:00 1970 +0000 @@ -51,7 +52,10 @@ * x y 2:0316ce92851d % look up stripped bookmark name changeset: 2:0316ce92851d +tag: X2 +tag: Y tag: tip +tag: x y user: test date: Thu Jan 01 00:00:00 1970 +0000 summary: 2 diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-convert-cvs-builtincvsps --- a/tests/test-convert-cvs-builtincvsps Sun Jan 04 21:32:40 2009 +0100 +++ b/tests/test-convert-cvs-builtincvsps Sun Jan 04 21:44:06 2009 +0100 @@ -100,5 +100,22 @@ hgcat b/c hg -R src-filemap log --template '#rev# #desc# files: #files#\n' +echo % commit a new revision with funny log message +cd src +sleep 1 +echo e >> a +cvscall -q commit -m'funny +---------------------------- +log message' . | grep '<--' |\ + sed -e 's:.*src/\(.*\),v.*:checking in src/\1,v:g' +cd .. + +echo % convert again +hg convert src src-hg | sed -e 's/connecting to.*cvsrepo/connecting to cvsrepo/g' + echo "graphlog = " >> $HGRCPATH hg -R src-hg glog --template '#rev# (#branches#) #desc# files: #files#\n' + +echo % testing debugcvsps +cd src +hg debugcvsps | sed -e 's/Author:.*/Author:/' -e 's/Date:.*/Date:/' diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-convert-cvs-builtincvsps.out --- a/tests/test-convert-cvs-builtincvsps.out Sun Jan 04 21:32:40 2009 +0100 +++ b/tests/test-convert-cvs-builtincvsps.out Sun Jan 04 21:44:06 2009 +0100 @@ -124,6 +124,22 @@ 2 update tags files: .hgtags 1 ci0 files: b/c 0 Initial revision files: b/c +% commit a new revision with funny log message +checking in src/a,v +% convert again +using builtin cvsps +collecting CVS rlog +9 log entries +creating changesets +6 changeset entries +connecting to cvsrepo +scanning source... +sorting... +converting... +0 funny +o 6 (branch) funny +| ---------------------------- +| log message files: a o 5 (branch) ci2 files: b/c | | o 4 () ci1 files: a b/c @@ -136,3 +152,85 @@ |/ o 0 () Initial revision files: a b/c +% testing debugcvsps +collecting CVS rlog +9 log entries +creating changesets +6 changeset entries +--------------------- +PatchSet 1 +Date: +Author: +Branch: HEAD +Tag: (none) +Log: +Initial revision + +Members: + a:INITIAL->1.1 + b/c:INITIAL->1.1 + +--------------------- +PatchSet 2 +Date: +Author: +Branch: INITIAL +Tag: start +Log: +import + +Members: + a:1.1->1.1.1.1 + b/c:1.1->1.1.1.1 + +--------------------- +PatchSet 3 +Date: +Author: +Branch: HEAD +Tag: (none) +Log: +ci0 + +Members: + b/c:1.1->1.2 + +--------------------- +PatchSet 4 +Date: +Author: +Branch: HEAD +Tag: (none) +Log: +ci1 + +Members: + a:1.1->1.2 + b/c:1.2->1.3 + +--------------------- +PatchSet 5 +Date: +Author: +Branch: branch +Tag: (none) +Log: +ci2 + +Members: + b/c:1.1->1.1.2.1 + +--------------------- +PatchSet 6 +Date: +Author: +Branch: branch +Tag: (none) +Log: +funny +---------------------------- +log message + +Members: + a:1.2->1.2.2.1 + diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-notify.out --- a/tests/test-notify.out Sun Jan 04 21:32:40 2009 +0100 +++ b/tests/test-notify.out Sun Jan 04 21:44:06 2009 +0100 @@ -150,7 +150,8 @@ b diffstat: -files patched: 1 + a | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (6 lines): diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-patchbomb --- a/tests/test-patchbomb Sun Jan 04 21:32:40 2009 +0100 +++ b/tests/test-patchbomb Sun Jan 04 21:44:06 2009 +0100 @@ -11,6 +11,8 @@ echo "[extensions]" >> $HGRCPATH echo "patchbomb=" >> $HGRCPATH +COLUMNS=80; export COLUMNS + hg init t cd t echo a > a diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-patchbomb.out --- a/tests/test-patchbomb.out Sun Jan 04 21:32:40 2009 +0100 +++ b/tests/test-patchbomb.out Sun Jan 04 21:44:06 2009 +0100 @@ -196,7 +196,8 @@ c -files patched: 1 + c | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) Displaying [PATCH] test ... @@ -211,7 +212,8 @@ To: foo Cc: bar -files patched: 1 + c | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) # HG changeset patch @@ -232,15 +234,19 @@ a -files patched: 1 + a | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) b -files patched: 1 + b | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) Final summary: -files patched: 2 + a | 1 + + b | 1 + + 2 files changed, 2 insertions(+), 0 deletions(-) Write the introductory message for the patch series. @@ -258,7 +264,9 @@ Cc: bar -files patched: 2 + a | 1 + + b | 1 + + 2 files changed, 2 insertions(+), 0 deletions(-) Displaying [PATCH 1 of 2] a ... Content-Type: text/plain; charset="us-ascii" @@ -274,7 +282,8 @@ To: foo Cc: bar -files patched: 1 + a | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) # HG changeset patch @@ -304,7 +313,8 @@ To: foo Cc: bar -files patched: 1 + b | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) # HG changeset patch diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-pull-http --- a/tests/test-pull-http Sun Jan 04 21:32:40 2009 +0100 +++ b/tests/test-pull-http Sun Jan 04 21:44:06 2009 +0100 @@ -19,7 +19,7 @@ hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log cat hg.pid >> $DAEMON_PIDS hg clone http://localhost:$HGPORT/ test3 | sed -e 's,:[0-9][0-9]*/,/,' -kill `cat hg.pid` +"$TESTDIR/killdaemons.py" echo % serve errors cat errors.log diff -r 5f7e3f17aece -r 81deb1ba4402 tests/test-walkrepo.py --- a/tests/test-walkrepo.py Sun Jan 04 21:32:40 2009 +0100 +++ b/tests/test-walkrepo.py Sun Jan 04 21:44:06 2009 +0100 @@ -24,26 +24,26 @@ reposet = frozenset(walkrepos('.', followsym=True)) if sym and (len(reposet) != 3): print "reposet = %r" % (reposet,) - raise SystemExit(1, "Found %d repositories when I should have found 3" % (len(reposet),)) + print "Found %d repositories when I should have found 3" % (len(reposet),) if (not sym) and (len(reposet) != 2): print "reposet = %r" % (reposet,) - raise SystemExit(1, "Found %d repositories when I should have found 2" % (len(reposet),)) + print "Found %d repositories when I should have found 2" % (len(reposet),) sub1set = frozenset((pjoin('.', 'sub1'), pjoin('.', 'circle', 'subdir', 'sub1'))) if len(sub1set & reposet) != 1: print "sub1set = %r" % (sub1set,) print "reposet = %r" % (reposet,) - raise SystemExit(1, "sub1set and reposet should have exactly one path in common.") + print "sub1set and reposet should have exactly one path in common." sub2set = frozenset((pjoin('.', 'subsub1'), pjoin('.', 'subsubdir', 'subsub1'))) if len(sub2set & reposet) != 1: print "sub2set = %r" % (sub2set,) print "reposet = %r" % (reposet,) - raise SystemExit(1, "sub1set and reposet should have exactly one path in common.") + print "sub1set and reposet should have exactly one path in common." sub3 = pjoin('.', 'circle', 'top1') if sym and not (sub3 in reposet): print "reposet = %r" % (reposet,) - raise SystemExit(1, "Symbolic links are supported and %s is not in reposet" % (sub3,)) + print "Symbolic links are supported and %s is not in reposet" % (sub3,) runtest() if sym: