# HG changeset patch # User Vadim Gelfer # Date 1148279965 25200 # Node ID 737deea2442c63f03f2c317dac697990c3add98f # Parent de0c05afa5113c1602e30f8f27de31bd11de596c# Parent d5c9ba87a7387f6c8a2cd266cec991f9bb4ff35e merge with crew. diff --git a/contrib/mercurial.el b/contrib/mercurial.el --- a/contrib/mercurial.el +++ b/contrib/mercurial.el @@ -382,14 +382,27 @@ Handle frickin' frackin' gratuitous even (set-buffer hg-prev-buffer)) (let ((path (or default (buffer-file-name)))) (if (or (not path) current-prefix-arg) - (expand-file-name - (read-file-name (format "File, directory or pattern%s: " - (or prompt "")) - (and path (file-name-directory path)) - nil nil - (and path (file-name-nondirectory path)) - 'hg-file-history)) - path)))) + (expand-file-name + (eval (list* 'read-file-name + (format "File, directory or pattern%s: " + (or prompt "")) + (and path (file-name-directory path)) + nil nil + (and path (file-name-nondirectory path)) + (if hg-running-xemacs + (cons (quote 'hg-file-history) nil) + nil)))) + path)))) + +(defun hg-read-number (&optional prompt default) + "Read a integer value." + (save-excursion + (if (or (not default) current-prefix-arg) + (string-to-number + (eval (list* 'read-string + (or prompt "") + (if default (cons (format "%d" default) nil) nil)))) + default))) (defun hg-read-config () "Return an alist of (key . value) pairs of Mercurial config data. @@ -950,36 +963,55 @@ With a prefix argument, prompt for the p (kill-entire-line)) (run-hooks 'hg-log-mode-hook)) -(defun hg-log (path &optional rev1 rev2) - "Display the revision history of PATH, between REV1 and REV2. -REV1 defaults to hg-log-limit changes from the tip revision, while -REV2 defaults to the tip. +(defun hg-log (path &optional rev1 rev2 log-limit) + "Display the revision history of PATH. +History is displayed between REV1 and REV2. +Number of displayed changesets is limited to LOG-LIMIT. +REV1 defaults to the tip, while +REV2 defaults to `hg-rev-completion-limit' changes from the tip revision. +LOG-LIMIT defaults to `hg-log-limit'. With a prefix argument, prompt for each parameter." (interactive (list (hg-read-file-name " to log") - (hg-read-rev " to start with" "-1") - (hg-read-rev " to end with" (format "-%d" hg-log-limit)))) + (hg-read-rev " to start with" + "tip") + (hg-read-rev " to end with" + (format "%d" (- hg-rev-completion-limit))) + (hg-read-number "Output limited to: " + hg-log-limit))) (let ((a-path (hg-abbrev-file-name path)) - (r1 (or rev1 (format "-%d" hg-log-limit))) - (r2 (or rev2 rev1 "-1"))) + (r1 (or rev1 (format "-%d" hg-rev-completion-limit))) + (r2 (or rev2 rev1 "tip")) + (limit (format "%d" (or log-limit hg-log-limit)))) (hg-view-output ((if (equal r1 r2) - (format "Mercurial: Log of rev %s of %s" rev1 a-path) - (format "Mercurial: Log from rev %s to %s of %s" - r1 r2 a-path))) - (let ((revs (format "%s:%s" r1 r2))) - (if (> (length path) (length (hg-root path))) - (call-process (hg-binary) nil t nil "log" "-r" revs path) - (call-process (hg-binary) nil t nil "log" "-r" revs))) + (format "Mercurial: Log of rev %s of %s" rev1 a-path) + (format + "Mercurial: at most %s log(s) from rev %s to %s of %s" + limit r1 r2 a-path))) + (eval (list* 'call-process (hg-binary) nil t nil + "log" + "-r" (format "%s:%s" r1 r2) + "-l" limit + (if (> (length path) (length (hg-root path))) + (cons path nil) + nil))) (hg-log-mode)))) -(defun hg-log-repo (path &optional rev1 rev2) +(defun hg-log-repo (path &optional rev1 rev2 log-limit) "Display the revision history of the repository containing PATH. -History is displayed between REV1, which defaults to the tip, and -REV2, which defaults to the initial revision. -Variable hg-log-limit controls the number of log entries displayed." +History is displayed between REV1 and REV2. +Number of displayed changesets is limited to LOG-LIMIT, +REV1 defaults to the tip, while +REV2 defaults to `hg-rev-completion-limit' changes from the tip revision. +LOG-LIMIT defaults to `hg-log-limit'. +With a prefix argument, prompt for each parameter." (interactive (list (hg-read-file-name " to log") - (hg-read-rev " to start with" "tip") - (hg-read-rev " to end with" (format "-%d" hg-log-limit)))) - (hg-log (hg-root path) rev1 rev2)) + (hg-read-rev " to start with" + "tip") + (hg-read-rev " to end with" + (format "%d" (- hg-rev-completion-limit))) + (hg-read-number "Output limited to: " + hg-log-limit))) + (hg-log (hg-root path) rev1 rev2 log-limit)) (defun hg-outgoing (&optional repo) "Display changesets present locally that are not present in REPO." diff --git a/contrib/win32/ReadMe.html b/contrib/win32/ReadMe.html --- a/contrib/win32/ReadMe.html +++ b/contrib/win32/ReadMe.html @@ -90,8 +90,14 @@ hg other Mercurial commands should work fine for you.

