Mercurial > hg
changeset 4237:da6b14877195
Merge some matcher fixes/cleanups and add some more hg locate tests
author | Alexis S. L. Carvalho <alexis@cecm.usp.br> |
---|---|
date | Fri, 16 Mar 2007 22:58:49 -0300 |
parents | 0d51eb296fb9 (diff) 34c4540c04c5 (current diff) |
children | 815ad65cfca9 |
files | mercurial/util.py tests/test-revert tests/test-revert.out |
diffstat | 127 files changed, 2408 insertions(+), 1971 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Fri Mar 16 22:48:23 2007 -0300 +++ b/Makefile Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/README Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/contrib/bash_completion Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/contrib/churn.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/contrib/convert-repo Fri Mar 16 22:58:49 2007 -0300 @@ -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 -u --cvs-direct -q"): + 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].rstrip() + 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 + "/objects"): + 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 Fri Mar 16 22:48:23 2007 -0300 +++ b/contrib/hgk Fri Mar 16 22:58:49 2007 -0300 @@ -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/contrib/purge/README Fri Mar 16 22:48:23 2007 -0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -What is "hg purge"? -=================== -"purge" is a simple extension for the Mercurial source control management -system (http://www.selenic.com/mercurial). -This extension adds a "purge" command to "hg" that removes files not known -to Mercurial, this is useful to test local and uncommitted changes in the -otherwise clean source tree. - -This means that Mercurial will delete: - - Unknown files: files marked with "?" by "hg status" - - Ignored files: files usually ignored by Mercurial because they match a - pattern in a ".hgignore" file - - Empty directories: infact Mercurial ignores directories unless they - contain files under source control managment -But it will leave untouched: - - Unmodified files tracked by Mercurial - - Modified files tracked by Mercurial - - New files added to the repository (with "hg add") - -Be careful with "hg purge", you could irreversibly delete some files you -forgot to add to the repository. If you only want to print the list of -files that this program would delete use: - hg purge --print - -To get the most recent version of "hg purge" visit its home page: - http://www.barisione.org/apps.html#hg-purge - -This program was inspired by the "cvspurge" script contained in CVS utilities -(http://www.red-bean.com/cvsutils/). - - -How to install -============== -The purge extension is distributed with Mercurial, to activate it you need to -put these lines in your ~/.hgrc: - - [extensions] - hgext.purge= - -For more information on the configuration files see the man page for "hgrc": - man 5 hgrc - - -How to use "hg purge" -==================== -For help on the usage of "hg purge" use: - hg help purge - - -License -======= -Copyright (C) 2006 - Marco Barisione <marco@barisione.org> - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -A copy of the GNU General Public License is distributed along -with Mercurial in the file COPYING.
--- a/contrib/purge/purge.py Fri Mar 16 22:48:23 2007 -0300 +++ b/contrib/purge/purge.py Fri Mar 16 22:58:49 2007 -0300 @@ -3,6 +3,16 @@ # This is a small extension for Mercurial (http://www.selenic.com/mercurial) # that removes files not known to mercurial # +# This program was inspired by the "cvspurge" script contained in CVS utilities +# (http://www.red-bean.com/cvsutils/). +# +# To enable the "purge" extension put these lines in your ~/.hgrc: +# [extensions] +# purge = /path/to/purge.py +# +# For help on the usage of "hg purge" use: +# hg help purge +# # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or @@ -18,118 +28,46 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. from mercurial import hg, util +from mercurial.i18n import _ import os -def _(s): - return s - -class Purge(object): - def __init__(self, act=True, abort_on_err=False, eol='\n'): - self._repo = None - self._ui = None - self._hg_root = None - self._act = act - self._abort_on_err = abort_on_err - self._eol = eol - - def purge(self, ui, repo, dirs=None): - self._repo = repo - self._ui = ui - self._hg_root = self._split_path(repo.root) - - if not dirs: - dirs = [repo.root] - - for path in dirs: - path = os.path.abspath(path) - for root, dirs, files in os.walk(path, topdown=False): - if '.hg' in self._split_path(root): - # Skip files in the .hg directory. - # Note that if the repository is in a directory - # called .hg this command does not work. - continue - for name in files: - self._remove_file(os.path.join(root, name)) - if not os.listdir(root): - # Remove this directory if it is empty. - self._remove_dir(root) - - self._repo = None - self._ui = None - self._hg_root = None - - def _error(self, msg): - if self._abort_on_err: +def dopurge(ui, repo, dirs=None, act=True, abort_on_err=False, eol='\n'): + def error(msg): + if abort_on_err: raise util.Abort(msg) else: - self._ui.warn(_('warning: %s\n') % msg) + ui.warn(_('warning: %s\n') % msg) - def _remove_file(self, name): - relative_name = self._relative_name(name) - # dirstate.state() requires a path relative to the root - # directory. - if self._repo.dirstate.state(relative_name) != '?': - return - self._ui.note(_('Removing file %s\n') % name) - if self._act: + def remove(remove_func, name): + if act: try: - os.remove(name) + remove_func(os.path.join(repo.root, name)) except OSError, e: - self._error(_('%s cannot be removed') % name) + error(_('%s cannot be removed') % name) else: - self._ui.write('%s%s' % (name, self._eol)) - - def _remove_dir(self, name): - self._ui.note(_('Removing directory %s\n') % name) - if self._act: - try: - os.rmdir(name) - except OSError, e: - self._error(_('%s cannot be removed') % name) - else: - self._ui.write('%s%s' % (name, self._eol)) + ui.write('%s%s' % (name, eol)) - def _relative_name(self, path): - ''' - Returns "path" but relative to the root directory of the - repository and with '\\' replaced with '/'. - This is needed because this is the format required by - self._repo.dirstate.state(). - ''' - splitted_path = self._split_path(path)[len(self._hg_root):] - # Even on Windows self._repo.dirstate.state() wants '/'. - return self._join_path(splitted_path).replace('\\', '/') + directories = [] + files = [] + roots, match, anypats = util.cmdmatcher(repo.root, repo.getcwd(), dirs) + for src, f, st in repo.dirstate.statwalk(files=roots, match=match, + ignored=True, directories=True): + if src == 'd': + directories.append(f) + elif src == 'f' and f not in repo.dirstate: + files.append(f) - def _split_path(self, path): - ''' - Returns a list of the single files/directories in "path". - For instance: - '/home/user/test' -> ['/', 'home', 'user', 'test'] - 'C:\\Mercurial' -> ['C:\\', 'Mercurial'] - ''' - ret = [] - while True: - head, tail = os.path.split(path) - if tail: - ret.append(tail) - if head == path: - ret.append(head) - break - path = head - ret.reverse() - return ret + directories.sort() - def _join_path(self, splitted_path): - ''' - Joins a list returned by _split_path(). - ''' - ret = '' - for part in splitted_path: - if ret: - ret = os.path.join(ret, part) - else: - ret = part - return ret + for f in files: + if f not in repo.dirstate: + ui.note(_('Removing file %s\n') % f) + remove(os.remove, f) + + for f in directories[::-1]: + if not os.listdir(repo.wjoin(f)): + ui.note(_('Removing directory %s\n') % f) + remove(os.rmdir, f) def purge(ui, repo, *dirs, **opts): @@ -162,8 +100,7 @@ if eol == '\0': # --print0 implies --print act = False - p = Purge(act, abort_on_err, eol) - p.purge(ui, repo, dirs) + dopurge(ui, repo, dirs, act, abort_on_err, eol) cmdtable = {
--- a/doc/Makefile Fri Mar 16 22:48:23 2007 -0300 +++ b/doc/Makefile Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/doc/hgrc.5.txt Fri Mar 16 22:58:49 2007 -0300 @@ -215,6 +215,15 @@ # (this extension will get loaded from the file specified) myfeature = ~/.hgext/myfeature.py +format:: + + usestore;; + Enable or disable the "store" repository format which improves + compatibility with systems that fold case or otherwise mangle + filenames. Enabled by default. Disabling this option will allow + you to store longer filenames in some situations at the expense of + compatibility. + hooks:: Commands or Python functions that get automatically executed by various actions such as starting or finishing a commit. Multiple @@ -507,6 +516,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 Fri Mar 16 22:48:23 2007 -0300 +++ b/hg Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/hgext/acl.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/hgext/bugzilla.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/hgext/extdiff.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/hgext/fetch.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/hgext/gpg.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/hgext/hbisect.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/hgext/hgk.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/hgext/mq.py Fri Mar 16 22:58:49 2007 -0300 @@ -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: @@ -304,6 +306,15 @@ message.insert(0, subject) return (message, comments, user, date, diffstart > 1) + def removeundo(self, repo): + undo = repo.sjoin('undo') + if not os.path.exists(undo): + return + try: + os.unlink(undo) + except OSError, inst: + self.ui.warn('error removing undo: %s\n' % str(inst)) + def printdiff(self, repo, node1, node2=None, files=None, fp=None, changes=None, opts={}): fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts) @@ -328,11 +339,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: @@ -346,6 +358,7 @@ patchf.write(comments) self.printdiff(repo, head, n, fp=patchf) patchf.close() + self.removeundo(repo) return (0, n) def qparents(self, repo, rev=None): @@ -378,6 +391,7 @@ pname = ".hg.patches.merge.marker" n = repo.commit(None, '[mq]: merge marker', user=None, force=1, wlock=wlock) + self.removeundo(repo) self.applied.append(statusentry(revlog.hex(n), pname)) self.applied_dirty = 1 @@ -485,6 +499,7 @@ err = 1 break tr.close() + self.removeundo(repo) return (err, n) def delete(self, repo, patches, opts): @@ -583,6 +598,7 @@ if r: r.add([patch]) if commitfiles: self.refresh(repo, short=True) + self.removeundo(repo) def strip(self, repo, rev, update=True, backup="all", wlock=None): def limitheads(chlog, stop): @@ -614,15 +630,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,11 +717,12 @@ backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip') chgrpfile = bundle(backupch) - stripall(rev, revnum) + stripall(revnum) change = chlog.read(rev) chlog.strip(revnum, revnum) repo.manifest.strip(repo.manifest.rev(change[0]), revnum) + self.removeundo(repo) if saveheads: self.ui.status("adding branch\n") commands.unbundle(self.ui, repo, "file:%s" % chgrpfile, @@ -802,10 +816,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 +868,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 +880,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() @@ -1054,7 +1084,7 @@ mm.append(m[i]) del m[i] repo.dirstate.update(m, 'n') - repo.dirstate.update(mm, 'n', st_mtime=0) + repo.dirstate.update(mm, 'n', st_mtime=-1, st_size=-1) repo.dirstate.forget(forget) if not msg: @@ -1070,6 +1100,7 @@ force=1, wlock=wlock) self.applied[-1] = statusentry(revlog.hex(n), patchfn) self.applied_dirty = 1 + self.removeundo(repo) else: self.printdiff(repo, patchparent, fp=patchf) patchf.close() @@ -1090,9 +1121,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) @@ -1248,6 +1283,7 @@ return 1 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line')) self.applied_dirty = 1 + self.removeundo(repo) def full_series_end(self): if len(self.applied) > 0: @@ -1348,7 +1384,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) @@ -1370,7 +1406,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: @@ -1384,7 +1420,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) @@ -1475,13 +1511,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): @@ -1600,6 +1639,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) @@ -1771,7 +1813,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']: @@ -1797,9 +1840,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 @@ -1822,7 +1866,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) @@ -2027,7 +2071,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) @@ -2041,14 +2085,15 @@ if not q.applied: return tagscache - mqtags = [(patch.rev, patch.name) for patch in q.applied] + mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied] mqtags.append((mqtags[-1][0], 'qtip')) mqtags.append((mqtags[0][0], 'qbase')) + mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent')) for patch in mqtags: if patch[1] in tagscache: self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1]) else: - tagscache[patch[1]] = revlog.bin(patch[0]) + tagscache[patch[1]] = patch[0] return tagscache
--- a/hgext/notify.py Fri Mar 16 22:48:23 2007 -0300 +++ b/hgext/notify.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/hgext/patchbomb.py Fri Mar 16 22:58:49 2007 -0300 @@ -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: @@ -217,8 +216,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' % ( @@ -228,21 +225,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 Fri Mar 16 22:48:23 2007 -0300 +++ b/hgext/transplant.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/appendfile.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/archival.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/bdiff.c Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/bundlerepo.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/changegroup.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/changelog.py Fri Mar 16 22:58:49 2007 -0300 @@ -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): """ @@ -59,7 +58,7 @@ changelog v0 doesn't use extra """ if not text: - return (nullid, "", (0, 0), [], "", {}) + return (nullid, "", (0, 0), [], "", {'branch': 'default'}) last = text.index("\n\n") desc = util.tolocal(text[last + 2:]) l = text[:last].split('\n') @@ -79,6 +78,8 @@ time, timezone, extra = extra_data time, timezone = float(time), int(timezone) extra = self.decode_extra(extra) + if not extra.get('branch'): + extra['branch'] = 'default' files = l[3:] return (manifest, user, (time, timezone), files, desc, extra) @@ -94,6 +95,8 @@ parseddate = "%d %d" % util.parsedate(date) else: parseddate = "%d %d" % util.makedate() + if extra and extra.get("branch") in ("default", ""): + del extra["branch"] if extra: extra = self.encode_extra(extra) parseddate = "%s %s" % (parseddate, extra)
--- a/mercurial/cmdutil.py Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/cmdutil.py Fri Mar 16 22:58:49 2007 -0300 @@ -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, bdiff, util, templater, patch revrangesep = ':' @@ -140,24 +138,32 @@ exact = dict.fromkeys(files) for src, fn in repo.walk(node=node, files=files, match=matchfn, badmatch=badmatch): - yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact + yield src, fn, util.pathto(repo.root, repo.getcwd(), fn), fn in exact def findrenames(repo, added=None, removed=None, threshold=0.5): + '''find renamed files -- yields (before, after, score) tuples''' 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 + bestname, bestscore = None, threshold for r in removed: - rr = repo.file(r).read(mf[r]) - delta = mdiff.textdiff(aa, rr) - if len(delta) < len(aa): - myscore = 1.0 - (float(len(delta)) / len(aa)) - if bestscore is None or myscore > bestscore: - bestscore, bestname = myscore, r - if bestname and bestscore >= threshold: + rr = ctx.filectx(r).data() + + # bdiff.blocks() returns blocks of matching lines + # count the number of bytes in each + equal = 0 + alines = mdiff.splitnewlines(aa) + matches = bdiff.blocks(aa, rr) + for x1,x2,y1,y2 in matches: + for line in alines[x1:x2]: + equal += len(line) + + myscore = equal*2.0 / (len(aa)+len(rr)) + if myscore >= bestscore: + bestname, bestscore = r, myscore + if bestname: yield bestname, a, bestscore def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None, @@ -174,7 +180,8 @@ mapping[abs] = rel, exact if repo.ui.verbose or not exact: repo.ui.status(_('adding %s\n') % ((pats and rel) or abs)) - if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel): + islink = os.path.islink(rel) + if repo.dirstate.state(abs) != 'r' and not islink and not os.path.exists(rel): remove.append(abs) mapping[abs] = rel, exact if repo.ui.verbose or not exact: @@ -196,12 +203,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 @@ -257,7 +263,8 @@ self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode))) - if branch: + # don't show the default branch name + if branch != 'default': branch = util.tolocal(branch) self.ui.write(_("branch: %s\n") % branch) for tag in self.repo.nodetags(changenode): @@ -265,11 +272,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]))) @@ -317,8 +319,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}', @@ -401,15 +403,9 @@ def showbranches(**args): branch = changes[5].get("branch") - if branch: + if branch != 'default': 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))] @@ -523,11 +519,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: @@ -549,12 +540,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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/commands.py Fri Mar 16 22:58:49 2007 -0300 @@ -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,27 +240,31 @@ 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')) ui.status(_('(use "backout --merge" ' 'if you want to auto-merge)\n')) -def branch(ui, repo, label=None): +def branch(ui, repo, label=None, **opts): """set or show the current branch name With <name>, set the current branch name. Otherwise, show the current branch name. + + Unless --force is specified, branch will not let you set a + branch name that shadows an existing branch. """ - if label is not None: - repo.opener("branch", "w").write(util.fromlocal(label) + '\n') + if label: + if not opts.get('force') and label in repo.branchtags(): + if label not in [p.branch() for p in repo.workingctx().parents()]: + raise util.Abort(_('a branch of the same name already exists' + ' (use --force to override)')) + repo.dirstate.setbranch(util.fromlocal(label)) else: - b = util.tolocal(repo.workingctx().branch()) - if b: - ui.write("%s\n" % b) + ui.write("%s\n" % util.tolocal(repo.dirstate.branch())) def branches(ui, repo): """list repository named branches @@ -489,7 +493,7 @@ # target: ossep def copy(origsrc, abssrc, relsrc, target, exact): abstarget = util.canonpath(repo.root, cwd, target) - reltarget = util.pathto(cwd, abstarget) + reltarget = util.pathto(repo.root, cwd, abstarget) prevsrc = targets.get(abstarget) if prevsrc is not None: ui.warn(_('%s: not overwriting - %s collides with %s\n') % @@ -685,15 +689,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) @@ -704,10 +705,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) @@ -1156,13 +1155,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)) @@ -1178,19 +1178,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() @@ -1216,10 +1214,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 @@ -1228,15 +1226,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 @@ -1447,7 +1444,7 @@ if not ui.quiet: branch = util.tolocal(repo.workingctx().branch()) - if branch: + if branch != 'default': output.append("(%s)" % branch) # multiple tags for a single parent separated by '/' @@ -1543,10 +1540,15 @@ setremoteconfig(ui, opts) other = hg.repository(ui, source) + ui.status(_('comparing with %s\n') % 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: @@ -1714,7 +1716,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': @@ -1781,7 +1782,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 @@ -1795,9 +1796,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 - ' @@ -1829,10 +1828,11 @@ revs = [repo.lookup(rev) for rev in opts['rev']] other = hg.repository(ui, dest) + ui.status(_('comparing with %s\n') % dest) 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() @@ -2145,8 +2145,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: @@ -2171,9 +2172,19 @@ # walk target manifest. + def badmatch(path): + if path in names: + return True + path_ = path + '/' + for f in names: + if f.startswith(path_): + return True + return False + for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node, - badmatch=names.has_key): - if abs in names: continue + badmatch=badmatch): + if abs in names or src == 'b': + continue names[abs] = (rel, exact) target_only[abs] = True @@ -2236,7 +2247,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 @@ -2413,11 +2424,12 @@ format = "%s %%s%s" % (char, end) for f in changes: - ui.write(format % util.pathto(cwd, f)) + ui.write(format % util.pathto(repo.root, cwd, f)) if ((all or opts.get('copies')) and not opts.get('no_status')): copied = repo.dirstate.copied(f) if copied: - ui.write(' %s%s' % (util.pathto(cwd, copied), end)) + ui.write(' %s%s' % (util.pathto(repo.root, cwd, copied), + end)) def tag(ui, repo, name, rev_=None, **opts): """add a tag for the current or given revision @@ -2444,14 +2456,20 @@ "please use 'hg tag [-r REV] NAME' instead\n")) if opts['rev']: raise util.Abort(_("use only one form to specify the revision")) + if opts['rev'] and opts['remove']: + raise util.Abort(_("--rev and --remove are incompatible")) if opts['rev']: rev_ = opts['rev'] + message = opts['message'] + if opts['remove']: + rev_ = nullid + if not message: + message = _('Removed tag %s') % name if not rev_ and repo.dirstate.parents()[1] != nullid: raise util.Abort(_('uncommitted merge - please provide a ' 'specific revision')) r = repo.changectx(rev_).node() - message = opts['message'] if not message: message = _('Added tag %s for changeset %s') % (name, short(r)) @@ -2470,9 +2488,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: @@ -2500,10 +2519,11 @@ 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. + Update the working directory to the specified revision, or the + tip of the current branch if none is specified. If there are no outstanding changes in the working directory and there is a linear relationship between the current version and the @@ -2520,40 +2540,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: - if node: - node = repo.lookup(node) - else: - wc = repo.workingctx() - node = repo.branchtags()[wc.branch()] - return node - def verify(ui, repo): """verify the integrity of the repository @@ -2654,7 +2645,10 @@ ('u', 'user', '', _('record user as committer')), ] + walkopts + commitopts, _('hg backout [OPTION]... REV')), - "branch": (branch, [], _('hg branch [NAME]')), + "branch": (branch, + [('f', 'force', None, + _('set branch name even if it shadows an existing branch'))], + _('hg branch [NAME]')), "branches": (branches, [], _('hg branches')), "bundle": (bundle, @@ -2759,8 +2753,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]')), @@ -2771,7 +2764,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...')), @@ -2803,8 +2796,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')), @@ -2825,8 +2817,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')), @@ -2841,8 +2832,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,13 +2930,13 @@ ('m', 'message', '', _('message for tag commit log entry')), ('d', 'date', '', _('record datecode as commit date')), ('u', 'user', '', _('record user as commiter')), - ('r', 'rev', '', _('revision to tag'))], + ('r', 'rev', '', _('revision to tag')), + ('', 'remove', None, _('remove a tag'))], _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')), "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]')), @@ -2957,9 +2947,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')), @@ -3116,9 +3104,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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/context.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 @@ -67,7 +66,7 @@ def date(self): return self._changeset[2] def files(self): return self._changeset[3] def description(self): return self._changeset[4] - def branch(self): return self._changeset[5].get("branch", "") + def branch(self): return self._changeset[5].get("branch") def parents(self): """return contexts for each parent changeset""" @@ -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 @@ -411,11 +412,7 @@ def deleted(self): return self._status[3] def unknown(self): return self._status[4] def clean(self): return self._status[5] - def branch(self): - try: - return self._repo.opener("branch").read().strip() - except IOError: - return "" + def branch(self): return self._repo.dirstate.branch() def parents(self): """return contexts for each parent changeset""" @@ -424,9 +421,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 +482,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 +503,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 (int(os.lstat(self._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 Fri Mar 16 22:58:49 2007 -0300 @@ -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', '_xmlplus', '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 Fri Mar 16 22:48:23 2007 -0300 +++ /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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/dirstate.py Fri Mar 16 22:58:49 2007 -0300 @@ -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" @@ -25,6 +24,7 @@ self.dirs = None self.copymap = {} self.ignorefunc = None + self._branch = None def wjoin(self, f): return os.path.join(self.root, f) @@ -33,10 +33,14 @@ cwd = os.getcwd() if cwd == self.root: return '' # self.root ends with a path separator if self.root is '/' or 'C:\' - common_prefix_len = len(self.root) - if not self.root.endswith(os.sep): - common_prefix_len += 1 - return cwd[common_prefix_len:] + rootsep = self.root + if not rootsep.endswith(os.sep): + rootsep += os.sep + if cwd.startswith(rootsep): + return cwd[len(rootsep):] + else: + # we're outside the repo. return an absolute path. + return cwd def hgignore(self): '''return the contents of .hgignore files as a list of patterns. @@ -137,6 +141,15 @@ self.lazyread() return self.pl + def branch(self): + if not self._branch: + try: + self._branch = self.opener("branch").read().strip()\ + or "default" + except IOError: + self._branch = "default" + return self._branch + def markdirty(self): if not self.dirty: self.dirty = 1 @@ -146,6 +159,10 @@ self.markdirty() self.pl = p1, p2 + def setbranch(self, branch): + self._branch = branch + self.opener("branch", "w").write(branch + '\n') + def state(self, key): try: return self[key][0] @@ -338,18 +355,17 @@ 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') % ( - util.pathto(self.getcwd(), f), + util.pathto(self.root, self.getcwd(), f), kind)) return False @@ -359,7 +375,7 @@ yield src, f def statwalk(self, files=None, match=util.always, ignored=False, - badmatch=None): + badmatch=None, directories=False): ''' walk recursively through the directory tree, finding all files matched by the match function @@ -367,6 +383,7 @@ results are yielded in a tuple (src, filename, st), where src is one of: 'f' the file was found in the directory tree + 'd' the file is a directory of the tree 'm' the file was only in the dirstate and not in the tree 'b' file was not found and matched badmatch @@ -399,6 +416,8 @@ # recursion free walker, faster than os.walk. def findfiles(s): work = [s] + if directories: + yield 'd', util.normpath(s[common_prefix_len:]), os.lstat(s) while work: top = work.pop() names = os.listdir(top) @@ -425,6 +444,8 @@ if stat.S_ISDIR(st.st_mode): if not ignore(p): work.append(p) + if directories: + yield 'd', np, st if imatch(np) and np in dc: yield 'm', np, st elif imatch(np): @@ -454,7 +475,7 @@ if not found: if inst.errno != errno.ENOENT or not badmatch: self.ui.warn('%s: %s\n' % ( - util.pathto(self.getcwd(), ff), + util.pathto(self.root, self.getcwd(), ff), inst.strerror)) elif badmatch and badmatch(ff) and imatch(nf): yield 'b', ff, None
--- a/mercurial/filelog.py Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/filelog.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/hg.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/hgweb/__init__.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/hgweb/common.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/hgweb/hgweb_mod.py Fri Mar 16 22:58:49 2007 -0300 @@ -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): @@ -623,9 +606,13 @@ 'zip': ('application/zip', 'zip', '.zip', None), } - def archive(self, req, cnode, type_): + def archive(self, req, id, type_): reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame)) - name = "%s-%s" % (reponame, short(cnode)) + cnode = self.repo.lookup(id) + arch_version = id + if cnode == id: + arch_version = short(cnode) + name = "%s-%s" % (reponame, arch_version) mimetype, artype, extension, encoding = self.archive_specs[type_] headers = [('Content-type', mimetype), ('Content-disposition', 'attachment; filename=%s%s' % @@ -789,6 +776,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 +787,7 @@ self.t = templater.templater(mapfile, templater.common_filters, defaults={"url": req.url, + "staticurl": staticurl, "urlbase": urlbase, "repo": self.reponame, "header": header, @@ -873,7 +864,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)) @@ -1002,12 +993,11 @@ req.write(z.flush()) def do_archive(self, req): - changeset = self.repo.lookup(req.form['node'][0]) type_ = req.form['type'][0] allowed = self.configlist("web", "allow_archive") if (type_ in self.archives and (type_ in allowed or self.configbool("web", "allow" + type_, False))): - self.archive(req, changeset, type_) + self.archive(req, req.form['node'][0], type_) return req.write(self.t("error")) @@ -1076,8 +1066,6 @@ headers={'status': '401 Unauthorized'}) return - req.httphdr("application/mercurial-0.1") - their_heads = req.form['heads'][0].split(' ') def check_heads(): @@ -1089,6 +1077,8 @@ bail(_('unsynced changes\n')) return + req.httphdr("application/mercurial-0.1") + # do not lock repo until all changegroup data is # streamed. save to temporary file. @@ -1099,67 +1089,78 @@ for s in util.filechunkiter(req, limit=length): fp.write(s) - lock = self.repo.lock() try: - if not check_heads(): - req.write('0\n') - req.write(_('unsynced changes\n')) - return + lock = self.repo.lock() + try: + if not check_heads(): + req.write('0\n') + req.write(_('unsynced changes\n')) + return - fp.seek(0) - header = fp.read(6) - if not header.startswith("HG"): - # old client with uncompressed bundle - def generator(f): - yield header - for chunk in f: - yield chunk - elif not header.startswith("HG10"): - req.write("0\n") - req.write(_("unknown bundle version\n")) - return - elif header == "HG10GZ": - def generator(f): - zd = zlib.decompressobj() - for chunk in f: - yield zd.decompress(chunk) - elif header == "HG10BZ": - def generator(f): - zd = bz2.BZ2Decompressor() - zd.decompress("BZ") - for chunk in f: - yield zd.decompress(chunk) - elif header == "HG10UN": - def generator(f): - for chunk in f: - yield chunk + fp.seek(0) + header = fp.read(6) + if not header.startswith("HG"): + # old client with uncompressed bundle + def generator(f): + yield header + for chunk in f: + yield chunk + elif not header.startswith("HG10"): + req.write("0\n") + req.write(_("unknown bundle version\n")) + return + elif header == "HG10GZ": + def generator(f): + zd = zlib.decompressobj() + for chunk in f: + yield zd.decompress(chunk) + elif header == "HG10BZ": + def generator(f): + zd = bz2.BZ2Decompressor() + zd.decompress("BZ") + for chunk in f: + yield zd.decompress(chunk) + elif header == "HG10UN": + def generator(f): + for chunk in f: + yield chunk + else: + req.write("0\n") + req.write(_("unknown bundle compression type\n")) + return + gen = generator(util.filechunkiter(fp, 4096)) + + # send addchangegroup output to client + + old_stdout = sys.stdout + sys.stdout = cStringIO.StringIO() + + try: + url = 'remote:%s:%s' % (proto, + req.env.get('REMOTE_HOST', '')) + try: + ret = self.repo.addchangegroup( + util.chunkbuffer(gen), 'serve', url) + except util.Abort, inst: + sys.stdout.write("abort: %s\n" % inst) + ret = 0 + finally: + val = sys.stdout.getvalue() + sys.stdout = old_stdout + req.write('%d\n' % ret) + req.write(val) + finally: + lock.release() + except (OSError, IOError), inst: + req.write('0\n') + filename = getattr(inst, 'filename', '') + # Don't send our filesystem layout to the client + if filename.startswith(self.repo.root): + filename = filename[len(self.repo.root)+1:] else: - req.write("0\n") - req.write(_("unknown bundle compression type\n")) - return - gen = generator(util.filechunkiter(fp, 4096)) - - # send addchangegroup output to client - - old_stdout = sys.stdout - sys.stdout = cStringIO.StringIO() - - try: - url = 'remote:%s:%s' % (proto, - req.env.get('REMOTE_HOST', '')) - try: - ret = self.repo.addchangegroup(util.chunkbuffer(gen), - 'serve', url) - except util.Abort, inst: - sys.stdout.write("abort: %s\n" % inst) - ret = 0 - finally: - val = sys.stdout.getvalue() - sys.stdout = old_stdout - req.write('%d\n' % ret) - req.write(val) - finally: - lock.release() + filename = '' + error = getattr(inst, 'strerror', 'Unknown error') + req.write('%s: %s\n' % (error, filename)) finally: fp.close() os.unlink(tempname)
--- a/mercurial/hgweb/hgwebdir_mod.py Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/hgweb/hgwebdir_mod.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/hgweb/request.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/hgweb/server.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/httprepo.py Fri Mar 16 22:58:49 2007 -0300 @@ -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): @@ -76,6 +75,14 @@ return userpass + '@' + hostport return hostport +# work around a bug in Python < 2.4.2 +# (it leaves a "\n" at the end of Proxy-authorization headers) +class request(urllib2.Request): + def add_header(self, key, val): + if key.lower() == 'proxy-authorization': + val = val.strip() + return urllib2.Request.add_header(self, key, val) + class httpsendfile(file): def __len__(self): return os.fstat(self.fileno()).st_size @@ -239,7 +246,7 @@ if data: self.ui.debug(_("sending %s bytes\n") % headers.get('content-length', 'X')) - resp = urllib2.urlopen(urllib2.Request(cu, data, headers)) + resp = urllib2.urlopen(request(cu, data, headers)) except urllib2.HTTPError, inst: if inst.code == 401: raise util.Abort(_('authorization failed'))
--- a/mercurial/i18n.py Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/i18n.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/localrepo.py Fri Mar 16 22:58:49 2007 -0300 @@ -6,18 +6,15 @@ # 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') supported = ('revlogv1', 'store') - branchcache_features = ('unnamed',) def __del__(self): self.transhandle = None @@ -44,17 +41,19 @@ if not os.path.exists(path): os.mkdir(path) os.mkdir(self.path) - os.mkdir(os.path.join(self.path, "store")) - requirements = ("revlogv1", "store") + requirements = ["revlogv1"] + if parentui.configbool('format', 'usestore', True): + os.mkdir(os.path.join(self.path, "store")) + requirements.append("store") + # create an invalid changelog + self.opener("00changelog.i", "a").write( + '\0\0\0\2' # represents revlogv2 + ' dummy changelog to prevent using the old repo layout' + ) reqfile = self.opener("requires", "w") for r in requirements: reqfile.write("%s\n" % r) reqfile.close() - # create an invalid changelog - self.opener("00changelog.i", "a").write( - '\0\0\0\2' # represents revlogv2 - ' dummy changelog to prevent using the old repo layout' - ) else: raise repo.RepoError(_("repository %s not found") % path) elif create: @@ -120,10 +119,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): @@ -141,32 +144,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): @@ -204,7 +209,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 @@ -212,6 +219,37 @@ tag_disallowed = ':\r\n' + def _tag(self, name, node, message, local, user, date, parent=None): + use_dirstate = parent is None + + for c in self.tag_disallowed: + if c in name: + raise util.Abort(_('%r cannot be used in a tag name') % c) + + self.hook('pretag', throw=True, node=hex(node), tag=name, local=local) + + if local: + # local tags are stored in the current charset + self.opener('localtags', 'a').write('%s %s\n' % (hex(node), name)) + self.hook('tag', node=hex(node), tag=name, local=local) + return + + # committed tags are stored in UTF-8 + line = '%s %s\n' % (hex(node), util.fromlocal(name)) + if use_dirstate: + self.wfile('.hgtags', 'ab').write(line) + else: + ntags = self.filectx('.hgtags', parent).data() + self.wfile('.hgtags', 'ab').write(ntags + line) + if use_dirstate and self.dirstate.state('.hgtags') == '?': + self.add(['.hgtags']) + + tagnode = self.commit(['.hgtags'], message, user, date, p1=parent) + + self.hook('tag', node=hex(node), tag=name, local=local) + + return tagnode + def tag(self, name, node, message, local, user, date): '''tag a revision with a symbolic name. @@ -230,83 +268,90 @@ date: date tuple to use if committing''' - for c in self.tag_disallowed: - if c in name: - raise util.Abort(_('%r cannot be used in a tag name') % c) - - self.hook('pretag', throw=True, node=hex(node), tag=name, local=local) - - if local: - # local tags are stored in the current charset - self.opener('localtags', 'a').write('%s %s\n' % (hex(node), name)) - self.hook('tag', node=hex(node), tag=name, local=local) - return - for x in self.status()[:5]: if '.hgtags' in x: raise util.Abort(_('working copy of .hgtags is changed ' '(please commit .hgtags manually)')) - # committed tags are stored in UTF-8 - line = '%s %s\n' % (hex(node), util.fromlocal(name)) - self.wfile('.hgtags', 'ab').write(line) - if self.dirstate.state('.hgtags') == '?': - self.add(['.hgtags']) - self.commit(['.hgtags'], message, user, date) - self.hook('tag', node=hex(node), tag=name, local=local) + self._tag(name, node, message, local, user, date) def tags(self): '''return a mapping of tag to node''' - if not self.tagscache: - self.tagscache = {} + if self.tagscache: + return self.tagscache + + globaltags = {} - def parsetag(line, context): - if not line: - return + def readtags(lines, fn): + filetags = {} + count = 0 + + def warn(msg): + self.ui.warn(_("%s, line %s: %s\n") % (fn, count, msg)) + + for l in lines: + count += 1 + if not l: + continue s = l.split(" ", 1) if len(s) != 2: - self.ui.warn(_("%s: cannot parse entry\n") % context) - return + warn(_("cannot parse entry")) + continue node, key = s key = util.tolocal(key.strip()) # stored in UTF-8 try: bin_n = bin(node) except TypeError: - self.ui.warn(_("%s: node '%s' is not well formed\n") % - (context, node)) - return + warn(_("node '%s' is not well formed") % node) + continue if bin_n not in self.changelog.nodemap: - self.ui.warn(_("%s: tag '%s' refers to unknown node\n") % - (context, key)) - return - self.tagscache[key] = bin_n + warn(_("tag '%s' refers to unknown node") % key) + continue + + h = {} + if key in filetags: + n, h = filetags[key] + h[n] = True + filetags[key] = (bin_n, h) - # read the tags file from each head, ending with the tip, - # and add each tag found to the map, with "newer" ones - # taking precedence - f = None - for rev, node, fnode in self._hgtagsnodes(): - f = (f and f.filectx(fnode) or - self.filectx('.hgtags', fileid=fnode)) - count = 0 - for l in f.data().splitlines(): - count += 1 - parsetag(l, _("%s, line %d") % (str(f), count)) + for k,nh in filetags.items(): + if k not in globaltags: + globaltags[k] = nh + continue + # we prefer the global tag if: + # it supercedes us OR + # mutual supercedes and it has a higher rank + # otherwise we win because we're tip-most + an, ah = nh + bn, bh = globaltags[k] + if bn != an and an in bh and \ + (bn not in ah or len(bh) > len(ah)): + an = bn + ah.update(bh) + globaltags[k] = an, ah - try: - f = self.opener("localtags") - count = 0 - for l in f: - # localtags are stored in the local character set - # while the internal tag table is stored in UTF-8 - l = util.fromlocal(l) - count += 1 - parsetag(l, _("localtags, line %d") % count) - except IOError: - pass + # read the tags file from each head, ending with the tip + f = None + for rev, node, fnode in self._hgtagsnodes(): + f = (f and f.filectx(fnode) or + self.filectx('.hgtags', fileid=fnode)) + readtags(f.data().splitlines(), f) - self.tagscache['tip'] = self.changelog.tip() + try: + data = util.fromlocal(self.opener("localtags").read()) + # localtags are stored in the local character set + # while the internal tag table is stored in UTF-8 + readtags(data.splitlines(), "localtags") + except IOError: + pass + + self.tagscache = {} + for k,nh in globaltags.items(): + n = nh[0] + if n != nullid: + self.tagscache[k] = n + self.tagscache['tip'] = self.changelog.tip() return self.tagscache @@ -320,7 +365,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: @@ -374,25 +419,9 @@ def _readbranchcache(self): partial = {} try: - f = self.opener("branches.cache") + f = self.opener("branch.cache") lines = f.read().split('\n') f.close() - features = lines.pop(0).strip() - if not features.startswith('features: '): - raise ValueError(_('branch cache: no features specified')) - features = features.split(' ', 1)[1].split() - missing_features = [] - for feature in self.branchcache_features: - try: - features.remove(feature) - except ValueError, inst: - missing_features.append(feature) - if missing_features: - raise ValueError(_('branch cache: missing features: %s') - % ', '.join(missing_features)) - if features: - raise ValueError(_('branch cache: unknown features: %s') - % ', '.join(features)) last, lrev = lines.pop(0).split(" ", 1) last, lrev = bin(last), int(lrev) if not (lrev < self.changelog.count() and @@ -413,8 +442,7 @@ def _writebranchcache(self, branches, tip, tiprev): try: - f = self.opener("branches.cache", "w") - f.write(" features: %s\n" % ' '.join(self.branchcache_features)) + f = self.opener("branch.cache", "w") f.write("%s %s\n" % (hex(tip), tiprev)) for label, node in branches.iteritems(): f.write("%s %s\n" % (hex(node), label)) @@ -497,17 +525,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) @@ -515,23 +541,36 @@ 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: + f = self.wjoin(filename) + try: + os.unlink(f) + except OSError: + pass + d = os.path.dirname(f) + if not os.path.exists(d): + os.makedirs(d) + os.symlink(data, f) + 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 @@ -671,11 +710,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, @@ -727,7 +766,7 @@ branchname = "" if use_dirstate: - oldname = c1[5].get("branch", "") # stored in UTF-8 + oldname = c1[5].get("branch") # stored in UTF-8 if not commit and not remove and not force and p2 == nullid and \ branchname == oldname: self.ui.status(_("nothing changed\n")) @@ -749,12 +788,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 @@ -764,11 +805,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() @@ -783,8 +826,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("") @@ -802,17 +847,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 @@ -854,7 +902,7 @@ yield 'b', fn else: self.ui.warn(_('%s: No such file in rev %s\n') % ( - util.pathto(self.getcwd(), fn), short(node))) + util.pathto(self.root, self.getcwd(), fn), short(node))) else: for src, fn in self.dirstate.walk(files, match, badmatch=badmatch): yield src, fn @@ -867,9 +915,9 @@ If node2 is None, compare node1 with working directory. """ - def fcmp(fn, mf): + def fcmp(fn, getnode): t1 = self.wread(fn) - return self.file(fn).cmp(mf.get(fn, nullid), t1) + return self.file(fn).cmp(getnode(fn), t1) def mfmatches(node): change = self.changelog.read(node) @@ -907,9 +955,11 @@ if compareworking: if lookup: # do a full compare of any files that might have changed - mf2 = mfmatches(self.dirstate.parents()[0]) + mnode = self.changelog.read(self.dirstate.parents()[0])[0] + getnode = lambda fn: (self.manifest.find(mnode, fn)[0] or + nullid) for f in lookup: - if fcmp(f, mf2): + if fcmp(f, getnode): modified.append(f) else: clean.append(f) @@ -920,9 +970,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] @@ -938,10 +990,12 @@ # reasonable order mf2keys = mf2.keys() mf2keys.sort() + getnode = lambda fn: mf1.get(fn, nullid) for fn in mf2keys: if mf1.has_key(fn): if mf1.flags(fn) != mf2.flags(fn) or \ - (mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1))): + (mf1[fn] != mf2[fn] and (mf2[fn] != "" or + fcmp(fn, getnode))): modified.append(fn) elif list_clean: clean.append(fn) @@ -961,11 +1015,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: @@ -1012,8 +1067,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): @@ -1036,112 +1090,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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/lock.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/mail.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/manifest.py Fri Mar 16 22:58:49 2007 -0300 @@ -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): @@ -108,7 +106,7 @@ def find(self, node, f): '''look up entry for a single file efficiently. - return (node, flag) pair if found, (None, None) if not.''' + return (node, flags) pair if found, (None, None) if not.''' if self.mapcache and node == self.mapcache[0]: return self.mapcache[1].get(f), self.mapcache[1].flags(f) text = self.revision(node) @@ -117,7 +115,7 @@ return None, None l = text[start:end] f, n = l.split('\0') - return bin(n[:40]), n[40:-1] == 'x' + return bin(n[:40]), n[40:-1] def add(self, map, transaction, link, p1=None, p2=None, changed=None):
--- a/mercurial/mdiff.py Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/mdiff.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/merge.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 @@ -221,12 +221,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)) @@ -251,21 +256,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) @@ -296,7 +301,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) @@ -305,14 +310,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 @@ -336,7 +341,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 @@ -347,35 +352,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 @@ -445,9 +447,15 @@ if not wlock: wlock = repo.wlock() + wc = repo.workingctx() + if node is None: + # tip of current branch + try: + node = repo.branchtags()[wc.branch()] + except KeyError: + raise util.Abort(_("branch %s not found") % wc.branch()) overwrite = force and not branchmerge forcemerge = force and branchmerge - wc = repo.workingctx() pl = wc.parents() p1, p2 = pl[0], repo.changectx(node) pa = p1.ancestor(p2) @@ -488,9 +496,9 @@ if not partial: recordupdates(repo, action, branchmerge) repo.dirstate.setparents(fp1, fp2) + if not branchmerge: + repo.dirstate.setbranch(p2.branch()) repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3]) - if not branchmerge: - repo.opener("branch", "w").write(p2.branch() + "\n") return stats
--- a/mercurial/mpatch.c Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/mpatch.c Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/node.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ /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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/patch.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 @@ -49,8 +48,6 @@ fd, tmpname = tempfile.mkstemp(prefix='hg-patch-') tmpfp = os.fdopen(fd, 'w') try: - hgpatch = False - msg = email.Parser.Parser().parse(fileobj) message = msg['Subject'] @@ -59,6 +56,10 @@ date = None if message: + if message.startswith('[PATCH'): + pend = message.find(']') + if pend >= 0: + message = message[pend+1:].lstrip() message = message.replace('\n\t', ' ') ui.debug('Subject: %s\n' % message) if user: @@ -74,6 +75,9 @@ payload = part.get_payload(decode=True) m = diffre.search(payload) if m: + hgpatch = False + ignoretext = False + ui.debug(_('found patch at byte %d\n') % m.start(0)) diffs_seen += 1 cfp = cStringIO.StringIO() @@ -93,7 +97,9 @@ ui.debug('From: %s\n' % user) elif line.startswith("# Date "): date = line[7:] - if not line.startswith('# '): + elif line == '---' and 'git-send-email' in msg['X-Mailer']: + ignoretext = True + if not line.startswith('# ') and not ignoretext: cfp.write(line) cfp.write('\n') message = cfp.getvalue() @@ -353,7 +359,7 @@ cfiles = patches.keys() cwd = repo.getcwd() if cwd: - cfiles = [util.pathto(cwd, f) for f in patches.keys()] + cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()] for f in patches: ctype, gp = patches[f] if ctype == 'RENAME': @@ -378,8 +384,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 +448,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] @@ -470,67 +475,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 @@ -544,20 +520,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' @@ -568,13 +545,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' @@ -583,7 +557,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): @@ -592,14 +566,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' @@ -609,7 +580,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) @@ -621,27 +595,28 @@ 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, seqno=seqno, revwidth=revwidth) - if fp not in (sys.stdout, repo.ui): + if fp != sys.stdout and hasattr(fp, 'name'): 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) @@ -649,7 +624,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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/repo.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/revlog.py Fri Mar 16 22:58:49 2007 -0300 @@ -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): """ @@ -425,10 +428,13 @@ self.nodemap[e[-1]] = n n += 1 if inline: + if e[1] < 0: + break off += e[1] if off > l: # some things don't seek well, just read it fp.read(off - l) + break if not st: break @@ -472,7 +478,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 +773,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 +802,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 +822,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 +1162,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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/sshrepo.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/sshserver.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/statichttprepo.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/streamclone.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/templater.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/transaction.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/ui.py Fri Mar 16 22:58:49 2007 -0300 @@ -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") @@ -362,7 +361,7 @@ def expandpath(self, loc, default=None): """Return repository location relative to cwd or from [paths]""" - if "://" in loc or os.path.isdir(loc): + if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')): return loc path = self.config("paths", loc) @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/util.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 @@ -314,13 +325,22 @@ _globchars = {'[': 1, '{': 1, '*': 1, '?': 1} -def pathto(n1, n2): +def pathto(root, n1, n2): '''return the relative path from one place to another. + root should use os.sep to separate directories n1 should use os.sep to separate directories n2 should use "/" to separate directories returns an os.sep-separated path. + + If n1 is a relative path, it's assumed it's + relative to root. + n2 should always be relative to root. ''' if not n1: return localpath(n2) + if os.path.isabs(n1): + if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]: + return os.path.join(root, localpath(n2)) + n2 = '/'.join((pconvert(root), n2)) a, b = n1.split(os.sep), n2.split('/') a.reverse() b.reverse() @@ -730,9 +750,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: @@ -773,19 +831,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:] @@ -797,10 +854,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): @@ -857,6 +914,8 @@ else: nulldev = '/dev/null' + _umask = os.umask(0) + os.umask(_umask) def rcfiles(path): rcs = [os.path.join(path, 'hgrc')] @@ -868,18 +927,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:] @@ -887,7 +946,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) @@ -898,12 +957,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 @@ -1332,6 +1413,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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/util_win32.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/mercurial/verify.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/setup.py Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/templates/gitweb/header.tmpl Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/templates/gitweb/map Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/templates/header.tmpl Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:58:49 2007 -0300 @@ -0,0 +1,2 @@ +#header# +#entries%indexentry#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/raw/manifest.tmpl Fri Mar 16 22:58:49 2007 -0300 @@ -0,0 +1,3 @@ +{header} +{dentries%manifestdirentry}{fentries%manifestfileentry} +{footer}
--- a/templates/raw/map Fri Mar 16 22:48:23 2007 -0300 +++ b/templates/raw/map Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/README Fri Mar 16 22:58:49 2007 -0300 @@ -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-abort-checkin Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-abort-checkin Fri Mar 16 22:58:49 2007 -0300 @@ -1,12 +1,19 @@ #!/bin/sh +cat > abortcommit.py <<EOF +from mercurial import util + +def hook(**args): + raise util.Abort("no commits allowed") + +def reposetup(ui, repo): + repo.ui.setconfig("hooks", "pretxncommit.nocommits", hook) +EOF +abspath=`pwd`/abortcommit.py + echo "[extensions]" >> $HGRCPATH echo "mq=" >> $HGRCPATH -cat > $HGTMP/false <<EOF -#!/bin/sh -exit 1 -EOF -chmod +x $HGTMP/false +echo "abortcommit = $abspath" >> $HGRCPATH hg init foo cd foo @@ -15,7 +22,7 @@ # mq may keep a reference to the repository so __del__ will not be called # and .hg/journal.dirstate will not be deleted: -HGEDITOR=$HGTMP/false hg ci -HGEDITOR=$HGTMP/false hg ci +hg ci -m foo +hg ci -m foo exit 0
--- a/tests/test-abort-checkin.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-abort-checkin.out Fri Mar 16 22:58:49 2007 -0300 @@ -1,6 +1,8 @@ -abort: edit failed: false exited with status 1 +error: pretxncommit.nocommits hook failed: no commits allowed +abort: no commits allowed transaction abort! rollback completed -abort: edit failed: false exited with status 1 +error: pretxncommit.nocommits hook failed: no commits allowed +abort: no commits allowed transaction abort! rollback completed
--- a/tests/test-acl Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-acl Fri Mar 16 22:58:49 2007 -0300 @@ -26,7 +26,7 @@ echo 'in foo' > foo/file.txt echo 'in foo/Bar' > foo/Bar/file.txt echo 'in quux' > quux/file.py -hg add +hg add -q hg ci -m 'add files' -d '1000000 0' echo >> foo/file.txt hg ci -m 'change foo/file' -d '1000001 0'
--- a/tests/test-acl.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-acl.out Fri Mar 16 22:58:49 2007 -0300 @@ -1,6 +1,3 @@ -adding foo/Bar/file.txt -adding foo/file.txt -adding quux/file.py 3:911600dab2ae requesting all changes adding changesets
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-addremove-similar Fri Mar 16 22:58:49 2007 -0300 @@ -0,0 +1,37 @@ +#!/bin/sh + +hg init rep; cd rep + +touch empty-file +python -c 'for x in range(10000): print x' > large-file + +hg addremove + +hg commit -m A + +rm large-file empty-file +python -c 'for x in range(10,10000): print x' > another-file + +hg addremove -s50 + +hg commit -m B + +cd .. + +hg init rep2; cd rep2 + +python -c 'for x in range(10000): print x' > large-file +python -c 'for x in range(50): print x' > tiny-file + +hg addremove + +hg commit -m A + +python -c 'for x in range(70): print x' > small-file +rm tiny-file +rm large-file + +hg addremove -s50 + +hg commit -m B +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-addremove-similar.out Fri Mar 16 22:58:49 2007 -0300 @@ -0,0 +1,12 @@ +adding empty-file +adding large-file +adding another-file +removing empty-file +removing large-file +recording removal of large-file as rename to another-file (99% similar) +adding large-file +adding tiny-file +adding small-file +removing large-file +removing tiny-file +recording removal of tiny-file as rename to small-file (82% similar)
--- a/tests/test-bad-extension Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-bad-extension Fri Mar 16 22:58:49 2007 -0300 @@ -1,9 +1,11 @@ #!/bin/sh -echo 'syntax error' > badext.py +echo 'raise Exception("bit bucket overflow")' > badext.py 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 Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-bad-extension.out Fri Mar 16 22:58:49 2007 -0300 @@ -1,4 +1,5 @@ -*** failed to import extension badext: invalid syntax (badext.py, line 1) +*** failed to import extension badext: bit bucket overflow +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 Fri Mar 16 22:48:23 2007 -0300 +++ /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 Fri Mar 16 22:48:23 2007 -0300 +++ /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-bundle.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-bundle.out Fri Mar 16 22:58:49 2007 -0300 @@ -150,6 +150,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: 0.0 +comparing with bundle://../full.hg searching for changes changeset: 4:5f4f3ceb285e parent: 0:5649c9d34dd8 @@ -179,6 +180,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: 0.3m +comparing with ../partial2 searching for changes changeset: 4:5f4f3ceb285e parent: 0:5649c9d34dd8 @@ -211,6 +213,7 @@ abort: No such file or directory: ../does-not-exist.hg 1 files updated, 0 files merged, 0 files removed, 0 files unresolved searching for changes +comparing with ../bundle.hg searching for changes changeset: 2:ed1b79f46b9a tag: tip
--- a/tests/test-commit.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-commit.out Fri Mar 16 22:58:49 2007 -0300 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-context.py Fri Mar 16 22:58:49 2007 -0300 @@ -0,0 +1,20 @@ +import os +from mercurial import hg, ui, commands + +u = ui.ui() + +repo = hg.repository(u, 'test1', create=1) +os.chdir('test1') +repo = hg.repository(u, '.') # FIXME: can't lock repo without doing this + +# create 'foo' with fixed time stamp +f = file('foo', 'w') +f.write('foo\n') +f.close() +os.utime('foo', (1000, 1000)) + +# add+commit 'foo' +repo.add(['foo']) +repo.commit(text='commit1', date="0 0") + +print "workingfilectx.date =", repo.workingctx().filectx('foo').date()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-context.py.out Fri Mar 16 22:58:49 2007 -0300 @@ -0,0 +1,1 @@ +workingfilectx.date = (1000, 0)
--- a/tests/test-diffdir Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-diffdir Fri Mar 16 22:58:49 2007 -0300 @@ -1,7 +1,6 @@ #!/bin/sh hg init -hg branch dummy # needed so -r "" doesn't point to the unnamed/default branch touch a hg add a hg ci -m "a" -d "1000000 0"
--- a/tests/test-diffdir.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-diffdir.out Fri Mar 16 22:58:49 2007 -0300 @@ -1,19 +1,19 @@ -diff -r 4da5fa99f904 b +diff -r acd8075edac9 b --- /dev/null +++ b/b @@ -0,0 +1,1 @@ +123 -diff -r 4da5fa99f904 b +diff -r acd8075edac9 b --- /dev/null +++ b/b @@ -0,0 +1,1 @@ +123 -diff -r 4da5fa99f904 a +diff -r acd8075edac9 a --- a/a +++ b/a @@ -0,0 +1,1 @@ +foo -diff -r 4da5fa99f904 b +diff -r acd8075edac9 b --- /dev/null +++ b/b @@ -0,0 +1,1 @@
--- a/tests/test-empty-group.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-empty-group.out Fri Mar 16 22:58:49 2007 -0300 @@ -21,6 +21,7 @@ adding file changes added 4 changesets with 3 changes to 3 files 3 files updated, 0 files merged, 0 files removed, 0 files unresolved +comparing with b searching for changes changeset: 4:fdb3c546e859 tag: tip @@ -30,6 +31,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: m2 +comparing with c searching for changes changeset: 3:f40f830c0024 parent: 2:de997049e034 @@ -38,6 +40,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: m1 +comparing with c searching for changes changeset: 3:f40f830c0024 tag: tip @@ -47,6 +50,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: m1 +comparing with b searching for changes changeset: 3:fdb3c546e859 tag: tip
--- a/tests/test-encoding.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-encoding.out Fri Mar 16 22:58:49 2007 -0300 @@ -126,13 +126,13 @@ é 3:770b9b11621d % ascii ? 5:db5520b4645f - 4:9cff3c980b58 +default 4:9cff3c980b58 % latin-1 é 5:db5520b4645f - 4:9cff3c980b58 +default 4:9cff3c980b58 % utf-8 é 5:db5520b4645f - 4:9cff3c980b58 +default 4:9cff3c980b58 % utf-8 changeset: 5:db5520b4645f branch: é
--- a/tests/test-globalopts.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-globalopts.out Fri Mar 16 22:58:49 2007 -0300 @@ -91,6 +91,7 @@ user: test date: Thu Jan 01 00:00:01 1970 +0000 files+: b +extra: branch=default description: b @@ -102,6 +103,7 @@ user: test date: Thu Jan 01 00:00:01 1970 +0000 files+: a +extra: branch=default description: a
--- a/tests/test-grep Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-grep Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-grep.out Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-hook Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-hook.out Fri Mar 16 22:58:49 2007 -0300 @@ -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-import Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-import Fri Mar 16 22:58:49 2007 -0300 @@ -32,6 +32,17 @@ hg --cwd b import -mpatch ../tip.patch rm -r b +echo % hg -R repo import +# put the clone in a subdir - having a directory named "a" +# used to hide a bug. +mkdir dir +hg clone -r0 a dir/b +hg --cwd a export tip > dir/tip.patch +cd dir +hg -R b import tip.patch +cd .. +rm -r dir + echo % import from stdin hg clone -r0 a b hg --cwd a export tip | hg --cwd b import -
--- a/tests/test-import.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-import.out Fri Mar 16 22:58:49 2007 -0300 @@ -30,6 +30,14 @@ added 1 changesets with 2 changes to 2 files 2 files updated, 0 files merged, 0 files removed, 0 files unresolved applying ../tip.patch +% hg -R repo import +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 2 changes to 2 files +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +applying tip.patch % import from stdin requesting all changes adding changesets
--- a/tests/test-incoming-outgoing.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-incoming-outgoing.out Fri Mar 16 22:58:49 2007 -0300 @@ -4,6 +4,7 @@ crosschecking files in changesets and manifests checking files 1 files, 9 changesets, 9 total revisions +comparing with http://localhost:20059/ changeset: 0:9cb21d99fe27 user: test date: Mon Jan 12 13:46:40 1970 +0000 @@ -50,6 +51,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: 8 +comparing with http://localhost:20059/ changeset: 0:9cb21d99fe27 user: test date: Mon Jan 12 13:46:40 1970 +0000 @@ -75,6 +77,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: 4 +comparing with test changeset: 0:9cb21d99fe27 user: test date: Mon Jan 12 13:46:40 1970 +0000 @@ -121,6 +124,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: 8 +comparing with test changeset: 0:9cb21d99fe27 user: test date: Mon Jan 12 13:46:40 1970 +0000 @@ -146,6 +150,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: 4 +comparing with http://localhost:20059/ changeset: 0:9cb21d99fe27 user: test date: Mon Jan 12 13:46:40 1970 +0000 @@ -192,6 +197,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: 8 +comparing with test changeset: 0:9cb21d99fe27 user: test date: Mon Jan 12 13:46:40 1970 +0000 @@ -266,6 +272,7 @@ crosschecking files in changesets and manifests checking files 1 files, 14 changesets, 14 total revisions +comparing with test searching for changes changeset: 9:3741c3ad1096 user: test @@ -293,6 +300,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: 13 +comparing with http://localhost:20059/ searching for changes changeset: 9:3741c3ad1096 user: test @@ -320,6 +328,7 @@ date: Mon Jan 12 13:46:40 1970 +0000 summary: 13 +comparing with http://localhost:20059/ searching for changes changeset: 9:3741c3ad1096 user: test
--- a/tests/test-init Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-init Fri Mar 16 22:58:49 2007 -0300 @@ -22,11 +22,31 @@ EOF chmod +x dummyssh +checknewrepo() +{ + name=$1 + + if [ -d $name/.hg/store ]; then + echo store created + fi + + if [ -f $name/.hg/00changelog.i ]; then + echo 00changelog.i created + fi + + cat $name/.hg/requires +} + echo "# creating 'local'" hg init local +checknewrepo local echo this > local/foo hg ci --cwd local -A -m "init" -d "1000000 0" +echo "# creating repo with old format" +hg --config format.usestore=false init old +checknewrepo old + echo "#test failure" hg init local
--- a/tests/test-init.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-init.out Fri Mar 16 22:58:49 2007 -0300 @@ -1,8 +1,15 @@ # creating 'local' +store created +00changelog.i created +revlogv1 +store adding foo +# creating repo with old format +revlogv1 #test failure abort: repository local already exists! # init+push to remote2 +comparing with local changeset: 0:c4e059d443be tag: tip user: test
--- a/tests/test-log Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-log Fri Mar 16 22:58:49 2007 -0300 @@ -47,7 +47,6 @@ # log --follow tests hg init ../follow cd ../follow -hg branch dummy # needed so -r "" doesn't point to the unnamed/default branch echo base > base hg ci -Ambase -d '1 0'
--- a/tests/test-log.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-log.out Fri Mar 16 22:58:49 2007 -0300 @@ -105,22 +105,19 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved adding b1 % log -f -changeset: 3:07a62f044f0d -branch: dummy +changeset: 3:e62f78d544b4 tag: tip -parent: 1:fb3d4e35b279 +parent: 1:3d5bf5654eda user: test date: Thu Jan 01 00:00:01 1970 +0000 summary: b1 -changeset: 1:fb3d4e35b279 -branch: dummy +changeset: 1:3d5bf5654eda user: test date: Thu Jan 01 00:00:01 1970 +0000 summary: r1 -changeset: 0:ea445bfed6b9 -branch: dummy +changeset: 0:67e992f2c4f3 user: test date: Thu Jan 01 00:00:01 1970 +0000 summary: base @@ -128,21 +125,18 @@ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved adding b2 % log -f -r 1:tip -changeset: 1:fb3d4e35b279 -branch: dummy +changeset: 1:3d5bf5654eda user: test date: Thu Jan 01 00:00:01 1970 +0000 summary: r1 -changeset: 2:e8882cbc828c -branch: dummy +changeset: 2:60c670bf5b30 user: test date: Thu Jan 01 00:00:01 1970 +0000 summary: r2 -changeset: 3:07a62f044f0d -branch: dummy -parent: 1:fb3d4e35b279 +changeset: 3:e62f78d544b4 +parent: 1:3d5bf5654eda user: test date: Thu Jan 01 00:00:01 1970 +0000 summary: b1 @@ -151,66 +145,57 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) % log --follow-first -changeset: 6:0f621dafa603 -branch: dummy +changeset: 6:2404bbcab562 tag: tip user: test date: Thu Jan 01 00:00:01 1970 +0000 summary: b1.1 -changeset: 5:0cf53fb6dfd5 -branch: dummy -parent: 3:07a62f044f0d -parent: 4:b76598590bc3 +changeset: 5:302e9dd6890d +parent: 3:e62f78d544b4 +parent: 4:ddb82e70d1a1 user: test date: Thu Jan 01 00:00:01 1970 +0000 summary: m12 -changeset: 3:07a62f044f0d -branch: dummy -parent: 1:fb3d4e35b279 +changeset: 3:e62f78d544b4 +parent: 1:3d5bf5654eda user: test date: Thu Jan 01 00:00:01 1970 +0000 summary: b1 -changeset: 1:fb3d4e35b279 -branch: dummy +changeset: 1:3d5bf5654eda user: test date: Thu Jan 01 00:00:01 1970 +0000 summary: r1 -changeset: 0:ea445bfed6b9 -branch: dummy +changeset: 0:67e992f2c4f3 user: test date: Thu Jan 01 00:00:01 1970 +0000 summary: base % log -P 2 -changeset: 6:0f621dafa603 -branch: dummy +changeset: 6:2404bbcab562 tag: tip user: test date: Thu Jan 01 00:00:01 1970 +0000 summary: b1.1 -changeset: 5:0cf53fb6dfd5 -branch: dummy -parent: 3:07a62f044f0d -parent: 4:b76598590bc3 +changeset: 5:302e9dd6890d +parent: 3:e62f78d544b4 +parent: 4:ddb82e70d1a1 user: test date: Thu Jan 01 00:00:01 1970 +0000 summary: m12 -changeset: 4:b76598590bc3 -branch: dummy -parent: 0:ea445bfed6b9 +changeset: 4:ddb82e70d1a1 +parent: 0:67e992f2c4f3 user: test date: Thu Jan 01 00:00:01 1970 +0000 summary: b2 -changeset: 3:07a62f044f0d -branch: dummy -parent: 1:fb3d4e35b279 +changeset: 3:e62f78d544b4 +parent: 1:3d5bf5654eda user: test date: Thu Jan 01 00:00:01 1970 +0000 summary: b1
--- a/tests/test-mq Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-mq Fri Mar 16 22:58:49 2007 -0300 @@ -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 @@ -147,6 +181,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 @@ -188,6 +262,9 @@ hg qpush -a hg st +echo % mq tags +hg log --template '{rev} {tags}\n' -r qparent:qtip + cat >>$HGRCPATH <<EOF [diff] git = True
--- a/tests/test-mq-caches Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-mq-caches Fri Mar 16 22:58:49 2007 -0300 @@ -5,7 +5,7 @@ show_branch_cache() { - branches=.hg/branches.cache + branches=.hg/branch.cache # force cache (re)generation hg log -r does-not-exist 2> /dev/null hg log -r tip --template 'tip: #rev#\n' @@ -61,7 +61,7 @@ echo echo '# removing the cache' -rm .hg/branches.cache +rm .hg/branch.cache show_branch_cache 1 echo
--- a/tests/test-mq-caches.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-mq-caches.out Fri Mar 16 22:58:49 2007 -0300 @@ -1,13 +1,12 @@ # mq patch on an empty repo tip: 0 -No .hg/branches.cache +No .hg/branch.cache tip: 0 -No .hg/branches.cache +No .hg/branch.cache # some regular revisions Patch queue now empty tip: 1 - features: unnamed 3f910abad313ff802d3a23a7529433872df9b3ae 1 3f910abad313ff802d3a23a7529433872df9b3ae bar 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo @@ -16,12 +15,10 @@ applying p1 Now at: p1 tip: 2 - features: unnamed 3f910abad313ff802d3a23a7529433872df9b3ae 1 3f910abad313ff802d3a23a7529433872df9b3ae bar 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo tip: 3 - features: unnamed 3f910abad313ff802d3a23a7529433872df9b3ae 1 3f910abad313ff802d3a23a7529433872df9b3ae bar 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo @@ -30,7 +27,6 @@ # removing the cache tip: 3 - features: unnamed 3f910abad313ff802d3a23a7529433872df9b3ae 1 3f910abad313ff802d3a23a7529433872df9b3ae bar 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo @@ -39,7 +35,6 @@ # importing rev 1 (the cache now ends in one of the patches) tip: 3 - features: unnamed 3f910abad313ff802d3a23a7529433872df9b3ae 1 3f910abad313ff802d3a23a7529433872df9b3ae bar 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo @@ -54,6 +49,5 @@ applying p2 Now at: p2 tip: 3 - features: unnamed 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff 0 9539f35bdc80732cc9a3f84e46508f1ed1ec8cff foo
--- a/tests/test-mq.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-mq.out Fri Mar 16 22:58:49 2007 -0300 @@ -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 .hg/patches/A +adding .hg/patches/B +A .hgignore +A A +A B +A series + .hgignore: +status +bleh + series: +A +B % qnew -m foo bar % qrefresh @@ -147,6 +167,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 @@ -190,6 +265,10 @@ Errors during apply, please fix and refresh bar ? foo ? foo.rej +% mq tags +0 qparent +1 qbase foo +2 qtip bar tip new file diff --git a/new b/new
--- a/tests/test-newbranch Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-newbranch Fri Mar 16 22:58:49 2007 -0300 @@ -12,7 +12,9 @@ hg ci -m "add branch name" -d "1000000 0" hg branch bar hg ci -m "change branch name" -d "1000000 0" -hg branch "" +echo % branch shadowing +hg branch default +hg branch -f default hg ci -m "clear branch name" -d "1000000 0" hg co foo @@ -30,39 +32,14 @@ echo % test for invalid branch cache hg rollback -cp .hg/branches.cache .hg/bc-invalid +cp .hg/branch.cache .hg/bc-invalid hg log -r foo -cp .hg/bc-invalid .hg/branches.cache +cp .hg/bc-invalid .hg/branch.cache hg --debug log -r foo -rm .hg/branches.cache -echo corrupted > .hg/branches.cache +rm .hg/branch.cache +echo corrupted > .hg/branch.cache hg log -qr foo -cat .hg/branches.cache - -echo % test for different branch cache features -echo '4909a3732169c0c20011c4f4b8fdff4e3d89b23f 4' > .hg/branches.cache -hg branches --debug -echo ' features: unnamed dummy foo bar' > .hg/branches.cache -hg branches --debug -echo ' features: dummy' > .hg/branches.cache -hg branches --debug - -echo % test old hg reading branch cache with feature list -python << EOF -import binascii -f = file('.hg/branches.cache') -lines = f.read().split('\n') -f.close() -firstline = lines[0] -last, lrev = lines.pop(0).rstrip().split(" ", 1) -try: - last, lrev = binascii.unhexlify(last), int(lrev) -except ValueError, inst: - if str(inst) == "invalid literal for int():%s" % firstline: - print "ValueError raised correctly, good." - else: - print "ValueError: %s" % inst -EOF +cat .hg/branch.cache echo % update with no arguments: tipmost revision of the current branch hg up -q -C 0 @@ -71,3 +48,7 @@ hg up -q 1 hg up -q hg id +hg branch foobar +hg up + +exit 0
--- a/tests/test-newbranch.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-newbranch.out Fri Mar 16 22:58:49 2007 -0300 @@ -1,4 +1,6 @@ foo +% branch shadowing +abort: a branch of the same name already exists (use --force to override) 0 files updated, 0 files merged, 0 files removed, 0 files unresolved foo 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -43,10 +45,10 @@ summary: initial foo 5:5f8fb06e083e - 3:bf1bc2f45e83 +default 3:bf1bc2f45e83 bar 2:67ec16bde7f1 foo - +default bar % test for invalid branch cache rolling back last transaction @@ -74,26 +76,11 @@ 4:4909a3732169 - features: unnamed 4909a3732169c0c20011c4f4b8fdff4e3d89b23f 4 -bf1bc2f45e834c75404d0ddab57d53beab56e2f8 +bf1bc2f45e834c75404d0ddab57d53beab56e2f8 default 4909a3732169c0c20011c4f4b8fdff4e3d89b23f foo 67ec16bde7f1575d523313b9bca000f6a6f12dca bar -% test for different branch cache features -branch cache: no features specified -foo 4:4909a3732169c0c20011c4f4b8fdff4e3d89b23f - 3:bf1bc2f45e834c75404d0ddab57d53beab56e2f8 -bar 2:67ec16bde7f1575d523313b9bca000f6a6f12dca -branch cache: unknown features: dummy, foo, bar -foo 4:4909a3732169c0c20011c4f4b8fdff4e3d89b23f - 3:bf1bc2f45e834c75404d0ddab57d53beab56e2f8 -bar 2:67ec16bde7f1575d523313b9bca000f6a6f12dca -branch cache: missing features: unnamed -foo 4:4909a3732169c0c20011c4f4b8fdff4e3d89b23f - 3:bf1bc2f45e834c75404d0ddab57d53beab56e2f8 -bar 2:67ec16bde7f1575d523313b9bca000f6a6f12dca -% test old hg reading branch cache with feature list -ValueError raised correctly, good. % update with no arguments: tipmost revision of the current branch bf1bc2f45e83 4909a3732169 (foo) tip +abort: branch foobar not found
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-purge Fri Mar 16 22:58:49 2007 -0300 @@ -0,0 +1,76 @@ +#!/bin/sh + +cat <<EOF >> $HGRCPATH +[extensions] +purge=${TESTDIR}/../contrib/purge/purge.py +EOF + +echo % init +hg init t +cd t + +echo % setup +echo r1 > r1 +hg ci -qAmr1 -d'0 0' +mkdir directory +echo r2 > directory/r2 +hg ci -qAmr2 -d'1 0' +echo 'ignored' > .hgignore +hg ci -qAmr3 -d'2 0' + +echo % delete an empty directory +mkdir empty_dir +hg purge -p +hg purge -v +ls + +echo % delete an untracked directory +mkdir untracked_dir +touch untracked_dir/untracked_file1 +touch untracked_dir/untracked_file2 +hg purge -p +hg purge -v +ls + +echo % delete an untracked file +touch untracked_file +hg purge -p +hg purge -v +ls + +echo % delete an untracked file in a tracked directory +touch directory/untracked_file +hg purge -p +hg purge -v +ls + +echo % delete nested directories +mkdir -p untracked_directory/nested_directory +hg purge -p +hg purge -v +ls + +echo % delete nested directories from a subdir +mkdir -p untracked_directory/nested_directory +cd directory +hg purge -p +hg purge -v +cd .. +ls + +echo % delete only part of the tree +mkdir -p untracked_directory/nested_directory +touch directory/untracked_file +cd directory +hg purge -p ../untracked_directory +hg purge -v ../untracked_directory +cd .. +ls +ls directory/untracked_file +rm directory/untracked_file + +echo % delete ignored files +touch ignored +hg purge -p +hg purge -v +ls
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-purge.out Fri Mar 16 22:58:49 2007 -0300 @@ -0,0 +1,49 @@ +% init +% setup +% delete an empty directory +empty_dir +Removing directory empty_dir +directory +r1 +% delete an untracked directory +untracked_dir/untracked_file1 +untracked_dir/untracked_file2 +Removing file untracked_dir/untracked_file1 +Removing file untracked_dir/untracked_file2 +Removing directory untracked_dir +directory +r1 +% delete an untracked file +untracked_file +Removing file untracked_file +directory +r1 +% delete an untracked file in a tracked directory +directory/untracked_file +Removing file directory/untracked_file +directory +r1 +% delete nested directories +untracked_directory/nested_directory +Removing directory untracked_directory/nested_directory +Removing directory untracked_directory +directory +r1 +% delete nested directories from a subdir +untracked_directory/nested_directory +Removing directory untracked_directory/nested_directory +Removing directory untracked_directory +directory +r1 +% delete only part of the tree +untracked_directory/nested_directory +Removing directory untracked_directory/nested_directory +Removing directory untracked_directory +directory +r1 +directory/untracked_file +% delete ignored files +ignored +Removing file ignored +directory +r1
--- a/tests/test-revert Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-revert Fri Mar 16 22:58:49 2007 -0300 @@ -88,7 +88,10 @@ echo %% issue332 hg ci -A -m b -d '1000001 0' echo foobar > b/b -hg revert b +mkdir newdir +echo foo > newdir/newfile +hg add newdir/newfile +hg revert b newdir echo foobar > b/b hg revert . true
--- a/tests/test-revert.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-revert.out Fri Mar 16 22:58:49 2007 -0300 @@ -61,4 +61,5 @@ %% issue332 adding b/b reverting b/b +forgetting newdir/newfile reverting b/b
--- a/tests/test-ssh.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-ssh.out Fri Mar 16 22:58:49 2007 -0300 @@ -33,6 +33,7 @@ # local change # updating rc # find outgoing +comparing with ssh://user@dummy/remote searching for changes changeset: 1:572896fe480d tag: tip @@ -41,6 +42,7 @@ summary: add # find incoming on the remote side +comparing with ssh://user@dummy/local searching for changes changeset: 1:572896fe480d tag: tip
--- a/tests/test-symlinks Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-symlinks Fri Mar 16 22:58:49 2007 -0300 @@ -55,3 +55,18 @@ ln -s x/f ../z # this should fail hg status ../z && { echo hg mistakenly exited with status 0; exit 1; } || : + +cd .. ; rm -r test +hg init test; cd test; + +echo '# try cloning symlink in a subdir' +echo '1. commit a symlink' +mkdir -p a/b/c +cd a/b/c +ln -s /path/to/symlink/source demo +cd ../../.. +hg stat +hg commit -A -m 'add symlink in a/b/c subdir' +echo '2. clone it' +cd .. +hg clone test testclone
--- a/tests/test-symlinks.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-symlinks.out Fri Mar 16 22:58:49 2007 -0300 @@ -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 @@ -13,3 +14,9 @@ A f # try symlink outside repo to file inside abort: ../z not under root +# try cloning symlink in a subdir +1. commit a symlink +? a/b/c/demo +adding a/b/c/demo +2. clone it +1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-tags Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-tags Fri Mar 16 22:58:49 2007 -0300 @@ -63,7 +63,7 @@ hg tags hg tip -# tags from later heads override previous ones +# test tag precedence rules cd .. hg init t2 cd t2 @@ -79,3 +79,7 @@ echo >> foo hg ci -m 'change foo 2' -d '1000000 0' # rev 4 hg tags + +hg tag --remove -d '1000000 0' bar +hg tip +hg tags
--- a/tests/test-tags.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-tags.out Fri Mar 16 22:58:49 2007 -0300 @@ -41,4 +41,11 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved 1 files updated, 0 files merged, 0 files removed, 0 files unresolved tip 4:36195b728445 -bar 0:b409d9da318e +bar 1:b204a97e6e8d +changeset: 5:57e1983b4a60 +tag: tip +user: test +date: Mon Jan 12 13:46:40 1970 +0000 +summary: Removed tag bar + +tip 5:57e1983b4a60
--- a/tests/test-transplant Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-transplant Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-transplant.out Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-ui-config Fri Mar 16 22:58:49 2007 -0300 @@ -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 Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-ui-config.out Fri Mar 16 22:58:49 2007 -0300 @@ -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
--- a/tests/test-walk Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-walk Fri Mar 16 22:58:49 2007 -0300 @@ -90,3 +90,9 @@ debugwalk fenugreek touch new debugwalk new +chdir .. +debugwalk -R t t/mammals/skunk +mkdir t2 +chdir t2 +debugwalk -R ../t ../t/mammals/skunk +debugwalk --cwd ../t mammals/skunk
--- a/tests/test-walk.out Fri Mar 16 22:48:23 2007 -0300 +++ b/tests/test-walk.out Fri Mar 16 22:58:49 2007 -0300 @@ -276,3 +276,16 @@ hg debugwalk new f new new exact +cd .. + +hg debugwalk -R t t/mammals/skunk +f mammals/skunk t/mammals/skunk exact + +cd t2 + +hg debugwalk -R ../t ../t/mammals/skunk +f mammals/skunk ../t/mammals/skunk exact + +hg debugwalk --cwd ../t mammals/skunk +f mammals/skunk mammals/skunk exact +