# # # add_file "tracvc/mtn/util.py" # content [c72b93f825a2809e226f99ff1fa8e653a165316d] # # patch "tracvc/mtn/automate.py" # from [519b5f3566f5b1f4ca1f2fca849dc0daad99cf98] # to [d230f62589d96210e61b354da82b8cf4c659f1a6] # # patch "tracvc/mtn/backend.py" # from [2aa223762f42cfd72193ced3879772534f70e0ae] # to [96c10000b1c2e58cef92cb6efe3563cd92a192b8] # ============================================================ --- tracvc/mtn/util.py c72b93f825a2809e226f99ff1fa8e653a165316d +++ tracvc/mtn/util.py c72b93f825a2809e226f99ff1fa8e653a165316d @@ -0,0 +1,56 @@ +""" +Trac Plugin for Monotone + +Copyright 2006, Thomas Moschny (address@hidden) + +{{{ +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 (at +your option) any later version. + +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. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA +}}} + +""" + +def add_slash(path): + """Prepend a slash. Throughout the this plugin, all paths + including the root dir, start with a slash.""" + + return '/' + path + + +def get_parent(path): + """Returns the name of the directory containing path, or None if + there is none (because path is '/' or None).""" + + path = path and path.rstrip('/') + return path and (path[0:path.rfind('/')] or '/') or None + + +def get_oldpath(path, renames): + """Find out the old name of a path taking into account the + renames, which is a dictionary: oldname = renames[newname].""" + + if path in renames: + # simple: path itself changes + return renames[path] + # one of the parents might have changed it's name + parent = get_parent(path) + while parent: + if parent in renames: + oldparent = renames[parent] + oldpath = path.replace(parent.rstrip('/'), + oldparent.rstrip('/'), 1) + return oldpath + parent = get_parent(parent) + return path ============================================================ --- tracvc/mtn/automate.py 519b5f3566f5b1f4ca1f2fca849dc0daad99cf98 +++ tracvc/mtn/automate.py d230f62589d96210e61b354da82b8cf4c659f1a6 @@ -31,6 +31,7 @@ except ImportError: import dummy_threading as _threading import basic_io +import util from cache import memoize @@ -272,7 +273,7 @@ if status == 0: return result @memoize(get_cachespec) - def changeset(self, rev): + def changesets(self, rev): """ Fetches and pre-processes a changeset. @@ -284,67 +285,33 @@ """ status, result = self.automate.command("get_revision", rev) if status != 0: return {} - changeset = {} + changesets = [] - def add_slash(path): - """Prepend a slash. Throughout the this plugin, all paths - including the root dir, start with a slash.""" - return '/' + path - - def get_parent(path): - """Returns the name of the directory containing path, or - None if there is none (because path is '/').""" - - path = path and path.rstrip('/') - return path and (path[0:path.rfind('/')] or '/') or None - - def get_oldpath(path, renames): - """Find out the name of a path in the parent revision - currently in question.""" - - if path in renames: - # simple: path itself changes - return renames[path] - # one of the parents might have changed it's name - parent = get_parent(path) - while parent: - if parent in renames: - oldparent = renames[parent] - oldpath = path.replace(parent.rstrip('/'), - oldparent.rstrip('/'), 1) - return oldpath - parent = get_parent(parent) - return path - for stanza in basic_io.get_stanzas(result): entry = basic_io.get_hash_from_stanza(stanza) if entry.has_key('old_revision'): - # oldrev is in effect until overwritten by a later stanza - oldrev = entry['old_revision'] - renames, changeset[oldrev] = {}, {} + # start a new changeset + cs = Changeset(entry['old_revision']) + changesets.append(cs) elif entry.has_key('add_dir'): - path = add_slash(entry['add_dir']) - changeset[oldrev][path] = ('dir', 'add', None) + path = util.add_slash(entry['add_dir']) + cs.added[path] = 'dir' elif entry.has_key('add_file'): - path = add_slash(entry['add_file']) - changeset[oldrev][path] = ('file', 'add', None) + path = util.add_slash(entry['add_file']) + cs.added[path] = 'file' elif entry.has_key('delete'): - path = add_slash(entry['delete']) - # deletes are simply listed - changeset[oldrev].setdefault(None, []).append(path) + path = util.add_slash(entry['delete']) + cs.deleted.append(path) elif entry.has_key('rename'): - oldpath = add_slash(entry['rename']) - newpath = add_slash(entry['to']) - # remember renames for edits - renames[newpath] = oldpath - changeset[oldrev][newpath] = (None, 'move', oldpath) + oldpath = util.add_slash(entry['rename']) + newpath = util.add_slash(entry['to']) + cs.renamed[newpath] = oldpath elif entry.has_key('patch'): - path = add_slash(entry['patch']) - oldpath = get_oldpath(path, renames) - changeset[oldrev][path] = ('file', 'edit', oldpath) + path = util.add_slash(entry['patch']) + cs.patched.append(path) # fixme: what about 'set' and 'clear'? These are edits, # but not if applied to new files. - return changeset + return changesets def branches(self): """Returns a list of (branch, oneoftheheads) tuples. Caveat: @@ -374,3 +341,11 @@ tags.append((self._u(match.group('tag')), match.group('rev'))) return tags +class Changeset(object): + + def __init__(self, oldrev): + self.oldrev = oldrev + self.added = {} + self.renamed = {} + self.patched = [] + self.deleted = [] ============================================================ --- tracvc/mtn/backend.py 2aa223762f42cfd72193ced3879772534f70e0ae +++ tracvc/mtn/backend.py 96c10000b1c2e58cef92cb6efe3563cd92a192b8 @@ -30,6 +30,7 @@ from trac.core import * from trac.config import Option from automate import MTN +import util class MonotoneConnector(Component): @@ -274,14 +275,9 @@ if self.isfile: return - def parent(path): - """Returns the name of the directory containing path.""" - path = path and path.rstrip('/') - return path and (path[0:path.rfind('/')] or '/') or None - def ischild(path): """Returns true, if we are parent of path.""" - return parent(path) == self.path + return util.get_parent(path) == self.path for path in filter(ischild, self.manifest.keys()): yield MonotoneNode(self.mtn, self.rev, path) @@ -348,27 +344,27 @@ Changeset.ADD, Changeset.COPY, Changeset.DELETE, Changeset.EDIT or Changeset.MOVE, and kind is one of Node.FILE or Node.DIRECTORY. """ - changeset = self.mtn.changeset(self.rev) - oldrevs = changeset.keys() - oldrevs.sort() - for oldrev in oldrevs: - changes = changeset[oldrev] - paths = changes.keys() - paths.sort() - for path in paths: - if path == None: - # deletions - oldpaths = changes[None] - oldpaths.sort() - for oldpath in oldpaths: - yield None, None, 'delete', oldpath, oldrev - else: - kind, change, oldpath = changes[path] - if change == 'edit' and path != oldpath: - # extra entry for the move itself - yield path, kind, 'move', oldpath, oldrev - yield path, kind, change, oldpath, oldrev + for cs in self.mtn.changesets(self.rev): + oldrev = cs.oldrev + + # deletions + for oldpath in cs.deleted: + yield None, None, 'delete', oldpath, oldrev + + # additions + for (path, kind) in cs.added.iteritems(): + yield path, kind, 'add', None, oldrev + + # pure renames + for (path, oldpath) in cs.renamed.iteritems(): + yield path, None, 'move', oldpath, oldrev + + # patches + for path in cs.patched: + oldpath = util.get_oldpath(path, cs.renamed) + yield path, 'file', 'edit', oldpath, oldrev + def get_properties(self): """Generator that provides additional metadata for this changeset.