##// END OF EJS Templates
dirstate: introduce a `is_changing_any` property...
dirstate: introduce a `is_changing_any` property This will embrace other cases than changing parents.

File last commit:

r50505:9cac281e default
r50918:e1cff854 default
Show More
remotefilelog.py
503 lines | 15.4 KiB | text/x-python | PythonLexer
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 # remotefilelog.py - filelog implementation where filelog history is stored
# remotely
#
# Copyright 2013 Facebook, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
import collections
import os
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 from mercurial.node import bin
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 from mercurial.i18n import _
from mercurial import (
ancestor,
error,
mdiff,
revlog,
)
from mercurial.utils import storageutil
flagutil: use the new mixin use in remotefilelog...
r43141 from mercurial.revlogutils import flagutil
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
from . import (
constants,
fileserverclient,
shallowutil,
)
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class remotefilelognodemap:
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def __init__(self, filename, store):
self._filename = filename
self._store = store
def __contains__(self, node):
missing = self._store.getmissing([(self._filename, node)])
return not bool(missing)
def __get__(self, node):
if node not in self:
raise KeyError(node)
return node
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class remotefilelog:
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
_generaldelta = True
flagprocessors: move _flagserrorclass attribute on revlog & co...
r43264 _flagserrorclass = error.RevlogError
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
def __init__(self, opener, path, repo):
self.opener = opener
self.filename = path
self.repo = repo
self.nodemap = remotefilelognodemap(self.filename, repo.contentstore)
self.version = 1
flagutil: use the new mixin use in remotefilelog...
r43141 self._flagprocessors = dict(flagutil.flagprocessors)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def read(self, node):
"""returns the file contents at this node"""
t = self.revision(node)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not t.startswith(b'\1\n'):
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 return t
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 s = t.index(b'\1\n', 2)
Augie Fackler
formatting: blacken the codebase...
r43346 return t[s + 2 :]
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
def add(self, text, meta, transaction, linknode, p1=None, p2=None):
# hash with the metadata, like in vanilla filelogs
Augie Fackler
formatting: blacken the codebase...
r43346 hashtext = shallowutil.createrevlogtext(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 text, meta.get(b'copy'), meta.get(b'copyrev')
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 node = storageutil.hashrevisionsha1(hashtext, p1, p2)
Augie Fackler
formatting: blacken the codebase...
r43346 return self.addrevision(
hashtext, transaction, linknode, p1, p2, node=node
)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
def _createfileblob(self, text, meta, flags, p1, p2, node, linknode):
# text passed to "_createfileblob" does not include filelog metadata
header = shallowutil.buildfileblobheader(len(text), flags)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 data = b"%s\0%s" % (header, text)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
realp1 = p1
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 copyfrom = b""
if meta and b'copy' in meta:
copyfrom = meta[b'copy']
realp1 = bin(meta[b'copyrev'])
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 data += b"%s%s%s%s%s\0" % (node, realp1, p2, linknode, copyfrom)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
visited = set()
pancestors = {}
queue = []
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 if realp1 != self.repo.nullid:
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 p1flog = self
if copyfrom:
p1flog = remotefilelog(self.opener, copyfrom, self.repo)
pancestors.update(p1flog.ancestormap(realp1))
queue.append(realp1)
visited.add(realp1)
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 if p2 != self.repo.nullid:
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 pancestors.update(self.ancestormap(p2))
queue.append(p2)
visited.add(p2)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ancestortext = b""
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
# add the ancestors in topological order
while queue:
c = queue.pop(0)
pa1, pa2, ancestorlinknode, pacopyfrom = pancestors[c]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 pacopyfrom = pacopyfrom or b''
ancestortext += b"%s%s%s%s%s\0" % (
Augie Fackler
formatting: blacken the codebase...
r43346 c,
pa1,
pa2,
ancestorlinknode,
pacopyfrom,
)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 if pa1 != self.repo.nullid and pa1 not in visited:
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 queue.append(pa1)
visited.add(pa1)
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 if pa2 != self.repo.nullid and pa2 not in visited:
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 queue.append(pa2)
visited.add(pa2)
data += ancestortext
return data
Augie Fackler
formatting: blacken the codebase...
r43346 def addrevision(
self,
text,
transaction,
linknode,
p1,
p2,
cachedelta=None,
node=None,
flags=revlog.REVIDX_DEFAULT_FLAGS,
sidedata=None,
):
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 # text passed to "addrevision" includes hg filelog metadata header
if node is None:
node = storageutil.hashrevisionsha1(text, p1, p2)
meta, metaoffset = storageutil.parsemeta(text)
Augie Fackler
formatting: blacken the codebase...
r43346 rawtext, validatehash = flagutil.processflagswrite(
Raphaël Gomès
sidedata: move to new sidedata storage in revlogv2...
r47443 self,
text,
flags,
Augie Fackler
formatting: blacken the codebase...
r43346 )
return self.addrawrevision(
rawtext,
transaction,
linknode,
p1,
p2,
node,
flags,
cachedelta,
_metatuple=(meta, metaoffset),
)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
Augie Fackler
formatting: blacken the codebase...
r43346 def addrawrevision(
self,
rawtext,
transaction,
linknode,
p1,
p2,
node,
flags,
cachedelta=None,
_metatuple=None,
):
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 if _metatuple:
# _metatuple: used by "addrevision" internally by remotefilelog
# meta was parsed confidently
meta, metaoffset = _metatuple
else:
# not from self.addrevision, but something else (repo._filecommit)
# calls addrawrevision directly. remotefilelog needs to get and
# strip filelog metadata.
# we don't have confidence about whether rawtext contains filelog
# metadata or not (flag processor could replace it), so we just
# parse it as best-effort.
# in LFS (flags != 0)'s case, the best way is to call LFS code to
# get the meta information, instead of storageutil.parsemeta.
meta, metaoffset = storageutil.parsemeta(rawtext)
if flags != 0:
# when flags != 0, be conservative and do not mangle rawtext, since
# a read flag processor expects the text not being mangled at all.
metaoffset = 0
if metaoffset:
# remotefilelog fileblob stores copy metadata in its ancestortext,
# not its main blob. so we need to remove filelog metadata
# (containing copy information) from text.
blobtext = rawtext[metaoffset:]
else:
blobtext = rawtext
Augie Fackler
formatting: blacken the codebase...
r43346 data = self._createfileblob(
blobtext, meta, flags, p1, p2, node, linknode
)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 self.repo.contentstore.addremotefilelognode(self.filename, node, data)
return node
def renamed(self, node):
ancestors = self.repo.metadatastore.getancestors(self.filename, node)
p1, p2, linknode, copyfrom = ancestors[node]
if copyfrom:
return (copyfrom, p1)
return False
def size(self, node):
"""return the size of a given revision"""
return len(self.read(node))
rawsize = size
def cmp(self, node, text):
"""compare text with a given file revision
returns True if text is different than what is stored.
"""
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 if node == self.repo.nullid:
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 return True
nodetext = self.read(node)
return nodetext != text
def __nonzero__(self):
return True
Augie Fackler
remotefilelog: implement __bool__ as well as __nonzero__ for py3...
r41287 __bool__ = __nonzero__
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def __len__(self):
Joerg Sonnenberger
exchange: add fast path for subrepo check on push...
r49381 if self.filename in (b'.hgtags', b'.hgsub', b'.hgsubstate'):
# Global tag and subrepository support require access to the
# file history for various performance sensitive operations.
# excludepattern should be used for repositories depending on
# those features to fallback to regular filelog.
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 return 0
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise RuntimeError(b"len not supported")
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
Augie Fackler
remotefilelog: add fake heads() method that allows viewing a file in hgweb...
r45060 def heads(self):
# Fake heads of the filelog to satisfy hgweb.
return []
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def empty(self):
return False
def flags(self, node):
if isinstance(node, int):
raise error.ProgrammingError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'remotefilelog does not accept integer rev for flags'
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 store = self.repo.contentstore
return store.getmeta(self.filename, node).get(constants.METAKEYFLAG, 0)
def parents(self, node):
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 if node == self.repo.nullid:
return self.repo.nullid, self.repo.nullid
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
ancestormap = self.repo.metadatastore.getancestors(self.filename, node)
p1, p2, linknode, copyfrom = ancestormap[node]
if copyfrom:
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 p1 = self.repo.nullid
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
return p1, p2
def parentrevs(self, rev):
# TODO(augie): this is a node and should be a rev, but for now
# nothing in core seems to actually break.
return self.parents(rev)
def linknode(self, node):
ancestormap = self.repo.metadatastore.getancestors(self.filename, node)
p1, p2, linknode, copyfrom = ancestormap[node]
return linknode
def linkrev(self, node):
return self.repo.unfiltered().changelog.rev(self.linknode(node))
Augie Fackler
formatting: blacken the codebase...
r43346 def emitrevisions(
self,
nodes,
nodesorder=None,
revisiondata=False,
assumehaveparentrevisions=False,
deltaprevious=False,
deltamode=None,
Raphaël Gomès
changegroup: add v4 changegroup for revlog v2 exchange...
r47445 sidedata_helpers=None,
debug: add an option to display statistic about a bundling operation...
r50505 debug_info=None,
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 # we don't use any of these parameters here
del nodesorder, revisiondata, assumehaveparentrevisions, deltaprevious
del deltamode
prevnode = None
for node in nodes:
p1, p2 = self.parents(node)
if prevnode is None:
basenode = prevnode = p1
if basenode == node:
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 basenode = self.repo.nullid
if basenode != self.repo.nullid:
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 revision = None
delta = self.revdiff(basenode, node)
else:
rawdata: update caller in remotefilelog...
r43039 revision = self.rawdata(node)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 delta = None
yield revlog.revlogrevisiondelta(
node=node,
p1node=p1,
p2node=p2,
linknode=self.linknode(node),
basenode=basenode,
flags=self.flags(node),
baserevisionsize=None,
revision=revision,
delta=delta,
Raphaël Gomès
changegroup: add v4 changegroup for revlog v2 exchange...
r47445 # Sidedata is not supported yet
sidedata=None,
Raphaël Gomès
cg4: introduce protocol flag to signify the presence of sidedata...
r47843 # Protocol flags are not used yet
protocol_flags=0,
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
def revdiff(self, node1, node2):
Augie Fackler
formatting: blacken the codebase...
r43346 return mdiff.textdiff(self.rawdata(node1), self.rawdata(node2))
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
def lookup(self, node):
if len(node) == 40:
node = bin(node)
if len(node) != 20:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.LookupError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 node, self.filename, _(b'invalid lookup input')
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
return node
def rev(self, node):
# This is a hack to make TortoiseHG work.
return node
def node(self, rev):
# This is a hack.
if isinstance(rev, int):
raise error.ProgrammingError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'remotefilelog does not convert integer rev to node'
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 return rev
def revision(self, node, raw=False):
"""returns the revlog contents at this node.
this includes the meta data traditionally included in file revlogs.
this is generally only used for bundling and communicating with vanilla
hg clients.
"""
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 if node == self.repo.nullid:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b""
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 if len(node) != 20:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.LookupError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 node, self.filename, _(b'invalid revision input')
Augie Fackler
formatting: blacken the codebase...
r43346 )
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 if (
node == self.repo.nodeconstants.wdirid
or node in self.repo.nodeconstants.wdirfilenodeids
):
Augie Fackler
remotefilelog: correctly reject wdir filenodes...
r42264 raise error.WdirUnsupported
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
store = self.repo.contentstore
rawtext = store.get(self.filename, node)
if raw:
return rawtext
flags = store.getmeta(self.filename, node).get(constants.METAKEYFLAG, 0)
if flags == 0:
return rawtext
flagprocessors: make `processflagsread` a module level function...
r43261 return flagutil.processflagsread(self, rawtext, flags)[0]
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
rawdata: implement the method for `remotefilelog` too...
r42951 def rawdata(self, node):
return self.revision(node, raw=False)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def _read(self, id):
"""reads the raw file blob from disk, cache, or server"""
fileservice = self.repo.fileservice
localcache = fileservice.localcache
Augie Fackler
formatting: blacken the codebase...
r43346 cachekey = fileserverclient.getcachekey(
self.repo.name, self.filename, id
)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 try:
return localcache.read(cachekey)
except KeyError:
pass
localkey = fileserverclient.getlocalkey(self.filename, id)
localpath = os.path.join(self.localpath, localkey)
try:
return shallowutil.readfile(localpath)
except IOError:
pass
fileservice.prefetch([(self.filename, id)])
try:
return localcache.read(cachekey)
except KeyError:
pass
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.LookupError(id, self.filename, _(b'no node'))
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
def ancestormap(self, node):
return self.repo.metadatastore.getancestors(self.filename, node)
def ancestor(self, a, b):
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 if a == self.repo.nullid or b == self.repo.nullid:
return self.repo.nullid
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
revmap, parentfunc = self._buildrevgraph(a, b)
Gregory Szorc
global: bulk replace simple pycompat.iteritems(x) with x.items()...
r49768 nodemap = {v: k for (k, v) in revmap.items()}
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
ancs = ancestor.ancestors(parentfunc, revmap[a], revmap[b])
if ancs:
# choose a consistent winner when there's a tie
return min(map(nodemap.__getitem__, ancs))
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 return self.repo.nullid
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
def commonancestorsheads(self, a, b):
"""calculate all the heads of the common ancestors of nodes a and b"""
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 if a == self.repo.nullid or b == self.repo.nullid:
return self.repo.nullid
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
revmap, parentfunc = self._buildrevgraph(a, b)
Gregory Szorc
global: bulk replace simple pycompat.iteritems(x) with x.items()...
r49768 nodemap = {v: k for (k, v) in revmap.items()}
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
ancs = ancestor.commonancestorsheads(parentfunc, revmap[a], revmap[b])
return map(nodemap.__getitem__, ancs)
def _buildrevgraph(self, a, b):
"""Builds a numeric revision graph for the given two nodes.
Returns a node->rev map and a rev->[revs] parent function.
"""
amap = self.ancestormap(a)
bmap = self.ancestormap(b)
# Union the two maps
parentsmap = collections.defaultdict(list)
allparents = set()
for mapping in (amap, bmap):
Gregory Szorc
global: bulk replace simple pycompat.iteritems(x) with x.items()...
r49768 for node, pdata in mapping.items():
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 parents = parentsmap[node]
p1, p2, linknode, copyfrom = pdata
# Don't follow renames (copyfrom).
# remotefilectx.ancestor does that.
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 if p1 != self.repo.nullid and not copyfrom:
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 parents.append(p1)
allparents.add(p1)
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 if p2 != self.repo.nullid:
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 parents.append(p2)
allparents.add(p2)
# Breadth first traversal to build linkrev graph
parentrevs = collections.defaultdict(list)
revmap = {}
Augie Fackler
formatting: blacken the codebase...
r43346 queue = collections.deque(
((None, n) for n in parentsmap if n not in allparents)
)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 while queue:
prevrev, current = queue.pop()
if current in revmap:
if prevrev:
parentrevs[prevrev].append(revmap[current])
continue
# Assign linkrevs in reverse order, so start at
# len(parentsmap) and work backwards.
currentrev = len(parentsmap) - len(revmap) - 1
revmap[current] = currentrev
if prevrev:
parentrevs[prevrev].append(currentrev)
for parent in parentsmap.get(current):
queue.appendleft((currentrev, parent))
return revmap, parentrevs.__getitem__
def strip(self, minlink, transaction):
pass
# misc unused things
def files(self):
return []
def checksize(self):
return 0, 0