Mercurial > hg-stable
changeset 1083:30974cf73435
Add some docstrings to revlog.py
author | mpm@selenic.com |
---|---|
date | Sat, 27 Aug 2005 01:43:48 -0700 |
parents | ce96e316278a |
children | 069b4311a81b |
files | mercurial/revlog.py |
diffstat | 1 files changed, 94 insertions(+), 19 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/revlog.py Sat Aug 27 01:13:28 2005 -0700 +++ b/mercurial/revlog.py Sat Aug 27 01:43:48 2005 -0700 @@ -1,12 +1,14 @@ -# revlog.py - storage back-end for mercurial -# -# This provides efficient delta storage with O(1) retrieve and append -# and O(changes) merge between branches -# -# Copyright 2005 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. +""" +revlog.py - storage back-end for mercurial + +This provides efficient delta storage with O(1) retrieve and append +and O(changes) merge between branches + +Copyright 2005 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. +""" import zlib, struct, sha, binascii, heapq from mercurial import mdiff @@ -16,6 +18,7 @@ def short(node): return hex(node[:6]) def compress(text): + """ generate a possibly-compressed representation of text """ if not text: return text if len(text) < 44: if text[0] == '\0': return text @@ -27,6 +30,7 @@ return bin def decompress(bin): + """ decompress the given input """ if not bin: return bin t = bin[0] if t == '\0': return bin @@ -35,6 +39,12 @@ raise RevlogError("unknown compression type %s" % t) def hash(text, p1, p2): + """generate a hash from the given text and its parent hashes + + This hash combines both the current file contents and its history + in a manner that makes it easy to distinguish nodes with the same + content in the revision graph. + """ l = [p1, p2] l.sort() s = sha.new(l[0]) @@ -46,6 +56,15 @@ indexformat = ">4l20s20s20s" class lazyparser: + """ + this class avoids the need to parse the entirety of large indices + + By default we parse and load 1000 entries at a time. + + If no position is specified, we load the whole index, and replace + the lazy objects in revlog with the underlying objects for + efficiency in cases where we look at most of the nodes. + """ def __init__(self, data, revlog): self.data = data self.s = struct.calcsize(indexformat) @@ -76,6 +95,7 @@ i += 1 class lazyindex: + """a lazy version of the index array""" def __init__(self, parser): self.p = parser def __len__(self): @@ -89,6 +109,7 @@ self.p.index.append(e) class lazymap: + """a lazy version of the node map""" def __init__(self, parser): self.p = parser def load(self, key): @@ -123,7 +144,37 @@ class RevlogError(Exception): pass class revlog: + """ + the underlying revision storage object + + A revlog consists of two parts, an index and the revision data. + + The index is a file with a fixed record size containing + information on each revision, includings its nodeid (hash), the + nodeids of its parents, the position and offset of its data within + the data file, and the revision it's based on. Finally, each entry + contains a linkrev entry that can serve as a pointer to external + data. + + The revision data itself is a linear collection of data chunks. + Each chunk represents a revision and is usually represented as a + delta against the previous chunk. To bound lookup time, runs of + deltas are limited to about 2 times the length of the original + version data. This makes retrieval of a version proportional to + its size, or O(1) relative to the number of revisions. + + Both pieces of the revlog are written to in an append-only + fashion, which means we never need to rewrite a file to insert or + remove data, and can use some simple techniques to avoid the need + for locking while reading. + """ def __init__(self, opener, indexfile, datafile): + """ + create a revlog object + + opener is a function that abstracts the file opening operation + and can be used to implement COW semantics or the like. + """ self.indexfile = indexfile self.datafile = datafile self.opener = opener @@ -193,12 +244,13 @@ return reachable def heads(self, stop=None): + """return the list of all nodes that have no children""" p = {} h = [] stoprev = 0 if stop and stop in self.nodemap: stoprev = self.rev(stop) - + for r in range(self.count() - 1, -1, -1): n = self.node(r) if n not in p: @@ -212,6 +264,7 @@ return h def children(self, node): + """find the children of a given node""" c = [] p = self.rev(node) for r in range(p + 1, self.count()): @@ -225,6 +278,7 @@ return c def lookup(self, id): + """locate a node based on revision number or subset of hex nodeid""" try: rev = int(id) if str(rev) != id: raise ValueError @@ -243,12 +297,15 @@ return None def diff(self, a, b): + """return a delta between two revisions""" return mdiff.textdiff(a, b) def patches(self, t, pl): + """apply a list of patches to a string""" return mdiff.patches(t, pl) def delta(self, node): + """return or calculate a delta between a node and its predecessor""" r = self.rev(node) b = self.base(r) if r == b: @@ -261,15 +318,18 @@ return decompress(data) def revision(self, node): + """return an uncompressed revision of a given""" if node == nullid: return "" if self.cache and self.cache[0] == node: return self.cache[2] + # look up what we need to read text = None rev = self.rev(node) start, length, base, link, p1, p2, node = self.index[rev] end = start + length if base != rev: start = self.start(base) + # do we have useful data cached? if self.cache and self.cache[1] >= base and self.cache[1] < rev: base = self.cache[1] start = self.start(base + 1) @@ -300,6 +360,14 @@ return text def addrevision(self, text, transaction, link, p1=None, p2=None, d=None): + """add a revision to the log + + text - the revision data to add + transaction - the transaction object used for rollback + link - the linkrev data to add + p1, p2 - the parent nodeids of the revision + d - an optional precomputed delta + """ if text is None: text = "" if p1 is None: p1 = self.tip() if p2 is None: p2 = nullid @@ -349,6 +417,7 @@ return node def ancestor(self, a, b): + """calculate the least common ancestor of nodes a and b""" # calculate the distance of every node from root dist = {nullid: 0} for i in xrange(self.count()): @@ -387,12 +456,14 @@ lx = x.next() def group(self, linkmap): - # given a list of changeset revs, return a set of deltas and - # metadata corresponding to nodes. the first delta is - # parent(nodes[0]) -> nodes[0] the receiver is guaranteed to - # have this parent as it has all history before these - # changesets. parent is parent[0] + """calculate a delta group + Given a list of changeset revs, return a set of deltas and + metadata corresponding to nodes. the first delta is + parent(nodes[0]) -> nodes[0] the receiver is guaranteed to + have this parent as it has all history before these + changesets. parent is parent[0] + """ revs = [] needed = {} @@ -498,11 +569,15 @@ yield struct.pack(">l", 0) def addgroup(self, revs, linkmapper, transaction, unique=0): - # given a set of deltas, add them to the revision log. the - # first delta is against its parent, which should be in our - # log, the rest are against the previous delta. + """ + add a delta group - # track the base of the current delta log + given a set of deltas, add them to the revision log. the + first delta is against its parent, which should be in our + log, the rest are against the previous delta. + """ + + #track the base of the current delta log r = self.count() t = r - 1 node = nullid