Configuration notes

-

The default editor is 'vi'. You can set the EDITOR environment variable - (or HGEDITOR) to specify your preference.

+

The default editor for commit messages is 'vi'. You can set the EDITOR + (or HGEDITOR) environment variable to specify your preference or set it in + mercurial.ini:

+
+[ui]
+editor = whatever
+
+

Reporting problems

diff --git a/doc/hgrc.5.txt b/doc/hgrc.5.txt --- a/doc/hgrc.5.txt +++ b/doc/hgrc.5.txt @@ -36,11 +36,14 @@ installed. files override per-installation options. (Unix) $HOME/.hgrc:: -(Windows) C:\Documents and Settings\USERNAME\Mercurial.ini +(Windows) C:\Documents and Settings\USERNAME\Mercurial.ini:: +(Windows) $HOME\Mercurial.ini:: Per-user configuration file, for the user running Mercurial. Options in this file apply to all Mercurial commands executed by any user in any directory. Options in this file override per-installation and per-system options. + On Windows system, one of these is chosen exclusively according + to definition of HOME environment variable. (Unix, Windows) /.hg/hgrc:: Per-repository configuration options that only apply in a diff --git a/hgext/notify.py b/hgext/notify.py --- a/hgext/notify.py +++ b/hgext/notify.py @@ -99,7 +99,9 @@ class notifier(object): def __init__(self, ui, repo, hooktype): self.ui = ui - self.ui.readconfig(self.ui.config('notify', 'config')) + cfg = self.ui.config('notify', 'config') + if cfg: + self.ui.readconfig(cfg) self.repo = repo self.stripcount = int(self.ui.config('notify', 'strip', 0)) self.root = self.strip(self.repo.root) @@ -123,7 +125,7 @@ class notifier(object): path = util.pconvert(path) count = self.stripcount - while path and count >= 0: + while count > 0: c = path.find('/') if c == -1: break @@ -225,6 +227,8 @@ class notifier(object): if not msgtext.endswith('\n'): self.ui.write('\n') else: + self.ui.status(_('notify: sending %d subscribers %d changes\n') % + (len(self.subs), count)) mail = self.ui.sendmail() mail.sendmail(templater.email(msg['From']), self.subs, msgtext) @@ -250,7 +254,12 @@ def hook(ui, repo, hooktype, node=None, if used as changegroup hook, send one email for all changesets in changegroup. else send one email per changeset.''' n = notifier(ui, repo, hooktype) - if not n.subs or n.skipsource(source): + if not n.subs: + ui.debug(_('notify: no subscribers to this repo\n')) + return + if n.skipsource(source): + ui.debug(_('notify: changes have source "%s" - skipping\n') % + source) return node = bin(node) if hooktype == 'changegroup': diff --git a/mercurial/hgweb/__init__.py b/mercurial/hgweb/__init__.py --- a/mercurial/hgweb/__init__.py +++ b/mercurial/hgweb/__init__.py @@ -519,7 +519,8 @@ class hgweb(object): mnode = hex(mn) mf = man.read(mn) rev = man.rev(mn) - node = self.repo.changelog.node(rev) + changerev = man.linkrev(mn) + node = self.repo.changelog.node(changerev) mff = man.readflags(mn) files = {} diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -166,37 +166,44 @@ class localrepository(object): return s = l.split(" ", 1) if len(s) != 2: - self.ui.warn(_("%s: ignoring invalid tag\n") % context) + self.ui.warn(_("%s: cannot parse entry\n") % context) return node, key = s + key = key.strip() try: bin_n = bin(node) except TypeError: - self.ui.warn(_("%s: ignoring invalid tag\n") % context) + self.ui.warn(_("%s: node '%s' is not well formed\n") % + (context, node)) return if bin_n not in self.changelog.nodemap: - self.ui.warn(_("%s: ignoring invalid tag\n") % context) + self.ui.warn(_("%s: tag '%s' refers to unknown node\n") % + (context, key)) return - self.tagscache[key.strip()] = bin_n + self.tagscache[key] = bin_n - # read each head of the tags file, ending with the tip + # read the tags file from each head, ending with the tip, # and add each tag found to the map, with "newer" ones # taking precedence + heads = self.heads() + heads.reverse() fl = self.file(".hgtags") - h = fl.heads() - h.reverse() - for r in h: + for node in heads: + change = self.changelog.read(node) + rev = self.changelog.rev(node) + fn, ff = self.manifest.find(change[0], '.hgtags') + if fn is None: continue count = 0 - for l in fl.read(r).splitlines(): + for l in fl.read(fn).splitlines(): count += 1 - parsetag(l, ".hgtags:%d" % count) - + parsetag(l, _(".hgtags (rev %d:%s), line %d") % + (rev, short(node), count)) try: f = self.opener("localtags") count = 0 for l in f: count += 1 - parsetag(l, "localtags:%d" % count) + parsetag(l, _("localtags, line %d") % count) except IOError: pass diff --git a/mercurial/manifest.py b/mercurial/manifest.py --- a/mercurial/manifest.py +++ b/mercurial/manifest.py @@ -43,48 +43,61 @@ class manifest(revlog): def diff(self, a, b): return mdiff.textdiff(str(a), str(b)) + def _search(self, m, s, lo=0, hi=None): + '''return a tuple (start, end) that says where to find s within m. + + If the string is found m[start:end] are the line containing + that string. If start == end the string was not found and + they indicate the proper sorted insertion point. This was + taken from bisect_left, and modified to find line start/end as + it goes along. + + m should be a buffer or a string + s is a string''' + def advance(i, c): + while i < lenm and m[i] != c: + i += 1 + return i + lenm = len(m) + if not hi: + hi = lenm + while lo < hi: + mid = (lo + hi) // 2 + start = mid + while start > 0 and m[start-1] != '\n': + start -= 1 + end = advance(start, '\0') + if m[start:end] < s: + # we know that after the null there are 40 bytes of sha1 + # this translates to the bisect lo = mid + 1 + lo = advance(end + 40, '\n') + 1 + else: + # this translates to the bisect hi = mid + hi = start + end = advance(lo, '\0') + found = m[lo:end] + if cmp(s, found) == 0: + # we know that after the null there are 40 bytes of sha1 + end = advance(end + 40, '\n') + return (lo, end+1) + else: + return (lo, lo) + + def find(self, node, f): + '''look up entry for a single file efficiently. + return (node, flag) pair if found, (None, None) if not.''' + if self.mapcache and node == self.mapcache[0]: + return self.mapcache[1].get(f), self.mapcache[2].get(f) + text = self.revision(node) + start, end = self._search(text, f) + if start == end: + return None, None + l = text[start:end] + f, n = l.split('\0') + return bin(n[:40]), n[40:-1] == 'x' + def add(self, map, flags, transaction, link, p1=None, p2=None, changed=None): - - # returns a tuple (start, end). If the string is found - # m[start:end] are the line containing that string. If start == end - # the string was not found and they indicate the proper sorted - # insertion point. This was taken from bisect_left, and modified - # to find line start/end as it goes along. - # - # m should be a buffer or a string - # s is a string - # - def manifestsearch(m, s, lo=0, hi=None): - def advance(i, c): - while i < lenm and m[i] != c: - i += 1 - return i - lenm = len(m) - if not hi: - hi = lenm - while lo < hi: - mid = (lo + hi) // 2 - start = mid - while start > 0 and m[start-1] != '\n': - start -= 1 - end = advance(start, '\0') - if m[start:end] < s: - # we know that after the null there are 40 bytes of sha1 - # this translates to the bisect lo = mid + 1 - lo = advance(end + 40, '\n') + 1 - else: - # this translates to the bisect hi = mid - hi = start - end = advance(lo, '\0') - found = m[lo:end] - if cmp(s, found) == 0: - # we know that after the null there are 40 bytes of sha1 - end = advance(end + 40, '\n') - return (lo, end+1) - else: - return (lo, lo) - # apply the changes collected during the bisect loop to our addlist # return a delta suitable for addrevision def addlistdelta(addlist, x): @@ -137,7 +150,7 @@ class manifest(revlog): for w in work: f = w[0] # bs will either be the index of the item or the insert point - start, end = manifestsearch(addbuf, f, start) + start, end = self._search(addbuf, f, start) if w[1] == 0: l = "%s\000%s%s\n" % (f, hex(map[f]), flags[f] and "x" or '') diff --git a/mercurial/packagescan.py b/mercurial/packagescan.py --- a/mercurial/packagescan.py +++ b/mercurial/packagescan.py @@ -1,5 +1,6 @@ # packagescan.py - Helper module for identifing used modules. # Used for the py2exe distutil. +# This module must be the first mercurial module imported in setup.py # # Copyright 2005 Volker Kleinfeld # @@ -8,25 +9,58 @@ import glob import os import sys -import demandload import ihooks +import types +import string + +# Install this module as fake demandload module +sys.modules['mercurial.demandload'] = sys.modules[__name__] -requiredmodules = {} # Will contain the modules imported by demandload +# Requiredmodules contains the modules imported by demandload. +# Please note that demandload can be invoked before the +# mercurial.packagescan.scan method is invoked in case a mercurial +# module is imported. +requiredmodules = {} def demandload(scope, modules): - """ fake demandload function that collects the required modules """ + """ fake demandload function that collects the required modules + foo import foo + foo bar import foo, bar + foo.bar import foo.bar + foo:bar from foo import bar + foo:bar,quux from foo import bar, quux + foo.bar:quux from foo.bar import quux""" + for m in modules.split(): mod = None try: - module, submodules = m.split(':') - submodules = submodules.split(',') + module, fromlist = m.split(':') + fromlist = fromlist.split(',') except: module = m - submodules = [] - mod = __import__(module, scope, scope, submodules) - scope[module] = mod - requiredmodules[mod.__name__] = 1 + fromlist = [] + mod = __import__(module, scope, scope, fromlist) + if fromlist == []: + # mod is only the top package, but we need all packages + comp = module.split('.') + i = 1 + mn = comp[0] + while True: + # mn and mod.__name__ might not be the same + scope[mn] = mod + requiredmodules[mod.__name__] = 1 + if len(comp) == i: break + mod = getattr(mod,comp[i]) + mn = string.join(comp[:i+1],'.') + i += 1 + else: + # mod is the last package in the component list + requiredmodules[mod.__name__] = 1 + for f in fromlist: + scope[f] = getattr(mod,f) + if type(scope[f]) == types.ModuleType: + requiredmodules[scope[f].__name__] = 1 -def getmodules(libpath,packagename): +def scan(libpath,packagename): """ helper for finding all required modules of package """ # Use the package in the build directory libpath = os.path.abspath(libpath) @@ -45,8 +79,6 @@ def getmodules(libpath,packagename): pymodulefiles = glob.glob('*.py') extmodulefiles = glob.glob('*.pyd') os.chdir(cwd) - # Install a fake demandload module - sys.modules['mercurial.demandload'] = sys.modules['mercurial.packagescan'] # Import all python modules and by that run the fake demandload for m in pymodulefiles: if m == '__init__.py': continue @@ -62,8 +94,9 @@ def getmodules(libpath,packagename): fullname = packagename+'.'+mname __import__(fullname,tmp,tmp) requiredmodules[fullname] = 1 - includes = requiredmodules.keys() - return includes + +def getmodules(): + return requiredmodules.keys() def importfrom(filename): """ diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -94,7 +94,7 @@ def patch(strip, patchname, ui): """apply the patch to the working directory. a list of patched files is returned""" patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch') - fp = os.popen('"%s" -p%d < "%s"' % (patcher, strip, patchname)) + fp = os.popen('%s -p%d < "%s"' % (patcher, strip, patchname)) files = {} for line in fp: line = line.rstrip() diff --git a/mercurial/util_win32.py b/mercurial/util_win32.py --- a/mercurial/util_win32.py +++ b/mercurial/util_win32.py @@ -194,7 +194,7 @@ def user_rcpath(): # We are on win < nt: fetch the APPDATA directory location and use # the parent directory as the user home dir. appdir = shell.SHGetPathFromIDList( - qshell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA)) + shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA)) userdir = os.path.dirname(appdir) return os.path.join(userdir, 'mercurial.ini') diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -13,6 +13,8 @@ import glob from distutils.core import setup, Extension from distutils.command.install_data import install_data +# mercurial.packagescan must be the first mercurial module imported +import mercurial.packagescan import mercurial.version # py2exe needs to be installed to work @@ -36,7 +38,6 @@ try: # Due to the use of demandload py2exe is not finding the modules. # packagescan.getmodules creates a list of modules included in # the mercurial package plus depdent modules. - import mercurial.packagescan from py2exe.build_exe import py2exe as build_exe class py2exe_for_demandload(build_exe): @@ -54,12 +55,10 @@ try: self.includes = [] else: self.includes = self.includes.split(',') - self.includes += mercurial.packagescan.getmodules(self.build_lib, - 'mercurial') - self.includes += mercurial.packagescan.getmodules(self.build_lib, - 'mercurial/hgweb') - self.includes += mercurial.packagescan.getmodules(self.build_lib, - 'hgext') + mercurial.packagescan.scan(self.build_lib,'mercurial') + mercurial.packagescan.scan(self.build_lib,'mercurial/hgweb') + mercurial.packagescan.scan(self.build_lib,'hgext') + self.includes += mercurial.packagescan.getmodules() build_exe.finalize_options(self) except ImportError: py2exe_for_demandload = None diff --git a/tests/test-globalopts b/tests/test-globalopts --- a/tests/test-globalopts +++ b/tests/test-globalopts @@ -62,7 +62,7 @@ echo %% --time hg --cwd a --time tip 2>&1 | grep '^Time:' | sed 's/[0-9][0-9]*/x/g' echo %% --version -hg --version -q | sed 's/version [a-f0-9+]*/version xxx/' +hg --version -q | sed 's/version \([a-f0-9+]*\|unknown\)/version xxx/' echo %% -h/--help hg -h diff --git a/tests/test-tags b/tests/test-tags --- a/tests/test-tags +++ b/tests/test-tags @@ -32,12 +32,31 @@ hg id hg status hg commit -m "merge" -d "1000000 0" + +# create fake head, make sure tag not visible afterwards +cp .hgtags tags +hg tag -d "1000000 0" last +hg rm .hgtags +hg commit -m "remove" -d "1000000 0" + +mv tags .hgtags +hg add .hgtags +hg commit -m "readd" -d "1000000 0" + +hg tags + # invalid tags echo "spam" >> .hgtags echo >> .hgtags echo "foo bar" >> .hgtags echo "$T invalid" | sed "s/..../a5a5/" >> .hg/localtags hg commit -m "tags" -d "1000000 0" + +# report tag parse error on other head +hg up 3 +echo 'x y' >> .hgtags +hg commit -m "head" -d "1000000 0" + hg tags hg tip diff --git a/tests/test-tags.out b/tests/test-tags.out --- a/tests/test-tags.out +++ b/tests/test-tags.out @@ -16,17 +16,26 @@ 1 files updated, 0 files merged, 0 files (branch merge, don't forget to commit) 8216907a933d+8a3ca90d111d+ tip M .hgtags -.hgtags:2: ignoring invalid tag -.hgtags:4: ignoring invalid tag -localtags:1: ignoring invalid tag -tip 4:fd868a874787a7b5af31e1675666ce691c803035 +tip 6:c6af9d771a81bb9c7f267ec03491224a9f8ba1cd first 0:0acdaf8983679e0aac16e811534eb49d7ee1f2b4 -changeset: 4:fd868a874787 -.hgtags:2: ignoring invalid tag -.hgtags:4: ignoring invalid tag -localtags:1: ignoring invalid tag +.hgtags (rev 7:39bba1bbbc4c), line 2: cannot parse entry +.hgtags (rev 7:39bba1bbbc4c), line 4: node 'foo' is not well formed +localtags, line 1: tag 'invalid' refers to unknown node +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +.hgtags (rev 7:39bba1bbbc4c), line 2: cannot parse entry +.hgtags (rev 7:39bba1bbbc4c), line 4: node 'foo' is not well formed +.hgtags (rev 8:4ca6f1b1a68c), line 2: node 'x' is not well formed +localtags, line 1: tag 'invalid' refers to unknown node +tip 8:4ca6f1b1a68c77be687a03aaeb1614671ba59b20 +first 0:0acdaf8983679e0aac16e811534eb49d7ee1f2b4 +changeset: 8:4ca6f1b1a68c +.hgtags (rev 7:39bba1bbbc4c), line 2: cannot parse entry +.hgtags (rev 7:39bba1bbbc4c), line 4: node 'foo' is not well formed +.hgtags (rev 8:4ca6f1b1a68c), line 2: node 'x' is not well formed +localtags, line 1: tag 'invalid' refers to unknown node tag: tip +parent: 3:b2ef3841386b user: test date: Mon Jan 12 13:46:40 1970 +0000 -summary: tags +summary: head