filelog.py
294 lines
| 8.6 KiB
| text/x-python
|
PythonLexer
/ mercurial / filelog.py
|
r1089 | # filelog.py - file history class for mercurial | ||
# | ||||
|
r47575 | # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com> | ||
|
r1089 | # | ||
|
r8225 | # This software may be used and distributed according to the terms of the | ||
|
r10263 | # GNU General Public License version 2 or any later version. | ||
|
r1089 | |||
|
r25948 | from __future__ import absolute_import | ||
|
r40425 | from .i18n import _ | ||
|
r47771 | from .node import nullrev | ||
|
r25948 | from . import ( | ||
|
r37515 | error, | ||
|
r43078 | revlog, | ||
) | ||||
from .interfaces import ( | ||||
|
r37459 | repository, | ||
|
r43079 | util as interfaceutil, | ||
|
r25948 | ) | ||
|
r43346 | from .utils import storageutil | ||
r47838 | from .revlogutils import ( | |||
constants as revlog_constants, | ||||
r48629 | rewrite, | |||
r47838 | ) | |||
|
r43346 | |||
|
r1089 | |||
|
r37828 | @interfaceutil.implementer(repository.ifilestorage) | ||
|
r37515 | class filelog(object): | ||
|
r4258 | def __init__(self, opener, path): | ||
|
r43346 | self._revlog = revlog.revlog( | ||
r47838 | opener, | |||
# XXX should use the unencoded path | ||||
target=(revlog_constants.KIND_FILELOG, path), | ||||
r47921 | radix=b'/'.join((b'data', path)), | |||
r47838 | censorable=True, | |||
|
r43346 | ) | ||
|
r39819 | # Full name of the user visible file, relative to the repository root. | ||
# Used by LFS. | ||||
|
r39892 | self._revlog.filename = path | ||
|
r47538 | self.nullid = self._revlog.nullid | ||
r48630 | opts = opener.options | |||
self._fix_issue6528 = opts.get(b'issue6528.fix-incoming', True) | ||||
|
r37515 | |||
def __len__(self): | ||||
return len(self._revlog) | ||||
def __iter__(self): | ||||
return self._revlog.__iter__() | ||||
|
r40423 | def hasnode(self, node): | ||
|
r47771 | if node in (self.nullid, nullrev): | ||
|
r40423 | return False | ||
try: | ||||
self._revlog.rev(node) | ||||
return True | ||||
except (TypeError, ValueError, IndexError, error.LookupError): | ||||
return False | ||||
|
r37515 | def revs(self, start=0, stop=None): | ||
return self._revlog.revs(start=start, stop=stop) | ||||
def parents(self, node): | ||||
return self._revlog.parents(node) | ||||
def parentrevs(self, rev): | ||||
return self._revlog.parentrevs(rev) | ||||
def rev(self, node): | ||||
return self._revlog.rev(node) | ||||
def node(self, rev): | ||||
return self._revlog.node(rev) | ||||
def lookup(self, node): | ||||
|
r43346 | return storageutil.fileidlookup( | ||
r47926 | self._revlog, node, self._revlog.display_id | |||
|
r43346 | ) | ||
|
r37515 | |||
def linkrev(self, rev): | ||||
return self._revlog.linkrev(rev) | ||||
def commonancestorsheads(self, node1, node2): | ||||
return self._revlog.commonancestorsheads(node1, node2) | ||||
|
r39819 | # Used by dagop.blockdescendants(). | ||
|
r37515 | def descendants(self, revs): | ||
return self._revlog.descendants(revs) | ||||
def heads(self, start=None, stop=None): | ||||
return self._revlog.heads(start, stop) | ||||
|
r39819 | # Used by hgweb, children extension. | ||
|
r37515 | def children(self, node): | ||
return self._revlog.children(node) | ||||
def iscensored(self, rev): | ||||
return self._revlog.iscensored(rev) | ||||
|
r49360 | def revision(self, node, _df=None): | ||
return self._revlog.revision(node, _df=_df) | ||||
|
r37515 | |||
r42946 | def rawdata(self, node, _df=None): | |||
return self._revlog.rawdata(node, _df=_df) | ||||
|
r43346 | def emitrevisions( | ||
self, | ||||
nodes, | ||||
nodesorder=None, | ||||
revisiondata=False, | ||||
assumehaveparentrevisions=False, | ||||
deltamode=repository.CG_DELTAMODE_STD, | ||||
|
r47449 | sidedata_helpers=None, | ||
|
r43346 | ): | ||
|
r39898 | return self._revlog.emitrevisions( | ||
|
r43346 | nodes, | ||
nodesorder=nodesorder, | ||||
revisiondata=revisiondata, | ||||
|
r39898 | assumehaveparentrevisions=assumehaveparentrevisions, | ||
|
r43346 | deltamode=deltamode, | ||
|
r47449 | sidedata_helpers=sidedata_helpers, | ||
|
r43346 | ) | ||
|
r39898 | |||
|
r43346 | def addrevision( | ||
self, | ||||
revisiondata, | ||||
transaction, | ||||
linkrev, | ||||
p1, | ||||
p2, | ||||
node=None, | ||||
flags=revlog.REVIDX_DEFAULT_FLAGS, | ||||
cachedelta=None, | ||||
): | ||||
return self._revlog.addrevision( | ||||
revisiondata, | ||||
transaction, | ||||
linkrev, | ||||
p1, | ||||
p2, | ||||
node=node, | ||||
flags=flags, | ||||
cachedelta=cachedelta, | ||||
) | ||||
|
r37515 | |||
|
r43346 | def addgroup( | ||
self, | ||||
deltas, | ||||
linkmapper, | ||||
transaction, | ||||
addrevisioncb=None, | ||||
|
r46373 | duplicaterevisioncb=None, | ||
|
r43346 | maybemissingparents=False, | ||
): | ||||
|
r40425 | if maybemissingparents: | ||
|
r43346 | raise error.Abort( | ||
_( | ||||
|
r43347 | b'revlog storage does not support missing ' | ||
b'parents write mode' | ||||
|
r43346 | ) | ||
) | ||||
|
r40425 | |||
r48628 | with self._revlog._writing(transaction): | |||
r48629 | ||||
r48630 | if self._fix_issue6528: | |||
deltas = rewrite.filter_delta_issue6528(self._revlog, deltas) | ||||
r48629 | ||||
r48628 | return self._revlog.addgroup( | |||
deltas, | ||||
linkmapper, | ||||
transaction, | ||||
addrevisioncb=addrevisioncb, | ||||
duplicaterevisioncb=duplicaterevisioncb, | ||||
) | ||||
|
r37515 | |||
def getstrippoint(self, minlink): | ||||
return self._revlog.getstrippoint(minlink) | ||||
def strip(self, minlink, transaction): | ||||
return self._revlog.strip(minlink, transaction) | ||||
|
r39814 | def censorrevision(self, tr, node, tombstone=b''): | ||
|
r40092 | return self._revlog.censorrevision(tr, node, tombstone=tombstone) | ||
|
r39814 | |||
|
r37515 | def files(self): | ||
return self._revlog.files() | ||||
|
r1089 | def read(self, node): | ||
|
r39916 | return storageutil.filtermetadata(self.revision(node)) | ||
|
r1089 | |||
def add(self, text, meta, transaction, link, p1=None, p2=None): | ||||
|
r43347 | if meta or text.startswith(b'\1\n'): | ||
|
r39914 | text = storageutil.packmeta(meta, text) | ||
|
r47258 | rev = self.addrevision(text, transaction, link, p1, p2) | ||
return self.node(rev) | ||||
|
r1089 | |||
|
r1116 | def renamed(self, node): | ||
|
r40041 | return storageutil.filerevisioncopied(self, node) | ||
|
r1116 | |||
|
r2898 | def size(self, rev): | ||
"""return the size of a given revision""" | ||||
# for revisions with renames, we have to go the slow way | ||||
node = self.node(rev) | ||||
|
r50066 | if self.iscensored(rev): | ||
return 0 | ||||
|
r2898 | if self.renamed(node): | ||
return len(self.read(node)) | ||||
|
r11540 | # XXX if self.read(node).startswith("\1\n"), this returns (size+4) | ||
|
r37515 | return self._revlog.size(rev) | ||
|
r2898 | |||
|
r2887 | def cmp(self, node, text): | ||
|
r11539 | """compare text with a given file revision | ||
returns True if text is different than what is stored. | ||||
""" | ||||
|
r40043 | return not storageutil.filedataequivalent(self, node, text) | ||
|
r37515 | |||
|
r39878 | def verifyintegrity(self, state): | ||
return self._revlog.verifyintegrity(state) | ||||
|
r43346 | def storageinfo( | ||
self, | ||||
exclusivefiles=False, | ||||
sharedfiles=False, | ||||
revisionscount=False, | ||||
trackedsize=False, | ||||
storedsize=False, | ||||
): | ||||
|
r39905 | return self._revlog.storageinfo( | ||
|
r43346 | exclusivefiles=exclusivefiles, | ||
sharedfiles=sharedfiles, | ||||
revisionscount=revisionscount, | ||||
trackedsize=trackedsize, | ||||
storedsize=storedsize, | ||||
) | ||||
|
r39905 | |||
|
r39819 | # Used by repo upgrade. | ||
|
r37515 | def clone(self, tr, destrevlog, **kwargs): | ||
if not isinstance(destrevlog, filelog): | ||||
|
r43347 | raise error.ProgrammingError(b'expected filelog to clone()') | ||
|
r37515 | |||
return self._revlog.clone(tr, destrevlog._revlog, **kwargs) | ||||
|
r43346 | |||
|
r39801 | class narrowfilelog(filelog): | ||
"""Filelog variation to be used with narrow stores.""" | ||||
def __init__(self, opener, path, narrowmatch): | ||||
super(narrowfilelog, self).__init__(opener, path) | ||||
self._narrowmatch = narrowmatch | ||||
def renamed(self, node): | ||||
res = super(narrowfilelog, self).renamed(node) | ||||
# Renames that come from outside the narrowspec are problematic | ||||
# because we may lack the base text for the rename. This can result | ||||
# in code attempting to walk the ancestry or compute a diff | ||||
# encountering a missing revision. We address this by silently | ||||
# removing rename metadata if the source file is outside the | ||||
# narrow spec. | ||||
# | ||||
# A better solution would be to see if the base revision is available, | ||||
# rather than assuming it isn't. | ||||
# | ||||
# An even better solution would be to teach all consumers of rename | ||||
# metadata that the base revision may not be available. | ||||
# | ||||
# TODO consider better ways of doing this. | ||||
if res and not self._narrowmatch(res[0]): | ||||
return None | ||||
return res | ||||
def size(self, rev): | ||||
# Because we have a custom renamed() that may lie, we need to call | ||||
# the base renamed() to report accurate results. | ||||
node = self.node(rev) | ||||
if super(narrowfilelog, self).renamed(node): | ||||
return len(self.read(node)) | ||||
else: | ||||
return super(narrowfilelog, self).size(rev) | ||||
def cmp(self, node, text): | ||||
|
r47280 | # We don't call `super` because narrow parents can be buggy in case of a | ||
# ambiguous dirstate. Always take the slow path until there is a better | ||||
# fix, see issue6150. | ||||
|
r39801 | |||
|
r47280 | # Censored files compare against the empty file. | ||
if self.iscensored(self.rev(node)): | ||||
return text != b'' | ||||
|
r39801 | |||
|
r47280 | return self.read(node) != text | ||