Mercurial > hg-stable
changeset 4108:226df1808f16
merge with crew-stable
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Tue, 20 Feb 2007 20:55:23 +0100 |
parents | e2ed92f4c0f7 (diff) f5f171785e29 (current diff) |
children | c097b6f4f082 20af6a2f0b0e |
files | mercurial/mdiff.py mercurial/patch.py |
diffstat | 90 files changed, 1594 insertions(+), 1467 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Sat Feb 17 09:54:44 2007 +0100 +++ b/Makefile Tue Feb 20 20:55:23 2007 +0100 @@ -67,10 +67,10 @@ TAR_OPTIONS="--owner=root --group=root --mode=u+w,go-w,a+rX-s" $(PYTHON) setup.py -q sdist tests: - cd tests && $(PYTHON) run-tests.py + cd tests && $(PYTHON) run-tests.py $(TESTFLAGS) test-%: - cd tests && $(PYTHON) run-tests.py $@ + cd tests && $(PYTHON) run-tests.py $(TESTFLAGS) $@ .PHONY: help all local build doc clean install install-bin install-doc \
--- a/README Sat Feb 17 09:54:44 2007 +0100 +++ b/README Tue Feb 20 20:55:23 2007 +0100 @@ -1,99 +1,10 @@ -MERCURIAL QUICK-START - -Setting up Mercurial: - - Note: some distributions fails to include bits of distutils by - default, you'll need python-dev to install. You'll also need a C - compiler and a 3-way merge tool like merge, tkdiff, or kdiff3. - - First, unpack the source: - - $ tar xvzf mercurial-<ver>.tar.gz - $ cd mercurial-<ver> - - When installing, change python to python2.3 or python2.4 if 2.2 is the - default on your system. - - To install system-wide: - - $ python setup.py install --force - - To install in your home directory (~/bin and ~/lib, actually), run: - - $ python setup.py install --home=${HOME} --force - $ export PYTHONPATH=${HOME}/lib/python # (or lib64/ on some systems) - $ export PATH=${HOME}/bin:$PATH # add these to your .bashrc - - And finally: - - $ hg debuginstall # run some basic tests - $ hg # show help - - If you get complaints about missing modules, you probably haven't set - PYTHONPATH correctly. - -Setting up a Mercurial project: - - $ hg init project # creates project directory - $ cd project - # copy files in, edit them - $ hg add # add all unknown files - $ hg commit # commit all changes, edit changelog entry - - Mercurial will look for a file named .hgignore in the root of your - repository which contains a set of regular expressions to ignore in - file paths. - -Branching and merging: +Basic install: - $ hg clone project project-work # create a new branch - $ cd project-work - $ <make changes> - $ hg commit - $ cd ../project - $ hg pull ../project-work # pull changesets from project-work - $ hg merge # merge the new tip from project-work into - # our working directory - $ hg commit # commit the result of the merge - -Importing patches: - - Simple: - $ patch < ../p/foo.patch - $ hg commit -A - - Fast: - $ cat ../p/patchlist | xargs hg import -p1 -b ../p - -Exporting a patch: - - (make changes) - $ hg commit - $ hg export tip > foo.patch # export latest change + $ make # see install targets + $ make install # do a system-wide install + $ hg debuginstall # sanity-check setup + $ hg # see help -Network support: - - # pull from the primary Mercurial repo - foo$ hg clone http://selenic.com/hg/ - foo$ cd hg - - # make your current repo available via http://server:8000/ - foo$ hg serve - - # pushing and pulling changes to/from a remote repo with SSH - foo$ hg push ssh://user@example.com/my/repository - foo$ hg pull ssh://user@example.com//home/somebody/his/repository +See http://www.selenic.com/mercurial/ for detailed installation +instructions, platform-specific notes, and Mercurial user information. - # merge changes from a remote machine (e.g. running 'hg serve') - bar$ hg pull http://foo:8000/ - bar$ hg merge # merge changes into your working directory - bar$ hg commit # commit merge in to your local repository - - # Set up a CGI server on your webserver - foo$ cp hgweb.cgi ~/public_html/hg/index.cgi - foo$ emacs ~/public_html/hg/index.cgi # adjust the defaults - -For more info: - - Documentation in doc/ - Mercurial website at http://selenic.com/mercurial
--- a/contrib/bash_completion Sat Feb 17 09:54:44 2007 +0100 +++ b/contrib/bash_completion Tue Feb 20 20:55:23 2007 +0100 @@ -145,6 +145,7 @@ # global options case "$prev" in -R|--repository) + _hg_paths _hg_repos return ;; @@ -477,3 +478,25 @@ { _hg_tags } + + +# transplant +_hg_cmd_transplant() +{ + case "$prev" in + -s|--source) + _hg_paths + _hg_repos + return + ;; + --filter) + # standard filename completion + return + ;; + esac + + # all other transplant options values and command parameters are revisions + _hg_tags + return +} +
--- a/contrib/churn.py Sat Feb 17 09:54:44 2007 +0100 +++ b/contrib/churn.py Tue Feb 20 20:55:23 2007 +0100 @@ -11,10 +11,9 @@ # # <alias email> <actual email> -from mercurial.demandload import * +import sys from mercurial.i18n import gettext as _ -demandload(globals(), 'time sys signal os') -demandload(globals(), 'mercurial:hg,mdiff,fancyopts,cmdutil,ui,util,templater,node') +from mercurial import hg, mdiff, cmdutil, ui, util, templater, node def __gather(ui, repo, node1, node2): def dirtywork(f, mmap1, mmap2):
--- a/contrib/convert-repo Sat Feb 17 09:54:44 2007 +0100 +++ b/contrib/convert-repo Tue Feb 20 20:55:23 2007 +0100 @@ -3,29 +3,49 @@ # This is a generalized framework for converting between SCM # repository formats. # -# In its current form, it's hardcoded to convert incrementally between -# git and Mercurial. -# # To use, run: # -# convert-repo <git-dir> <hg-dir> <mapfile> +# convert-repo <source> [<dest> [<mapfile>]] # -# (don't forget to create the <hg-dir> repository beforehand) +# Currently accepted source formats: git, cvs +# Currently accepted destination formats: hg # -# The <mapfile> is a simple text file that maps a git commit hash to -# the hash in Mercurial for that version, like so: +# If destination isn't given, a new Mercurial repo named <src>-hg will +# be created. If <mapfile> isn't given, it will be put in a default +# location (<dest>/.hg/shamap by default) # -# <git hash> <mercurial hash> +# The <mapfile> is a simple text file that maps each source commit ID to +# the destination ID for that revision, like so: +# +# <source ID> <destination ID> # # If the file doesn't exist, it's automatically created. It's updated # on each commit copied, so convert-repo can be interrupted and can # be run repeatedly to copy new commits. -import sys, os, zlib, sha, time - +import sys, os, zlib, sha, time, re, locale, socket os.environ["HGENCODING"] = "utf-8" +from mercurial import hg, ui, util, fancyopts -from mercurial import hg, ui, util +class Abort(Exception): pass +class NoRepo(Exception): pass + +class commit: + def __init__(self, **parts): + for x in "author date desc parents".split(): + if not x in parts: + abort("commit missing field %s\n" % x) + self.__dict__.update(parts) + +quiet = 0 +def status(msg): + if not quiet: sys.stdout.write(str(msg)) + +def warn(msg): + sys.stderr.write(str(msg)) + +def abort(msg): + raise Abort(msg) def recode(s): try: @@ -36,9 +56,245 @@ except: return s.decode("utf-8", "replace").encode("utf-8") +# CVS conversion code inspired by hg-cvs-import and git-cvsimport +class convert_cvs: + def __init__(self, path): + self.path = path + cvs = os.path.join(path, "CVS") + if not os.path.exists(cvs): + raise NoRepo("couldn't open CVS repo %s" % path) + + self.changeset = {} + self.files = {} + self.tags = {} + self.lastbranch = {} + self.parent = {} + self.socket = None + self.cvsroot = file(os.path.join(cvs, "Root")).read()[:-1] + self.cvsrepo = file(os.path.join(cvs, "Repository")).read()[:-1] + self.encoding = locale.getpreferredencoding() + self._parse() + self._connect() + + def _parse(self): + if self.changeset: + return + + d = os.getcwd() + try: + os.chdir(self.path) + id = None + state = 0 + for l in os.popen("cvsps -A"): + if state == 0: # header + if l.startswith("PatchSet"): + id = l[9:-2] + elif l.startswith("Date"): + date = util.parsedate(l[6:-1], ["%Y/%m/%d %H:%M:%S"]) + date = util.datestr(date) + elif l.startswith("Branch"): + branch = l[8:-1] + self.parent[id] = self.lastbranch.get(branch,'bad') + self.lastbranch[branch] = id + elif l.startswith("Ancestor branch"): + ancestor = l[17:-1] + self.parent[id] = self.lastbranch[ancestor] + elif l.startswith("Author"): + author = self.recode(l[8:-1]) + elif l.startswith("Tag: "): + t = l[5:-1] + if t != "(none) ": + self.tags[t] = id + elif l.startswith("Log:"): + state = 1 + log = "" + elif state == 1: # log + if l == "Members: \n": + files = {} + log = self.recode(log[:-1]) + if log.isspace(): + log = "*** empty log message ***\n" + state = 2 + else: + log += l + elif state == 2: + if l == "\n": # + state = 0 + p = [self.parent[id]] + if id == "1": + p = [] + c = commit(author=author, date=date, parents=p, + desc=log, branch=branch) + self.changeset[id] = c + self.files[id] = files + else: + file,rev = l[1:-2].rsplit(':',1) + rev = rev.split("->")[1] + files[file] = rev + + self.heads = self.lastbranch.values() + finally: + os.chdir(d) + + def _connect(self): + root = self.cvsroot + conntype = None + user, host = None, None + cmd = ['cvs', 'server'] + + status("connecting to %s\n" % root) + + if root.startswith(":pserver:"): + root = root[9:] + m = re.match(r'(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?(.*)', root) + if m: + conntype = "pserver" + user, passw, serv, port, root = m.groups() + if not user: + user = "anonymous" + rr = ":pserver:" + user + "@" + serv + ":" + root + if port: + rr2, port = "-", int(port) + else: + rr2, port = rr, 2401 + rr += str(port) + + if not passw: + passw = "A" + pf = open(os.path.join(os.environ["HOME"], ".cvspass")) + for l in pf: + # :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z + m = re.match(r'(/\d+\s+/)?(.*)', l) + l = m.group(2) + w, p = l.split(' ', 1) + if w in [rr, rr2]: + passw = p + break + pf.close() + + sck = socket.socket() + sck.connect((serv, port)) + sck.send("\n".join(["BEGIN AUTH REQUEST", root, user, passw, "END AUTH REQUEST", ""])) + if sck.recv(128) != "I LOVE YOU\n": + raise NoRepo("CVS pserver authentication failed") + + self.writep = self.readp = sck.makefile('r+') + + if not conntype and root.startswith(":local:"): + conntype = "local" + root = root[7:] + + if not conntype: + # :ext:user@host/home/user/path/to/cvsroot + if root.startswith(":ext:"): + root = root[5:] + m = re.match(r'(?:([^@:/]+)@)?([^:/]+):?(.*)', root) + if not m: + conntype = "local" + else: + conntype = "rsh" + user, host, root = m.group(1), m.group(2), m.group(3) + + if conntype != "pserver": + if conntype == "rsh": + rsh = os.environ.get("CVS_RSH" or "rsh") + if user: + cmd = [rsh, '-l', user, host] + cmd + else: + cmd = [rsh, host] + cmd + + self.writep, self.readp = os.popen2(cmd) + + self.realroot = root + + self.writep.write("Root %s\n" % root) + self.writep.write("Valid-responses ok error Valid-requests Mode" + " M Mbinary E Checked-in Created Updated" + " Merged Removed\n") + self.writep.write("valid-requests\n") + self.writep.flush() + r = self.readp.readline() + if not r.startswith("Valid-requests"): + abort("server sucks\n") + if "UseUnchanged" in r: + self.writep.write("UseUnchanged\n") + self.writep.flush() + r = self.readp.readline() + + def getheads(self): + return self.heads + + def _getfile(self, name, rev): + if rev.endswith("(DEAD)"): + raise IOError + + args = ("-N -P -kk -r %s --" % rev).split() + args.append(os.path.join(self.cvsrepo, name)) + for x in args: + self.writep.write("Argument %s\n" % x) + self.writep.write("Directory .\n%s\nco\n" % self.realroot) + self.writep.flush() + + data = "" + while 1: + line = self.readp.readline() + if line.startswith("Created ") or line.startswith("Updated "): + self.readp.readline() # path + self.readp.readline() # entries + mode = self.readp.readline()[:-1] + count = int(self.readp.readline()[:-1]) + data = self.readp.read(count) + elif line.startswith(" "): + data += line[1:] + elif line.startswith("M "): + pass + elif line.startswith("Mbinary "): + count = int(self.readp.readline()[:-1]) + data = self.readp.read(count) + else: + if line == "ok\n": + return (data, "x" in mode and "x" or "") + elif line.startswith("E "): + warn("cvs server: %s\n" % line[2:]) + elif line.startswith("Remove"): + l = self.readp.readline() + l = self.readp.readline() + if l != "ok\n": + abort("unknown CVS response: %s\n" % l) + else: + abort("unknown CVS response: %s\n" % line) + + def getfile(self, file, rev): + data, mode = self._getfile(file, rev) + self.modecache[(file, rev)] = mode + return data + + def getmode(self, file, rev): + return self.modecache[(file, rev)] + + def getchanges(self, rev): + self.modecache = {} + files = self.files[rev] + cl = files.items() + cl.sort() + return cl + + def recode(self, text): + return text.decode(self.encoding, "replace").encode("utf-8") + + def getcommit(self, rev): + return self.changeset[rev] + + def gettags(self): + return self.tags + class convert_git: def __init__(self, path): + if os.path.isdir(path + "/.git"): + path += "/.git" self.path = path + if not os.path.exists(path + "/HEAD"): + raise NoRepo("couldn't open GIT repo %s" % path) def getheads(self): fh = os.popen("GIT_DIR=%s git-rev-parse --verify HEAD" % self.path) @@ -52,7 +308,11 @@ def getfile(self, name, rev): return self.catfile(rev, "blob") + def getmode(self, name, rev): + return self.modecache[(name, rev)] + def getchanges(self, version): + self.modecache = {} fh = os.popen("GIT_DIR=%s git-diff-tree --root -m -r %s" % (self.path, version)) changes = [] for l in fh: @@ -61,7 +321,9 @@ m = m.split() h = m[3] p = (m[1] == "100755") - changes.append((f, h, p)) + s = (m[1] == "120000") + self.modecache[(f, h)] = (p and "x") or (s and "l") or "" + changes.append((f, h)) return changes def getcommit(self, version): @@ -92,37 +354,47 @@ tzs, tzh, tzm = tz[-5:-4] + "1", tz[-4:-2], tz[-2:] tz = -int(tzs) * (int(tzh) * 3600 + int(tzm)) date = tm + " " + str(tz) - return (parents, author, date, message) + + c = commit(parents=parents, date=date, author=author, desc=message) + return c def gettags(self): tags = {} - for f in os.listdir(self.path + "/refs/tags"): - try: - h = file(self.path + "/refs/tags/" + f).read().strip() - c = self.catfile(h, "tag") # read the commit hash - h = c.splitlines()[0].split()[1] - tags[f] = h - except: - pass + fh = os.popen('git-ls-remote --tags "%s" 2>/dev/null' % self.path) + prefix = 'refs/tags/' + for line in fh: + line = line.strip() + if not line.endswith("^{}"): + continue + node, tag = line.split(None, 1) + if not tag.startswith(prefix): + continue + tag = tag[len(prefix):-3] + tags[tag] = node + return tags class convert_mercurial: def __init__(self, path): self.path = path u = ui.ui() - self.repo = hg.repository(u, path) + try: + self.repo = hg.repository(u, path) + except: + raise NoRepo("could open hg repo %s" % path) + + def mapfile(self): + return os.path.join(self.path, ".hg", "shamap") def getheads(self): h = self.repo.changelog.heads() return [ hg.hex(x) for x in h ] def putfile(self, f, e, data): - self.repo.wfile(f, "w").write(data) + self.repo.wwrite(f, data, e) if self.repo.dirstate.state(f) == '?': self.repo.dirstate.update([f], "a") - util.set_exec(self.repo.wjoin(f), e) - def delfile(self, f): try: os.unlink(self.repo.wjoin(f)) @@ -130,7 +402,7 @@ except: pass - def putcommit(self, files, parents, author, dest, text): + def putcommit(self, files, parents, commit): seen = {} pl = [] for p in parents: @@ -143,11 +415,18 @@ if len(parents) < 2: parents.append("0" * 40) p2 = parents.pop(0) + text = commit.desc + extra = {} + try: + extra["branch"] = commit.branch + except AttributeError: + pass + while parents: p1 = p2 p2 = parents.pop(0) - self.repo.rawcommit(files, text, author, dest, - hg.bin(p1), hg.bin(p2)) + a = self.repo.rawcommit(files, text, commit.author, commit.date, + hg.bin(p1), hg.bin(p2), extra=extra) text = "(octopus merge fixup)\n" p2 = hg.hex(self.repo.changelog.tip()) @@ -170,7 +449,7 @@ newlines.sort() if newlines != oldlines: - #print "updating tags" + status("updating tags\n") f = self.repo.wfile(".hgtags", "w") f.write("".join(newlines)) f.close() @@ -180,11 +459,25 @@ date, self.repo.changelog.tip(), hg.nullid) return hg.hex(self.repo.changelog.tip()) +converters = [convert_cvs, convert_git, convert_mercurial] + +def converter(path): + if not os.path.isdir(path): + abort("%s: not a directory\n" % path) + for c in converters: + try: + return c(path) + except NoRepo: + pass + abort("%s: unknown repository type\n" % path) + class convert: - def __init__(self, source, dest, mapfile): + def __init__(self, source, dest, mapfile, opts): + self.source = source self.dest = dest self.mapfile = mapfile + self.opts = opts self.commitcache = {} self.map = {} @@ -204,7 +497,7 @@ if n in known or n in self.map: continue known[n] = 1 self.commitcache[n] = self.source.getcommit(n) - cp = self.commitcache[n][0] + cp = self.commitcache[n].parents for p in cp: parents.setdefault(n, []).append(p) visit.append(p) @@ -247,42 +540,60 @@ if not dep: # all n's parents are in the list removed[n] = 1 - s.append(n) + if n not in self.map: + s.append(n) if n in children: for c in children[n]: visit.insert(0, c) + if opts.get('datesort'): + depth = {} + for n in s: + depth[n] = 0 + pl = [p for p in self.commitcache[n].parents if p not in self.map] + if pl: + depth[n] = max([depth[p] for p in pl]) + 1 + + s = [(depth[n], self.commitcache[n].date, n) for n in s] + s.sort() + s = [e[2] for e in s] + return s def copy(self, rev): - p, a, d, t = self.commitcache[rev] + c = self.commitcache[rev] files = self.source.getchanges(rev) - for f,v,e in files: + for f,v in files: try: data = self.source.getfile(f, v) except IOError, inst: self.dest.delfile(f) else: + e = self.source.getmode(f, v) self.dest.putfile(f, e, data) - r = [self.map[v] for v in p] - f = [f for f,v,e in files] - self.map[rev] = self.dest.putcommit(f, r, a, d, t) + r = [self.map[v] for v in c.parents] + f = [f for f,v in files] + self.map[rev] = self.dest.putcommit(f, r, c) file(self.mapfile, "a").write("%s %s\n" % (rev, self.map[rev])) def convert(self): + status("scanning source...\n") heads = self.source.getheads() parents = self.walktree(heads) + status("sorting...\n") t = self.toposort(parents) - t = [n for n in t if n not in self.map] num = len(t) c = None + status("converting...\n") for c in t: num -= 1 - desc = self.commitcache[c][3].splitlines()[0] - #print num, desc + desc = self.commitcache[c].desc + if "\n" in desc: + desc = desc.splitlines()[0] + status("%d %s\n" % (num, desc)) self.copy(c) tags = self.source.gettags() @@ -299,9 +610,41 @@ if nrev: file(self.mapfile, "a").write("%s %s\n" % (c, nrev)) -gitpath, hgpath, mapfile = sys.argv[1:] -if os.path.isdir(gitpath + "/.git"): - gitpath += "/.git" +def command(src, dest=None, mapfile=None, **opts): + srcc = converter(src) + if not hasattr(srcc, "getcommit"): + abort("%s: can't read from this repo type\n" % src) + + if not dest: + dest = src + "-hg" + status("assuming destination %s\n" % dest) + if not os.path.isdir(dest): + status("creating repository %s\n" % dest) + os.system("hg init " + dest) + destc = converter(dest) + if not hasattr(destc, "putcommit"): + abort("%s: can't write to this repo type\n" % src) -c = convert(convert_git(gitpath), convert_mercurial(hgpath), mapfile) -c.convert() + if not mapfile: + try: + mapfile = destc.mapfile() + except: + mapfile = os.path.join(destc, "map") + + c = convert(srcc, destc, mapfile, opts) + c.convert() + +options = [('q', 'quiet', None, 'suppress output'), + ('', 'datesort', None, 'try to sort changesets by date')] +opts = {} +args = fancyopts.fancyopts(sys.argv[1:], options, opts) + +if opts['quiet']: + quiet = 1 + +try: + command(*args, **opts) +except Abort, inst: + warn(inst) +except KeyboardInterrupt: + status("interrupted\n")
--- a/contrib/hgk Sat Feb 17 09:54:44 2007 +0100 +++ b/contrib/hgk Tue Feb 20 20:55:23 2007 +0100 @@ -43,7 +43,9 @@ } if [catch { set parse_args [concat --default HEAD $revargs] - set parsed_args [split [eval exec hg debug-rev-parse $parse_args] "\n"] + set parse_temp [eval exec hg debug-rev-parse $parse_args] + regsub -all "\r\n" $parse_temp "\n" parse_temp + set parsed_args [split $parse_temp "\n"] } err] { # if git-rev-parse failed for some reason... if {$rargs == {}} { @@ -108,6 +110,7 @@ set leftover {} } set start [expr {$i + 1}] + regsub -all "\r\n" $cmit "\n" cmit set j [string first "\n" $cmit] set ok 0 if {$j >= 0} { @@ -209,6 +212,7 @@ incr ncleft($p) } } + regsub -all "\r\n" $contents "\n" contents foreach line [split $contents "\n"] { if {$inhdr} { set line [split $line] @@ -257,7 +261,8 @@ global tagids idtags headids idheads tagcontents set tags [exec hg tags] - set lines [split $tags '\n'] + regsub -all "\r\n" $tags "\n" tags + set lines [split $tags "\n"] foreach f $lines { set f [regexp -all -inline {\S+} $f] set direct [lindex $f 0] @@ -2856,6 +2861,7 @@ if {$ids != $diffids || $bdf != $blobdifffd($ids)} { return } + regsub -all "\r" $line "" line $ctext conf -state normal if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} { # start of a new file @@ -2914,7 +2920,7 @@ } elseif {$diffinhdr || $x == "\\"} { # e.g. "\ No newline at end of file" $ctext insert end "$line\n" filesep - } else { + } elseif {$line != ""} { # Something else we don't recognize if {$curdifftag != "Comments"} { $ctext insert end "\n"
--- a/doc/Makefile Sat Feb 17 09:54:44 2007 +0100 +++ b/doc/Makefile Tue Feb 20 20:55:23 2007 +0100 @@ -2,7 +2,7 @@ MAN=$(SOURCES:%.txt=%) HTML=$(SOURCES:%.txt=%.html) PREFIX=/usr/local -MANDIR=$(PREFIX)/man +MANDIR=$(PREFIX)/share/man INSTALL=install -c all: man html @@ -36,8 +36,8 @@ install: man for i in $(MAN) ; do \ subdir=`echo $$i | sed -n 's/..*\.\([0-9]\)$$/man\1/p'` ; \ - mkdir -p $(MANDIR)/$$subdir ; \ - $(INSTALL) $$i $(MANDIR)/$$subdir ; \ + mkdir -p $(DESTDIR)/$(MANDIR)/$$subdir ; \ + $(INSTALL) $$i $(DESTDIR)/$(MANDIR)/$$subdir ; \ done clean:
--- a/doc/hgrc.5.txt Sat Feb 17 09:54:44 2007 +0100 +++ b/doc/hgrc.5.txt Tue Feb 20 20:55:23 2007 +0100 @@ -507,6 +507,11 @@ push_ssl;; Whether to require that inbound pushes be transported over SSL to prevent password sniffing. Default is true. + staticurl;; + Base URL to use for static files. If unset, static files (e.g. + the hgicon.png favicon) will be served by the CGI script itself. + Use this setting to serve them directly with the HTTP server. + Example: "http://hgserver/static/" stripes;; How many lines a "zebra stripe" should span in multiline output. Default is 1; set to 0 to disable.
--- a/hg Sat Feb 17 09:54:44 2007 +0100 +++ b/hg Tue Feb 20 20:55:23 2007 +0100 @@ -7,6 +7,5 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from mercurial import commands - -commands.run() +import mercurial.commands +mercurial.commands.run()
--- a/hgext/acl.py Sat Feb 17 09:54:44 2007 +0100 +++ b/hgext/acl.py Tue Feb 20 20:55:23 2007 +0100 @@ -45,10 +45,10 @@ # glob pattern = user4, user5 # ** = user6 -from mercurial.demandload import * -from mercurial.i18n import gettext as _ +from mercurial.i18n import _ from mercurial.node import * -demandload(globals(), 'getpass mercurial:util') +from mercurial import util +import getpass class checker(object): '''acl checker.''' @@ -91,7 +91,7 @@ def check(self, node): '''return if access allowed, raise exception if not.''' - files = self.repo.changelog.read(node)[3] + files = self.repo.changectx(node).files() if self.deniable: for f in files: if self.deny(f):
--- a/hgext/bugzilla.py Sat Feb 17 09:54:44 2007 +0100 +++ b/hgext/bugzilla.py Tue Feb 20 20:55:23 2007 +0100 @@ -52,10 +52,10 @@ # [usermap] # committer_email = bugzilla_user_name -from mercurial.demandload import * -from mercurial.i18n import gettext as _ +from mercurial.i18n import _ from mercurial.node import * -demandload(globals(), 'mercurial:cmdutil,templater,util os re time') +from mercurial import cmdutil, templater, util +import os, re, time MySQLdb = None @@ -222,7 +222,7 @@ _bug_re = None _split_re = None - def find_bug_ids(self, node, desc): + def find_bug_ids(self, ctx): '''find valid bug ids that are referred to in changeset comments and that do not already have references to this changeset.''' @@ -235,7 +235,7 @@ start = 0 ids = {} while True: - m = bugzilla._bug_re.search(desc, start) + m = bugzilla._bug_re.search(ctx.description(), start) if not m: break start = m.end() @@ -246,10 +246,10 @@ if ids: ids = self.filter_real_bug_ids(ids) if ids: - ids = self.filter_unknown_bug_ids(node, ids) + ids = self.filter_unknown_bug_ids(ctx.node(), ids) return ids - def update(self, bugid, node, changes): + def update(self, bugid, ctx): '''update bugzilla bug with reference to changeset.''' def webroot(root): @@ -268,7 +268,7 @@ mapfile = self.ui.config('bugzilla', 'style') tmpl = self.ui.config('bugzilla', 'template') t = cmdutil.changeset_templater(self.ui, self.repo, - False, None, mapfile, False) + False, mapfile, False) if not mapfile and not tmpl: tmpl = _('changeset {node|short} in repo {root} refers ' 'to bug {bug}.\ndetails:\n\t{desc|tabindent}') @@ -276,13 +276,13 @@ tmpl = templater.parsestring(tmpl, quoted=False) t.use_template(tmpl) self.ui.pushbuffer() - t.show(changenode=node, changes=changes, + t.show(changenode=ctx.node(), changes=ctx.changeset(), bug=str(bugid), hgweb=self.ui.config('web', 'baseurl'), root=self.repo.root, webroot=webroot(self.repo.root)) data = self.ui.popbuffer() - self.add_comment(bugid, data, templater.email(changes[1])) + self.add_comment(bugid, data, templater.email(ctx.user())) def hook(ui, repo, hooktype, node=None, **kwargs): '''add comment to bugzilla for each changeset that refers to a @@ -300,12 +300,11 @@ hooktype) try: bz = bugzilla(ui, repo) - bin_node = bin(node) - changes = repo.changelog.read(bin_node) - ids = bz.find_bug_ids(bin_node, changes[4]) + ctx = repo.changctx(node) + ids = bz.find_bug_ids(ctx) if ids: for id in ids: - bz.update(id, bin_node, changes) + bz.update(id, ctx) bz.notify(ids) except MySQLdb.MySQLError, err: raise util.Abort(_('database error: %s') % err[1])
--- a/hgext/extdiff.py Sat Feb 17 09:54:44 2007 +0100 +++ b/hgext/extdiff.py Tue Feb 20 20:55:23 2007 +0100 @@ -48,16 +48,15 @@ # needed files, so running the external diff program will actually be # pretty fast (at least faster than having to compare the entire tree). -from mercurial.demandload import demandload -from mercurial.i18n import gettext as _ +from mercurial.i18n import _ from mercurial.node import * -demandload(globals(), 'mercurial:cmdutil,util os shutil tempfile') +from mercurial import cmdutil, util +import os, shutil, tempfile def dodiff(ui, repo, diffcmd, diffopts, pats, opts): def snapshot_node(files, node): '''snapshot files as of some revision''' - changes = repo.changelog.read(node) - mf = repo.manifest.read(changes[0]) + mf = repo.changectx(node).manifest() dirname = os.path.basename(repo.root) if dirname == "": dirname = "root" @@ -77,7 +76,8 @@ destdir = os.path.dirname(dest) if not os.path.isdir(destdir): os.makedirs(destdir) - repo.wwrite(wfn, repo.file(fn).read(mf[fn]), open(dest, 'wb')) + data = repo.wwritedata(wfn, repo.file(wfn).read(mf[wfn])) + open(dest, 'wb').write(data) return dirname def snapshot_wdir(files):
--- a/hgext/fetch.py Sat Feb 17 09:54:44 2007 +0100 +++ b/hgext/fetch.py Tue Feb 20 20:55:23 2007 +0100 @@ -5,10 +5,9 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from mercurial.demandload import * -from mercurial.i18n import gettext as _ +from mercurial.i18n import _ from mercurial.node import * -demandload(globals(), 'mercurial:commands,hg,node,util') +from mercurial import commands, hg, node, util def fetch(ui, repo, source='default', **opts): '''Pull changes from a remote repository, merge new changes if needed.
--- a/hgext/gpg.py Sat Feb 17 09:54:44 2007 +0100 +++ b/hgext/gpg.py Tue Feb 20 20:55:23 2007 +0100 @@ -8,7 +8,7 @@ import os, tempfile, binascii from mercurial import util from mercurial import node as hgnode -from mercurial.i18n import gettext as _ +from mercurial.i18n import _ class gpg: def __init__(self, path, key=None):
--- a/hgext/hbisect.py Sat Feb 17 09:54:44 2007 +0100 +++ b/hgext/hbisect.py Tue Feb 20 20:55:23 2007 +0100 @@ -6,9 +6,9 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from mercurial.i18n import gettext as _ -from mercurial.demandload import demandload -demandload(globals(), "os sys sets mercurial:hg,util,commands,cmdutil") +from mercurial.i18n import _ +from mercurial import hg, util, commands, cmdutil +import os, sys, sets versionstr = "0.0.3"
--- a/hgext/hgk.py Sat Feb 17 09:54:44 2007 +0100 +++ b/hgext/hgk.py Tue Feb 20 20:55:23 2007 +0100 @@ -5,26 +5,18 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from mercurial.demandload import * -demandload(globals(), 'time sys signal os') -demandload(globals(), 'mercurial:hg,fancyopts,commands,ui,util,patch,revlog') +import sys, os +from mercurial import hg, fancyopts, commands, ui, util, patch, revlog def difftree(ui, repo, node1=None, node2=None, *files, **opts): """diff trees from two commits""" def __difftree(repo, node1, node2, files=[]): - if node2: - change = repo.changelog.read(node2) - mmap2 = repo.manifest.read(change[0]) - status = repo.status(node1, node2, files=files)[:5] - modified, added, removed, deleted, unknown = status - else: - status = repo.status(node1, files=files)[:5] - modified, added, removed, deleted, unknown = status - if not node1: - node1 = repo.dirstate.parents()[0] + assert node2 is not None + mmap = repo.changectx(node1).manifest() + mmap2 = repo.changectx(node2).manifest() + status = repo.status(node1, node2, files=files)[:5] + modified, added, removed, deleted, unknown = status - change = repo.changelog.read(node1) - mmap = repo.manifest.read(change[0]) empty = hg.short(hg.nullid) for f in modified: @@ -70,32 +62,30 @@ if not opts['stdin']: break -def catcommit(repo, n, prefix, changes=None): +def catcommit(repo, n, prefix, ctx=None): nlprefix = '\n' + prefix; - (p1, p2) = repo.changelog.parents(n) - (h, h1, h2) = map(hg.short, (n, p1, p2)) - (i1, i2) = map(repo.changelog.rev, (p1, p2)) - if not changes: - changes = repo.changelog.read(n) - print "tree %s" % (hg.short(changes[0])) - if i1 != hg.nullrev: print "parent %s" % (h1) - if i2 != hg.nullrev: print "parent %s" % (h2) - date_ar = changes[2] - date = int(float(date_ar[0])) - lines = changes[4].splitlines() + if ctx is None: + ctx = repo.changectx(n) + (p1, p2) = ctx.parents() + print "tree %s" % (hg.short(ctx.changeset()[0])) # use ctx.node() instead ?? + if p1: print "parent %s" % (hg.short(p1.node())) + if p2: print "parent %s" % (hg.short(p2.node())) + date = ctx.date() + description = ctx.description().replace("\0", "") + lines = description.splitlines() if lines and lines[-1].startswith('committer:'): committer = lines[-1].split(': ')[1].rstrip() else: - committer = changes[1] + committer = ctx.user() - print "author %s %s %s" % (changes[1], date, date_ar[1]) - print "committer %s %s %s" % (committer, date, date_ar[1]) - print "revision %d" % repo.changelog.rev(n) + print "author %s %s %s" % (ctx.user(), int(date[0]), date[1]) + print "committer %s %s %s" % (committer, int(date[0]), date[1]) + print "revision %d" % ctx.rev() print "" if prefix != "": - print "%s%s" % (prefix, changes[4].replace('\n', nlprefix).strip()) + print "%s%s" % (prefix, description.replace('\n', nlprefix).strip()) else: - print changes[4] + print description if prefix: sys.stdout.write('\0') @@ -146,8 +136,7 @@ # you can specify a commit to stop at by starting the sha1 with ^ def revtree(args, repo, full="tree", maxnr=0, parents=False): def chlogwalk(): - ch = repo.changelog - count = ch.count() + count = repo.changelog.count() i = count l = [0] * 100 chunk = 100 @@ -163,7 +152,8 @@ l[chunk - x:] = [0] * (chunk - x) break if full != None: - l[x] = ch.read(ch.node(i + x)) + l[x] = repo.changectx(i + x) + l[x].changeset() # force reading else: l[x] = 1 for x in xrange(chunk-1, -1, -1): @@ -217,7 +207,7 @@ # walk the repository looking for commits that are in our # reachability graph - for i, changes in chlogwalk(): + for i, ctx in chlogwalk(): n = repo.changelog.node(i) mask = is_reachable(want_sha1, reachable, n) if mask: @@ -232,13 +222,13 @@ print hg.short(n) + parentstr elif full == "commit": print hg.short(n) + parentstr - catcommit(repo, n, ' ', changes) + catcommit(repo, n, ' ', ctx) else: (p1, p2) = repo.changelog.parents(n) (h, h1, h2) = map(hg.short, (n, p1, p2)) (i1, i2) = map(repo.changelog.rev, (p1, p2)) - date = changes[2][0] + date = ctx.date()[0] print "%s %s:%s" % (date, h, mask), mask = is_reachable(want_sha1, reachable, p1) if i1 != hg.nullrev and mask > 0:
--- a/hgext/mq.py Sat Feb 17 09:54:44 2007 +0100 +++ b/hgext/mq.py Tue Feb 20 20:55:23 2007 +0100 @@ -29,14 +29,16 @@ refresh contents of top applied patch qrefresh ''' -from mercurial.demandload import * -from mercurial.i18n import gettext as _ -from mercurial import commands -demandload(globals(), "os sys re struct traceback errno bz2") -demandload(globals(), "mercurial:cmdutil,hg,patch,revlog,util,changegroup") +from mercurial.i18n import _ +from mercurial import commands, cmdutil, hg, patch, revlog, util, changegroup +import os, sys, re, errno commands.norepo += " qclone qversion" +# Patch names looks like unix-file names. +# They must be joinable with queue directory and result in the patch path. +normname = util.normpath + class statusentry: def __init__(self, rev, name=None): if not name: @@ -328,11 +330,12 @@ hg.clean(repo, head, wlock=wlock) self.strip(repo, n, update=False, backup='strip', wlock=wlock) - c = repo.changelog.read(rev) + ctx = repo.changectx(rev) ret = hg.merge(repo, rev, wlock=wlock) if ret: raise util.Abort(_("update returned %d") % ret) - n = repo.commit(None, c[4], c[1], force=1, wlock=wlock) + n = repo.commit(None, ctx.description(), ctx.user(), + force=1, wlock=wlock) if n == None: raise util.Abort(_("repo commit failed")) try: @@ -614,15 +617,12 @@ self.ui.warn("saving bundle to %s\n" % name) return changegroup.writebundle(cg, name, "HG10BZ") - def stripall(rev, revnum): - cl = repo.changelog - c = cl.read(rev) - mm = repo.manifest.read(c[0]) + def stripall(revnum): + mm = repo.changectx(rev).manifest() seen = {} - for x in xrange(revnum, cl.count()): - c = cl.read(cl.node(x)) - for f in c[3]: + for x in xrange(revnum, repo.changelog.count()): + for f in repo.changectx(x).files(): if f in seen: continue seen[f] = 1 @@ -704,7 +704,7 @@ backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip') chgrpfile = bundle(backupch) - stripall(rev, revnum) + stripall(revnum) change = chlog.read(rev) chlog.strip(revnum, revnum) @@ -802,10 +802,29 @@ if not wlock: wlock = repo.wlock() patch = self.lookup(patch) - if patch and self.isapplied(patch): - raise util.Abort(_("patch %s is already applied") % patch) + # Suppose our series file is: A B C and the current 'top' patch is B. + # qpush C should be performed (moving forward) + # qpush B is a NOP (no change) + # qpush A is an error (can't go backwards with qpush) + if patch: + info = self.isapplied(patch) + if info: + if info[0] < len(self.applied) - 1: + raise util.Abort(_("cannot push to a previous patch: %s") % + patch) + if info[0] < len(self.series) - 1: + self.ui.warn(_('qpush: %s is already at the top\n') % patch) + else: + self.ui.warn(_('all patches are currently applied\n')) + return + + # Following the above example, starting at 'top' of B: + # qpush should be performed (pushes C), but a subsequent qpush without + # an argument is an error (nothing to apply). This allows a loop + # of "...while hg qpush..." to work as it detects an error when done if self.series_end() == len(self.series): - raise util.Abort(_("patch series fully applied")) + self.ui.warn(_('patch series already fully applied\n')) + return 1 if not force: self.check_localchanges(repo) @@ -835,14 +854,7 @@ wlock=None): def getfile(f, rev): t = repo.file(f).read(rev) - try: - repo.wfile(f, "w").write(t) - except IOError: - try: - os.makedirs(os.path.dirname(repo.wjoin(f))) - except OSError, err: - if err.errno != errno.EEXIST: raise - repo.wfile(f, "w").write(t) + repo.wfile(f, "w").write(t) if not wlock: wlock = repo.wlock() @@ -854,8 +866,12 @@ info = self.isapplied(patch) if not info: raise util.Abort(_("patch %s is not applied") % patch) + if len(self.applied) == 0: - raise util.Abort(_("no patches applied")) + # Allow qpop -a to work repeatedly, + # but not qpop without an argument + self.ui.warn(_("no patches applied\n")) + return not all if not update: parents = repo.dirstate.parents() @@ -1089,9 +1105,13 @@ self.push(repo, force=True, wlock=wlock) def init(self, repo, create=False): - if os.path.isdir(self.path): + if not create and os.path.isdir(self.path): raise util.Abort(_("patch queue directory already exists")) - os.mkdir(self.path) + try: + os.mkdir(self.path) + except OSError, inst: + if inst.errno != errno.EEXIST or not create: + raise if create: return self.qrepo(create=True) @@ -1347,7 +1367,7 @@ lastparent = p1 if not patchname: - patchname = '%d.diff' % r + patchname = normname('%d.diff' % r) checkseries(patchname) checkfile(patchname) self.full_series.insert(0, patchname) @@ -1369,7 +1389,7 @@ if filename == '-': raise util.Abort(_('-e is incompatible with import from -')) if not patchname: - patchname = filename + patchname = normname(filename) if not os.path.isfile(self.join(patchname)): raise util.Abort(_("patch %s does not exist") % patchname) else: @@ -1383,7 +1403,7 @@ except IOError: raise util.Abort(_("unable to read %s") % patchname) if not patchname: - patchname = os.path.basename(filename) + patchname = normname(os.path.basename(filename)) checkfile(patchname) patchf = self.opener(patchname, "w") patchf.write(text) @@ -1474,13 +1494,16 @@ r = q.init(repo, create=opts['create_repo']) q.save_dirty() if r: - fp = r.wopener('.hgignore', 'w') - print >> fp, 'syntax: glob' - print >> fp, 'status' - print >> fp, 'guards' - fp.close() - r.wopener('series', 'w').close() + if not os.path.exists(r.wjoin('.hgignore')): + fp = r.wopener('.hgignore', 'w') + fp.write('syntax: glob\n') + fp.write('status\n') + fp.write('guards\n') + fp.close() + if not os.path.exists(r.wjoin('series')): + r.wopener('series', 'w').close() r.add(['.hgignore', 'series']) + commands.add(ui, r) return 0 def clone(ui, source, dest=None, **opts): @@ -1595,6 +1618,9 @@ If any file patterns are provided, the refreshed patch will contain only the modifications that match those patterns; the remaining modifications will remain in the working directory. + + hg add/remove/copy/rename work as usual, though you might want to use + git-style patches (--git or [diff] git=1) to track copies and renames. """ q = repo.mq message = commands.logmessage(opts) @@ -1763,7 +1789,8 @@ if opts['all']: if not q.series: - raise util.Abort(_('no patches in series')) + ui.warn(_('no patches in series\n')) + return 0 patch = q.series[-1] if opts['merge']: if opts['name']: @@ -1789,9 +1816,10 @@ localupdate = False else: q = repo.mq - q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all']) + ret = q.pop(repo, patch, force=opts['force'], update=localupdate, + all=opts['all']) q.save_dirty() - return 0 + return ret def rename(ui, repo, patch, name=None, **opts): """rename a patch @@ -1814,7 +1842,7 @@ patch = q.lookup('qtip') absdest = q.join(name) if os.path.isdir(absdest): - name = os.path.join(name, os.path.basename(patch)) + name = normname(os.path.join(name, os.path.basename(patch))) absdest = q.join(name) if os.path.exists(absdest): raise util.Abort(_('%s already exists') % absdest) @@ -2019,7 +2047,7 @@ return super(mqrepo, self).commit(*args, **opts) def push(self, remote, force=False, revs=None): - if self.mq.applied and not force: + if self.mq.applied and not force and not revs: raise util.Abort(_('source has mq patches applied')) return super(mqrepo, self).push(remote, force, revs)
--- a/hgext/notify.py Sat Feb 17 09:54:44 2007 +0100 +++ b/hgext/notify.py Tue Feb 20 20:55:23 2007 +0100 @@ -65,11 +65,10 @@ # if you like, you can put notify config file in repo that users can # push changes to, they can manage their own subscriptions. -from mercurial.demandload import * -from mercurial.i18n import gettext as _ +from mercurial.i18n import _ from mercurial.node import * -demandload(globals(), 'mercurial:patch,cmdutil,templater,util,mail') -demandload(globals(), 'email.Parser fnmatch socket time') +from mercurial import patch, cmdutil, templater, util, mail +import email.Parser, fnmatch, socket, time # template for single changeset can include email headers. single_template = ''' @@ -113,7 +112,7 @@ template = (self.ui.config('notify', hooktype) or self.ui.config('notify', 'template')) self.t = cmdutil.changeset_templater(self.ui, self.repo, - False, None, mapfile, False) + False, mapfile, False) if not mapfile and not template: template = deftemplates.get(hooktype) or single_template if template:
--- a/hgext/patchbomb.py Sat Feb 17 09:54:44 2007 +0100 +++ b/hgext/patchbomb.py Tue Feb 20 20:55:23 2007 +0100 @@ -63,11 +63,10 @@ # # That should be all. Now your patchbomb is on its way out. -from mercurial.demandload import * -demandload(globals(), '''email.MIMEMultipart email.MIMEText email.Utils - mercurial:cmdutil,commands,hg,mail,ui,patch,util - os errno popen2 socket sys tempfile''') -from mercurial.i18n import gettext as _ +import os, errno, socket +import email.MIMEMultipart, email.MIMEText, email.Utils +from mercurial import cmdutil, commands, hg, mail, ui, patch, util +from mercurial.i18n import _ from mercurial.node import * try: @@ -216,8 +215,6 @@ bcc = [a.strip() for a in bcc if a.strip()] if len(patches) > 1: - ui.write(_('\nWrite the introductory message for the patch series.\n\n')) - tlen = len(str(len(patches))) subj = '[PATCH %0*d of %d] %s' % ( @@ -227,21 +224,13 @@ prompt('Subject:', rest = ' [PATCH %0*d of %d] ' % (tlen, 0, len(patches)))) - ui.write(_('Finish with ^D or a dot on a line by itself.\n\n')) - - body = [] - - while True: - try: l = raw_input() - except EOFError: break - if l == '.': break - body.append(l) - + body = '' if opts['diffstat']: d = cdiffstat(_('Final summary:\n'), jumbo) - if d: body.append('\n' + d) + if d: body = '\n' + d - body = '\n'.join(body) + '\n' + ui.write(_('\nWrite the introductory message for the patch series.\n\n')) + body = ui.edit(body, sender) msg = email.MIMEText.MIMEText(body) msg['Subject'] = subj
--- a/hgext/transplant.py Sat Feb 17 09:54:44 2007 +0100 +++ b/hgext/transplant.py Tue Feb 20 20:55:23 2007 +0100 @@ -5,11 +5,10 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from mercurial.demandload import * -from mercurial.i18n import gettext as _ -demandload(globals(), 'os tempfile') -demandload(globals(), 'mercurial:bundlerepo,cmdutil,commands,hg,merge,patch') -demandload(globals(), 'mercurial:revlog,util') +from mercurial.i18n import _ +import os, tempfile +from mercurial import bundlerepo, changegroup, cmdutil, commands, hg, merge +from mercurial import patch, revlog, util '''patch transplanting tool @@ -120,7 +119,8 @@ if pulls: if source != repo: repo.pull(source, heads=pulls, lock=lock) - merge.update(repo, pulls[-1], wlock=wlock) + merge.update(repo, pulls[-1], False, False, None, + wlock=wlock) p1, p2 = repo.dirstate.parents() pulls = [] @@ -162,7 +162,7 @@ os.unlink(patchfile) if pulls: repo.pull(source, heads=pulls, lock=lock) - merge.update(repo, pulls[-1], wlock=wlock) + merge.update(repo, pulls[-1], False, False, None, wlock=wlock) finally: self.saveseries(revmap, merges) self.transplants.write() @@ -473,7 +473,7 @@ bundle = None if not source.local(): cg = source.changegroup(incoming, 'incoming') - bundle = commands.write_bundle(cg, compress=False) + bundle = changegroup.writebundle(cg, None, 'HG10UN') source = bundlerepo.bundlerepository(ui, repo.root, bundle) return (source, incoming, bundle) @@ -575,6 +575,7 @@ tp.apply(repo, source, revmap, merges, opts) finally: if bundle: + source.close() os.unlink(bundle) cmdtable = { @@ -588,5 +589,5 @@ ('', 'log', None, _('append transplant info to log message')), ('c', 'continue', None, _('continue last transplant session after repair')), ('', 'filter', '', _('filter changesets through FILTER'))], - _('hg transplant [-s REPOSITORY] [-b BRANCH] [-p REV] [-m REV] [-n] REV...')) + _('hg transplant [-s REPOSITORY] [-b BRANCH [-a]] [-p REV] [-m REV] [REV]...')) }
--- a/mercurial/appendfile.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/appendfile.py Tue Feb 20 20:55:23 2007 +0100 @@ -5,8 +5,7 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from demandload import * -demandload(globals(), "cStringIO changelog errno manifest os tempfile util") +import cStringIO, changelog, errno, manifest, os, tempfile, util # writes to metadata files are ordered. reads: changelog, manifest, # normal files. writes: normal files, manifest, changelog.
--- a/mercurial/archival.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/archival.py Tue Feb 20 20:55:23 2007 +0100 @@ -5,10 +5,9 @@ # This software may be used and distributed according to the terms of # the GNU General Public License, incorporated herein by reference. -from demandload import * -from i18n import gettext as _ +from i18n import _ from node import * -demandload(globals(), 'cStringIO os stat tarfile time util zipfile') +import cStringIO, os, stat, tarfile, time, util, zipfile def tidyprefix(dest, prefix, suffixes): '''choose prefix to use for names in archive. make sure prefix is @@ -155,15 +154,12 @@ def write(name, mode, data): if matchfn and not matchfn(name): return if decode: - fp = cStringIO.StringIO() - repo.wwrite(name, data, fp) - data = fp.getvalue() + data = repo.wwritedata(name, data) archiver.addfile(name, mode, data) - change = repo.changelog.read(node) - mn = change[0] - archiver = archivers[kind](dest, prefix, mtime or change[2][0]) - m = repo.manifest.read(mn) + ctx = repo.changectx(node) + archiver = archivers[kind](dest, prefix, mtime or ctx.date()[0]) + m = ctx.manifest() items = m.items() items.sort() write('.hg_archival.txt', 0644,
--- a/mercurial/bdiff.c Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/bdiff.c Tue Feb 20 20:55:23 2007 +0100 @@ -33,7 +33,11 @@ } #else #include <sys/types.h> +#ifdef __BEOS__ +#include <ByteOrder.h> +#else #include <arpa/inet.h> +#endif #include <inttypes.h> #endif
--- a/mercurial/bundlerepo.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/bundlerepo.py Tue Feb 20 20:55:23 2007 +0100 @@ -11,9 +11,8 @@ """ from node import * -from i18n import gettext as _ -from demandload import demandload -demandload(globals(), "changegroup util os struct bz2 tempfile") +from i18n import _ +import changegroup, util, os, struct, bz2, tempfile import localrepo, changelog, manifest, filelog, revlog @@ -50,7 +49,7 @@ continue for p in (p1, p2): if not p in self.nodemap: - raise revlog.RevlogError(_("unknown parent %s") % short(p1)) + raise revlog.LookupError(_("unknown parent %s") % short(p1)) if linkmapper is None: link = n else:
--- a/mercurial/changegroup.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/changegroup.py Tue Feb 20 20:55:23 2007 +0100 @@ -6,9 +6,9 @@ This software may be used and distributed according to the terms of the GNU General Public License, incorporated herein by reference. """ -from i18n import gettext as _ -from demandload import * -demandload(globals(), "struct os bz2 zlib util tempfile") + +from i18n import _ +import struct, os, bz2, zlib, util, tempfile def getchunk(source): """get a chunk from a changegroup""" @@ -67,8 +67,6 @@ 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")
--- a/mercurial/changelog.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/changelog.py Tue Feb 20 20:55:23 2007 +0100 @@ -6,9 +6,8 @@ # of the GNU General Public License, incorporated herein by reference. from revlog import * -from i18n import gettext as _ -from demandload import demandload -demandload(globals(), "os time util") +from i18n import _ +import os, time, util def _string_escape(text): """
--- a/mercurial/cmdutil.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/cmdutil.py Tue Feb 20 20:55:23 2007 +0100 @@ -5,11 +5,9 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from demandload import demandload from node import * -from i18n import gettext as _ -demandload(globals(), 'os sys') -demandload(globals(), 'mdiff util templater patch') +from i18n import _ +import os, sys, mdiff, util, templater, patch revrangesep = ':' @@ -150,13 +148,12 @@ def findrenames(repo, added=None, removed=None, threshold=0.5): if added is None or removed is None: added, removed = repo.status()[1:3] - changes = repo.changelog.read(repo.dirstate.parents()[0]) - mf = repo.manifest.read(changes[0]) + ctx = repo.changectx() for a in added: aa = repo.wread(a) bestscore, bestname = None, None for r in removed: - rr = repo.file(r).read(mf[r]) + rr = ctx.filectx(r).data() delta = mdiff.textdiff(aa, rr) if len(delta) < len(aa): myscore = 1.0 - (float(len(delta)) / len(aa)) @@ -201,12 +198,11 @@ class changeset_printer(object): '''show changeset information when templating not requested.''' - def __init__(self, ui, repo, patch, brinfo, buffered): + def __init__(self, ui, repo, patch, buffered): self.ui = ui self.repo = repo self.buffered = buffered self.patch = patch - self.brinfo = brinfo self.header = {} self.hunk = {} self.lastheader = None @@ -270,11 +266,6 @@ for parent in parents: self.ui.write(_("parent: %d:%s\n") % parent) - 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") % (self.repo.manifest.rev(changes[0]), hex(changes[0]))) @@ -322,8 +313,8 @@ class changeset_templater(changeset_printer): '''format changeset information.''' - def __init__(self, ui, repo, patch, brinfo, mapfile, buffered): - changeset_printer.__init__(self, ui, repo, patch, brinfo, buffered) + def __init__(self, ui, repo, patch, mapfile, buffered): + changeset_printer.__init__(self, ui, repo, patch, buffered) self.t = templater.templater(mapfile, templater.common_filters, cache={'parent': '{rev}:{node|short} ', 'manifest': '{rev}:{node|short}', @@ -409,12 +400,6 @@ if branch: branch = util.tolocal(branch) return showlist('branch', [branch], plural='branches', **args) - # add old style branches if requested - 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))] @@ -528,11 +513,6 @@ if opts.get('patch'): patch = matchfn or util.always - 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: @@ -554,12 +534,12 @@ or templater.templatepath(mapfile)) if mapname: mapfile = mapname try: - t = changeset_templater(ui, repo, patch, br, mapfile, buffered) + t = changeset_templater(ui, repo, patch, 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, br, buffered) + return changeset_printer(ui, repo, patch, buffered) def finddate(ui, repo, date): """Find the tipmost changeset that matches the given date spec"""
--- a/mercurial/commands.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/commands.py Tue Feb 20 20:55:23 2007 +0100 @@ -5,14 +5,14 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from demandload import demandload +import demandimport; demandimport.enable() from node import * -from i18n import gettext as _ -demandload(globals(), "bisect os re sys signal imp urllib pdb shlex stat") -demandload(globals(), "fancyopts ui hg util lock revlog bundlerepo") -demandload(globals(), "difflib patch time help mdiff tempfile") -demandload(globals(), "traceback errno version atexit socket") -demandload(globals(), "archival changegroup cmdutil hgweb.server sshserver") +from i18n import _ +import bisect, os, re, sys, signal, imp, urllib, pdb, shlex, stat +import fancyopts, ui, hg, util, lock, revlog, bundlerepo +import difflib, patch, time, help, mdiff, tempfile +import traceback, errno, version, atexit, socket +import archival, changegroup, cmdutil, hgweb.server, sshserver class UnknownCommand(Exception): """Exception raised if command is not in the command table.""" @@ -240,8 +240,7 @@ if op1 != node: if opts['merge']: ui.status(_('merging with changeset %s\n') % nice(op1)) - n = _lookup(repo, hex(op1)) - hg.merge(repo, n) + hg.merge(repo, hex(op1)) else: ui.status(_('the backout changeset is a new head - ' 'do not forget to merge\n')) @@ -686,15 +685,12 @@ clist.sort() ui.write("%s\n" % "\n".join(clist)) -def debugrebuildstate(ui, repo, rev=None): +def debugrebuildstate(ui, repo, rev=""): """rebuild the dirstate as it would look like for the given revision""" - if not rev: + if rev == "": rev = repo.changelog.tip() - else: - rev = repo.lookup(rev) - change = repo.changelog.read(rev) - n = change[0] - files = repo.manifest.read(n) + ctx = repo.changectx(rev) + files = ctx.manifest() wlock = repo.wlock() repo.dirstate.rebuild(rev, files) @@ -705,10 +701,8 @@ dc = repo.dirstate.map keys = dc.keys() keys.sort() - m1n = repo.changelog.read(parent1)[0] - m2n = repo.changelog.read(parent2)[0] - m1 = repo.manifest.read(m1n) - m2 = repo.manifest.read(m2n) + m1 = repo.changectx(parent1).manifest() + m2 = repo.changectx(parent2).manifest() errors = 0 for f in dc: state = repo.dirstate.state(f) @@ -1157,13 +1151,14 @@ prev = {} def display(fn, rev, states, prevstates): - counts = {'-': 0, '+': 0} + found = False filerevmatches = {} - if incrementing or not opts['all']: - a, b, r = prevstates, states, rev + r = prev.get(fn, -1) + if opts['all']: + iter = difflinestates(states, prevstates) else: - a, b, r = states, prevstates, prev.get(fn, -1) - for change, l in difflinestates(a, b): + iter = [('', l) for l in prevstates] + for change, l in iter: cols = [fn, str(r)] if opts['line_number']: cols.append(str(l.linenum)) @@ -1179,19 +1174,17 @@ else: cols.append(l.line) ui.write(sep.join(cols), eol) - counts[change] += 1 - return counts['+'], counts['-'] + found = True + return found fstate = {} skip = {} get = util.cachefunc(lambda r: repo.changectx(r).changeset()) changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts) - count = 0 - incrementing = False + found = False follow = opts.get('follow') for st, rev, fns in changeiter: if st == 'window': - incrementing = rev matches.clear() elif st == 'add': mf = repo.changectx(rev).manifest() @@ -1217,10 +1210,10 @@ if copy: skip[copy] = True continue - if incrementing or not opts['all'] or fstate[fn]: - pos, neg = display(fn, rev, m, fstate[fn]) - count += pos + neg - if pos and not opts['all']: + if fn in prev or fstate[fn]: + r = display(fn, rev, m, fstate[fn]) + found = found or r + if r and not opts['all']: skip[fn] = True if copy: skip[copy] = True @@ -1229,15 +1222,14 @@ fstate[copy] = m prev[fn] = rev - if not incrementing: - fstate = fstate.items() - fstate.sort() - for fn, state in fstate: - if fn in skip: - continue - if fn not in copies.get(prev[fn], {}): - display(fn, rev, {}, state) - return (count == 0 and 1) or 0 + fstate = fstate.items() + fstate.sort() + for fn, state in fstate: + if fn in skip: + continue + if fn not in copies.get(prev[fn], {}): + found = display(fn, rev, {}, state) or found + return (not found and 1) or 0 def heads(ui, repo, **opts): """show current repository heads @@ -1546,8 +1538,12 @@ other = hg.repository(ui, source) incoming = repo.findincoming(other, force=opts["force"]) if not incoming: + try: + os.unlink(opts["bundle"]) + except: + pass ui.status(_("no changes found\n")) - return + return 1 cleanup = None try: @@ -1712,7 +1708,6 @@ if opts["date"]: df = util.matchdate(opts["date"]) - displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn) for st, rev, fns in changeiter: if st == 'add': @@ -1779,7 +1774,7 @@ ui.write("%3s " % (m.execf(f) and "755" or "644")) ui.write("%s\n" % f) -def merge(ui, repo, node=None, force=None, branch=None): +def merge(ui, repo, node=None, force=None): """merge working directory with another revision Merge the contents of the current working directory and the @@ -1793,9 +1788,7 @@ revision to merge with must be provided. """ - if node or branch: - node = _lookup(repo, node, branch) - else: + if not node: heads = repo.heads() if len(heads) > 2: raise util.Abort(_('repo has %d heads - ' @@ -1830,7 +1823,7 @@ o = repo.findoutgoing(other, force=opts['force']) if not o: ui.status(_("no changes found\n")) - return + return 1 o = repo.changelog.nodesbetween(o, revs)[0] if opts['newest_first']: o.reverse() @@ -2143,8 +2136,9 @@ if not opts['rev'] and p2 != nullid: raise util.Abort(_('uncommitted merge - please provide a ' 'specific revision')) - node = repo.changectx(opts['rev']).node() - mf = repo.manifest.read(repo.changelog.read(node)[0]) + ctx = repo.changectx(opts['rev']) + node = ctx.node() + mf = ctx.manifest() if node == parent: pmf = mf else: @@ -2234,7 +2228,7 @@ if pmf is None: # only need parent manifest in this unlikely case, # so do not read by default - pmf = repo.manifest.read(repo.changelog.read(parent)[0]) + pmf = repo.changectx(parent).manifest() if abs in pmf: if mfentry: # if version of file is same in parent and target @@ -2468,9 +2462,10 @@ hexfunc = ui.debugflag and hex or short for t, n in l: try: + hn = hexfunc(n) r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n)) - except KeyError: - r = " ?:?" + except revlog.LookupError: + r = " ?:%s" % hn if ui.quiet: ui.write("%s\n" % t) else: @@ -2499,7 +2494,7 @@ modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname) return postincoming(ui, repo, modheads, opts['update']) -def update(ui, repo, node=None, clean=False, branch=None, date=None): +def update(ui, repo, node=None, clean=False, date=None): """update working directory Update the working directory to the specified revision. @@ -2519,36 +2514,11 @@ raise util.Abort(_("you can't specify a revision and a date")) node = cmdutil.finddate(ui, repo, date) - node = _lookup(repo, node, branch) if clean: return hg.clean(repo, node) else: return hg.update(repo, node) -def _lookup(repo, node, branch=None): - if branch: - repo.ui.warn(_("the --branch option is deprecated, " - "please use 'hg branch' instead\n")) - br = repo.branchlookup(branch=branch) - found = [] - for x in br: - if branch in br[x]: - found.append(x) - 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) - raise util.Abort("") - if len(found) == 1: - node = found[0] - repo.ui.warn(_("Using head %s for branch %s\n") - % (short(node), branch)) - else: - raise util.Abort(_("branch %s not found") % branch) - else: - node = node and repo.lookup(node) or repo.changelog.tip() - return node - def verify(ui, repo): """verify the integrity of the repository @@ -2754,8 +2724,7 @@ _('hg grep [OPTION]... PATTERN [FILE]...')), "heads": (heads, - [('b', 'branches', None, _('show branches (DEPRECATED)')), - ('', 'style', '', _('display using template map file')), + [('', 'style', '', _('display using template map file')), ('r', 'rev', '', _('show only heads which are descendants of rev')), ('', 'template', '', _('display with template'))], _('hg heads [-r REV]')), @@ -2766,7 +2735,7 @@ [('p', 'strip', 1, _('directory strip option for patch. This has the same\n' 'meaning as the corresponding patch option')), - ('b', 'base', '', _('base path (DEPRECATED)')), + ('b', 'base', '', _('base path')), ('f', 'force', None, _('skip check for outstanding uncommitted changes'))] + commitopts, _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')), @@ -2798,8 +2767,7 @@ _('hg locate [OPTION]... [PATTERN]...')), "^log|history": (log, - [('b', 'branches', None, _('show branches (DEPRECATED)')), - ('f', 'follow', None, + [('f', 'follow', None, _('follow changeset history, or file history across copies and renames')), ('', 'follow-first', None, _('only follow the first parent of merge changesets')), @@ -2820,8 +2788,7 @@ "manifest": (manifest, [], _('hg manifest [REV]')), "^merge": (merge, - [('b', 'branch', '', _('merge with head of a specific branch (DEPRECATED)')), - ('f', 'force', None, _('force a merge with outstanding changes'))], + [('f', 'force', None, _('force a merge with outstanding changes'))], _('hg merge [-f] [REV]')), "outgoing|out": (outgoing, [('M', 'no-merges', None, _('do not show merges')), @@ -2836,8 +2803,7 @@ _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')), "^parents": (parents, - [('b', 'branches', None, _('show branches (DEPRECATED)')), - ('r', 'rev', '', _('show parents from the specified rev')), + [('r', 'rev', '', _('show parents from the specified rev')), ('', 'style', '', _('display using template map file')), ('', 'template', '', _('display with template'))], _('hg parents [-r REV] [FILE]')), @@ -2940,8 +2906,7 @@ "tags": (tags, [], _('hg tags')), "tip": (tip, - [('b', 'branches', None, _('show branches (DEPRECATED)')), - ('', 'style', '', _('display using template map file')), + [('', 'style', '', _('display using template map file')), ('p', 'patch', None, _('show patch')), ('', 'template', '', _('display with template'))], _('hg tip [-p]')), @@ -2952,9 +2917,7 @@ _('hg unbundle [-u] FILE')), "^update|up|checkout|co": (update, - [('b', 'branch', '', - _('checkout the head of a specific branch (DEPRECATED)')), - ('C', 'clean', None, _('overwrite locally modified files')), + [('C', 'clean', None, _('overwrite locally modified files')), ('d', 'date', '', _('tipmost revision matching date'))], _('hg update [-C] [-d DATE] [REV]')), "verify": (verify, [], _('hg verify')), @@ -3111,9 +3074,10 @@ if reposetup: hg.repo_setup_hooks.append(reposetup) cmdtable = getattr(mod, 'cmdtable', {}) - for t in cmdtable: - if t in table: - ui.warn(_("module %s overrides %s\n") % (name, t)) + overrides = [cmd for cmd in cmdtable if cmd in table] + if overrides: + ui.warn(_("extension '%s' overrides commands: %s\n") + % (name, " ".join(overrides))) table.update(cmdtable) def parseconfig(config):
--- a/mercurial/context.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/context.py Tue Feb 20 20:55:23 2007 +0100 @@ -6,9 +6,8 @@ # of the GNU General Public License, incorporated herein by reference. from node import * -from i18n import gettext as _ -from demandload import demandload -demandload(globals(), "ancestor bdiff repo revlog util os") +from i18n import _ +import ancestor, bdiff, repo, revlog, util, os, errno class changectx(object): """A changecontext object makes access to data related to a particular @@ -84,21 +83,22 @@ try: return self._manifest[path] except KeyError: - raise repo.LookupError(_("'%s' not found in manifest") % path) + raise revlog.LookupError(_("'%s' not found in manifest") % path) if '_manifestdelta' in self.__dict__ or path in self.files(): if path in self._manifestdelta: return self._manifestdelta[path] node, flag = self._repo.manifest.find(self._changeset[0], path) if not node: - raise repo.LookupError(_("'%s' not found in manifest") % path) + raise revlog.LookupError(_("'%s' not found in manifest") % path) return node - def filectx(self, path, fileid=None): + def filectx(self, path, fileid=None, filelog=None): """get a file context from this changeset""" if fileid is None: fileid = self.filenode(path) - return filectx(self._repo, path, fileid=fileid, changectx=self) + return filectx(self._repo, path, fileid=fileid, + changectx=self, filelog=filelog) def filectxs(self): """generate a file context for each file in this changeset's @@ -126,16 +126,18 @@ self._repo = repo self._path = path - assert changeid is not None or fileid is not None + assert (changeid is not None + or fileid is not None + or changectx is not None) if filelog: self._filelog = filelog - if changectx: - self._changectx = changectx - self._changeid = changectx.node() if fileid is None: - self._changeid = changeid + if changectx is None: + self._changeid = changeid + else: + self._changectx = changectx else: self._fileid = fileid @@ -150,13 +152,10 @@ self._changeid = self._filelog.linkrev(self._filenode) return self._changeid elif name == '_filenode': - try: - if '_fileid' in self.__dict__: - self._filenode = self._filelog.lookup(self._fileid) - else: - self._filenode = self._changectx.filenode(self._path) - except revlog.RevlogError, inst: - raise repo.LookupError(str(inst)) + if '_fileid' in self.__dict__: + self._filenode = self._filelog.lookup(self._fileid) + else: + self._filenode = self._changectx.filenode(self._path) return self._filenode elif name == '_filerev': self._filerev = self._filelog.rev(self._filenode) @@ -168,7 +167,7 @@ try: n = self._filenode return True - except repo.LookupError: + except revlog.LookupError: # file is missing return False @@ -379,13 +378,15 @@ """generate a manifest corresponding to the working directory""" man = self._parents[0].manifest().copy() + is_exec = util.execfunc(self._repo.root, man.execf) + is_link = util.linkfunc(self._repo.root, man.linkf) copied = self._repo.dirstate.copies() modified, added, removed, deleted, unknown = self._status[:5] for i, l in (("a", added), ("m", modified), ("u", unknown)): for f in l: man[f] = man.get(copied.get(f, f), nullid) + i try: - man.set(f, util.is_exec(self._repo.wjoin(f), man.execf(f))) + man.set(f, is_exec(f), is_link(f)) except OSError: pass @@ -424,9 +425,10 @@ def children(self): return [] - def filectx(self, path): + def filectx(self, path, filelog=None): """get a file context from the working directory""" - return workingfilectx(self._repo, path, workingctx=self) + return workingfilectx(self._repo, path, workingctx=self, + filelog=filelog) def ancestor(self, c2): """return the ancestor context of self and c2""" @@ -484,7 +486,7 @@ rp = self._repopath if rp == self._path: return None - return rp, self._workingctx._parents._manifest.get(rp, nullid) + return rp, self._changectx._parents[0]._manifest.get(rp, nullid) def parents(self): '''return parent filectxs, following copies if necessary''' @@ -505,5 +507,12 @@ return [] def size(self): return os.stat(self._repo.wjoin(self._path)).st_size + def date(self): + t, tz = self._changectx.date() + try: + return (os.lstat(repo.wjoin(self._path)).st_mtime, tz) + except OSError, err: + if err.errno != errno.ENOENT: raise + return (t, tz) def cmp(self, text): return self._repo.wread(self._path) == text
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/demandimport.py Tue Feb 20 20:55:23 2007 +0100 @@ -0,0 +1,115 @@ +# demandimport.py - global demand-loading of modules for Mercurial +# +# Copyright 2006 Matt Mackall <mpm@selenic.com> +# +# This software may be used and distributed according to the terms +# of the GNU General Public License, incorporated herein by reference. + +''' +demandimport - automatic demandloading of modules + +To enable this module, do: + + import demandimport; demandimport.enable() + +Imports of the following forms will be demand-loaded: + + import a, b.c + import a.b as c + from a import b,c # a will be loaded immediately + +These imports will not be delayed: + + from a import * + b = __import__(a) +''' + +_origimport = __import__ + +class _demandmod(object): + """module demand-loader and proxy""" + def __init__(self, name, globals, locals): + if '.' in name: + head, rest = name.split('.', 1) + after = [rest] + else: + head = name + after = [] + object.__setattr__(self, "_data", (head, globals, locals, after)) + object.__setattr__(self, "_module", None) + def _extend(self, name): + """add to the list of submodules to load""" + self._data[3].append(name) + def _load(self): + if not self._module: + head, globals, locals, after = self._data + mod = _origimport(head, globals, locals) + # load submodules + def subload(mod, p): + h, t = p, None + if '.' in p: + h, t = p.split('.', 1) + if not hasattr(mod, h): + setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__)) + elif t: + subload(getattr(mod, h), t) + + for x in after: + subload(mod, x) + + # are we in the locals dictionary still? + if locals and locals.get(head) == self: + locals[head] = mod + object.__setattr__(self, "_module", mod) + def __repr__(self): + return "<unloaded module '%s'>" % self._data[0] + def __call__(self, *args, **kwargs): + raise TypeError("'unloaded module' object is not callable") + def __getattribute__(self, attr): + if attr in ('_data', '_extend', '_load', '_module'): + return object.__getattribute__(self, attr) + self._load() + return getattr(self._module, attr) + def __setattr__(self, attr, val): + self._load() + setattr(self._module, attr, val) + +def _demandimport(name, globals=None, locals=None, fromlist=None): + if not locals or name in ignore or fromlist == ('*',): + # these cases we can't really delay + return _origimport(name, globals, locals, fromlist) + elif not fromlist: + # import a [as b] + if '.' in name: # a.b + base, rest = name.split('.', 1) + # email.__init__ loading email.mime + if globals and globals.get('__name__', None) == base: + return _origimport(name, globals, locals, fromlist) + # if a is already demand-loaded, add b to its submodule list + if base in locals: + if isinstance(locals[base], _demandmod): + locals[base]._extend(rest) + return locals[base] + return _demandmod(name, globals, locals) + else: + # from a import b,c,d + mod = _origimport(name, globals, locals) + # recurse down the module chain + for comp in name.split('.')[1:]: + mod = getattr(mod, comp) + for x in fromlist: + # set requested submodules for demand load + if not(hasattr(mod, x)): + setattr(mod, x, _demandmod(x, mod.__dict__, mod.__dict__)) + return mod + +ignore = ['_hashlib', 'fcntl', 'win32com.gen_py'] + +def enable(): + "enable global demand-loading of modules" + __builtins__["__import__"] = _demandimport + +def disable(): + "disable global demand-loading of modules" + __builtins__["__import__"] = _origimport +
--- a/mercurial/demandload.py Sat Feb 17 09:54:44 2007 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ -'''Demand load modules when used, not when imported.''' - -__author__ = '''Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>. -This software may be used and distributed according to the terms -of the GNU General Public License, incorporated herein by reference.''' - -# this is based on matt's original demandload module. it is a -# complete rewrite. some time, we may need to support syntax of -# "import foo as bar". - -class _importer(object): - '''import a module. it is not imported until needed, and is - imported at most once per scope.''' - - def __init__(self, scope, modname, fromlist): - '''scope is context (globals() or locals()) in which import - should be made. modname is name of module to import. - fromlist is list of modules for "from foo import ..." - emulation.''' - - self.scope = scope - self.modname = modname - self.fromlist = fromlist - self.mod = None - - def module(self): - '''import the module if needed, and return.''' - if self.mod is None: - self.mod = __import__(self.modname, self.scope, self.scope, - self.fromlist) - del self.modname, self.fromlist - return self.mod - -class _replacer(object): - '''placeholder for a demand loaded module. demandload puts this in - a target scope. when an attribute of this object is looked up, - this object is replaced in the target scope with the actual - module. - - we use __getattribute__ to avoid namespace clashes between - placeholder object and real module.''' - - def __init__(self, importer, target): - self.importer = importer - self.target = target - # consider case where we do this: - # demandload(globals(), 'foo.bar foo.quux') - # foo will already exist in target scope when we get to - # foo.quux. so we remember that we will need to demandload - # quux into foo's scope when we really load it. - self.later = [] - - def module(self): - return object.__getattribute__(self, 'importer').module() - - def __getattribute__(self, key): - '''look up an attribute in a module and return it. replace the - name of the module in the caller\'s dict with the actual - module.''' - - module = object.__getattribute__(self, 'module')() - target = object.__getattribute__(self, 'target') - importer = object.__getattribute__(self, 'importer') - later = object.__getattribute__(self, 'later') - - if later: - demandload(module.__dict__, ' '.join(later)) - - importer.scope[target] = module - - return getattr(module, key) - -class _replacer_from(_replacer): - '''placeholder for a demand loaded module. used for "from foo - import ..." emulation. semantics of this are different than - regular import, so different implementation needed.''' - - def module(self): - importer = object.__getattribute__(self, 'importer') - target = object.__getattribute__(self, 'target') - - return getattr(importer.module(), target) - - def __call__(self, *args, **kwargs): - target = object.__getattribute__(self, 'module')() - return target(*args, **kwargs) - -def demandload(scope, modules): - '''import modules into scope when each is first used. - - scope should be the value of globals() in the module calling this - function, or locals() in the calling function. - - modules is a string listing module names, separated by white - space. names are handled like this: - - foo import foo - foo bar import foo, bar - foo@bar import foo as 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 mod in modules.split(): - col = mod.find(':') - if col >= 0: - fromlist = mod[col+1:].split(',') - mod = mod[:col] - else: - fromlist = [] - as_ = None - if '@' in mod: - mod, as_ = mod.split("@") - importer = _importer(scope, mod, fromlist) - if fromlist: - for name in fromlist: - scope[name] = _replacer_from(importer, name) - else: - dot = mod.find('.') - if dot >= 0: - basemod = mod[:dot] - val = scope.get(basemod) - # if base module has already been demandload()ed, - # remember to load this submodule into its namespace - # when needed. - if isinstance(val, _replacer): - later = object.__getattribute__(val, 'later') - later.append(mod[dot+1:]) - continue - else: - basemod = mod - if not as_: - as_ = basemod - scope[as_] = _replacer(importer, as_)
--- a/mercurial/dirstate.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/dirstate.py Tue Feb 20 20:55:23 2007 +0100 @@ -8,9 +8,8 @@ """ from node import * -from i18n import gettext as _ -from demandload import * -demandload(globals(), "struct os time bisect stat strutil util re errno") +from i18n import _ +import struct, os, time, bisect, stat, strutil, util, re, errno class dirstate(object): format = ">cllll" @@ -338,14 +337,13 @@ return ret def supported_type(self, f, st, verbose=False): - if stat.S_ISREG(st.st_mode): + if stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode): return True if verbose: kind = 'unknown' if stat.S_ISCHR(st.st_mode): kind = _('character device') elif stat.S_ISBLK(st.st_mode): kind = _('block device') elif stat.S_ISFIFO(st.st_mode): kind = _('fifo') - elif stat.S_ISLNK(st.st_mode): kind = _('symbolic link') elif stat.S_ISSOCK(st.st_mode): kind = _('socket') elif stat.S_ISDIR(st.st_mode): kind = _('directory') self.ui.warn(_('%s: unsupported file type (type is %s)\n') % (
--- a/mercurial/filelog.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/filelog.py Tue Feb 20 20:55:23 2007 +0100 @@ -6,8 +6,7 @@ # of the GNU General Public License, incorporated herein by reference. from revlog import * -from demandload import * -demandload(globals(), "os") +import os class filelog(revlog): def __init__(self, opener, path, defversion=REVLOG_DEFAULT_VERSION):
--- a/mercurial/hg.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/hg.py Tue Feb 20 20:55:23 2007 +0100 @@ -8,10 +8,11 @@ from node import * from repo import * -from demandload import * -from i18n import gettext as _ -demandload(globals(), "localrepo bundlerepo httprepo sshrepo statichttprepo") -demandload(globals(), "errno lock os shutil util merge@_merge verify@_verify") +from i18n import _ +import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo +import errno, lock, os, shutil, util +import merge as _merge +import verify as _verify def _local(path): return (os.path.isfile(util.drop_scheme('file', path)) and
--- a/mercurial/hgweb/__init__.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/hgweb/__init__.py Tue Feb 20 20:55:23 2007 +0100 @@ -6,6 +6,11 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from mercurial.demandload import demandload -demandload(globals(), "mercurial.hgweb.hgweb_mod:hgweb") -demandload(globals(), "mercurial.hgweb.hgwebdir_mod:hgwebdir") +import hgweb_mod, hgwebdir_mod + +def hgweb(*args, **kwargs): + return hgweb_mod.hgweb(*args, **kwargs) + +def hgwebdir(*args, **kwargs): + return hgwebdir_mod.hgwebdir(*args, **kwargs) +
--- a/mercurial/hgweb/common.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/hgweb/common.py Tue Feb 20 20:55:23 2007 +0100 @@ -7,7 +7,6 @@ # of the GNU General Public License, incorporated herein by reference. import os, mimetypes -import os.path def get_mtime(repo_path): store_path = os.path.join(repo_path, ".hg") @@ -39,7 +38,7 @@ os.stat(path) ct = mimetypes.guess_type(path)[0] or "text/plain" req.header([('Content-type', ct), - ('Content-length', os.path.getsize(path))]) + ('Content-length', str(os.path.getsize(path)))]) return file(path, 'rb').read() except (TypeError, OSError): # illegal fname or unreadable file
--- a/mercurial/hgweb/hgweb_mod.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/hgweb/hgweb_mod.py Tue Feb 20 20:55:23 2007 +0100 @@ -6,17 +6,13 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -import os -import os.path -import mimetypes -from mercurial.demandload import demandload -demandload(globals(), "re zlib ConfigParser mimetools cStringIO sys tempfile") -demandload(globals(), 'urllib bz2') -demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,streamclone,patch") -demandload(globals(), "mercurial:revlog,templater") -demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile,style_map") +import os, mimetypes, re, zlib, mimetools, cStringIO, sys +import tempfile, urllib, bz2 from mercurial.node import * from mercurial.i18n import gettext as _ +from mercurial import mdiff, ui, hg, util, archival, streamclone, patch +from mercurial import revlog, templater +from common import get_mtime, staticfile, style_map def _up(p): if p[0] != "/": @@ -172,14 +168,10 @@ yield self.t("diffline", line=l) r = self.repo - cl = r.changelog - mf = r.manifest - change1 = cl.read(node1) - change2 = cl.read(node2) - mmap1 = mf.read(change1[0]) - mmap2 = mf.read(change2[0]) - date1 = util.datestr(change1[2]) - date2 = util.datestr(change2[2]) + c1 = r.changectx(node1) + c2 = r.changectx(node2) + date1 = util.datestr(c1.date()) + date2 = util.datestr(c2.date()) modified, added, removed, deleted, unknown = r.status(node1, node2)[:5] if files: @@ -188,17 +180,17 @@ diffopts = patch.diffopts(self.repo.ui, untrusted=True) for f in modified: - to = r.file(f).read(mmap1[f]) - tn = r.file(f).read(mmap2[f]) + to = c1.filectx(f).data() + tn = c2.filectx(f).data() yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, opts=diffopts), f, tn) for f in added: to = None - tn = r.file(f).read(mmap2[f]) + tn = c2.filectx(f).data() yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, opts=diffopts), f, tn) for f in removed: - to = r.file(f).read(mmap1[f]) + to = c1.filectx(f).data() tn = None yield diffblock(mdiff.unidiff(to, date1, tn, date2, f, opts=diffopts), f, tn) @@ -497,8 +489,6 @@ archives=self.archivelist(hex(node))) def tags(self): - cl = self.repo.changelog - i = self.repo.tagslist() i.reverse() @@ -509,7 +499,7 @@ continue yield {"parity": self.stripes(parity), "tag": k, - "date": cl.read(n)[2], + "date": self.repo.changectx(n).date(), "node": hex(n)} parity += 1 @@ -519,8 +509,6 @@ entriesnotip=lambda **x: entries(True, **x)) def summary(self): - cl = self.repo.changelog - i = self.repo.tagslist() i.reverse() @@ -535,14 +523,11 @@ if count > 10: # limit to 10 tags break; - c = cl.read(n) - t = c[2] - yield self.t("tagentry", - parity = self.stripes(parity), - tag = k, - node = hex(n), - date = t) + parity=self.stripes(parity), + tag=k, + node=hex(n), + date=self.repo.changectx(n).date()) parity += 1 def heads(**map): @@ -564,40 +549,38 @@ def changelist(**map): parity = 0 - cl = self.repo.changelog l = [] # build a list in forward order for efficiency for i in xrange(start, end): - n = cl.node(i) - changes = cl.read(n) - hn = hex(n) - t = changes[2] + ctx = self.repo.changectx(i) + hn = hex(ctx.node()) l.insert(0, self.t( 'shortlogentry', - parity = parity, - author = changes[1], - desc = changes[4], - date = t, - rev = i, - node = hn)) + parity=parity, + author=ctx.user(), + desc=ctx.description(), + date=ctx.date(), + rev=i, + node=hn)) parity = 1 - parity yield l + cl = self.repo.changelog count = cl.count() start = max(0, count - self.maxchanges) end = min(count, start + self.maxchanges) yield self.t("summary", - desc = self.config("web", "description", "unknown"), - owner = (self.config("ui", "username") or # preferred - self.config("web", "contact") or # deprecated - self.config("web", "author", "unknown")), # also - lastchange = cl.read(cl.tip())[2], - tags = tagentries, - heads = heads, - shortlog = changelist, - node = hex(cl.tip()), + desc=self.config("web", "description", "unknown"), + owner=(self.config("ui", "username") or # preferred + self.config("web", "contact") or # deprecated + self.config("web", "author", "unknown")), # also + lastchange=cl.read(cl.tip())[2], + tags=tagentries, + heads=heads, + shortlog=changelist, + node=hex(cl.tip()), archives=self.archivelist("tip")) def filediff(self, fctx): @@ -789,6 +772,9 @@ port = req.env["SERVER_PORT"] port = port != "80" and (":" + port) or "" urlbase = 'http://%s%s' % (req.env['SERVER_NAME'], port) + staticurl = self.config("web", "staticurl") or req.url + 'static/' + if not staticurl.endswith('/'): + staticurl += '/' if not self.reponame: self.reponame = (self.config("web", "name") @@ -797,6 +783,7 @@ self.t = templater.templater(mapfile, templater.common_filters, defaults={"url": req.url, + "staticurl": staticurl, "urlbase": urlbase, "repo": self.reponame, "header": header, @@ -873,7 +860,7 @@ try: req.write(self.filerevision(self.filectx(req))) return - except hg.RepoError: + except revlog.LookupError: pass req.write(self.manifest(self.changectx(req), path))
--- a/mercurial/hgweb/hgwebdir_mod.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/hgweb/hgwebdir_mod.py Tue Feb 20 20:55:23 2007 +0100 @@ -6,13 +6,12 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -import os -from mercurial.demandload import demandload -demandload(globals(), "mimetools cStringIO") -demandload(globals(), "mercurial:ui,hg,util,templater") -demandload(globals(), "mercurial.hgweb.hgweb_mod:hgweb") -demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile,style_map") +from mercurial import demandimport; demandimport.enable() +import os, mimetools, cStringIO from mercurial.i18n import gettext as _ +from mercurial import ui, hg, util, templater +from common import get_mtime, staticfile, style_map +from hgweb_mod import hgweb # This is a stopgap class hgwebdir(object): @@ -31,8 +30,11 @@ self.repos = cleannames(config.items()) self.repos.sort() else: - cp = util.configparser() - cp.read(config) + if isinstance(config, util.configparser): + cp = config + else: + cp = util.configparser() + cp.read(config) self.repos = [] if cp.has_section('web'): if cp.has_option('web', 'motd'): @@ -86,6 +88,10 @@ if not url.endswith('/'): url += '/' + staticurl = config('web', 'staticurl') or url + 'static/' + if not staticurl.endswith('/'): + staticurl += '/' + style = self.style if style is None: style = config('web', 'style', '') @@ -96,7 +102,8 @@ defaults={"header": header, "footer": footer, "motd": motd, - "url": url}) + "url": url, + "staticurl": staticurl}) def archivelist(ui, nodeid, url): allowed = ui.configlist("web", "allow_archive", untrusted=True)
--- a/mercurial/hgweb/request.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/hgweb/request.py Tue Feb 20 20:55:23 2007 +0100 @@ -6,8 +6,7 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from mercurial.demandload import demandload -demandload(globals(), "socket sys cgi os errno") +import socket, cgi, errno from mercurial.i18n import gettext as _ class wsgiapplication(object):
--- a/mercurial/hgweb/server.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/hgweb/server.py Tue Feb 20 20:55:23 2007 +0100 @@ -6,11 +6,11 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from mercurial.demandload import demandload -import os, sys, errno -demandload(globals(), "urllib BaseHTTPServer socket SocketServer traceback") -demandload(globals(), "mercurial:ui,hg,util,templater") -demandload(globals(), "hgweb_mod:hgweb hgwebdir_mod:hgwebdir request:wsgiapplication") +import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback +from mercurial import ui, hg, util, templater +from hgweb_mod import hgweb +from hgwebdir_mod import hgwebdir +from request import wsgiapplication from mercurial.i18n import gettext as _ def _splitURI(uri):
--- a/mercurial/httprepo.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/httprepo.py Tue Feb 20 20:55:23 2007 +0100 @@ -8,10 +8,9 @@ from node import * from remoterepo import * -from i18n import gettext as _ -from demandload import * -demandload(globals(), "hg os urllib urllib2 urlparse zlib util httplib") -demandload(globals(), "errno keepalive tempfile socket changegroup") +from i18n import _ +import hg, os, urllib, urllib2, urlparse, zlib, util, httplib +import errno, keepalive, tempfile, socket, changegroup class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm): def __init__(self, ui):
--- a/mercurial/i18n.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/i18n.py Tue Feb 20 20:55:23 2007 +0100 @@ -7,9 +7,7 @@ of the GNU General Public License, incorporated herein by reference. """ -# the import from gettext is _really_ slow -# for now we use a dummy function -gettext = lambda x: x -#import gettext -#t = gettext.translation('hg', '/usr/share/locale', fallback=1) -#gettext = t.gettext +import gettext +t = gettext.translation('hg', fallback=1) +gettext = t.gettext +_ = gettext
--- a/mercurial/localrepo.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/localrepo.py Tue Feb 20 20:55:23 2007 +0100 @@ -6,13 +6,11 @@ # of the GNU General Public License, incorporated herein by reference. from node import * -from i18n import gettext as _ -from demandload import * -import repo -demandload(globals(), "appendfile changegroup") -demandload(globals(), "changelog dirstate filelog manifest context") -demandload(globals(), "re lock transaction tempfile stat mdiff errno ui") -demandload(globals(), "os revlog time util") +from i18n import _ +import repo, appendfile, changegroup +import changelog, dirstate, filelog, manifest, context +import re, lock, transaction, tempfile, stat, mdiff, errno, ui +import os, revlog, time, util class localrepository(repo.repository): capabilities = ('lookup', 'changegroupsubset') @@ -119,10 +117,14 @@ self.tagscache = None self.branchcache = None self.nodetagscache = None - self.encodepats = None - self.decodepats = None + self.filterpats = {} self.transhandle = None + self._link = lambda x: False + if util.checklink(self.root): + r = self.root # avoid circular reference in lambda + self._link = lambda x: util.is_link(os.path.join(r, x)) + self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root) def url(self): @@ -140,32 +142,34 @@ be run as hooks without wrappers to convert return values.''' self.ui.note(_("calling hook %s: %s\n") % (hname, funcname)) - d = funcname.rfind('.') - if d == -1: - raise util.Abort(_('%s hook is invalid ("%s" not in a module)') - % (hname, funcname)) - modname = funcname[:d] - try: - obj = __import__(modname) - except ImportError: + obj = funcname + if not callable(obj): + d = funcname.rfind('.') + if d == -1: + raise util.Abort(_('%s hook is invalid ("%s" not in ' + 'a module)') % (hname, funcname)) + modname = funcname[:d] try: - # extensions are loaded with hgext_ prefix - obj = __import__("hgext_%s" % modname) + obj = __import__(modname) except ImportError: + try: + # extensions are loaded with hgext_ prefix + obj = __import__("hgext_%s" % modname) + except ImportError: + raise util.Abort(_('%s hook is invalid ' + '(import of "%s" failed)') % + (hname, modname)) + try: + for p in funcname.split('.')[1:]: + obj = getattr(obj, p) + except AttributeError, err: raise util.Abort(_('%s hook is invalid ' - '(import of "%s" failed)') % - (hname, modname)) - try: - for p in funcname.split('.')[1:]: - obj = getattr(obj, p) - except AttributeError, err: - raise util.Abort(_('%s hook is invalid ' - '("%s" is not defined)') % - (hname, funcname)) - if not callable(obj): - raise util.Abort(_('%s hook is invalid ' - '("%s" is not callable)') % - (hname, funcname)) + '("%s" is not defined)') % + (hname, funcname)) + if not callable(obj): + raise util.Abort(_('%s hook is invalid ' + '("%s" is not callable)') % + (hname, funcname)) try: r = obj(ui=self.ui, repo=self, hooktype=name, **args) except (KeyboardInterrupt, util.SignalInterrupt): @@ -203,7 +207,9 @@ if hname.split(".", 1)[0] == name and cmd] hooks.sort() for hname, cmd in hooks: - if cmd.startswith('python:'): + if callable(cmd): + r = callhook(hname, cmd) or r + elif cmd.startswith('python:'): r = callhook(hname, cmd[7:].strip()) or r else: r = runhook(hname, cmd) or r @@ -319,7 +325,7 @@ rev = c.rev() try: fnode = c.filenode('.hgtags') - except repo.LookupError: + except revlog.LookupError: continue ret.append((rev, node, fnode)) if fnode in last: @@ -480,17 +486,15 @@ def wfile(self, f, mode='r'): return self.wopener(f, mode) - def wread(self, filename): - if self.encodepats == None: + def _filter(self, filter, filename, data): + if filter not in self.filterpats: l = [] - for pat, cmd in self.ui.configitems("encode"): + for pat, cmd in self.ui.configitems(filter): mf = util.matcher(self.root, "", [pat], [], [])[1] l.append((mf, cmd)) - self.encodepats = l + self.filterpats[filter] = l - data = self.wopener(filename, 'r').read() - - for mf, cmd in self.encodepats: + for mf, cmd in self.filterpats[filter]: if mf(filename): self.ui.debug(_("filtering %s through %s\n") % (filename, cmd)) data = util.filter(data, cmd) @@ -498,23 +502,32 @@ return data - def wwrite(self, filename, data, fd=None): - if self.decodepats == None: - l = [] - for pat, cmd in self.ui.configitems("decode"): - mf = util.matcher(self.root, "", [pat], [], [])[1] - l.append((mf, cmd)) - self.decodepats = l + def wread(self, filename): + if self._link(filename): + data = os.readlink(self.wjoin(filename)) + else: + data = self.wopener(filename, 'r').read() + return self._filter("encode", filename, data) - for mf, cmd in self.decodepats: - if mf(filename): - self.ui.debug(_("filtering %s through %s\n") % (filename, cmd)) - data = util.filter(data, cmd) - break + def wwrite(self, filename, data, flags): + data = self._filter("decode", filename, data) + if "l" in flags: + try: + os.unlink(self.wjoin(filename)) + except OSError: + pass + os.symlink(data, self.wjoin(filename)) + else: + try: + if self._link(filename): + os.unlink(self.wjoin(filename)) + except OSError: + pass + self.wopener(filename, 'w').write(data) + util.set_exec(self.wjoin(filename), "x" in flags) - if fd: - return fd.write(data) - return self.wopener(filename, 'w').write(data) + def wwritedata(self, filename, data): + return self._filter("decode", filename, data) def transaction(self): tr = self.transhandle @@ -654,11 +667,11 @@ changelist.append(fn) return fl.add(t, meta, transaction, linkrev, fp1, fp2) - def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None): + def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None, extra={}): if p1 is None: p1, p2 = self.dirstate.parents() return self.commit(files=files, text=text, user=user, date=date, - p1=p1, p2=p2, wlock=wlock) + p1=p1, p2=p2, wlock=wlock, extra=extra) def commit(self, files=None, text="", user=None, date=None, match=util.always, force=False, lock=None, wlock=None, @@ -732,12 +745,14 @@ new = {} linkrev = self.changelog.count() commit.sort() + is_exec = util.execfunc(self.root, m1.execf) + is_link = util.linkfunc(self.root, m1.linkf) for f in commit: self.ui.note(f + "\n") try: new[f] = self.filecommit(f, m1, m2, linkrev, tr, changed) - m1.set(f, util.is_exec(self.wjoin(f), m1.execf(f))) - except IOError: + m1.set(f, is_exec(f), is_link(f)) + except (OSError, IOError): if use_dirstate: self.ui.warn(_("trouble committing %s!\n") % f) raise @@ -747,11 +762,13 @@ # update manifest m1.update(new) remove.sort() + removed = [] for f in remove: if f in m1: del m1[f] - mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0], (new, remove)) + removed.append(f) + mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0], (new, removed)) # add changeset new = new.keys() @@ -766,8 +783,10 @@ edittext.append("HG: user: %s" % user) if p2 != nullid: edittext.append("HG: branch merge") + if branchname: + edittext.append("HG: branch %s" % util.tolocal(branchname)) edittext.extend(["HG: changed %s" % f for f in changed]) - edittext.extend(["HG: removed %s" % f for f in remove]) + edittext.extend(["HG: removed %s" % f for f in removed]) if not changed and not remove: edittext.append("HG: no files changed") edittext.append("") @@ -785,17 +804,20 @@ text = '\n'.join(lines) if branchname: extra["branch"] = branchname - n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, + n = self.changelog.add(mn, changed + removed, text, tr, p1, p2, user, date, extra) self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1, parent2=xp2) tr.close() + if self.branchcache and "branch" in extra: + self.branchcache[util.tolocal(extra["branch"])] = n + if use_dirstate or update_dirstate: self.dirstate.setparents(n) if use_dirstate: self.dirstate.update(new, "n") - self.dirstate.forget(remove) + self.dirstate.forget(removed) self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2) return n @@ -895,9 +917,11 @@ # generate a pseudo-manifest for the working dir # XXX: create it in dirstate.py ? mf2 = mfmatches(self.dirstate.parents()[0]) + is_exec = util.execfunc(self.root, mf2.execf) + is_link = util.linkfunc(self.root, mf2.linkf) for f in lookup + modified + added: mf2[f] = "" - mf2.set(f, execf=util.is_exec(self.wjoin(f), mf2.execf(f))) + mf2.set(f, is_exec(f), is_link(f)) for f in removed: if f in mf2: del mf2[f] @@ -936,11 +960,12 @@ wlock = self.wlock() for f in list: p = self.wjoin(f) - if not os.path.exists(p): + islink = os.path.islink(p) + if not islink and not os.path.exists(p): self.ui.warn(_("%s does not exist!\n") % f) - elif not os.path.isfile(p): - self.ui.warn(_("%s not added: only files supported currently\n") - % f) + elif not islink and not os.path.isfile(p): + self.ui.warn(_("%s not added: only files and symlinks " + "supported currently\n") % f) elif self.dirstate.state(f) in 'an': self.ui.warn(_("%s already tracked!\n") % f) else: @@ -987,8 +1012,7 @@ self.ui.warn("%s not removed!\n" % f) else: t = self.file(f).read(m[f]) - self.wwrite(f, t) - util.set_exec(self.wjoin(f), m.execf(f)) + self.wwrite(f, t, m.flags(f)) self.dirstate.update([f], "n") def copy(self, source, dest, wlock=None): @@ -1011,112 +1035,6 @@ heads.sort() return [n for (r, n) in heads] - # branchlookup returns a dict giving a list of branches for - # each head. A branch is defined as the tag of a node or - # the branch of the node's parents. If a node has multiple - # branch tags, tags are eliminated if they are visible from other - # branch tags. - # - # So, for this graph: a->b->c->d->e - # \ / - # aa -----/ - # a has tag 2.6.12 - # d has tag 2.6.13 - # e would have branch tags for 2.6.12 and 2.6.13. Because the node - # for 2.6.12 can be reached from the node 2.6.13, that is eliminated - # from the list. - # - # It is possible that more than one head will have the same branch tag. - # callers need to check the result for multiple heads under the same - # branch tag if that is a problem for them (ie checkout of a specific - # branch). - # - # passing in a specific branch will limit the depth of the search - # through the parents. It won't limit the branches returned in the - # result though. - def branchlookup(self, heads=None, branch=None): - if not heads: - heads = self.heads() - headt = [ h for h in heads ] - chlog = self.changelog - branches = {} - merges = [] - seenmerge = {} - - # traverse the tree once for each head, recording in the branches - # dict which tags are visible from this head. The branches - # dict also records which tags are visible from each tag - # while we traverse. - while headt or merges: - if merges: - n, found = merges.pop() - visit = [n] - else: - h = headt.pop() - visit = [h] - found = [h] - seen = {} - while visit: - n = visit.pop() - if n in seen: - continue - pp = chlog.parents(n) - tags = self.nodetags(n) - if tags: - for x in tags: - if x == 'tip': - continue - for f in found: - branches.setdefault(f, {})[n] = 1 - branches.setdefault(n, {})[n] = 1 - break - if n not in found: - found.append(n) - if branch in tags: - continue - seen[n] = 1 - if pp[1] != nullid and n not in seenmerge: - merges.append((pp[1], [x for x in found])) - seenmerge[n] = 1 - if pp[0] != nullid: - visit.append(pp[0]) - # traverse the branches dict, eliminating branch tags from each - # head that are visible from another branch tag for that head. - out = {} - viscache = {} - for h in heads: - def visible(node): - if node in viscache: - return viscache[node] - ret = {} - visit = [node] - while visit: - x = visit.pop() - if x in viscache: - ret.update(viscache[x]) - elif x not in ret: - ret[x] = 1 - if x in branches: - visit[len(visit):] = branches[x].keys() - viscache[node] = ret - return ret - if h not in branches: - continue - # O(n^2), but somewhat limited. This only searches the - # tags visible from a specific head, not all the tags in the - # whole repo. - for b in branches[h]: - vis = False - for bb in branches[h].keys(): - if b != bb: - if b in visible(bb): - vis = True - break - if not vis: - l = out.setdefault(h, []) - l[len(l):] = self.nodetags(b) - return out - def branches(self, nodes): if not nodes: nodes = [self.changelog.tip()]
--- a/mercurial/lock.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/lock.py Tue Feb 20 20:55:23 2007 +0100 @@ -5,8 +5,7 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from demandload import * -demandload(globals(), 'errno os socket time util') +import errno, os, socket, time, util class LockException(IOError): def __init__(self, errno, strerror, filename, desc):
--- a/mercurial/mail.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/mail.py Tue Feb 20 20:55:23 2007 +0100 @@ -5,9 +5,8 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from i18n import gettext as _ -from demandload import * -demandload(globals(), "os re smtplib templater util socket") +from i18n import _ +import os, smtplib, templater, util, socket def _smtp(ui): '''send mail using smtp.'''
--- a/mercurial/manifest.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/manifest.py Tue Feb 20 20:55:23 2007 +0100 @@ -6,10 +6,8 @@ # of the GNU General Public License, incorporated herein by reference. from revlog import * -from i18n import gettext as _ -from demandload import * -demandload(globals(), "array bisect struct") -demandload(globals(), "mdiff") +from i18n import _ +import array, bisect, struct, mdiff class manifestdict(dict): def __init__(self, mapping=None, flags=None):
--- a/mercurial/mdiff.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/mdiff.py Tue Feb 20 20:55:23 2007 +0100 @@ -5,9 +5,7 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from demandload import demandload -import bdiff, mpatch -demandload(globals(), "re struct util md5") +import bdiff, mpatch, re, struct, util, md5 def splitnewlines(text): '''like str.splitlines, but only split on newlines.'''
--- a/mercurial/merge.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/merge.py Tue Feb 20 20:55:23 2007 +0100 @@ -6,9 +6,8 @@ # of the GNU General Public License, incorporated herein by reference. from node import * -from i18n import gettext as _ -from demandload import * -demandload(globals(), "errno util os tempfile") +from i18n import _ +import errno, util, os, tempfile def filemerge(repo, fw, fo, wctx, mctx): """perform a 3-way merge in the working directory @@ -21,8 +20,9 @@ def temp(prefix, ctx): pre = "%s~%s." % (os.path.basename(ctx.path()), prefix) (fd, name) = tempfile.mkstemp(prefix=pre) + data = repo.wwritedata(ctx.path(), ctx.data()) f = os.fdopen(fd, "wb") - repo.wwrite(ctx.path(), ctx.data(), f) + f.write(data) f.close() return name @@ -220,12 +220,17 @@ copy = {} def fmerge(f, f2=None, fa=None): - """merge executable flags""" + """merge flags""" if not f2: f2 = f fa = f a, b, c = ma.execf(fa), m1.execf(f), m2.execf(f2) - return ((a^b) | (a^c)) ^ a + if ((a^b) | (a^c)) ^ a: + return 'x' + a, b, c = ma.linkf(fa), m1.linkf(f), m2.linkf(f2) + if ((a^b) | (a^c)) ^ a: + return 'l' + return '' def act(msg, m, f, *args): repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m)) @@ -250,21 +255,21 @@ # is remote's version newer? # or are we going back in time and clean? elif overwrite or m2[f] != a or (backwards and not n[20:]): - act("remote is newer", "g", f, m2.execf(f)) + act("remote is newer", "g", f, m2.flags(f)) # local is newer, not overwrite, check mode bits - elif fmerge(f) != m1.execf(f): - act("update permissions", "e", f, m2.execf(f)) + elif fmerge(f) != m1.flags(f): + act("update permissions", "e", f, m2.flags(f)) # contents same, check mode bits - elif m1.execf(f) != m2.execf(f): - if overwrite or fmerge(f) != m1.execf(f): - act("update permissions", "e", f, m2.execf(f)) + elif m1.flags(f) != m2.flags(f): + if overwrite or fmerge(f) != m1.flags(f): + act("update permissions", "e", f, m2.flags(f)) elif f in copied: continue elif f in copy: f2 = copy[f] if f2 not in m2: # directory rename act("remote renamed directory to " + f2, "d", - f, None, f2, m1.execf(f)) + f, None, f2, m1.flags(f)) elif f2 in m1: # case 2 A,B/B/B act("local copied to " + f2, "m", f, f2, f, fmerge(f, f2, f2), False) @@ -295,7 +300,7 @@ f2 = copy[f] if f2 not in m1: # directory rename act("local renamed directory to " + f2, "d", - None, f, f2, m2.execf(f)) + None, f, f2, m2.flags(f)) elif f2 in m2: # rename case 1, A/A,B/A act("remote copied to " + f, "m", f2, f, f, fmerge(f2, f, f2), False) @@ -304,14 +309,14 @@ f2, f, f, fmerge(f2, f, f2), True) elif f in ma: if overwrite or backwards: - act("recreating", "g", f, m2.execf(f)) + act("recreating", "g", f, m2.flags(f)) elif n != ma[f]: if repo.ui.prompt( (_("remote changed %s which local deleted\n") % f) + _("(k)eep or (d)elete?"), _("[kd]"), _("k")) == _("k"): - act("prompt recreating", "g", f, m2.execf(f)) + act("prompt recreating", "g", f, m2.flags(f)) else: - act("remote created", "g", f, m2.execf(f)) + act("remote created", "g", f, m2.flags(f)) return action @@ -335,7 +340,7 @@ (f, inst.strerror)) removed += 1 elif m == "m": # merge - f2, fd, flag, move = a[2:] + f2, fd, flags, move = a[2:] r = filemerge(repo, f, f2, wctx, mctx) if r > 0: unresolved += 1 @@ -346,35 +351,32 @@ merged += 1 if f != fd: repo.ui.debug(_("copying %s to %s\n") % (f, fd)) - repo.wwrite(fd, repo.wread(f)) + repo.wwrite(fd, repo.wread(f), flags) if move: repo.ui.debug(_("removing %s\n") % f) os.unlink(repo.wjoin(f)) - util.set_exec(repo.wjoin(fd), flag) + util.set_exec(repo.wjoin(fd), "x" in flags) elif m == "g": # get - flag = a[2] + flags = a[2] repo.ui.note(_("getting %s\n") % f) t = mctx.filectx(f).data() - repo.wwrite(f, t) - util.set_exec(repo.wjoin(f), flag) + repo.wwrite(f, t, flags) updated += 1 elif m == "d": # directory rename - f2, fd, flag = a[2:] + f2, fd, flags = a[2:] if f: repo.ui.note(_("moving %s to %s\n") % (f, fd)) t = wctx.filectx(f).data() - repo.wwrite(fd, t) - util.set_exec(repo.wjoin(fd), flag) + repo.wwrite(fd, t, flags) util.unlink(repo.wjoin(f)) if f2: repo.ui.note(_("getting %s to %s\n") % (f2, fd)) t = mctx.filectx(f2).data() - repo.wwrite(fd, t) - util.set_exec(repo.wjoin(fd), flag) + repo.wwrite(fd, t, flags) updated += 1 elif m == "e": # exec - flag = a[2] - util.set_exec(repo.wjoin(f), flag) + flags = a[2] + util.set_exec(repo.wjoin(f), flags) return updated, merged, removed, unresolved @@ -441,6 +443,9 @@ wlock = working dir lock, if already held """ + if node is None: + node = "tip" + if not wlock: wlock = repo.wlock()
--- a/mercurial/mpatch.c Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/mpatch.c Tue Feb 20 20:55:23 2007 +0100 @@ -42,7 +42,11 @@ #else /* not windows */ # include <sys/types.h> -# include <arpa/inet.h> +# ifdef __BEOS__ +# include <ByteOrder.h> +# else +# include <arpa/inet.h> +# endif # include <inttypes.h> #endif
--- a/mercurial/node.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/node.py Tue Feb 20 20:55:23 2007 +0100 @@ -7,8 +7,7 @@ of the GNU General Public License, incorporated herein by reference. """ -from demandload import demandload -demandload(globals(), "binascii") +import binascii nullrev = -1 nullid = "\0" * 20
--- a/mercurial/packagescan.py Sat Feb 17 09:54:44 2007 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -# 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, 2006 Volker Kleinfeld <Volker.Kleinfeld@gmx.de> -# -# This software may be used and distributed according to the terms -# of the GNU General Public License, incorporated herein by reference. -import glob -import os -import sys -import ihooks -import types -import string - -# Install this module as fake demandload module -sys.modules['mercurial.demandload'] = sys.modules[__name__] - -# 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 - foo import foo - foo bar import foo, bar - foo.bar import foo.bar - foo@bar import foo as 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, fromlist = m.split(':') - fromlist = fromlist.split(',') - except: - module = m - fromlist = [] - as_ = None - if '@' in module: - module, as_ = module.split('@') - 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 - if not as_: - as_ = mn - scope[as_] = 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 - -class SkipPackage(Exception): - def __init__(self, reason): - self.reason = reason - -scan_in_progress = False - -def scan(libpath, packagename): - """ helper for finding all required modules of package <packagename> """ - global scan_in_progress - scan_in_progress = True - # Use the package in the build directory - libpath = os.path.abspath(libpath) - sys.path.insert(0, libpath) - packdir = os.path.join(libpath, packagename.replace('.', '/')) - # A normal import would not find the package in - # the build directory. ihook is used to force the import. - # After the package is imported the import scope for - # the following imports is settled. - p = importfrom(packdir) - globals()[packagename] = p - sys.modules[packagename] = p - # Fetch the python modules in the package - cwd = os.getcwd() - os.chdir(packdir) - pymodulefiles = glob.glob('*.py') - extmodulefiles = glob.glob('*.pyd') - os.chdir(cwd) - # Import all python modules and by that run the fake demandload - for m in pymodulefiles: - if m == '__init__.py': continue - tmp = {} - mname, ext = os.path.splitext(m) - fullname = packagename+'.'+mname - try: - __import__(fullname, tmp, tmp) - except SkipPackage, inst: - print >> sys.stderr, 'skipping %s: %s' % (fullname, inst.reason) - continue - requiredmodules[fullname] = 1 - # Import all extension modules and by that run the fake demandload - for m in extmodulefiles: - tmp = {} - mname, ext = os.path.splitext(m) - fullname = packagename+'.'+mname - __import__(fullname, tmp, tmp) - requiredmodules[fullname] = 1 - -def getmodules(): - return requiredmodules.keys() - -def importfrom(filename): - """ - import module/package from a named file and returns the module. - It does not check on sys.modules or includes the module in the scope. - """ - loader = ihooks.BasicModuleLoader() - path, file = os.path.split(filename) - name, ext = os.path.splitext(file) - m = loader.find_module_in_dir(name, path) - if not m: - raise ImportError, name - m = loader.load_module(name, m) - return m
--- a/mercurial/patch.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/patch.py Tue Feb 20 20:55:23 2007 +0100 @@ -5,12 +5,11 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from demandload import demandload -from i18n import gettext as _ +from i18n import _ from node import * -demandload(globals(), "base85 cmdutil mdiff util") -demandload(globals(), "cStringIO email.Parser errno os popen2 re shutil sha") -demandload(globals(), "sys tempfile zlib") +import base85, cmdutil, mdiff, util, context, revlog +import cStringIO, email.Parser, os, popen2, re, sha +import sys, tempfile, zlib # helper functions @@ -378,8 +377,9 @@ dst = os.path.join(repo.root, gp.path) # patch won't create empty files if ctype == 'ADD' and not os.path.exists(dst): - repo.wwrite(gp.path, '') - util.set_exec(dst, x) + repo.wwrite(gp.path, '', x and 'x' or '') + else: + util.set_exec(dst, x) cmdutil.addremove(repo, cfiles, wlock=wlock) files = patches.keys() files.extend([r for r in removes if r not in files]) @@ -441,27 +441,25 @@ if not node1: node1 = repo.dirstate.parents()[0] - clcache = {} - def getchangelog(n): - if n not in clcache: - clcache[n] = repo.changelog.read(n) - return clcache[n] - mcache = {} - def getmanifest(n): - if n not in mcache: - mcache[n] = repo.manifest.read(n) - return mcache[n] - fcache = {} - def getfile(f): - if f not in fcache: - fcache[f] = repo.file(f) - return fcache[f] + ccache = {} + def getctx(r): + if r not in ccache: + ccache[r] = context.changectx(repo, r) + return ccache[r] + + flcache = {} + def getfilectx(f, ctx): + flctx = ctx.filectx(f, filelog=flcache.get(f)) + if f not in flcache: + flcache[f] = flctx._filelog + return flctx # reading the data for node1 early allows it to play nicely # with repo.status and the revlog cache. - change = getchangelog(node1) - mmap = getmanifest(change[0]) - date1 = util.datestr(change[2]) + ctx1 = context.changectx(repo, node1) + # force manifest reading + man1 = ctx1.manifest() + date1 = util.datestr(ctx1.date()) if not changes: changes = repo.status(node1, node2, files, match=match)[:5] @@ -481,67 +479,38 @@ if not modified and not added and not removed: return - # returns False if there was no rename between n1 and n2 - # returns None if the file was created between n1 and n2 - # returns the (file, node) present in n1 that was renamed to f in n2 - def renamedbetween(f, n1, n2): - r1, r2 = map(repo.changelog.rev, (n1, n2)) + if node2: + ctx2 = context.changectx(repo, node2) + else: + ctx2 = context.workingctx(repo) + man2 = ctx2.manifest() + + # returns False if there was no rename between ctx1 and ctx2 + # returns None if the file was created between ctx1 and ctx2 + # returns the (file, node) present in ctx1 that was renamed to f in ctx2 + def renamed(f): + startrev = ctx1.rev() + c = ctx2 + crev = c.rev() + if crev is None: + crev = repo.changelog.count() orig = f - src = None - while r2 > r1: - cl = getchangelog(n2) - if f in cl[3]: - m = getmanifest(cl[0]) + while crev > startrev: + if f in c.files(): try: - src = getfile(f).renamed(m[f]) - except KeyError: + src = getfilectx(f, c).renamed() + except revlog.LookupError: return None if src: f = src[0] - n2 = repo.changelog.parents(n2)[0] - r2 = repo.changelog.rev(n2) - cl = getchangelog(n1) - m = getmanifest(cl[0]) - if f not in m: + crev = c.parents()[0].rev() + # try to reuse + c = getctx(crev) + if f not in man1: return None if f == orig: return False - return f, m[f] - - if node2: - change = getchangelog(node2) - mmap2 = getmanifest(change[0]) - _date2 = util.datestr(change[2]) - def date2(f): - return _date2 - def read(f): - return getfile(f).read(mmap2[f]) - def renamed(f): - return renamedbetween(f, node1, node2) - else: - tz = util.makedate()[1] - _date2 = util.datestr() - def date2(f): - try: - return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz)) - except OSError, err: - if err.errno != errno.ENOENT: raise - return _date2 - def read(f): - return repo.wread(f) - def renamed(f): - src = repo.dirstate.copied(f) - parent = repo.dirstate.parents()[0] - if src: - f = src - of = renamedbetween(f, node1, parent) - if of or of is None: - return of - elif src: - cl = getchangelog(parent)[0] - return (src, getmanifest(cl)[src]) - else: - return None + return f if repo.ui.quiet: r = None @@ -555,20 +524,21 @@ src = renamed(f) if src: copied[f] = src - srcs = [x[1][0] for x in copied.items()] + srcs = [x[1] for x in copied.items()] all = modified + added + removed all.sort() gone = {} + for f in all: to = None tn = None dodiff = True header = [] - if f in mmap: - to = getfile(f).read(mmap[f]) + if f in man1: + to = getfilectx(f, ctx1).data() if f not in removed: - tn = read(f) + tn = getfilectx(f, ctx2).data() if opts.git: def gitmode(x): return x and '100755' or '100644' @@ -579,13 +549,10 @@ a, b = f, f if f in added: - if node2: - mode = gitmode(mmap2.execf(f)) - else: - mode = gitmode(util.is_exec(repo.wjoin(f), None)) + mode = gitmode(man2.execf(f)) if f in copied: - a, arev = copied[f] - omode = gitmode(mmap.execf(a)) + a = copied[f] + omode = gitmode(man1.execf(a)) addmodehdr(header, omode, mode) if a in removed and a not in gone: op = 'rename' @@ -594,7 +561,7 @@ op = 'copy' header.append('%s from %s\n' % (op, a)) header.append('%s to %s\n' % (op, f)) - to = getfile(a).read(arev) + to = getfilectx(a, ctx1).data() else: header.append('new file mode %s\n' % mode) if util.binary(tn): @@ -603,14 +570,11 @@ if f in srcs: dodiff = False else: - mode = gitmode(mmap.execf(f)) + mode = gitmode(man1.execf(f)) header.append('deleted file mode %s\n' % mode) else: - omode = gitmode(mmap.execf(f)) - if node2: - nmode = gitmode(mmap2.execf(f)) - else: - nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f))) + omode = gitmode(man1.execf(f)) + nmode = gitmode(man2.execf(f)) addmodehdr(header, omode, nmode) if util.binary(to) or util.binary(tn): dodiff = 'binary' @@ -620,7 +584,10 @@ if dodiff == 'binary': text = b85diff(fp, to, tn) else: - text = mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts) + text = mdiff.unidiff(to, date1, + # ctx2 date may be dynamic + tn, util.datestr(ctx2.date()), + f, r, opts=opts) if text or len(header) > 1: fp.write(''.join(header)) fp.write(text) @@ -632,12 +599,13 @@ total = len(revs) revwidth = max([len(str(rev)) for rev in revs]) - def single(node, seqno, fp): - parents = [p for p in repo.changelog.parents(node) if p != nullid] + def single(rev, seqno, fp): + ctx = repo.changectx(rev) + node = ctx.node() + parents = [p.node() for p in ctx.parents() if p] if switch_parent: parents.reverse() prev = (parents and parents[0]) or nullid - change = repo.changelog.read(node) if not fp: fp = cmdutil.make_file(repo, template, node, total=total, @@ -646,13 +614,13 @@ repo.ui.note("%s\n" % fp.name) fp.write("# HG changeset patch\n") - fp.write("# User %s\n" % change[1]) - fp.write("# Date %d %d\n" % change[2]) + fp.write("# User %s\n" % ctx.user()) + fp.write("# Date %d %d\n" % ctx.date()) fp.write("# Node ID %s\n" % hex(node)) fp.write("# Parent %s\n" % hex(prev)) if len(parents) > 1: fp.write("# Parent %s\n" % hex(parents[1])) - fp.write(change[4].rstrip()) + fp.write(ctx.description().rstrip()) fp.write("\n\n") diff(repo, prev, node, fp=fp, opts=opts) @@ -660,7 +628,7 @@ fp.close() for seqno, rev in enumerate(revs): - single(repo.lookup(rev), seqno+1, fp) + single(rev, seqno+1, fp) def diffstat(patchlines): fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
--- a/mercurial/repo.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/repo.py Tue Feb 20 20:55:23 2007 +0100 @@ -9,9 +9,6 @@ class RepoError(Exception): pass -class LookupError(RepoError): - pass - class repository(object): def capable(self, name): '''tell whether repo supports named capability.
--- a/mercurial/revlog.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/revlog.py Tue Feb 20 20:55:23 2007 +0100 @@ -11,10 +11,9 @@ """ from node import * -from i18n import gettext as _ -from demandload import demandload -demandload(globals(), "binascii changegroup errno ancestor mdiff os") -demandload(globals(), "sha struct util zlib") +from i18n import _ +import binascii, changegroup, errno, ancestor, mdiff, os +import sha, struct, util, zlib # revlog version strings REVLOGV0 = 0 @@ -147,6 +146,9 @@ lend = len(data) / self.s i = blockstart / self.s off = 0 + # lazyindex supports __delitem__ + if lend > len(self.index) - i: + lend = len(self.index) - i for x in xrange(lend): if self.index[i + x] == None: b = data[off : off + self.s] @@ -282,6 +284,7 @@ del self.p.map[key] class RevlogError(Exception): pass +class LookupError(RevlogError): pass class revlog(object): """ @@ -472,7 +475,7 @@ try: return self.nodemap[node] except KeyError: - raise RevlogError(_('%s: no node %s') % (self.indexfile, hex(node))) + raise LookupError(_('%s: no node %s') % (self.indexfile, hex(node))) def linkrev(self, node): return (node == nullid) and nullrev or self.index[self.rev(node)][-4] def parents(self, node): @@ -767,7 +770,7 @@ node = id r = self.rev(node) # quick search the index return node - except RevlogError: + except LookupError: pass # may be partial hex id try: # str(rev) @@ -796,7 +799,7 @@ for n in self.nodemap: if n.startswith(bin_id) and hex(n).startswith(id): if node is not None: - raise RevlogError(_("Ambiguous identifier")) + raise LookupError(_("Ambiguous identifier")) node = n if node is not None: return node @@ -816,7 +819,7 @@ if n: return n - raise RevlogError(_("No match found")) + raise LookupError(_("No match found")) def cmp(self, node, text): """compare text with a given file revision""" @@ -1156,13 +1159,13 @@ for p in (p1, p2): if not p in self.nodemap: - raise RevlogError(_("unknown parent %s") % short(p)) + raise LookupError(_("unknown parent %s") % short(p)) if not chain: # retrieve the parent revision of the delta chain chain = p1 if not chain in self.nodemap: - raise RevlogError(_("unknown base %s") % short(chain[:4])) + raise LookupError(_("unknown base %s") % short(chain[:4])) # full versions are inserted when the needed deltas become # comparable to the uncompressed text or when the previous
--- a/mercurial/sshrepo.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/sshrepo.py Tue Feb 20 20:55:23 2007 +0100 @@ -7,9 +7,8 @@ from node import * from remoterepo import * -from i18n import gettext as _ -from demandload import * -demandload(globals(), "hg os re stat util") +from i18n import _ +import hg, os, re, stat, util class sshrepository(remoterepository): def __init__(self, ui, path, create=0):
--- a/mercurial/sshserver.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/sshserver.py Tue Feb 20 20:55:23 2007 +0100 @@ -6,10 +6,9 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from demandload import demandload -from i18n import gettext as _ +from i18n import _ from node import * -demandload(globals(), "os streamclone sys tempfile util") +import os, streamclone, sys, tempfile, util class sshserver(object): def __init__(self, ui, repo):
--- a/mercurial/statichttprepo.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/statichttprepo.py Tue Feb 20 20:55:23 2007 +0100 @@ -7,10 +7,9 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from demandload import * -from i18n import gettext as _ -demandload(globals(), "changelog filelog httprangereader") -demandload(globals(), "repo localrepo manifest os urllib urllib2 util") +from i18n import _ +import changelog, filelog, httprangereader +import repo, localrepo, manifest, os, urllib, urllib2, util class rangereader(httprangereader.httprangereader): def read(self, size=None):
--- a/mercurial/streamclone.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/streamclone.py Tue Feb 20 20:55:23 2007 +0100 @@ -5,9 +5,8 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from demandload import demandload -from i18n import gettext as _ -demandload(globals(), "os stat util lock") +from i18n import _ +import os, stat, util, lock # if server supports streaming clone, it advertises "stream" # capability with value that is version+flags of repo it is serving.
--- a/mercurial/templater.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/templater.py Tue Feb 20 20:55:23 2007 +0100 @@ -5,10 +5,9 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from demandload import demandload -from i18n import gettext as _ +from i18n import _ from node import * -demandload(globals(), "cgi re sys os time urllib util textwrap") +import cgi, re, sys, os, time, urllib, util, textwrap def parsestring(s, quoted=True): '''parse a string using simple c-like syntax.
--- a/mercurial/transaction.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/transaction.py Tue Feb 20 20:55:23 2007 +0100 @@ -11,9 +11,8 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from demandload import demandload -from i18n import gettext as _ -demandload(globals(), 'os') +from i18n import _ +import os class transaction(object): def __init__(self, report, opener, journal, after=None):
--- a/mercurial/ui.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/ui.py Tue Feb 20 20:55:23 2007 +0100 @@ -5,10 +5,9 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. -from i18n import gettext as _ -from demandload import * -demandload(globals(), "errno getpass os re socket sys tempfile") -demandload(globals(), "ConfigParser traceback util") +from i18n import _ +import errno, getpass, os, re, socket, sys, tempfile +import ConfigParser, traceback, util def dupconfig(orig): new = util.configparser(orig.defaults()) @@ -310,7 +309,7 @@ sections.sort() for section in sections: for name, value in self.configitems(section, untrusted): - yield section, name, value.replace('\n', '\\n') + yield section, name, str(value).replace('\n', '\\n') def extensions(self): result = self.configitems("extensions") @@ -388,6 +387,9 @@ if not sys.stdout.closed: sys.stdout.flush() for a in args: sys.stderr.write(str(a)) + # stderr may be buffered under win32 when redirected to files, + # including stdout. + if not sys.stderr.closed: sys.stderr.flush() except IOError, inst: if inst.errno != errno.EPIPE: raise
--- a/mercurial/util.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/util.py Tue Feb 20 20:55:23 2007 +0100 @@ -12,10 +12,9 @@ platform-specific details from the core. """ -from i18n import gettext as _ -from demandload import * -demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile") -demandload(globals(), "os threading time calendar ConfigParser locale glob") +from i18n import _ +import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile +import os, threading, time, calendar, ConfigParser, locale, glob try: _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding() \ @@ -117,11 +116,23 @@ class SignalInterrupt(Exception): """Exception raised on SIGTERM and SIGHUP.""" -# like SafeConfigParser but with case-sensitive keys +# differences from SafeConfigParser: +# - case-sensitive keys +# - allows values that are not strings (this means that you may not +# be able to save the configuration to a file) class configparser(ConfigParser.SafeConfigParser): def optionxform(self, optionstr): return optionstr + def set(self, section, option, value): + return ConfigParser.ConfigParser.set(self, section, option, value) + + def _interpolate(self, section, option, rawval, vars): + if not isinstance(rawval, basestring): + return rawval + return ConfigParser.SafeConfigParser._interpolate(self, section, + option, rawval, vars) + def cachefunc(func): '''cache the result of function calls''' # XXX doesn't handle keywords args @@ -715,9 +726,47 @@ except: return True +def checkexec(path): + """ + Check whether the given path is on a filesystem with UNIX-like exec flags + + Requires a directory (like /foo/.hg) + """ + fh, fn = tempfile.mkstemp("", "", path) + os.close(fh) + m = os.stat(fn).st_mode + os.chmod(fn, m ^ 0111) + r = (os.stat(fn).st_mode != m) + os.unlink(fn) + return r + +def execfunc(path, fallback): + '''return an is_exec() function with default to fallback''' + if checkexec(path): + return lambda x: is_exec(os.path.join(path, x)) + return fallback + +def checklink(path): + """check whether the given path is on a symlink-capable filesystem""" + # mktemp is not racy because symlink creation will fail if the + # file already exists + name = tempfile.mktemp(dir=path) + try: + os.symlink(".", name) + os.unlink(name) + return True + except (OSError, AttributeError): + return False + +def linkfunc(path, fallback): + '''return an is_link() function with default to fallback''' + if checklink(path): + return lambda x: is_link(os.path.join(path, x)) + return fallback + # Platform specific variants if os.name == 'nt': - demandload(globals(), "msvcrt") + import msvcrt nulldev = 'NUL:' class winstdout: @@ -750,19 +799,18 @@ except: return [r'c:\mercurial\mercurial.ini'] - def os_rcpath(): - '''return default os-specific hgrc search path''' - path = system_rcpath() - path.append(user_rcpath()) + def user_rcpath(): + '''return os-specific hgrc search path to the user dir''' + try: + userrc = user_rcpath_win32() + except: + userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini') + path = [userrc] userprofile = os.environ.get('USERPROFILE') if userprofile: path.append(os.path.join(userprofile, 'mercurial.ini')) return path - def user_rcpath(): - '''return os-specific hgrc search path to the user dir''' - return os.path.join(os.path.expanduser('~'), 'mercurial.ini') - def parse_patch_output(output_line): """parses the output produced by patch and returns the file name""" pf = output_line[14:] @@ -774,10 +822,10 @@ '''return False if pid dead, True if running or not known''' return True - def is_exec(f, last): - return last + def set_exec(f, mode): + pass - def set_exec(f, mode): + def set_link(f, mode): pass def set_binary(fd): @@ -834,6 +882,8 @@ else: nulldev = '/dev/null' + _umask = os.umask(0) + os.umask(_umask) def rcfiles(path): rcs = [os.path.join(path, 'hgrc')] @@ -845,18 +895,18 @@ pass return rcs - def os_rcpath(): - '''return default os-specific hgrc search path''' + def system_rcpath(): path = [] # old mod_python does not set sys.argv if len(getattr(sys, 'argv', [])) > 0: path.extend(rcfiles(os.path.dirname(sys.argv[0]) + '/../etc/mercurial')) path.extend(rcfiles('/etc/mercurial')) - path.append(os.path.expanduser('~/.hgrc')) - path = [os.path.normpath(f) for f in path] return path + def user_rcpath(): + return [os.path.expanduser('~/.hgrc')] + def parse_patch_output(output_line): """parses the output produced by patch and returns the file name""" pf = output_line[14:] @@ -864,7 +914,7 @@ pf = pf[1:-1] # Remove the quotes return pf - def is_exec(f, last): + def is_exec(f): """check whether a file is executable""" return (os.lstat(f).st_mode & 0100 != 0) @@ -875,12 +925,34 @@ if mode: # Turn on +x for every +r bit when making a file executable # and obey umask. - umask = os.umask(0) - os.umask(umask) - os.chmod(f, s | (s & 0444) >> 2 & ~umask) + os.chmod(f, s | (s & 0444) >> 2 & ~_umask) else: os.chmod(f, s & 0666) + def is_link(f): + """check whether a file is a symlink""" + return (os.lstat(f).st_mode & 0120000 == 0120000) + + def set_link(f, mode): + """make a file a symbolic link/regular file + + if a file is changed to a link, its contents become the link data + if a link is changed to a file, its link data become its contents + """ + + m = is_link(f) + if m == bool(mode): + return + + if mode: # switch file to link + data = file(f).read() + os.unlink(f) + os.symlink(data, f) + else: + data = os.readlink(f) + os.unlink(f) + file(f, "w").write(data) + def set_binary(fd): pass @@ -1309,6 +1381,13 @@ _rcpath = None +def os_rcpath(): + '''return default os-specific hgrc search path''' + path = system_rcpath() + path.extend(user_rcpath()) + path = [os.path.normpath(f) for f in path] + return path + def rcpath(): '''return hgrc search path. if env var HGRCPATH is set, use it. for each item in path, if directory, use files ending in .rc,
--- a/mercurial/util_win32.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/util_win32.py Tue Feb 20 20:55:23 2007 +0100 @@ -13,10 +13,10 @@ import win32api -from demandload import * -from i18n import gettext as _ -demandload(globals(), 'errno os pywintypes win32con win32file win32process') -demandload(globals(), 'cStringIO win32com.shell:shell,shellcon winerror') +from i18n import _ +import errno, os, pywintypes, win32con, win32file, win32process +import cStringIO, winerror +from win32com.shell import shell,shellcon class WinError: winerror_map = { @@ -187,7 +187,7 @@ filename = win32api.GetModuleFileName(0) return [os.path.join(os.path.dirname(filename), 'mercurial.ini')] -def user_rcpath(): +def user_rcpath_win32(): '''return os-specific hgrc search path to the user dir''' userdir = os.path.expanduser('~') if userdir == '~':
--- a/mercurial/verify.py Sat Feb 17 09:54:44 2007 +0100 +++ b/mercurial/verify.py Tue Feb 20 20:55:23 2007 +0100 @@ -6,7 +6,7 @@ # of the GNU General Public License, incorporated herein by reference. from node import * -from i18n import gettext as _ +from i18n import _ import revlog, mdiff def verify(repo):
--- a/setup.py Sat Feb 17 09:54:44 2007 +0100 +++ b/setup.py Tue Feb 20 20:55:23 2007 +0100 @@ -13,9 +13,11 @@ 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 +import mercurial.demandimport +mercurial.demandimport.enable = lambda: None + +extra = {} # py2exe needs to be installed to work try: @@ -35,34 +37,10 @@ except ImportError: pass - # 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 dependant modules. - from py2exe.build_exe import py2exe as build_exe + extra['console'] = ['hg'] - class py2exe_for_demandload(build_exe): - """ overwrites the py2exe command class for getting the build - directory and for setting the 'includes' option.""" - def initialize_options(self): - self.build_lib = None - build_exe.initialize_options(self) - def finalize_options(self): - # Get the build directory, ie. where to search for modules. - self.set_undefined_options('build', - ('build_lib', 'build_lib')) - # Sets the 'includes' option with the list of needed modules - if not self.includes: - self.includes = [] - else: - self.includes = self.includes.split(',') - 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 - + pass # specify version string, otherwise 'hg identify' will be used: version = '' @@ -75,10 +53,6 @@ mercurial.version.remember_version(version) cmdclass = {'install_data': install_package_data} -py2exe_opts = {} -if py2exe_for_demandload is not None: - cmdclass['py2exe'] = py2exe_for_demandload - py2exe_opts['console'] = ['hg'] setup(name='mercurial', version=mercurial.version.get_version(), @@ -100,4 +74,4 @@ license='COPYING', readme='contrib/macosx/Readme.html', welcome='contrib/macosx/Welcome.html')), - **py2exe_opts) + **extra)
--- a/templates/gitweb/header.tmpl Sat Feb 17 09:54:44 2007 +0100 +++ b/templates/gitweb/header.tmpl Tue Feb 20 20:55:23 2007 +0100 @@ -4,7 +4,7 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US"> <head> -<link rel="icon" href="{url}static/hgicon.png" type="image/png"> +<link rel="icon" href="{staticurl}hgicon.png" type="image/png"> <meta name="robots" content="index, nofollow"/> -<link rel="stylesheet" href="{url}static/style-gitweb.css" type="text/css" /> +<link rel="stylesheet" href="{staticurl}style-gitweb.css" type="text/css" />
--- a/templates/gitweb/map Sat Feb 17 09:54:44 2007 +0100 +++ b/templates/gitweb/map Tue Feb 20 20:55:23 2007 +0100 @@ -48,7 +48,7 @@ filediffchild = '<tr><th class="child">child {rev}:</th><td class="child"><a href="{url}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td></tr>' filelogchild = '<tr><td align="right">child #rev#: </td><td><a href="{url}file{node|short}/#file|urlescape#{sessionvars%urlparameter}">#node|short#</a></td></tr>' shortlog = shortlog.tmpl -shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author#</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a></td></tr>' +shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author#</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b></a></td><td class="link" nowrap><a href="{url}rev/#node|short#{sessionvars%urlparameter}">changeset</a> | <a href="{url}file/#node|short#{sessionvars%urlparameter}">manifest</a></td></tr>' filelogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="{url}rev/#node|short#{sessionvars%urlparameter}"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="{url}file/#node|short#/#file|urlescape#{sessionvars%urlparameter}">file</a> | <a href="{url}diff/#node|short#/#file|urlescape#{sessionvars%urlparameter}">diff</a> | <a href="{url}annotate/#node|short#/#file|urlescape#{sessionvars%urlparameter}">annotate</a> #rename%filelogrename#</td></tr>' archiveentry = ' | <a href="{url}archive/{node|short}{extension}">#type|escape#</a> ' indexentry = '<tr class="parity#parity#"><td><a class="list" href="#url#{sessionvars%urlparameter}"><b>#name|escape#</b></a></td><td>#description#</td><td>#contact|obfuscate#</td><td class="age">#lastchange|age# ago</td><td class="indexlinks"><a class="rss_logo" href="#url#rss-log">RSS</a> #archives%archiveentry#</td></tr>'
--- a/templates/header.tmpl Sat Feb 17 09:54:44 2007 +0100 +++ b/templates/header.tmpl Tue Feb 20 20:55:23 2007 +0100 @@ -3,6 +3,6 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> -<link rel="icon" href="#url#static/hgicon.png" type="image/png"> +<link rel="icon" href="#staticurl#hgicon.png" type="image/png"> <meta name="robots" content="index, nofollow" /> -<link rel="stylesheet" href="#url#static/style.css" type="text/css" /> +<link rel="stylesheet" href="#staticurl#style.css" type="text/css" />
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/raw/index.tmpl Tue Feb 20 20:55:23 2007 +0100 @@ -0,0 +1,2 @@ +#header# +#entries%indexentry#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/raw/manifest.tmpl Tue Feb 20 20:55:23 2007 +0100 @@ -0,0 +1,3 @@ +{header} +{dentries%manifestdirentry}{fentries%manifestfileentry} +{footer}
--- a/templates/raw/map Sat Feb 17 09:54:44 2007 +0100 +++ b/templates/raw/map Tue Feb 20 20:55:23 2007 +0100 @@ -14,3 +14,8 @@ filediff = filediff.tmpl fileannotate = fileannotate.tmpl annotateline = '#author#@#rev#: #line#' +manifest = manifest.tmpl +manifestdirentry = 'drwxr-xr-x {basename}\n' +manifestfileentry = '{permissions|permissions} {size} {basename}\n' +index = index.tmpl +indexentry = '#url#\n'
--- a/tests/README Sat Feb 17 09:54:44 2007 +0100 +++ b/tests/README Tue Feb 20 20:55:23 2007 +0100 @@ -1,93 +1,7 @@ -A simple testing framework - To run the tests, do: cd tests/ python run-tests.py -This finds all scripts in the test directory named test-* and executes -them. The scripts can be either shell scripts or Python. Each test is -run in a temporary directory that is removed when the test is complete. - -A test-<x> succeeds if the script returns success and its output -matches test-<x>.out. If the new output doesn't match, it is stored in -test-<x>.err. - -There are some tricky points here that you should be aware of when -writing tests: - -- hg commit and hg merge want user interaction - - for commit use -m "text" - for hg merge, set HGMERGE to something noninteractive (like true or merge) - -- changeset hashes will change based on user and date which make - things like hg history output change - - use commit -m "test" -u test -d "1000000 0" - -- diff and export may show the current time - - use -D/--nodates to strip the dates - -- You can append your own hgrc settings to the file that the environment - variable HGRCPATH points to. This file is cleared before running a test. - -You also need to be careful that the tests are portable from one platform -to another. You're probably working on Linux, where the GNU toolchain has -more (or different) functionality than on MacOS, *BSD, Solaris, AIX, etc. -While testing on all platforms is the only sure-fire way to make sure that -you've written portable code, here's a list of problems that have been -found and fixed in the tests. Another, more comprehensive list may be -found in the GNU Autoconf manual, online here: - - http://www.gnu.org/software/autoconf/manual/html_node/Portable-Shell.html - -sh: - -The Bourne shell is a very basic shell. /bin/sh on Linux is typically -bash, which even in Bourne-shell mode has many features that Bourne shells -on other Unix systems don't have (and even on Linux /bin/sh isn't -guaranteed to be bash). You'll need to be careful about constructs that -seem ubiquitous, but are actually not available in the least common -denominator. While using another shell (ksh, bash explicitly, posix shell, -etc.) explicitly may seem like another option, these may not exist in a -portable location, and so are generally probably not a good idea. You may -find that rewriting the test in python will be easier. - -- don't use pushd/popd; save the output of "pwd" and use "cd" in place of - the pushd, and cd back to the saved pwd instead of popd. - -- don't use math expressions like let, (( ... )), or $(( ... )); use "expr" - instead. - -grep: - -- don't use the -q option; redirect stdout to /dev/null instead. - -- don't use extended regular expressions with grep; use egrep instead, and - don't escape any regex operators. - -sed: - -- make sure that the beginning-of-line matcher ("^") is at the very - beginning of the expression -- it may not be supported inside parens. - -echo: - -- echo may interpret "\n" and print a newline; use printf instead if you - want a literal "\n" (backslash + n). - -false: - -- false is guaranteed only to return a non-zero value; you cannot depend on - it being 1. On Solaris in particular, /bin/false returns 255. Rewrite - your test to not depend on a particular return value, or create a - temporary "false" executable, and call that instead. - -diff: - -- don't use the -N option. There's no particularly good workaround short - of writing a reasonably complicated replacement script, but substituting - gdiff for diff if you can't rewrite the test not to need -N will probably - do. +See http://www.selenic.com/mercurial/wiki/index.cgi/WritingTests for +more information on writing tests.
--- a/tests/test-bad-extension Sat Feb 17 09:54:44 2007 +0100 +++ b/tests/test-bad-extension Tue Feb 20 20:55:23 2007 +0100 @@ -4,6 +4,8 @@ abspath=`pwd`/badext.py echo '[extensions]' >> $HGRCPATH +echo "gpg =" >> $HGRCPATH +echo "hgext.gpg =" >> $HGRCPATH echo "badext = $abspath" >> $HGRCPATH hg -q help help
--- a/tests/test-bad-extension.out Sat Feb 17 09:54:44 2007 +0100 +++ b/tests/test-bad-extension.out Tue Feb 20 20:55:23 2007 +0100 @@ -1,4 +1,5 @@ *** failed to import extension badext: invalid syntax (badext.py, line 1) +extension 'hgext.gpg' overrides commands: sigs sigcheck sign hg help [COMMAND] show help for a command, extension, or list of commands
--- a/tests/test-branch Sat Feb 17 09:54:44 2007 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -#!/bin/sh -# -# test for branch handling -# -# XXX: need more tests - -hg init -echo a > a -echo b > b -hg ci -A -m 0 -d "1000000 0" -echo aa > a -echo bb > b -hg ci -m 1 -d "1000000 0" -hg tag -l foo -hg update 0 -hg parents -b - -# test update -hg update -b foo -hg parents - -# test merge -hg update 0 -echo c > c -hg ci -A -m 0.0 -d "1000000 0" -hg merge -b foo -hg parents -b - -# re-test with more branches -hg update -C 0 -echo d > d -hg ci -A -m 0.0 -d "1000000 0" -hg merge -b foo -hg parents -b
--- a/tests/test-branch.out Sat Feb 17 09:54:44 2007 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -adding a -adding b -2 files updated, 0 files merged, 0 files removed, 0 files unresolved -the --branches option is deprecated, please use 'hg branches' instead -changeset: 0:b544c4ac4389 -user: test -date: Mon Jan 12 13:46:40 1970 +0000 -summary: 0 - -the --branch option is deprecated, please use 'hg branch' instead -Using head f4ac749470f2 for branch foo -2 files updated, 0 files merged, 0 files removed, 0 files unresolved -changeset: 1:f4ac749470f2 -tag: foo -tag: tip -user: test -date: Mon Jan 12 13:46:40 1970 +0000 -summary: 1 - -2 files updated, 0 files merged, 0 files removed, 0 files unresolved -adding c -the --branch option is deprecated, please use 'hg branch' instead -Using head f4ac749470f2 for branch foo -2 files updated, 0 files merged, 0 files removed, 0 files unresolved -(branch merge, don't forget to commit) -the --branches option is deprecated, please use 'hg branches' instead -changeset: 2:1505d56ee00e -tag: tip -parent: 0:b544c4ac4389 -user: test -date: Mon Jan 12 13:46:40 1970 +0000 -summary: 0.0 - -changeset: 1:f4ac749470f2 -tag: foo -branch: foo -user: test -date: Mon Jan 12 13:46:40 1970 +0000 -summary: 1 - -2 files updated, 0 files merged, 1 files removed, 0 files unresolved -adding d -the --branch option is deprecated, please use 'hg branch' instead -Using head f4ac749470f2 for branch foo -2 files updated, 0 files merged, 0 files removed, 0 files unresolved -(branch merge, don't forget to commit) -the --branches option is deprecated, please use 'hg branches' instead -changeset: 3:53b72df12ae5 -tag: tip -parent: 0:b544c4ac4389 -user: test -date: Mon Jan 12 13:46:40 1970 +0000 -summary: 0.0 - -changeset: 1:f4ac749470f2 -tag: foo -branch: foo -user: test -date: Mon Jan 12 13:46:40 1970 +0000 -summary: 1 -
--- a/tests/test-commit.out Sat Feb 17 09:54:44 2007 +0100 +++ b/tests/test-commit.out Tue Feb 20 20:55:23 2007 +0100 @@ -21,8 +21,7 @@ dir/file does-not-exist: No such file or directory abort: file .../test/does-not-exist not found! -baz: unsupported file type (type is symbolic link) -abort: can't commit .../test/baz: unsupported file type! +abort: file .../test/baz not tracked! abort: file .../test/quux not tracked! dir/file % partial subdir commit test
--- a/tests/test-grep Sat Feb 17 09:54:44 2007 +0100 +++ b/tests/test-grep Tue Feb 20 20:55:23 2007 +0100 @@ -17,8 +17,11 @@ head -n 3 port > port1 mv port1 port hg commit -m 4 -u spam -d '4 0' +echo % simple hg grep port port +echo % all hg grep --all -nu port port +echo % other hg grep import port hg cp port port2 @@ -28,3 +31,22 @@ echo deport >> port2 hg commit -m 5 -u eggs -d '6 0' hg grep -f --all -nu port port2 + +cd .. +hg init t2 +cd t2 +hg grep foobar foo +hg grep foobar +echo blue >> color +echo black >> color +hg add color +hg ci -m 0 -d '0 0' +echo orange >> color +hg ci -m 1 -d '0 0' +echo black > color +hg ci -m 2 -d '0 0' +echo orange >> color +echo blue >> color +hg ci -m 3 -d '0 0' +hg grep orange +hg grep --all orange
--- a/tests/test-grep.out Sat Feb 17 09:54:44 2007 +0100 +++ b/tests/test-grep.out Tue Feb 20 20:55:23 2007 +0100 @@ -1,6 +1,8 @@ +% simple port:4:export port:4:vaportight port:4:import/export +% all port:4:4:-:spam:import/export port:3:4:+:eggs:import/export port:2:1:-:spam:import @@ -10,6 +12,7 @@ port:2:3:+:spam:import/export port:1:2:+:eggs:export port:0:1:+:spam:import +% other port:4:import/export % follow port:0:import @@ -23,3 +26,7 @@ port:2:3:+:spam:import/export port:1:2:+:eggs:export port:0:1:+:spam:import +color:3:orange +color:3:+:orange +color:2:-:orange +color:1:+:orange
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-hgweb Tue Feb 20 20:55:23 2007 +0100 @@ -0,0 +1,13 @@ +#!/bin/sh + +hg init test +cd test +mkdir da +echo foo > da/foo +echo foo > foo +hg ci -Ambase -d '0 0' +hg serve -p 20060 -d --pid-file=hg.pid +echo % manifest +("$TESTDIR/get-with-headers.py" localhost:20060 '/file/tip/?style=raw') +("$TESTDIR/get-with-headers.py" localhost:20060 '/file/tip/da?style=raw') +kill `cat hg.pid`
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-hgweb.out Tue Feb 20 20:55:23 2007 +0100 @@ -0,0 +1,16 @@ +adding da/foo +adding foo +% manifest +200 Script output follows + + +drwxr-xr-x da +-rw-r--r-- 4 foo + + +200 Script output follows + + +-rw-r--r-- 4 foo + +
--- a/tests/test-hook Sat Feb 17 09:54:44 2007 +0100 +++ b/tests/test-hook Tue Feb 20 20:55:23 2007 +0100 @@ -183,4 +183,26 @@ echo a >> a hg --traceback commit -A -m a 2>&1 | grep '^Traceback' +cd .. +hg init c +cd c + +cat > hookext.py <<EOF +def autohook(**args): + print "Automatically installed hook" + +def reposetup(ui, repo): + repo.ui.setconfig("hooks", "commit.auto", autohook) +EOF +echo '[extensions]' >> .hg/hgrc +echo 'hookext = hookext.py' >> .hg/hgrc + +touch foo +hg add foo +hg ci -m 'add foo' +echo >> foo +hg ci --debug -m 'change foo' | sed -e 's/ at .*>/>/' + +hg showconfig hooks | sed -e 's/ at .*>/>/' + exit 0
--- a/tests/test-hook.out Sat Feb 17 09:54:44 2007 +0100 +++ b/tests/test-hook.out Tue Feb 20 20:55:23 2007 +0100 @@ -138,3 +138,8 @@ (run 'hg update' to get a working copy) # make sure --traceback works Traceback (most recent call last): +Automatically installed hook +foo +calling hook commit.auto: <function autohook> +Automatically installed hook +hooks.commit.auto=<function autohook>
--- a/tests/test-mq Sat Feb 17 09:54:44 2007 +0100 +++ b/tests/test-mq Tue Feb 20 20:55:23 2007 +0100 @@ -40,6 +40,40 @@ hg -R c qnew test.patch hg -R c/.hg/patches st +echo '% qinit; qinit -c' +hg init d +cd d +hg qinit +hg qinit -c +# qinit -c should create both files if they don't exist +echo ' .hgignore:' +cat .hg/patches/.hgignore +echo ' series:' +cat .hg/patches/series +hg qinit -c 2>&1 | sed -e 's/repository.*already/repository already/' +cd .. + +echo '% qinit; <stuff>; qinit -c' +hg init e +cd e +hg qnew A +echo foo > foo +hg add foo +hg qrefresh +hg qnew B +echo >> foo +hg qrefresh +echo status >> .hg/patches/.hgignore +echo bleh >> .hg/patches/.hgignore +hg qinit -c +hg -R .hg/patches status +# qinit -c shouldn't touch these files if they already exist +echo ' .hgignore:' +cat .hg/patches/.hgignore +echo ' series:' +cat .hg/patches/series +cd .. + cd a echo % qnew -m @@ -135,6 +169,46 @@ hg qpop -a hg push ../../k +echo % qpush/qpop error codes +errorcode() +{ + hg "$@" && echo " $@ succeeds" || echo " $@ fails" +} + +# we want to start with some patches applied +hg qpush -a +echo " % pops all patches and succeeds" +errorcode qpop -a +echo " % does nothing and succeeds" +errorcode qpop -a +echo " % fails - nothing else to pop" +errorcode qpop +echo " % pushes a patch and succeeds" +errorcode qpush +echo " % pops a patch and succeeds" +errorcode qpop +echo " % pushes up to test1b.patch and succeeds" +errorcode qpush test1b.patch +echo " % does nothing and succeeds" +errorcode qpush test1b.patch +echo " % does nothing and succeeds" +errorcode qpop test1b.patch +echo " % fails - can't push to this patch" +errorcode qpush test.patch +echo " % fails - can't pop to this patch" +errorcode qpop test2.patch +echo " % pops up to test.patch and succeeds" +errorcode qpop test.patch +echo " % pushes all patches and succeeds" +errorcode qpush -a +echo " % does nothing and succeeds" +errorcode qpush -a +echo " % fails - nothing else to push" +errorcode qpush +echo " % does nothing and succeeds" +errorcode qpush test2.patch + + echo % strip cd ../../b echo x>x
--- a/tests/test-mq.out Sat Feb 17 09:54:44 2007 +0100 +++ b/tests/test-mq.out Tue Feb 20 20:55:23 2007 +0100 @@ -60,6 +60,26 @@ A .hgignore A series A test.patch +% qinit; qinit -c + .hgignore: +syntax: glob +status +guards + series: +abort: repository already exists! +% qinit; <stuff>; qinit -c +adding A +adding B +A .hgignore +A A +A B +A series + .hgignore: +status +bleh + series: +A +B % qnew -m foo bar % qrefresh @@ -136,6 +156,61 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files +% qpush/qpop error codes +applying test.patch +applying test1b.patch +applying test2.patch +Now at: test2.patch + % pops all patches and succeeds +Patch queue now empty + qpop -a succeeds + % does nothing and succeeds +no patches applied + qpop -a succeeds + % fails - nothing else to pop +no patches applied + qpop fails + % pushes a patch and succeeds +applying test.patch +Now at: test.patch + qpush succeeds + % pops a patch and succeeds +Patch queue now empty + qpop succeeds + % pushes up to test1b.patch and succeeds +applying test.patch +applying test1b.patch +Now at: test1b.patch + qpush test1b.patch succeeds + % does nothing and succeeds +qpush: test1b.patch is already at the top + qpush test1b.patch succeeds + % does nothing and succeeds +qpop: test1b.patch is already at the top + qpop test1b.patch succeeds + % fails - can't push to this patch +abort: cannot push to a previous patch: test.patch + qpush test.patch fails + % fails - can't pop to this patch +abort: patch test2.patch is not applied + qpop test2.patch fails + % pops up to test.patch and succeeds +Now at: test.patch + qpop test.patch succeeds + % pushes all patches and succeeds +applying test1b.patch +applying test2.patch +Now at: test2.patch + qpush -a succeeds + % does nothing and succeeds +all patches are currently applied + qpush -a succeeds + % fails - nothing else to push +patch series already fully applied + qpush fails + % does nothing and succeeds +all patches are currently applied + qpush test2.patch succeeds % strip adding x 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
--- a/tests/test-symlinks.out Sat Feb 17 09:54:44 2007 +0100 +++ b/tests/test-symlinks.out Tue Feb 20 20:55:23 2007 +0100 @@ -1,11 +1,12 @@ +adding bar adding foo adding bomb adding a.c adding dir/a.o adding dir/b.o +M dir/b.o ! a.c ! dir/a.o -! dir/b.o ? .hgignore a.c: unsupported file type (type is fifo) ! a.c
--- a/tests/test-transplant Sat Feb 17 09:54:44 2007 +0100 +++ b/tests/test-transplant Tue Feb 20 20:55:23 2007 +0100 @@ -22,28 +22,25 @@ hg log --template '{rev} {parents} {desc}\n' -cd .. -hg clone t rebase -cd rebase +hg clone . ../rebase +cd ../rebase hg up -C 1 echo '% rebase b onto r1' hg transplant -a -b tip hg log --template '{rev} {parents} {desc}\n' -cd .. -hg clone t prune -cd prune +hg clone ../t ../prune +cd ../prune hg up -C 1 echo '% rebase b onto r1, skipping b2' hg transplant -a -b tip -p 3 hg log --template '{rev} {parents} {desc}\n' -cd .. echo '% remote transplant' -hg clone -r 1 t remote -cd remote +hg clone -r 1 ../t ../remote +cd ../remote hg transplant --log -s ../t 2 4 hg log --template '{rev} {parents} {desc}\n' @@ -54,11 +51,19 @@ echo '% skip local changes transplanted to the source' echo b4 > b4 hg ci -Amb4 -d '3 0' -cd .. -hg clone t pullback -cd pullback +hg clone ../t ../pullback +cd ../pullback hg transplant -s ../remote -a -b tip +echo '% remote transplant with pull' +hg -R ../t serve -p 20062 -d --pid-file=../t.pid +cat ../t.pid >> $DAEMON_PIDS + +hg clone -r 0 ../t ../rp +cd ../rp +hg transplant -s http://localhost:20062/ 2 4 +hg log --template '{rev} {parents} {desc}\n' + echo '% transplant --continue' hg init ../tc cd ../tc
--- a/tests/test-transplant.out Sat Feb 17 09:54:44 2007 +0100 +++ b/tests/test-transplant.out Tue Feb 20 20:55:23 2007 +0100 @@ -75,6 +75,24 @@ searching for changes applying 4333daefcb15 4333daefcb15 transplanted to 5f42c04e07cc +% remote transplant with pull +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +searching for changes +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +applying a53251cdf717 +a53251cdf717 transplanted to 8d9279348abb +2 b3 +1 b1 +0 r1 % transplant --continue adding foo adding toremove
--- a/tests/test-ui-config Sat Feb 17 09:54:44 2007 +0100 +++ b/tests/test-ui-config Tue Feb 20 20:55:23 2007 +0100 @@ -1,5 +1,6 @@ #!/usr/bin/env python +import ConfigParser from mercurial import ui, util, commands testui = ui.ui() @@ -70,3 +71,21 @@ except util.Abort, inst: print inst print "---" + +cp = util.configparser() +cp.add_section('foo') +cp.set('foo', 'bar', 'baz') +try: + # should fail - keys are case-sensitive + cp.get('foo', 'Bar') +except ConfigParser.NoOptionError, inst: + print inst + +def function(): + pass + +cp.add_section('hook') +# values that aren't strings should work +cp.set('hook', 'commit', function) +f = cp.get('hook', 'commit') +print "f %s= function" % (f == function and '=' or '!')
--- a/tests/test-ui-config.out Sat Feb 17 09:54:44 2007 +0100 +++ b/tests/test-ui-config.out Tue Feb 20 20:55:23 2007 +0100 @@ -43,3 +43,5 @@ Error in configuration section [interpolation] parameter 'value5': '%' must be followed by '%' or '(', found: '%bad2' --- +No option 'Bar' in section: 'foo' +f == function