Mercurial > hg
changeset 3672:e8730b5b8a32
Merge with crew.
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Thu, 16 Nov 2006 08:52:55 +0100 |
parents | 86d3f966201d (diff) d2d8d23944a9 (current diff) |
children | eb0b4a2d70a9 |
files | mercurial/commands.py |
diffstat | 8 files changed, 521 insertions(+), 613 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/changegroup.py Wed Nov 15 19:18:57 2006 -0200 +++ b/mercurial/changegroup.py Thu Nov 16 08:52:55 2006 +0100 @@ -8,7 +8,7 @@ """ from i18n import gettext as _ from demandload import * -demandload(globals(), "struct util") +demandload(globals(), "struct os bz2 zlib util tempfile") def getchunk(source): """get a chunk from a changegroup""" @@ -41,3 +41,79 @@ def closechunk(): return struct.pack(">l", 0) +class nocompress(object): + def compress(self, x): + return x + def flush(self): + return "" + +bundletypes = { + "": nocompress, + "HG10UN": nocompress, + "HG10": lambda: bz2.BZ2Compressor(9), + "HG10GZ": zlib.compressobj, +} + +def writebundle(cg, filename, type): + """Write a bundle file and return its filename. + + Existing files will not be overwritten. + If no filename is specified, a temporary file is created. + bz2 compression can be turned off. + The bundle file will be deleted in case of errors. + """ + + fh = None + cleanup = None + try: + if filename: + if os.path.exists(filename): + raise util.Abort(_("file '%s' already exists") % filename) + fh = open(filename, "wb") + else: + fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg") + fh = os.fdopen(fd, "wb") + cleanup = filename + + fh.write(type) + z = bundletypes[type]() + + # parse the changegroup data, otherwise we will block + # in case of sshrepo because we don't know the end of the stream + + # an empty chunkiter is the end of the changegroup + empty = False + while not empty: + empty = True + for chunk in chunkiter(cg): + empty = False + fh.write(z.compress(genchunk(chunk))) + fh.write(z.compress(closechunk())) + fh.write(z.flush()) + cleanup = None + return filename + finally: + if fh is not None: + fh.close() + if cleanup is not None: + os.unlink(cleanup) + +def readbundle(fh): + header = fh.read(6) + if not header.startswith("HG"): + raise util.Abort(_("%s: not a Mercurial bundle file") % fname) + elif not header.startswith("HG10"): + raise util.Abort(_("%s: unknown bundle version") % fname) + + if header == "HG10BZ": + def generator(f): + zd = bz2.BZ2Decompressor() + zd.decompress("BZ") + for chunk in util.filechunkiter(f, 4096): + yield zd.decompress(chunk) + return util.chunkbuffer(generator(fh)) + elif header == "HG10UN": + return fh + + raise util.Abort(_("%s: unknown bundle compression type") + % fname)
--- a/mercurial/cmdutil.py Wed Nov 15 19:18:57 2006 -0200 +++ b/mercurial/cmdutil.py Thu Nov 16 08:52:55 2006 +0100 @@ -238,18 +238,19 @@ class changeset_printer(object): '''show changeset information when templating not requested.''' - def __init__(self, ui, repo, patch, buffered): + def __init__(self, ui, repo, patch, brinfo, buffered): self.ui = ui self.repo = repo self.buffered = buffered self.patch = patch + self.brinfo = brinfo if buffered: self.ui = uibuffer(ui) def flush(self, rev): return self.ui.flush(rev) - def show(self, rev=0, changenode=None, brinfo=None, copies=None): + def show(self, rev=0, changenode=None, copies=None): '''show a single changeset or file revision''' if self.buffered: self.ui.mark(rev) @@ -288,9 +289,10 @@ for parent in parents: self.ui.write(_("parent: %d:%s\n") % parent) - if brinfo and changenode in brinfo: - br = brinfo[changenode] - self.ui.write(_("branch: %s\n") % " ".join(br)) + if self.brinfo: + br = self.repo.branchlookup([changenode]) + if br: + self.ui.write(_("branch: %s\n") % " ".join(br[changenode])) if self.ui.debugflag: self.ui.write(_("manifest: %d:%s\n") % @@ -339,8 +341,8 @@ class changeset_templater(changeset_printer): '''format changeset information.''' - def __init__(self, ui, repo, patch, mapfile, buffered): - changeset_printer.__init__(self, ui, repo, patch, buffered) + def __init__(self, ui, repo, patch, brinfo, mapfile, buffered): + changeset_printer.__init__(self, ui, repo, patch, brinfo, buffered) self.t = templater.templater(mapfile, templater.common_filters, cache={'parent': '{rev}:{node|short} ', 'manifest': '{rev}:{node|short}', @@ -350,7 +352,7 @@ '''set template string to use''' self.t.cache['changeset'] = t - def show(self, rev=0, changenode=None, brinfo=None, copies=[], **props): + def show(self, rev=0, changenode=None, copies=[], **props): '''show a single changeset or file revision''' if self.buffered: self.ui.mark(rev) @@ -426,11 +428,13 @@ def showbranches(**args): branch = changes[5].get("branch") if branch: - yield showlist('branch', [branch], plural='branches', **args) + return showlist('branch', [branch], plural='branches', **args) # add old style branches if requested - if brinfo and changenode in brinfo: - yield showlist('branch', brinfo[changenode], - plural='branches', **args) + if self.brinfo: + br = self.repo.branchlookup([changenode]) + if changenode in br: + return showlist('branch', br[changenode], + plural='branches', **args) def showparents(**args): parents = [[('rev', log.rev(p)), ('node', hex(p))] @@ -471,7 +475,7 @@ return self.t('manifest', **args) else: def showfiles(**args): - yield showlist('file', changes[3], **args) + return showlist('file', changes[3], **args) showadds = '' showdels = '' showmanifest = '' @@ -555,6 +559,11 @@ """ # options patch = opts.get('patch') + br = None + if opts.get('branches'): + ui.warn(_("the --branches option is deprecated, " + "please use 'hg branches' instead\n")) + br = True tmpl = opts.get('template') mapfile = None if tmpl: @@ -576,10 +585,203 @@ or templater.templatepath(mapfile)) if mapname: mapfile = mapname try: - t = changeset_templater(ui, repo, patch, mapfile, buffered) + t = changeset_templater(ui, repo, patch, br, mapfile, buffered) except SyntaxError, inst: raise util.Abort(inst.args[0]) if tmpl: t.use_template(tmpl) return t - return changeset_printer(ui, repo, patch, buffered) + return changeset_printer(ui, repo, patch, br, buffered) + +def walkchangerevs(ui, repo, pats, change, opts): + '''Iterate over files and the revs they changed in. + + Callers most commonly need to iterate backwards over the history + it is interested in. Doing so has awful (quadratic-looking) + performance, so we use iterators in a "windowed" way. + + We walk a window of revisions in the desired order. Within the + window, we first walk forwards to gather data, then in the desired + order (usually backwards) to display it. + + This function returns an (iterator, matchfn) tuple. The iterator + yields 3-tuples. They will be of one of the following forms: + + "window", incrementing, lastrev: stepping through a window, + positive if walking forwards through revs, last rev in the + sequence iterated over - use to reset state for the current window + + "add", rev, fns: out-of-order traversal of the given file names + fns, which changed during revision rev - use to gather data for + possible display + + "iter", rev, None: in-order traversal of the revs earlier iterated + over with "add" - use to display data''' + + def increasing_windows(start, end, windowsize=8, sizelimit=512): + if start < end: + while start < end: + yield start, min(windowsize, end-start) + start += windowsize + if windowsize < sizelimit: + windowsize *= 2 + else: + while start > end: + yield start, min(windowsize, start-end-1) + start -= windowsize + if windowsize < sizelimit: + windowsize *= 2 + + files, matchfn, anypats = matchpats(repo, pats, opts) + follow = opts.get('follow') or opts.get('follow_first') + + if repo.changelog.count() == 0: + return [], matchfn + + if follow: + defrange = '%s:0' % repo.changectx().rev() + else: + defrange = 'tip:0' + revs = revrange(ui, repo, opts['rev'] or [defrange]) + wanted = {} + slowpath = anypats or opts.get('removed') + fncache = {} + if not slowpath and not files: + # No files, no patterns. Display all revs. + wanted = dict.fromkeys(revs) + copies = [] + if not slowpath: + # Only files, no patterns. Check the history of each file. + def filerevgen(filelog, node): + cl_count = repo.changelog.count() + if node is None: + last = filelog.count() - 1 + else: + last = filelog.rev(node) + for i, window in increasing_windows(last, nullrev): + revs = [] + for j in xrange(i - window, i + 1): + n = filelog.node(j) + revs.append((filelog.linkrev(n), + follow and filelog.renamed(n))) + revs.reverse() + for rev in revs: + # only yield rev for which we have the changelog, it can + # happen while doing "hg log" during a pull or commit + if rev[0] < cl_count: + yield rev + def iterfiles(): + for filename in files: + yield filename, None + for filename_node in copies: + yield filename_node + minrev, maxrev = min(revs), max(revs) + for file_, node in iterfiles(): + filelog = repo.file(file_) + # A zero count may be a directory or deleted file, so + # try to find matching entries on the slow path. + if filelog.count() == 0: + slowpath = True + break + for rev, copied in filerevgen(filelog, node): + if rev <= maxrev: + if rev < minrev: + break + fncache.setdefault(rev, []) + fncache[rev].append(file_) + wanted[rev] = 1 + if follow and copied: + copies.append(copied) + if slowpath: + if follow: + raise util.Abort(_('can only follow copies/renames for explicit ' + 'file names')) + + # The slow path checks files modified in every changeset. + def changerevgen(): + for i, window in increasing_windows(repo.changelog.count()-1, + nullrev): + for j in xrange(i - window, i + 1): + yield j, change(j)[3] + + for rev, changefiles in changerevgen(): + matches = filter(matchfn, changefiles) + if matches: + fncache[rev] = matches + wanted[rev] = 1 + + class followfilter: + def __init__(self, onlyfirst=False): + self.startrev = nullrev + self.roots = [] + self.onlyfirst = onlyfirst + + def match(self, rev): + def realparents(rev): + if self.onlyfirst: + return repo.changelog.parentrevs(rev)[0:1] + else: + return filter(lambda x: x != nullrev, + repo.changelog.parentrevs(rev)) + + if self.startrev == nullrev: + self.startrev = rev + return True + + if rev > self.startrev: + # forward: all descendants + if not self.roots: + self.roots.append(self.startrev) + for parent in realparents(rev): + if parent in self.roots: + self.roots.append(rev) + return True + else: + # backwards: all parents + if not self.roots: + self.roots.extend(realparents(self.startrev)) + if rev in self.roots: + self.roots.remove(rev) + self.roots.extend(realparents(rev)) + return True + + return False + + # it might be worthwhile to do this in the iterator if the rev range + # is descending and the prune args are all within that range + for rev in opts.get('prune', ()): + rev = repo.changelog.rev(repo.lookup(rev)) + ff = followfilter() + stop = min(revs[0], revs[-1]) + for x in xrange(rev, stop-1, -1): + if ff.match(x) and x in wanted: + del wanted[x] + + def iterate(): + if follow and not files: + ff = followfilter(onlyfirst=opts.get('follow_first')) + def want(rev): + if ff.match(rev) and rev in wanted: + return True + return False + else: + def want(rev): + return rev in wanted + + for i, window in increasing_windows(0, len(revs)): + yield 'window', revs[0] < revs[-1], revs[-1] + nrevs = [rev for rev in revs[i:i+window] if want(rev)] + srevs = list(nrevs) + srevs.sort() + for rev in srevs: + fns = fncache.get(rev) + if not fns: + def fns_generator(): + for f in change(rev)[3]: + if matchfn(f): + yield f + fns = fns_generator() + yield 'add', rev, fns + for rev in nrevs: + yield 'iter', rev, None + return iterate(), matchfn
--- a/mercurial/commands.py Wed Nov 15 19:18:57 2006 -0200 +++ b/mercurial/commands.py Thu Nov 16 08:52:55 2006 +0100 @@ -10,8 +10,8 @@ from i18n import gettext as _ demandload(globals(), "os re sys signal imp urllib pdb shlex") demandload(globals(), "fancyopts ui hg util lock revlog bundlerepo") -demandload(globals(), "difflib patch tempfile time") -demandload(globals(), "traceback errno version atexit bz2") +demandload(globals(), "difflib patch time") +demandload(globals(), "traceback errno version atexit") demandload(globals(), "archival changegroup cmdutil hgweb.server sshserver") class UnknownCommand(Exception): @@ -24,12 +24,6 @@ if modified or added or removed or deleted: raise util.Abort(_("outstanding uncommitted changes")) -def relpath(repo, args): - cwd = repo.getcwd() - if cwd: - return [util.normpath(os.path.join(cwd, x)) for x in args] - return args - def logmessage(opts): """ get the log message according to -m and -l option """ message = opts['message'] @@ -49,252 +43,6 @@ (logfile, inst.strerror)) return message -def walkchangerevs(ui, repo, pats, change, opts): - '''Iterate over files and the revs they changed in. - - Callers most commonly need to iterate backwards over the history - it is interested in. Doing so has awful (quadratic-looking) - performance, so we use iterators in a "windowed" way. - - We walk a window of revisions in the desired order. Within the - window, we first walk forwards to gather data, then in the desired - order (usually backwards) to display it. - - This function returns an (iterator, matchfn) tuple. The iterator - yields 3-tuples. They will be of one of the following forms: - - "window", incrementing, lastrev: stepping through a window, - positive if walking forwards through revs, last rev in the - sequence iterated over - use to reset state for the current window - - "add", rev, fns: out-of-order traversal of the given file names - fns, which changed during revision rev - use to gather data for - possible display - - "iter", rev, None: in-order traversal of the revs earlier iterated - over with "add" - use to display data''' - - def increasing_windows(start, end, windowsize=8, sizelimit=512): - if start < end: - while start < end: - yield start, min(windowsize, end-start) - start += windowsize - if windowsize < sizelimit: - windowsize *= 2 - else: - while start > end: - yield start, min(windowsize, start-end-1) - start -= windowsize - if windowsize < sizelimit: - windowsize *= 2 - - files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts) - follow = opts.get('follow') or opts.get('follow_first') - - if repo.changelog.count() == 0: - return [], matchfn - - if follow: - defrange = '%s:0' % repo.changectx().rev() - else: - defrange = 'tip:0' - revs = cmdutil.revrange(ui, repo, opts['rev'] or [defrange]) - wanted = {} - slowpath = anypats - fncache = {} - - if not slowpath and not files: - # No files, no patterns. Display all revs. - wanted = dict.fromkeys(revs) - copies = [] - if not slowpath: - # Only files, no patterns. Check the history of each file. - def filerevgen(filelog, node): - cl_count = repo.changelog.count() - if node is None: - last = filelog.count() - 1 - else: - last = filelog.rev(node) - for i, window in increasing_windows(last, nullrev): - revs = [] - for j in xrange(i - window, i + 1): - n = filelog.node(j) - revs.append((filelog.linkrev(n), - follow and filelog.renamed(n))) - revs.reverse() - for rev in revs: - # only yield rev for which we have the changelog, it can - # happen while doing "hg log" during a pull or commit - if rev[0] < cl_count: - yield rev - def iterfiles(): - for filename in files: - yield filename, None - for filename_node in copies: - yield filename_node - minrev, maxrev = min(revs), max(revs) - for file_, node in iterfiles(): - filelog = repo.file(file_) - # A zero count may be a directory or deleted file, so - # try to find matching entries on the slow path. - if filelog.count() == 0: - slowpath = True - break - for rev, copied in filerevgen(filelog, node): - if rev <= maxrev: - if rev < minrev: - break - fncache.setdefault(rev, []) - fncache[rev].append(file_) - wanted[rev] = 1 - if follow and copied: - copies.append(copied) - if slowpath: - if follow: - raise util.Abort(_('can only follow copies/renames for explicit ' - 'file names')) - - # The slow path checks files modified in every changeset. - def changerevgen(): - for i, window in increasing_windows(repo.changelog.count()-1, - nullrev): - for j in xrange(i - window, i + 1): - yield j, change(j)[3] - - for rev, changefiles in changerevgen(): - matches = filter(matchfn, changefiles) - if matches: - fncache[rev] = matches - wanted[rev] = 1 - - class followfilter: - def __init__(self, onlyfirst=False): - self.startrev = nullrev - self.roots = [] - self.onlyfirst = onlyfirst - - def match(self, rev): - def realparents(rev): - if self.onlyfirst: - return repo.changelog.parentrevs(rev)[0:1] - else: - return filter(lambda x: x != nullrev, - repo.changelog.parentrevs(rev)) - - if self.startrev == nullrev: - self.startrev = rev - return True - - if rev > self.startrev: - # forward: all descendants - if not self.roots: - self.roots.append(self.startrev) - for parent in realparents(rev): - if parent in self.roots: - self.roots.append(rev) - return True - else: - # backwards: all parents - if not self.roots: - self.roots.extend(realparents(self.startrev)) - if rev in self.roots: - self.roots.remove(rev) - self.roots.extend(realparents(rev)) - return True - - return False - - # it might be worthwhile to do this in the iterator if the rev range - # is descending and the prune args are all within that range - for rev in opts.get('prune', ()): - rev = repo.changelog.rev(repo.lookup(rev)) - ff = followfilter() - stop = min(revs[0], revs[-1]) - for x in xrange(rev, stop-1, -1): - if ff.match(x) and x in wanted: - del wanted[x] - - def iterate(): - if follow and not files: - ff = followfilter(onlyfirst=opts.get('follow_first')) - def want(rev): - if ff.match(rev) and rev in wanted: - return True - return False - else: - def want(rev): - return rev in wanted - - for i, window in increasing_windows(0, len(revs)): - yield 'window', revs[0] < revs[-1], revs[-1] - nrevs = [rev for rev in revs[i:i+window] if want(rev)] - srevs = list(nrevs) - srevs.sort() - for rev in srevs: - fns = fncache.get(rev) - if not fns: - def fns_generator(): - for f in change(rev)[3]: - if matchfn(f): - yield f - fns = fns_generator() - yield 'add', rev, fns - for rev in nrevs: - yield 'iter', rev, None - return iterate(), matchfn - -def write_bundle(cg, filename=None, compress=True): - """Write a bundle file and return its filename. - - Existing files will not be overwritten. - If no filename is specified, a temporary file is created. - bz2 compression can be turned off. - The bundle file will be deleted in case of errors. - """ - class nocompress(object): - def compress(self, x): - return x - def flush(self): - return "" - - fh = None - cleanup = None - try: - if filename: - if os.path.exists(filename): - raise util.Abort(_("file '%s' already exists") % filename) - fh = open(filename, "wb") - else: - fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg") - fh = os.fdopen(fd, "wb") - cleanup = filename - - if compress: - fh.write("HG10") - z = bz2.BZ2Compressor(9) - else: - fh.write("HG10UN") - z = nocompress() - # parse the changegroup data, otherwise we will block - # in case of sshrepo because we don't know the end of the stream - - # an empty chunkiter is the end of the changegroup - empty = False - while not empty: - empty = True - for chunk in changegroup.chunkiter(cg): - empty = False - fh.write(z.compress(changegroup.genchunk(chunk))) - fh.write(z.compress(changegroup.closechunk())) - fh.write(z.flush()) - cleanup = None - return filename - finally: - if fh is not None: - fh.close() - if cleanup is not None: - os.unlink(cleanup) - def setremoteconfig(ui, opts): "copy remote options to ui tree" if opts.get('ssh'): @@ -302,153 +50,6 @@ if opts.get('remotecmd'): ui.setconfig("ui", "remotecmd", opts['remotecmd']) -def show_version(ui): - """output version and copyright information""" - ui.write(_("Mercurial Distributed SCM (version %s)\n") - % version.get_version()) - ui.status(_( - "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n" - "This is free software; see the source for copying conditions. " - "There is NO\nwarranty; " - "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" - )) - -def help_(ui, name=None, with_version=False): - """show help for a command, extension, or list of commands - - With no arguments, print a list of commands and short help. - - Given a command name, print help for that command. - - Given an extension name, print help for that extension, and the - commands it provides.""" - option_lists = [] - - def helpcmd(name): - if with_version: - show_version(ui) - ui.write('\n') - aliases, i = findcmd(ui, name) - # synopsis - ui.write("%s\n\n" % i[2]) - - # description - doc = i[0].__doc__ - if not doc: - doc = _("(No help text available)") - if ui.quiet: - doc = doc.splitlines(0)[0] - ui.write("%s\n" % doc.rstrip()) - - if not ui.quiet: - # aliases - if len(aliases) > 1: - ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:])) - - # options - if i[1]: - option_lists.append(("options", i[1])) - - def helplist(select=None): - h = {} - cmds = {} - for c, e in table.items(): - f = c.split("|", 1)[0] - if select and not select(f): - continue - if name == "shortlist" and not f.startswith("^"): - continue - f = f.lstrip("^") - if not ui.debugflag and f.startswith("debug"): - continue - doc = e[0].__doc__ - if not doc: - doc = _("(No help text available)") - h[f] = doc.splitlines(0)[0].rstrip() - cmds[f] = c.lstrip("^") - - fns = h.keys() - fns.sort() - m = max(map(len, fns)) - for f in fns: - if ui.verbose: - commands = cmds[f].replace("|",", ") - ui.write(" %s:\n %s\n"%(commands, h[f])) - else: - ui.write(' %-*s %s\n' % (m, f, h[f])) - - def helpext(name): - try: - mod = findext(name) - except KeyError: - raise UnknownCommand(name) - - doc = (mod.__doc__ or _('No help text available')).splitlines(0) - ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0])) - for d in doc[1:]: - ui.write(d, '\n') - - ui.status('\n') - if ui.verbose: - ui.status(_('list of commands:\n\n')) - else: - ui.status(_('list of commands (use "hg help -v %s" ' - 'to show aliases and global options):\n\n') % name) - - modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable]) - helplist(modcmds.has_key) - - if name and name != 'shortlist': - try: - helpcmd(name) - except UnknownCommand: - helpext(name) - - else: - # program name - if ui.verbose or with_version: - show_version(ui) - else: - ui.status(_("Mercurial Distributed SCM\n")) - ui.status('\n') - - # list of commands - if name == "shortlist": - ui.status(_('basic commands (use "hg help" ' - 'for the full list or option "-v" for details):\n\n')) - elif ui.verbose: - ui.status(_('list of commands:\n\n')) - else: - ui.status(_('list of commands (use "hg help -v" ' - 'to show aliases and global options):\n\n')) - - helplist() - - # global options - if ui.verbose: - option_lists.append(("global options", globalopts)) - - # list all option lists - opt_output = [] - for title, options in option_lists: - opt_output.append(("\n%s:\n" % title, None)) - for shortopt, longopt, default, desc in options: - if "DEPRECATED" in desc and not ui.verbose: continue - opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt, - longopt and " --%s" % longopt), - "%s%s" % (desc, - default - and _(" (default: %s)") % default - or ""))) - - if opt_output: - opts_len = max([len(line[0]) for line in opt_output if line[1]]) - for first, second in opt_output: - if second: - ui.write(" %-*s %s\n" % (opts_len, first, second)) - else: - ui.write("%s\n" % first) - # Commands start here, listed alphabetically def add(ui, repo, *pats, **opts): @@ -703,7 +304,7 @@ # create the right base # XXX: nodesbetween / changegroup* should be "fixed" instead o = [] - has = {nullid: None} + has = {nullid: None} for n in base: has.update(repo.changelog.reachable(n)) if revs: @@ -731,7 +332,7 @@ cg = repo.changegroupsubset(o, revs, 'bundle') else: cg = repo.changegroup(o, 'bundle') - write_bundle(cg, fname) + changegroup.writebundle(cg, fname, "HG10") def cat(ui, repo, file1, *pats, **opts): """output the latest or given revisions of files @@ -817,8 +418,15 @@ cmdutil.addremove(repo, pats, opts) fns, match, anypats = cmdutil.matchpats(repo, pats, opts) if pats: - modified, added, removed = repo.status(files=fns, match=match)[:3] + status = repo.status(files=fns, match=match) + modified, added, removed, deleted, unknown = status[:5] files = modified + added + removed + for f in fns: + if f not in modified + added + removed: + if f in unknown: + raise util.Abort(_("file %s not tracked!") % f) + else: + raise util.Abort(_("file %s not found!") % f) else: files = [] try: @@ -1024,10 +632,6 @@ operation is recorded, but no copying is performed. This command takes effect in the next commit. - - NOTE: This command should be treated as experimental. While it - should properly record copied files, this information is not yet - fully used by merge, nor fully reported by log. """ wlock = repo.wlock(0) errs, copied = docopy(ui, repo, pats, opts, wlock) @@ -1190,25 +794,17 @@ ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i)) ui.write("}\n") -def debugrename(ui, repo, file, rev=None): +def debugrename(ui, repo, file1, *pats, **opts): """dump rename information""" - r = repo.file(relpath(repo, [file])[0]) - if rev: - try: - # assume all revision numbers are for changesets - n = repo.lookup(rev) - change = repo.changelog.read(n) - m = repo.manifest.read(change[0]) - n = m[relpath(repo, [file])[0]] - except (hg.RepoError, KeyError): - n = r.lookup(rev) - else: - n = r.tip() - m = r.renamed(n) - if m: - ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1]))) - else: - ui.write(_("not renamed\n")) + + ctx = repo.changectx(opts.get('rev', 'tip')) + for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts, + ctx.node()): + m = ctx.filectx(abs).renamed() + if m: + ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1]))) + else: + ui.write(_("%s not renamed\n") % rel) def debugwalk(ui, repo, *pats, **opts): """show how files match on given patterns""" @@ -1377,7 +973,7 @@ if opts['all']: cols.append(change) if opts['user']: - cols.append(ui.shortuser(getchange(r)[1])) + cols.append(ui.shortuser(get(r)[1])) if opts['files_with_matches']: c = (fn, r) if c in filerevmatches: @@ -1391,8 +987,8 @@ fstate = {} skip = {} - getchange = util.cachefunc(lambda r:repo.changectx(r).changeset()) - changeiter, matchfn = walkchangerevs(ui, repo, pats, getchange, opts) + get = util.cachefunc(lambda r:repo.changectx(r).changeset()) + changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts) count = 0 incrementing = False follow = opts.get('follow') @@ -1459,14 +1055,145 @@ heads = repo.heads(repo.lookup(opts['rev'])) else: heads = repo.heads() - br = None - if opts['branches']: - ui.warn(_("the --branches option is deprecated, " - "please use 'hg branches' instead\n")) - br = repo.branchlookup(heads) displayer = cmdutil.show_changeset(ui, repo, opts) for n in heads: - displayer.show(changenode=n, brinfo=br) + displayer.show(changenode=n) + +def help_(ui, name=None, with_version=False): + """show help for a command, extension, or list of commands + + With no arguments, print a list of commands and short help. + + Given a command name, print help for that command. + + Given an extension name, print help for that extension, and the + commands it provides.""" + option_lists = [] + + def helpcmd(name): + if with_version: + version_(ui) + ui.write('\n') + aliases, i = findcmd(ui, name) + # synopsis + ui.write("%s\n\n" % i[2]) + + # description + doc = i[0].__doc__ + if not doc: + doc = _("(No help text available)") + if ui.quiet: + doc = doc.splitlines(0)[0] + ui.write("%s\n" % doc.rstrip()) + + if not ui.quiet: + # aliases + if len(aliases) > 1: + ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:])) + + # options + if i[1]: + option_lists.append(("options", i[1])) + + def helplist(select=None): + h = {} + cmds = {} + for c, e in table.items(): + f = c.split("|", 1)[0] + if select and not select(f): + continue + if name == "shortlist" and not f.startswith("^"): + continue + f = f.lstrip("^") + if not ui.debugflag and f.startswith("debug"): + continue + doc = e[0].__doc__ + if not doc: + doc = _("(No help text available)") + h[f] = doc.splitlines(0)[0].rstrip() + cmds[f] = c.lstrip("^") + + fns = h.keys() + fns.sort() + m = max(map(len, fns)) + for f in fns: + if ui.verbose: + commands = cmds[f].replace("|",", ") + ui.write(" %s:\n %s\n"%(commands, h[f])) + else: + ui.write(' %-*s %s\n' % (m, f, h[f])) + + def helpext(name): + try: + mod = findext(name) + except KeyError: + raise UnknownCommand(name) + + doc = (mod.__doc__ or _('No help text available')).splitlines(0) + ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0])) + for d in doc[1:]: + ui.write(d, '\n') + + ui.status('\n') + if ui.verbose: + ui.status(_('list of commands:\n\n')) + else: + ui.status(_('list of commands (use "hg help -v %s" ' + 'to show aliases and global options):\n\n') % name) + + modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable]) + helplist(modcmds.has_key) + + if name and name != 'shortlist': + try: + helpcmd(name) + except UnknownCommand: + helpext(name) + + else: + # program name + if ui.verbose or with_version: + version_(ui) + else: + ui.status(_("Mercurial Distributed SCM\n")) + ui.status('\n') + + # list of commands + if name == "shortlist": + ui.status(_('basic commands (use "hg help" ' + 'for the full list or option "-v" for details):\n\n')) + elif ui.verbose: + ui.status(_('list of commands:\n\n')) + else: + ui.status(_('list of commands (use "hg help -v" ' + 'to show aliases and global options):\n\n')) + + helplist() + + # global options + if ui.verbose: + option_lists.append(("global options", globalopts)) + + # list all option lists + opt_output = [] + for title, options in option_lists: + opt_output.append(("\n%s:\n" % title, None)) + for shortopt, longopt, default, desc in options: + if "DEPRECATED" in desc and not ui.verbose: continue + opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt, + longopt and " --%s" % longopt), + "%s%s" % (desc, + default + and _(" (default: %s)") % default + or ""))) + + if opt_output: + opts_len = max([len(line[0]) for line in opt_output if line[1]]) + for first, second in opt_output: + if second: + ui.write(" %-*s %s\n" % (opts_len, first, second)) + else: + ui.write("%s\n" % first) def identify(ui, repo): """print information about the working copy @@ -1597,7 +1324,8 @@ if fname or not other.local(): # create a bundle (uncompressed if other repo is not local) cg = other.changegroup(incoming, "incoming") - fname = cleanup = write_bundle(cg, fname, compress=other.local()) + type = other.local() and "HG10" or "HG10UN" + fname = cleanup = changegroup.writebundle(cg, fname, type) # keep written bundle? if opts["bundle"]: cleanup = None @@ -1694,12 +1422,8 @@ files and full commit message is shown. """ - getchange = util.cachefunc(lambda r:repo.changectx(r).changeset()) - changeiter, matchfn = walkchangerevs(ui, repo, pats, getchange, opts) - - if opts['branches']: - ui.warn(_("the --branches option is deprecated, " - "please use 'hg branches' instead\n")) + get = util.cachefunc(lambda r:repo.changectx(r).changeset()) + changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts) if opts['limit']: try: @@ -1759,7 +1483,7 @@ continue if opts['keyword']: - changes = getchange(rev) + changes = get(rev) miss = 0 for k in [kw.lower() for kw in opts['keyword']]: if not (k in changes[1].lower() or @@ -1770,18 +1494,14 @@ if miss: continue - br = None - if opts['branches']: - br = repo.branchlookup([repo.changelog.node(rev)]) - copies = [] if opts.get('copies') and rev: - mf = getchange(rev)[0] - for fn in getchange(rev)[3]: + mf = get(rev)[0] + for fn in get(rev)[3]: rename = getrenamed(fn, rev, mf) if rename: copies.append((fn, rename[0])) - displayer.show(rev, changenode, brinfo=br, copies=copies) + displayer.show(rev, changenode, copies=copies) elif st == 'iter': if count == limit: break if displayer.flush(rev): @@ -1875,22 +1595,12 @@ continue displayer.show(changenode=n) -def parents(ui, repo, file_=None, rev=None, branches=None, **opts): +def parents(ui, repo, file_=None, **opts): """show the parents of the working dir or revision Print the working directory's parent revisions. """ - # legacy - if file_ and not rev: - try: - rev = repo.lookup(file_) - file_ = None - except hg.RepoError: - pass - else: - ui.warn(_("'hg parent REV' is deprecated, " - "please use 'hg parents -r REV instead\n")) - + rev = opts.get('rev') if rev: if file_: ctx = repo.filectx(file_, changeid=rev) @@ -1900,15 +1610,10 @@ else: p = repo.dirstate.parents() - br = None - if branches is not None: - ui.warn(_("the --branches option is deprecated, " - "please use 'hg branches' instead\n")) - br = repo.branchlookup(p) displayer = cmdutil.show_changeset(ui, repo, opts) for n in p: if n != nullid: - displayer.show(changenode=n, brinfo=br) + displayer.show(changenode=n) def paths(ui, repo, search=None): """show definition of symbolic path names @@ -2034,7 +1739,7 @@ r = repo.push(other, opts['force'], revs=revs) return r == 0 -def rawcommit(ui, repo, *flist, **rc): +def rawcommit(ui, repo, *pats, **opts): """raw commit interface (DEPRECATED) (DEPRECATED) @@ -2049,23 +1754,16 @@ ui.warn(_("(the rawcommit command is deprecated)\n")) - message = rc['message'] - if not message and rc['logfile']: - try: - message = open(rc['logfile']).read() - except IOError: - pass - if not message and not rc['logfile']: - raise util.Abort(_("missing commit message")) - - files = relpath(repo, list(flist)) - if rc['files']: - files += open(rc['files']).read().splitlines() - - rc['parent'] = map(repo.lookup, rc['parent']) + message = logmessage(opts) + + files, match, anypats = cmdutil.matchpats(repo, pats, opts) + if opts['files']: + files += open(opts['files']).read().splitlines() + + parents = [repo.lookup(p) for p in opts['parent']] try: - repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent']) + repo.rawcommit(files, message, opts['user'], opts['date'], *parents) except ValueError, inst: raise util.Abort(str(inst)) @@ -2140,10 +1838,6 @@ operation is recorded, but no copying is performed. This command takes effect in the next commit. - - NOTE: This command should be treated as experimental. While it - should properly record rename files, this information is not yet - fully used by merge, nor fully reported by log. """ wlock = repo.wlock(0) errs, copied = docopy(ui, repo, pats, opts, wlock) @@ -2526,13 +2220,7 @@ Show the tip revision. """ - n = repo.changelog.tip() - br = None - if opts['branches']: - ui.warn(_("the --branches option is deprecated, " - "please use 'hg branches' instead\n")) - br = repo.branchlookup([n]) - cmdutil.show_changeset(ui, repo, opts).show(changenode=n, brinfo=br) + cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count()) def unbundle(ui, repo, fname, **opts): """apply a changegroup file @@ -2540,29 +2228,8 @@ Apply a compressed changegroup file generated by the bundle command. """ - f = urllib.urlopen(fname) - - header = f.read(6) - if not header.startswith("HG"): - raise util.Abort(_("%s: not a Mercurial bundle file") % fname) - elif not header.startswith("HG10"): - raise util.Abort(_("%s: unknown bundle version") % fname) - elif header == "HG10BZ": - def generator(f): - zd = bz2.BZ2Decompressor() - zd.decompress("BZ") - for chunk in f: - yield zd.decompress(chunk) - elif header == "HG10UN": - def generator(f): - for chunk in f: - yield chunk - else: - raise util.Abort(_("%s: unknown bundle compression type") - % fname) - gen = generator(util.filechunkiter(f, 4096)) - modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle', - 'bundle:' + fname) + gen = changegroup.readbundle(urllib.urlopen(fname)) + modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname) return postincoming(ui, repo, modheads, opts['update']) def update(ui, repo, node=None, merge=False, clean=False, force=None, @@ -2599,8 +2266,7 @@ if len(found) > 1: repo.ui.warn(_("Found multiple heads for %s\n") % branch) for x in found: - cmdutil.show_changeset(ui, repo, {}).show( - changenode=x, brinfo=br) + cmdutil.show_changeset(ui, repo, {}).show(changenode=x) raise util.Abort("") if len(found) == 1: node = found[0] @@ -2624,6 +2290,17 @@ """ return hg.verify(repo) +def version_(ui): + """output version and copyright information""" + ui.write(_("Mercurial Distributed SCM (version %s)\n") + % version.get_version()) + ui.status(_( + "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n" + "This is free software; see the source for copying conditions. " + "There is NO\nwarranty; " + "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" + )) + # Command options and aliases are listed here, alphabetically globalopts = [ @@ -2854,6 +2531,7 @@ ('k', 'keyword', [], _('search for a keyword')), ('l', 'limit', '', _('limit number of changes displayed')), ('r', 'rev', [], _('show the specified revision or range')), + ('', 'removed', None, _('include revs where files were removed')), ('M', 'no-merges', None, _('do not show merges')), ('', 'style', '', _('display using template map file')), ('m', 'only-merges', None, _('show only merges')), @@ -3003,7 +2681,7 @@ ('f', 'force', None, _('force a merge with outstanding changes'))], _('hg update [-C] [-f] [REV]')), "verify": (verify, [], _('hg verify')), - "version": (show_version, [], _('hg version')), + "version": (version_, [], _('hg version')), } norepo = ("clone init version help debugancestor debugcomplete debugdata" @@ -3225,7 +2903,7 @@ if options['help']: return help_(u, cmd, options['version']) elif options['version']: - return show_version(u) + return version_(u) elif not cmd: return help_(u, 'shortlist')
--- a/mercurial/httprepo.py Wed Nov 15 19:18:57 2006 -0200 +++ b/mercurial/httprepo.py Thu Nov 16 08:52:55 2006 +0100 @@ -11,7 +11,7 @@ from i18n import gettext as _ from demandload import * demandload(globals(), "hg os urllib urllib2 urlparse zlib util httplib") -demandload(globals(), "errno keepalive tempfile socket") +demandload(globals(), "errno keepalive tempfile socket changegroup") class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm): def __init__(self, ui): @@ -114,6 +114,15 @@ class httphandler(basehttphandler): pass +def zgenerator(f): + zd = zlib.decompressobj() + try: + for chunk in util.filechunkiter(f): + yield zd.decompress(chunk) + except httplib.HTTPException, inst: + raise IOError(None, _('connection ended unexpectedly')) + yield zd.flush() + class httprepository(remoterepository): def __init__(self, ui, path): self.path = path @@ -305,79 +314,30 @@ def changegroup(self, nodes, kind): n = " ".join(map(hex, nodes)) f = self.do_cmd("changegroup", roots=n) - - def zgenerator(f): - zd = zlib.decompressobj() - try: - for chnk in f: - yield zd.decompress(chnk) - except httplib.HTTPException, inst: - raise IOError(None, _('connection ended unexpectedly')) - yield zd.flush() - - return util.chunkbuffer(zgenerator(util.filechunkiter(f))) + return util.chunkbuffer(zgenerator(f)) def changegroupsubset(self, bases, heads, source): baselst = " ".join([hex(n) for n in bases]) headlst = " ".join([hex(n) for n in heads]) f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst) - - def zgenerator(f): - zd = zlib.decompressobj() - try: - for chnk in f: - yield zd.decompress(chnk) - except httplib.HTTPException: - raise IOError(None, _('connection ended unexpectedly')) - yield zd.flush() - - return util.chunkbuffer(zgenerator(util.filechunkiter(f))) + return util.chunkbuffer(zgenerator(f)) def unbundle(self, cg, heads, source): # have to stream bundle to a temp file because we do not have # http 1.1 chunked transfer. - # XXX duplication from commands.py - class nocompress(object): - def compress(self, x): - return x - def flush(self): - return "" - - unbundleversions = self.capable('unbundle') - try: - unbundleversions = unbundleversions.split(',') - except AttributeError: - unbundleversions = [""] + type = "" + types = self.capable('unbundle') + if types: + for x in types.split(','): + if x in changegroup.bundletypes: + type = x + break - while unbundleversions: - header = unbundleversions[0] - if header == "HG10GZ": - self.ui.note(_("using zlib compression\n")) - z = zlib.compressobj() - break - elif header == "HG10UN": - self.ui.note(_("using no compression\n")) - z = nocompress() - break - elif header == "": - self.ui.note(_("old server without compression support," - " sending uncompressed\n")) - z = nocompress() - break - unbundleversions.pop(0) - if not unbundleversions: - raise util.Abort(_("The server doesn't accept any bundle format" - " method we know.")) - - fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-') - fp = os.fdopen(fd, 'wb+') + tempname = changegroup.writebundle(cg, None, type) + fp = file(tempname, "rb") try: - fp.write(header) - for chunk in util.filechunkiter(cg): - fp.write(z.compress(chunk)) - fp.write(z.flush()) - length = fp.tell() + length = os.stat(tempname).st_size try: rfp = self.do_cmd( 'unbundle', data=fp,
--- a/mercurial/templater.py Wed Nov 15 19:18:57 2006 -0200 +++ b/mercurial/templater.py Thu Nov 16 08:52:55 2006 +0100 @@ -156,8 +156,7 @@ def stringify(thing): '''turn nested template iterator into string.''' if hasattr(thing, '__iter__'): - return "".join([stringify(t) for t in thing]) - if thing is None: return "" + return "".join([stringify(t) for t in thing if t is not None]) return str(thing) para_re = None
--- a/mercurial/ui.py Wed Nov 15 19:18:57 2006 -0200 +++ b/mercurial/ui.py Thu Nov 16 08:52:55 2006 +0100 @@ -29,8 +29,6 @@ interactive=True, traceback=False, report_untrusted=True, parentui=None): self.overlay = None - self.header = [] - self.prev_header = [] if parentui is None: # this is the parent of all ui children self.parentui = None @@ -362,11 +360,6 @@ return path or loc def write(self, *args): - if self.header: - if self.header != self.prev_header: - self.prev_header = self.header - self.write(*self.header) - self.header = [] for a in args: sys.stdout.write(str(a))
--- a/tests/test-copy2.out Wed Nov 15 19:18:57 2006 -0200 +++ b/tests/test-copy2.out Thu Nov 16 08:52:55 2006 +0100 @@ -4,9 +4,9 @@ # should match rev offset length base linkrev nodeid p1 p2 0 0 5 0 0 2ed2a3912a0b 000000000000 000000000000 -renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd +bar renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd # should not be renamed -not renamed +bar not renamed # should show copy copy: foo -> bar # should show no parents for tip @@ -18,5 +18,5 @@ rev offset length base linkrev nodeid p1 p2 0 0 5 0 0 2ed2a3912a0b 000000000000 000000000000 1 5 7 1 2 dd12c926cf16 2ed2a3912a0b 000000000000 -renamed from foo:dd12c926cf165e3eb4cf87b084955cb617221c17 +bar renamed from foo:dd12c926cf165e3eb4cf87b084955cb617221c17 # should show no copies
--- a/tests/test-rename-merge1.out Wed Nov 15 19:18:57 2006 -0200 +++ b/tests/test-rename-merge1.out Thu Nov 16 08:52:55 2006 +0100 @@ -22,4 +22,4 @@ rev offset length base linkrev nodeid p1 p2 0 0 67 0 1 dc51707dfc98 000000000000 000000000000 1 67 72 1 3 b2494a44f0a9 000000000000 dc51707dfc98 -renamed from a:dd03b83622e78778b403775d0d074b9ac7387a66 +b renamed from a:dd03b83622e78778b403775d0d074b9ac7387a66