diff -r b285ab731fff -r 4c5d9635b517 hgkw/keyword.py --- a/hgkw/keyword.py Fri Jan 05 14:51:58 2007 +0100 +++ b/hgkw/keyword.py Sat Jan 06 14:34:49 2007 +0100 @@ -10,41 +10,30 @@ files (like LaTeX packages), that are mostly addressed to an audience not running a version control system. -Supported keywords are (changeset 000000000000): - $Revision: 000000000000 $ - $Author: Your Name $ - $Date: %a %b %d %H:%M:%S %Y %z $ - $RCSFile: basename,v $ - $Source: /path/to/basename,v $ - $Id: basename,v 000000000000 %Y-%m-%d %H:%M:%S %z shortname $ - $Header: /path/to/basename,v 000000000000 %Y-%m-%d %H:%M:%S %z shortname $ - -The extension, according to its hackish nature, is a hybrid and consists -actually in 2 parts: - - 1. pure extension code (reposetup) that is triggered on checkout and - logging of changes. - 2. a pretxncommit hook (hgrc (5)) that expands keywords immediately - at commit time in the working directory. +Supported $keywords$ are: + Revision: changeset id + Author: full username + Date: %a %b %d %H:%M:%S %Y %z $ + RCSFile: basename,v + Source: /path/to/basename,v + Id: basename,v csetid %Y-%m-%d %H:%M:%S %s shortname + Header: /path/to/basename,v csetid %Y-%m-%d %H:%M:%S %s shortname Simple setup in hgrc: # enable extension + # keyword.py in hgext folder, specify full path otherwise hgext.keyword = # filename patterns for expansion are configured in this section [keyword] - *.sty = expand + **.py = expand ... - - # set up pretxncommit hook - [hooks] - pretxncommit = - pretxncommit.keyword = python:hgext.keyword.pretxnkw ''' +from mercurial.node import * from mercurial.i18n import _ -from mercurial import context, util +from mercurial import context, filelog, revlog, util import os.path, re @@ -53,14 +42,12 @@ def kwexpand(matchobj, repo, path, changeid=None, fileid=None, filelog=None): - '''Called by kwfilelog.read and pretxnkw. + '''Called by kwrepo.commit and kwfilelog.read. Sets supported keywords as local variables and evaluates them to their expansion if matchobj is equal to string representation.''' - c = context.filectx(repo, path, changeid=changeid, fileid=fileid, filelog=filelog) date = c.date() - Revision = c.changectx() Author = c.user() RCSFile = os.path.basename(path)+',v' @@ -71,18 +58,27 @@ util.shortuser(Author)) Header = '%s %s' % (Source, revdateauth) Id = '%s %s' % (RCSFile, revdateauth) - return '$%s: %s $' % (matchobj.group(1), eval(matchobj.group(1))) -def kwfmatchers(ui, repo): - '''Returns filename matchers from ui keyword section.''' - return [util.matcher(repo.root, '', [pat], [], [])[1] +def kwfmatches(ui, repo, files): + '''Selects candidates for keyword substitution + configured in keyword section in hgrc.''' + files = [f for f in files if not f.startswith('.hg')] + if not files: + return [] + candidates = [] + fmatchers = [util.matcher(repo.root, '', [pat], [], [])[1] for pat, opt in ui.configitems('keyword') if opt == 'expand'] + for f in files: + for mf in fmatchers: + if mf(f): + candidates.append(f) + break + return candidates def reposetup(ui, repo): - from mercurial import filelog, revlog if not repo.local(): return @@ -93,6 +89,164 @@ f = f[1:] return filelog.filelog(self.sopener, f, self, self.revlogversion) + def commit(self, files=None, text="", user=None, date=None, + match=util.always, force=False, lock=None, wlock=None, + force_editor=False, p1=None, p2=None, extra={}): + + commit = [] + remove = [] + changed = [] + use_dirstate = (p1 is None) # not rawcommit + extra = extra.copy() + + if use_dirstate: + if files: + for f in files: + s = self.dirstate.state(f) + if s in 'nmai': + commit.append(f) + elif s == 'r': + remove.append(f) + else: + ui.warn(_("%s not tracked!\n") % f) + else: + changes = self.status(match=match)[:5] + modified, added, removed, deleted, unknown = changes + commit = modified + added + remove = removed + else: + commit = files + + if use_dirstate: + p1, p2 = self.dirstate.parents() + update_dirstate = True + else: + p1, p2 = p1, p2 or nullid + update_dirstate = (self.dirstate.parents()[0] == p1) + + c1 = self.changelog.read(p1) + c2 = self.changelog.read(p2) + m1 = self.manifest.read(c1[0]).copy() + m2 = self.manifest.read(c2[0]) + + if use_dirstate: + branchname = self.workingctx().branch() + try: + branchname = branchname.decode('UTF-8').encode('UTF-8') + except UnicodeDecodeError: + raise util.Abort(_('branch name not in UTF-8!')) + else: + branchname = "" + + if use_dirstate: + oldname = c1[5].get("branch", "") # stored in UTF-8 + if not commit and not remove and not force and p2 == nullid and \ + branchname == oldname: + ui.status(_("nothing changed\n")) + return None + + xp1 = hex(p1) + if p2 == nullid: xp2 = '' + else: xp2 = hex(p2) + + self.hook("precommit", throw=True, parent1=xp1, parent2=xp2) + + if not wlock: + wlock = self.wlock() + if not lock: + lock = self.lock() + tr = self.transaction() + + # check in files + 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: + ui.note(f + "\n") + try: + new[f] = self.filecommit(f, m1, m2, linkrev, tr, changed) + m1.set(f, is_exec(f), is_link(f)) + except OSError: + if use_dirstate: + ui.warn(_("trouble committing %s!\n") % f) + raise + else: + remove.append(f) + + # update manifest + m1.update(new) + remove.sort() + removed = [] + + for f in remove: + if f in m1: + del m1[f] + removed.append(f) + mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0], (new, removed)) + + # add changeset + new = new.keys() + new.sort() + + user = user or ui.username() + if not text or force_editor: + edittext = [] + if text: + edittext.append(text) + edittext.append("") + edittext.append("HG: user: %s" % user) + if p2 != nullid: + edittext.append("HG: branch merge") + edittext.extend(["HG: changed %s" % f for f in changed]) + edittext.extend(["HG: removed %s" % f for f in removed]) + if not changed and not remove: + edittext.append("HG: no files changed") + edittext.append("") + # run editor in the repository root + olddir = os.getcwd() + os.chdir(self.root) + text = ui.edit("\n".join(edittext), user) + os.chdir(olddir) + + lines = [line.rstrip() for line in text.rstrip().splitlines()] + while lines and not lines[0]: + del lines[0] + if not lines: + return None + text = '\n'.join(lines) + if branchname: + extra["branch"] = branchname + 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) + + # substitute keywords + for f in kwfmatches(ui, self, changed): + data = self.wfile(f).read() + if not util.binary(data): + data, kwct = re_kw.subn(lambda m: + kwexpand(m, self, f, changeid=hex(n)), + data) + if kwct: + ui.debug(_('overwriting %s expanding keywords\n' + % f)) + self.wfile(f, 'w').write(data) + + tr.close() + + if use_dirstate or update_dirstate: + self.dirstate.setparents(n) + if use_dirstate: + self.dirstate.update(new, "n") + self.dirstate.forget(removed) + + self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2) + return n + + class kwfilelog(filelog.filelog): def __init__(self, opener, path, repo, defversion=revlog.REVLOG_DEFAULT_VERSION): @@ -102,14 +256,12 @@ def read(self, node): data = super(kwfilelog, self).read(node) - if not self._path.startswith('.hg') and not util.binary(data): - for mf in kwfmatchers(ui, self._repo): - if mf(self._path): - ui.debug(_('expanding keywords in %s\n' % self._path)) - return re_kw.sub(lambda m: - kwexpand(m, self._repo, self._path, - fileid=node, filelog=self), - data) + if not util.binary(data) and \ + kwfmatches(ui, self._repo, [self._path]): + ui.debug(_('expanding keywords in %s\n' % self._path)) + return re_kw.sub(lambda m: + kwexpand(m, self._repo, self._path, + fileid=node, filelog=self), data) return data def size(self, rev): @@ -127,37 +279,3 @@ filelog.filelog = kwfilelog repo.__class__ = kwrepo - - -def pretxnkw(ui, repo, hooktype, **args): - '''pretxncommit hook that collects candidates for keyword expansion - on commit and expands keywords in working dir.''' - from mercurial import cmdutil, commands - import sys - - if hooktype != 'pretxncommit': - return True - - cmd, sysargs, globalopts, cmdopts = commands.parse(ui, sys.argv[1:])[1:] - if repr(cmd).split()[1] in ('tag', 'import_'): - return False - - files, match, anypats = cmdutil.matchpats(repo, sysargs, cmdopts) - modified, added = repo.status(files=files, match=match)[:2] - candidates = [f for f in modified + added if not f.startswith('.hg')] - if not candidates: - return False - - fmatchers = kwfmatchers(ui, repo) - for f in candidates: - for mf in fmatchers: - if mf(f): - data = repo.wfile(f).read() - if not util.binary(data): - data, kwct = re_kw.subn(lambda m: - kwexpand(m, repo, f, changeid=args['node']), - data) - if kwct: - ui.debug(_('overwriting %s expanding keywords\n' % f)) - repo.wfile(f, 'w').write(data) - break