summaryrefslogtreecommitdiff
path: root/mercurial/revlog.py
diff options
context:
space:
mode:
Diffstat (limited to 'mercurial/revlog.py')
-rw-r--r--mercurial/revlog.py210
1 files changed, 88 insertions, 122 deletions
diff --git a/mercurial/revlog.py b/mercurial/revlog.py
index 8ed1d82..97151aa 100644
--- a/mercurial/revlog.py
+++ b/mercurial/revlog.py
@@ -75,6 +75,35 @@ def hash(text, p1, p2):
s.update(text)
return s.digest()
+def compress(text):
+ """ generate a possibly-compressed representation of text """
+ if not text:
+ return ("", text)
+ l = len(text)
+ bin = None
+ if l < 44:
+ pass
+ elif l > 1000000:
+ # zlib makes an internal copy, thus doubling memory usage for
+ # large files, so lets do this in pieces
+ z = zlib.compressobj()
+ p = []
+ pos = 0
+ while pos < l:
+ pos2 = pos + 2**20
+ p.append(z.compress(text[pos:pos2]))
+ pos = pos2
+ p.append(z.flush())
+ if sum(map(len, p)) < l:
+ bin = "".join(p)
+ else:
+ bin = _compress(text)
+ if bin is None or len(bin) > l:
+ if text[0] == '\0':
+ return ("", text)
+ return ('u', text)
+ return ("", bin)
+
def decompress(bin):
""" decompress the given input """
if not bin:
@@ -83,10 +112,7 @@ def decompress(bin):
if t == '\0':
return bin
if t == 'x':
- try:
- return _decompress(bin)
- except zlib.error, e:
- raise RevlogError(_("revlog decompress error: %s") % str(e))
+ return _decompress(bin)
if t == 'u':
return bin[1:]
raise RevlogError(_("unknown compression type %r") % t)
@@ -148,7 +174,7 @@ class revlogio(object):
def parseindex(self, data, inline):
# call the C implementation to parse the index data
index, cache = parsers.parse_index2(data, inline)
- return index, getattr(index, 'nodemap', None), cache
+ return index, None, cache
def packentry(self, entry, node, version, rev):
p = _pack(indexformatng, *entry)
@@ -200,10 +226,9 @@ class revlog(object):
self._nodepos = None
v = REVLOG_DEFAULT_VERSION
- opts = getattr(opener, 'options', None)
- if opts is not None:
- if 'revlogv1' in opts:
- if 'generaldelta' in opts:
+ if hasattr(opener, 'options'):
+ if 'revlogv1' in opener.options:
+ if 'generaldelta' in opener.options:
v |= REVLOGGENERALDELTA
else:
v = 0
@@ -262,28 +287,10 @@ class revlog(object):
self.rev(self.node(0))
return self._nodecache
- def hasnode(self, node):
- try:
- self.rev(node)
- return True
- except KeyError:
- return False
-
- def clearcaches(self):
- try:
- self._nodecache.clearcaches()
- except AttributeError:
- self._nodecache = {nullid: nullrev}
- self._nodepos = None
-
def rev(self, node):
try:
return self._nodecache[node]
- except RevlogError:
- # parsers.c radix tree lookup failed
- raise LookupError(node, self.indexfile, _('no node'))
except KeyError:
- # pure python cache lookup failed
n = self._nodecache
i = self.index
p = self._nodepos
@@ -332,35 +339,47 @@ class revlog(object):
return len(t)
size = rawsize
- def ancestors(self, revs, stoprev=0):
+ def reachable(self, node, stop=None):
+ """return the set of all nodes ancestral to a given node, including
+ the node itself, stopping when stop is matched"""
+ reachable = set((node,))
+ visit = [node]
+ if stop:
+ stopn = self.rev(stop)
+ else:
+ stopn = 0
+ while visit:
+ n = visit.pop(0)
+ if n == stop:
+ continue
+ if n == nullid:
+ continue
+ for p in self.parents(n):
+ if self.rev(p) < stopn:
+ continue
+ if p not in reachable:
+ reachable.add(p)
+ visit.append(p)
+ return reachable
+
+ def ancestors(self, *revs):
"""Generate the ancestors of 'revs' in reverse topological order.
- Does not generate revs lower than stoprev.
Yield a sequence of revision numbers starting with the parents
of each revision in revs, i.e., each revision is *not* considered
an ancestor of itself. Results are in breadth-first order:
parents of each rev in revs, then parents of those, etc. Result
does not include the null revision."""
- visit = util.deque(revs)
+ visit = list(revs)
seen = set([nullrev])
while visit:
- for parent in self.parentrevs(visit.popleft()):
- if parent < stoprev:
- continue
+ for parent in self.parentrevs(visit.pop(0)):
if parent not in seen:
visit.append(parent)
seen.add(parent)
yield parent
- def incancestors(self, revs, stoprev=0):
- """Identical to ancestors() except it also generates the
- revisions, 'revs'"""
- for rev in revs:
- yield rev
- for rev in self.ancestors(revs, stoprev):
- yield rev
-
- def descendants(self, revs):
+ def descendants(self, *revs):
"""Generate the descendants of 'revs' in revision order.
Yield a sequence of revision numbers starting with a child of
@@ -383,10 +402,13 @@ class revlog(object):
def findcommonmissing(self, common=None, heads=None):
"""Return a tuple of the ancestors of common and the ancestors of heads
- that are not ancestors of common. In revset terminology, we return the
- tuple:
+ that are not ancestors of common.
- ::common, (::heads) - (::common)
+ More specifically, the second element is a list of nodes N such that
+ every N satisfies the following constraints:
+
+ 1. N is an ancestor of some node in 'heads'
+ 2. N is not an ancestor of any node in 'common'
The list is sorted by revision number, meaning it is
topologically sorted.
@@ -403,15 +425,15 @@ class revlog(object):
heads = [self.rev(n) for n in heads]
# we want the ancestors, but inclusive
- has = set(self.ancestors(common))
+ has = set(self.ancestors(*common))
has.add(nullrev)
has.update(common)
# take all ancestors from heads that aren't in has
missing = set()
- visit = util.deque(r for r in heads if r not in has)
+ visit = [r for r in heads if r not in has]
while visit:
- r = visit.popleft()
+ r = visit.pop(0)
if r in missing:
continue
else:
@@ -597,10 +619,6 @@ class revlog(object):
return (orderedout, roots, heads)
def headrevs(self):
- try:
- return self.index.headrevs()
- except AttributeError:
- pass
count = len(self)
if not count:
return [nullrev]
@@ -662,7 +680,7 @@ class revlog(object):
def descendant(self, start, end):
if start == nullrev:
return True
- for i in self.descendants([start]):
+ for i in self.descendants(start):
if i == end:
return True
elif i > end:
@@ -688,7 +706,7 @@ class revlog(object):
return self.node(c)
def _match(self, id):
- if isinstance(id, int):
+ if isinstance(id, (long, int)):
# rev
return self.node(id)
if len(id) == 20:
@@ -722,15 +740,6 @@ class revlog(object):
pass
def _partialmatch(self, id):
- try:
- return self.index.partialmatch(id)
- except RevlogError:
- # parsers.c radix tree lookup gave multiple matches
- raise LookupError(id, self.indexfile, _("ambiguous identifier"))
- except (AttributeError, ValueError):
- # we are pure python, or key was too short to search radix tree
- pass
-
if id in self._pcache:
return self._pcache[id]
@@ -790,10 +799,9 @@ class revlog(object):
readahead = max(65536, length)
df.seek(offset)
d = df.read(readahead)
- df.close()
self._addchunk(offset, d)
if readahead > length:
- return util.buffer(d, 0, length)
+ return d[:length]
return d
def _getchunk(self, offset, length):
@@ -806,7 +814,7 @@ class revlog(object):
if cachestart >= 0 and cacheend <= l:
if cachestart == 0 and cacheend == l:
return d # avoid a copy
- return util.buffer(d, cachestart, cacheend - cachestart)
+ return d[cachestart:cacheend]
return self._loadchunk(offset, length)
@@ -839,22 +847,13 @@ class revlog(object):
def revdiff(self, rev1, rev2):
"""return or calculate a delta between two revisions"""
if rev1 != nullrev and self.deltaparent(rev2) == rev1:
- return str(self._chunk(rev2))
+ return self._chunk(rev2)
- return mdiff.textdiff(self.revision(rev1),
- self.revision(rev2))
-
- def revision(self, nodeorrev):
- """return an uncompressed revision of a given node or revision
- number.
- """
- if isinstance(nodeorrev, int):
- rev = nodeorrev
- node = self.node(rev)
- else:
- node = nodeorrev
- rev = None
+ return mdiff.textdiff(self.revision(self.node(rev1)),
+ self.revision(self.node(rev2)))
+ def revision(self, node):
+ """return an uncompressed revision of a given node"""
cachedrev = None
if node == nullid:
return ""
@@ -865,8 +864,7 @@ class revlog(object):
# look up what we need to read
text = None
- if rev is None:
- rev = self.rev(node)
+ rev = self.rev(node)
# check rev flags
if self.flags(rev) & ~REVIDX_KNOWN_FLAGS:
@@ -898,7 +896,7 @@ class revlog(object):
self._chunkraw(base, rev)
if text is None:
- text = str(self._chunkbase(base))
+ text = self._chunkbase(base)
bins = [self._chunk(r) for r in chain]
text = mdiff.patches(text, bins)
@@ -947,9 +945,9 @@ class revlog(object):
e = self._io.packentry(self.index[i], self.node, self.version, i)
fp.write(e)
- # if we don't call close, the temp file will never replace the
+ # if we don't call rename, the temp file will never replace the
# real index
- fp.close()
+ fp.rename()
tr.replace(self.indexfile, trindex * self._io.size)
self._chunkclear()
@@ -979,35 +977,6 @@ class revlog(object):
dfh.close()
ifh.close()
- def compress(self, text):
- """ generate a possibly-compressed representation of text """
- if not text:
- return ("", text)
- l = len(text)
- bin = None
- if l < 44:
- pass
- elif l > 1000000:
- # zlib makes an internal copy, thus doubling memory usage for
- # large files, so lets do this in pieces
- z = zlib.compressobj()
- p = []
- pos = 0
- while pos < l:
- pos2 = pos + 2**20
- p.append(z.compress(text[pos:pos2]))
- pos = pos2
- p.append(z.flush())
- if sum(map(len, p)) < l:
- bin = "".join(p)
- else:
- bin = _compress(text)
- if bin is None or len(bin) > l:
- if text[0] == '\0':
- return ("", text)
- return ('u', text)
- return ("", bin)
-
def _addrevision(self, node, text, transaction, link, p1, p2,
cachedelta, ifh, dfh):
"""internal function to add revisions to the log
@@ -1040,7 +1009,7 @@ class revlog(object):
t = buildtext()
ptext = self.revision(self.node(rev))
delta = mdiff.textdiff(ptext, t)
- data = self.compress(delta)
+ data = compress(delta)
l = len(data[1]) + len(data[0])
if basecache[0] == rev:
chainbase = basecache[1]
@@ -1084,7 +1053,7 @@ class revlog(object):
textlen = len(text)
if d is None or dist > textlen * 2:
text = buildtext()
- data = self.compress(text)
+ data = compress(text)
l = len(data[1]) + len(data[0])
base = chainbase = curr
@@ -1163,7 +1132,6 @@ class revlog(object):
"""
# track the base of the current delta log
- content = []
node = None
r = len(self)
@@ -1194,8 +1162,6 @@ class revlog(object):
deltabase = chunkdata['deltabase']
delta = chunkdata['delta']
- content.append(node)
-
link = linkmapper(cs)
if node in self.nodemap:
# this can happen if two branches make the same change
@@ -1203,7 +1169,7 @@ class revlog(object):
continue
for p in (p1, p2):
- if p not in self.nodemap:
+ if not p in self.nodemap:
raise LookupError(p, self.indexfile,
_('unknown parent'))
@@ -1225,7 +1191,7 @@ class revlog(object):
dfh.close()
ifh.close()
- return content
+ return node
def strip(self, minlink, transaction):
"""truncate the revlog on the first revision with a linkrev >= minlink
@@ -1239,7 +1205,7 @@ class revlog(object):
So we truncate the revlog on the first of these revisions, and
trust that the caller has saved the revisions that shouldn't be
- removed and that it'll re-add them after this truncation.
+ removed and that it'll readd them after this truncation.
"""
if len(self) == 0:
return