# # # patch "tracvc/mtn/automate.py" # from [f18c2a31d3b5403dde30549534de8708194d1a4d] # to [3b57c05489935c86fe72f0560c3cb3b40f2a2a71] # # patch "tracvc/mtn/backend.py" # from [a1f437359571759a080b8bebacb2fd672baef901] # to [adb26e42b5c2d1364a9bf42fb6ec558c8f2b611c] # ============================================================ --- tracvc/mtn/automate.py f18c2a31d3b5403dde30549534de8708194d1a4d +++ tracvc/mtn/automate.py 3b57c05489935c86fe72f0560c3cb3b40f2a2a71 @@ -36,46 +36,29 @@ REVID_RULE = re.compile(r'^[0-9a-f]{40}$ REVID_RULE = re.compile(r'^[0-9a-f]{40}$') -class Connection: - """Starts monotone and communicates with it through a pipe.""" - def __init__(self, db, cmd, binary): +class Automate: + """General interface to the 'automate stdio' command.""" + + def __init__(self, db, binary): (self.to_child, self.from_child) = os.popen2( - "%s --norc --db=%s %s" % (binary, db, cmd), "b") + "%s --norc --automate-stdio-size=1048576 --db=%s automate stdio" % + (binary, db), "b") + self.lock = _threading.Lock() - def read(self, maxlen = -1): + def _read(self, maxlen = -1): return self.from_child.read(maxlen) - - def write(self, data): + + def _write(self, data): return self.to_child.write(data) - def flush(self): + def _flush(self): return self.to_child.flush() - - -class List: - """General interface to the 'list' command.""" - - def __init__(self, db, binary): - self.db = db - self.binary = binary - - def get_list(self, what, cmd = "list"): - conn = Connection(self.db, "%s %s" % (cmd, what) , self.binary) - return conn.read() - - -class Automate: - """General interface to the 'automate stdio' command.""" - - def __init__(self, db, binary): - self.conn = Connection(db, "automate stdio", binary) - self.lock = _threading.Lock() def _read_until_colon(self): result = '' while True: - char = self.conn.read(1) + char = self._read(1) if char == ':': break result += char return result @@ -87,7 +70,7 @@ class Automate: status = int(self._read_until_colon()) cont = self._read_until_colon() size = int(self._read_until_colon()) - val = self.conn.read(size) + val = self._read(size) return status, cont, val def _get_result(self): @@ -107,8 +90,8 @@ class Automate: for arg in args: cmdstring += lstring(arg) cmdstring += "e" - self.conn.write(cmdstring) - self.conn.flush() + self._write(cmdstring) + self._flush() def command(self, cmd, *args): """Send a command to mtn. Returns a tuple (status, result).""" @@ -125,7 +108,6 @@ class MTN: def __init__(self, db, log, binary, cachespec): self.automate = Automate(db, binary) - self.list = List(db, binary) self.log = log self.roots_cache = [] self.cachespec = cachespec @@ -203,7 +185,7 @@ class MTN: """ Returns a processed manifest for rev. - The manifest is a dictionary: path -> ( kind, file_id, attrs), + The manifest is a dictionary: path -> (kind, file_id, attrs), with kind being 'file' or 'dir', and attrs being a dictionary attr_name -> attr_value. """ @@ -350,30 +332,20 @@ class MTN: tags.append((entry['tag'], entry['revision'])) tags.sort(lambda x, y: cmp(x[0], y[0])) return tags - - @memoize(get_cachespec) - def roster(self, rev): - """Returns the roster associated with a certain revision. - !Note! get_roster is currently an unofficial monotone command - and maybe subject to changes in the future.""" - byname = {} - byident = {} - result = self.list.get_list(rev, "get_roster") + def content_changed(self, rev, file): + """Returns the list of content marks for the path, starting at + the specified revision. + + Currently returns an empty list for directories.""" + status, result = self.automate.command("get_content_changed", rev, file[1:]) + if status != 0: return [] + revs = [] for stanza in basic_io.get_stanzas(result): - rawinfo = basic_io.get_hash_from_stanza(stanza) - if rawinfo.has_key('dir'): - info = NodeInfo('dir', '/' + rawinfo['dir'], int(rawinfo['ident']), - rawinfo['birth'], rawinfo['path_mark']) - elif rawinfo.has_key('file'): - info = NodeInfo('file', '/' + rawinfo['file'], int(rawinfo['ident']), - rawinfo['birth'], rawinfo['path_mark'], - rawinfo['content'], rawinfo['content_mark']) - else: - continue - byname[info.name] = info - byident[info.ident] = info - return (byname, byident) + entry = basic_io.get_hash_from_stanza(stanza) + if 'content_mark' in entry: + revs.append(entry['content_mark']) + return revs class Changeset(object): @@ -384,16 +356,3 @@ class Changeset(object): self.renamed = {} # newname -> oldname self.patched = [] # list of newnames self.deleted = [] # list of oldnames - - -class NodeInfo(object): - - def __init__(self, kind, name, ident, birth, path_mark, - content = None, content_mark = None ): - self.kind = kind - self.content = content - self.name = name - self.ident = ident # mtn's internal file number - self.birth = birth # revision in which this nide was born - self.path_mark = path_mark # marked rev wrt to the node's name - self.content_mark = content_mark # marked rev wrt to the node's content ============================================================ --- tracvc/mtn/backend.py a1f437359571759a080b8bebacb2fd672baef901 +++ tracvc/mtn/backend.py adb26e42b5c2d1364a9bf42fb6ec558c8f2b611c @@ -107,7 +107,6 @@ class MonotoneRepository(Repository): class MonotoneRepository(Repository): - def __init__(self, path, log, binary, cachespec): self.mtn = MTN(path, log, binary, cachespec) Repository.__init__(self, 'mtn:%s' % path, None, log) @@ -284,17 +283,16 @@ class MonotoneNode(Node): if not path in self.manifest: raise NoSuchNode(path, rev) - self.id = self.manifest[path][1] + self.content_id = self.manifest[path][1] self.created_path = path self.created_rev = rev kind = self.manifest[path][0] # 'file' or 'dir' if kind == 'file': + # FIXME: we can't handle multiple marks + rev = self.mtn.content_changed(rev, path)[0] - curr = self.mtn.roster(rev)[0][path] - rev = curr.content_mark - # trac bug, or at least problematic behavior: in the # browser window, Node.path is used for the link behind # the path, but Node.rev is not, so better don't set @@ -305,7 +303,6 @@ class MonotoneNode(Node): Node.__init__(self, path, rev, kind) - def get_content(self): """ Return a stream for reading the content of the node. This method @@ -314,7 +311,7 @@ class MonotoneNode(Node): """ if self.isdir: return None - return StringIO(self.mtn.file(self.id)) + return StringIO(self.mtn.file(self.content_id)) def get_entries(self): """ @@ -353,7 +350,7 @@ class MonotoneNode(Node): def get_content_length(self): if self.isdir: return None - return self.mtn.file_length(self.id) + return self.mtn.file_length(self.content_id) def get_content_type(self): if self.isdir: