##// END OF EJS Templates
arbitraryfilectx: use our existing helpers for reading and writing files...
arbitraryfilectx: use our existing helpers for reading and writing files Differential Revision: https://phab.mercurial-scm.org/D12090

File last commit:

r48219:cac0e062 default
r49521:57604015 default
Show More
changelog.py
633 lines | 19.7 KiB | text/x-python | PythonLexer
mpm@selenic.com
changelog: adjust imports, comment
r1095 # changelog.py - changelog class for mercurial
mpm@selenic.com
Break apart hg.py...
r1089 #
Raphaël Gomès
contributor: change mentions of mpm to olivia...
r47575 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
mpm@selenic.com
Break apart hg.py...
r1089 #
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
mpm@selenic.com
Break apart hg.py...
r1089
Gregory Szorc
changelog: use absolute_import
r25922 from __future__ import absolute_import
from .i18n import _
from .node import (
bin,
hex,
)
Augie Fackler
formatting: blacken the codebase...
r43346 from .thirdparty import attr
Gregory Szorc
changelog: use absolute_import
r25922
from . import (
encoding,
error,
metadata: move computation related to files touched in a dedicated module...
r45466 metadata,
Pulkit Goyal
py3: use pycompat.bytestr to convert str to bytes...
r36246 pycompat,
Gregory Szorc
changelog: use absolute_import
r25922 revlog,
)
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 from .utils import (
dateutil,
stringutil,
)
revlog: introduce an explicit tracking of what the revlog is about...
r47838 from .revlogutils import (
constants as revlog_constants,
flagutil,
)
mpm@selenic.com
Break apart hg.py...
r1089
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _defaultextra = {b'branch': b'default'}
Matt Mackall
changelog: micro-optimizations to changelog.read()
r16267
Augie Fackler
formatting: blacken the codebase...
r43346
Benoit Boissinot
[extendedchangelog] encode/decode function...
r3232 def _string_escape(text):
"""
Yuya Nishihara
doctest: replace chr() with pycompat.bytechr()
r34135 >>> from .pycompat import bytechr as chr
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> d = {b'nl': chr(10), b'bs': chr(92), b'cr': chr(13), b'nul': chr(0)}
Martin von Zweigbergk
changelog: extract a _string_unescape() to mirror _string_escape()...
r42285 >>> s = b"ab%(nl)scd%(bs)s%(bs)sn%(nul)s12ab%(cr)scd%(bs)s%(nl)s" % d
Benoit Boissinot
[extendedchangelog] encode/decode function...
r3232 >>> s
Martin von Zweigbergk
changelog: extract a _string_unescape() to mirror _string_escape()...
r42285 'ab\\ncd\\\\\\\\n\\x0012ab\\rcd\\\\\\n'
Benoit Boissinot
[extendedchangelog] encode/decode function...
r3232 >>> res = _string_escape(s)
Martin von Zweigbergk
changelog: extract a _string_unescape() to mirror _string_escape()...
r42285 >>> s == _string_unescape(res)
Benoit Boissinot
[extendedchangelog] encode/decode function...
r3232 True
"""
# subset of the string_escape codec
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 text = (
text.replace(b'\\', b'\\\\')
.replace(b'\n', b'\\n')
.replace(b'\r', b'\\r')
)
return text.replace(b'\0', b'\\0')
Benoit Boissinot
[extendedchangelog] encode/decode function...
r3232
Augie Fackler
formatting: blacken the codebase...
r43346
Martin von Zweigbergk
changelog: extract a _string_unescape() to mirror _string_escape()...
r42285 def _string_unescape(text):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if b'\\0' in text:
Martin von Zweigbergk
changelog: extract a _string_unescape() to mirror _string_escape()...
r42285 # fix up \0 without getting into trouble with \\0
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 text = text.replace(b'\\\\', b'\\\\\n')
text = text.replace(b'\\0', b'\0')
text = text.replace(b'\n', b'')
Martin von Zweigbergk
changelog: extract a _string_unescape() to mirror _string_escape()...
r42285 return stringutil.unescapestr(text)
Augie Fackler
formatting: blacken the codebase...
r43346
Martin Geisler
changelog: turn {de,en}code_extra methods into functions...
r8443 def decodeextra(text):
Matt Mackall
changelog: handle decoding of NULs in extra more carefully (issue3156)...
r15661 """
Yuya Nishihara
doctest: replace chr() with pycompat.bytechr()
r34135 >>> from .pycompat import bytechr as chr
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> sorted(decodeextra(encodeextra({b'foo': b'bar', b'baz': chr(0) + b'2'})
Yuya Nishihara
doctest: replace .iteritems() with .items()
r34134 ... ).items())
Mads Kiilerich
tests: fix doctest stability over Python versions...
r18379 [('baz', '\\x002'), ('branch', 'default'), ('foo', 'bar')]
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> sorted(decodeextra(encodeextra({b'foo': b'bar',
... b'baz': chr(92) + chr(0) + b'2'})
Yuya Nishihara
doctest: replace .iteritems() with .items()
r34134 ... ).items())
Mads Kiilerich
tests: fix doctest stability over Python versions...
r18379 [('baz', '\\\\\\x002'), ('branch', 'default'), ('foo', 'bar')]
Matt Mackall
changelog: handle decoding of NULs in extra more carefully (issue3156)...
r15661 """
Matt Mackall
changelog: micro-optimizations to changelog.read()
r16267 extra = _defaultextra.copy()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 for l in text.split(b'\0'):
Martin Geisler
changelog: turn {de,en}code_extra methods into functions...
r8443 if l:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 k, v = _string_unescape(l).split(b':', 1)
Martin Geisler
changelog: turn {de,en}code_extra methods into functions...
r8443 extra[k] = v
return extra
Augie Fackler
formatting: blacken the codebase...
r43346
Martin Geisler
changelog: turn {de,en}code_extra methods into functions...
r8443 def encodeextra(d):
# keys must be sorted to produce a deterministic changelog entry
Martin von Zweigbergk
py3: require values in changelog extras to be bytes...
r45095 items = [_string_escape(b'%s:%s' % (k, d[k])) for k in sorted(d)]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b"\0".join(items)
Martin Geisler
changelog: turn {de,en}code_extra methods into functions...
r8443
Augie Fackler
formatting: blacken the codebase...
r43346
Pierre-Yves David
changelog: extract description cleaning logic in a dedicated function...
r17810 def stripdesc(desc):
"""strip trailing whitespace and leading and trailing empty lines"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b'\n'.join([l.rstrip() for l in desc.splitlines()]).strip(b'\n')
Pierre-Yves David
changelog: extract description cleaning logic in a dedicated function...
r17810
Augie Fackler
formatting: blacken the codebase...
r43346
Benoit Boissinot
use new style classes
r8778 class appender(object):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """the changelog index must be updated last on disk, so we use this class
to delay writes to it"""
Augie Fackler
formatting: blacken the codebase...
r43346
FUJIWARA Katsunori
changelog: use "vfs.fstat()" instead of "util.fstat()"...
r19899 def __init__(self, vfs, name, mode, buf):
Matt Mackall
restructure changelog file appending...
r4261 self.data = buf
FUJIWARA Katsunori
changelog: use "vfs.fstat()" instead of "util.fstat()"...
r19899 fp = vfs(name, mode)
Matt Mackall
restructure changelog file appending...
r4261 self.fp = fp
self.offset = fp.tell()
FUJIWARA Katsunori
changelog: use "vfs.fstat()" instead of "util.fstat()"...
r19899 self.size = vfs.fstat(fp).st_size
Durham Goode
changelog: keep track of file end in appender (issue5444)...
r30596 self._end = self.size
Matt Mackall
restructure changelog file appending...
r4261
def end(self):
Durham Goode
changelog: keep track of file end in appender (issue5444)...
r30596 return self._end
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
restructure changelog file appending...
r4261 def tell(self):
return self.offset
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
restructure changelog file appending...
r4261 def flush(self):
pass
Boris Feld
changelog: add the missing 'closed' property on 'appender' object...
r35981
@property
def closed(self):
return self.fp.closed
Matt Mackall
restructure changelog file appending...
r4261 def close(self):
Benoit Boissinot
fix bogus close spotted by pychecker (no close() in global scope)
r4961 self.fp.close()
Matt Mackall
restructure changelog file appending...
r4261
def seek(self, offset, whence=0):
'''virtual file offset spans real file and data'''
if whence == 0:
self.offset = offset
elif whence == 1:
self.offset += offset
elif whence == 2:
self.offset = self.end() + offset
if self.offset < self.size:
self.fp.seek(self.offset)
def read(self, count=-1):
'''only trick here is reads that span real file and data'''
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ret = b""
Matt Mackall
restructure changelog file appending...
r4261 if self.offset < self.size:
s = self.fp.read(count)
ret = s
self.offset += len(s)
if count > 0:
count -= len(s)
if count != 0:
doff = self.offset - self.size
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.data.insert(0, b"".join(self.data))
Matt Mackall
restructure changelog file appending...
r4261 del self.data[1:]
Augie Fackler
formatting: blacken the codebase...
r43346 s = self.data[0][doff : doff + count]
Matt Mackall
restructure changelog file appending...
r4261 self.offset += len(s)
ret += s
return ret
def write(self, s):
Yuya Nishihara
py3: use bytes() to cast to immutable bytes in changelog.appender.write()
r31642 self.data.append(bytes(s))
Matt Mackall
restructure changelog file appending...
r4261 self.offset += len(s)
Durham Goode
changelog: keep track of file end in appender (issue5444)...
r30596 self._end += len(s)
Matt Mackall
restructure changelog file appending...
r4261
Boris Feld
changelog: implement context manager method for 'appender' object...
r35980 def __enter__(self):
self.fp.__enter__()
return self
def __exit__(self, *args):
return self.fp.__exit__(*args)
Augie Fackler
formatting: blacken the codebase...
r43346
changelog: change the implementation of `_divertopener`...
r44985 class _divertopener(object):
def __init__(self, opener, target):
self._opener = opener
self._target = target
Augie Fackler
formatting: blacken the codebase...
r43346
changelog: change the implementation of `_divertopener`...
r44985 def __call__(self, name, mode=b'r', checkambig=False, **kwargs):
if name != self._target:
return self._opener(name, mode, **kwargs)
return self._opener(name + b".a", mode, **kwargs)
Augie Fackler
formatting: blacken the codebase...
r43346
changelog: change the implementation of `_divertopener`...
r44985 def __getattr__(self, attr):
return getattr(self._opener, attr)
Pierre-Yves David
changelog: rework the delayupdate mechanism...
r23201
Augie Fackler
formatting: blacken the codebase...
r43346
Pierre-Yves David
changelog: rework the delayupdate mechanism...
r23201 def _delayopener(opener, target, buf):
"""build an opener that stores chunks in 'buf' instead of 'target'"""
Augie Fackler
formatting: blacken the codebase...
r43346
changelog: fix the diverted opener to accept more kwargs...
r44507 def _delay(name, mode=b'r', checkambig=False, **kwargs):
Pierre-Yves David
changelog: rework the delayupdate mechanism...
r23201 if name != target:
changelog: fix the diverted opener to accept more kwargs...
r44507 return opener(name, mode, **kwargs)
assert not kwargs
FUJIWARA Katsunori
changelog: use "vfs.fstat()" instead of "util.fstat()"...
r19899 return appender(opener, name, mode, buf)
Augie Fackler
formatting: blacken the codebase...
r43346
Pierre-Yves David
changelog: rework the delayupdate mechanism...
r23201 return _delay
Matt Mackall
changelog: move delayopener outside of class to eliminate reference cycle
r9166
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
changelog: use attrs instead of namedtuple...
r34399 @attr.s
class _changelogrevision(object):
# Extensions might modify _defaultextra, so let the constructor below pass
# it in
extra = attr.ib()
Joerg Sonnenberger
node: introduce nodeconstants class...
r47538 manifest = attr.ib()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 user = attr.ib(default=b'')
Siddharth Agarwal
changelog: use attrs instead of namedtuple...
r34399 date = attr.ib(default=(0, 0))
Gregory Szorc
changelog: use a Factory for default value for files...
r34442 files = attr.ib(default=attr.Factory(list))
Martin von Zweigbergk
context: get filesadded() and filesremoved() from changeset if configured...
r42599 filesadded = attr.ib(default=None)
filesremoved = attr.ib(default=None)
Martin von Zweigbergk
changelog: define changelogrevision.p[12]copies for null revision...
r42487 p1copies = attr.ib(default=None)
p2copies = attr.ib(default=None)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 description = attr.ib(default=b'')
Joerg Sonnenberger
changelog: move branchinfo to changelogrevision...
r47082 branchinfo = attr.ib(default=(_defaultextra[b'branch'], False))
Gregory Szorc
changelog: add class to represent parsed changelog revisions...
r28487
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
changelog: add class to represent parsed changelog revisions...
r28487 class changelogrevision(object):
"""Holds results of a parsed changelog revision.
Changelog revisions consist of multiple pieces of data, including
the manifest node, user, and date. This object exposes a view into
the parsed object.
"""
__slots__ = (
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 '_offsets',
'_text',
'_sidedata',
'_cpsd',
changelog: add a `changes` property on `changelogrevision`...
r46144 '_changes',
Gregory Szorc
changelog: add class to represent parsed changelog revisions...
r28487 )
Joerg Sonnenberger
node: introduce nodeconstants class...
r47538 def __new__(cls, cl, text, sidedata, cpsd):
Gregory Szorc
changelog: add class to represent parsed changelog revisions...
r28487 if not text:
Joerg Sonnenberger
node: replace nullid and friends with nodeconstants class...
r47771 return _changelogrevision(extra=_defaultextra, manifest=cl.nullid)
Gregory Szorc
changelog: add class to represent parsed changelog revisions...
r28487
self = super(changelogrevision, cls).__new__(cls)
# We could return here and implement the following as an __init__.
# But doing it here is equivalent and saves an extra function call.
# format used:
# nodeid\n : manifest node in ascii
# user\n : user, no \n or \r allowed
# time tz extra\n : date (time is int or float, timezone is int)
# : extra is metadata, encoded and separated by '\0'
# : older versions ignore it
# files\n\n : files modified by the cset, no \n or \r allowed
# (.*) : comment (free text, ideally utf-8)
#
# changelog v0 doesn't use extra
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 nl1 = text.index(b'\n')
nl2 = text.index(b'\n', nl1 + 1)
nl3 = text.index(b'\n', nl2 + 1)
Gregory Szorc
changelog: add class to represent parsed changelog revisions...
r28487
Gregory Szorc
changelog: lazily parse files...
r28493 # The list of files may be empty. Which means nl3 is the first of the
# double newline that precedes the description.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if text[nl3 + 1 : nl3 + 2] == b'\n':
Gregory Szorc
changelog: avoid slicing raw data until needed...
r28495 doublenl = nl3
Gregory Szorc
changelog: lazily parse files...
r28493 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 doublenl = text.index(b'\n\n', nl3 + 1)
Gregory Szorc
changelog: avoid slicing raw data until needed...
r28495
self._offsets = (nl1, nl2, nl3, doublenl)
self._text = text
sidedatacopies: get and store sidedata in the changelogrevision object...
r43413 self._sidedata = sidedata
sidedatacopies: only read from copies when in this mode...
r43504 self._cpsd = cpsd
changelog: add a `changes` property on `changelogrevision`...
r46144 self._changes = None
Gregory Szorc
changelog: add class to represent parsed changelog revisions...
r28487
return self
Gregory Szorc
changelog: lazily parse description...
r28489 @property
Gregory Szorc
changelog: lazily parse manifest node...
r28490 def manifest(self):
Augie Fackler
formatting: blacken the codebase...
r43346 return bin(self._text[0 : self._offsets[0]])
Gregory Szorc
changelog: lazily parse manifest node...
r28490
@property
Gregory Szorc
changelog: lazily parse user...
r28491 def user(self):
Gregory Szorc
changelog: avoid slicing raw data until needed...
r28495 off = self._offsets
Augie Fackler
formatting: blacken the codebase...
r43346 return encoding.tolocal(self._text[off[0] + 1 : off[1]])
Gregory Szorc
changelog: lazily parse user...
r28491
@property
Gregory Szorc
changelog: lazily parse date/extra field...
r28492 def _rawdate(self):
Gregory Szorc
changelog: avoid slicing raw data until needed...
r28495 off = self._offsets
Augie Fackler
formatting: blacken the codebase...
r43346 dateextra = self._text[off[1] + 1 : off[2]]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return dateextra.split(b' ', 2)[0:2]
Gregory Szorc
changelog: lazily parse date/extra field...
r28492
@property
def _rawextra(self):
Gregory Szorc
changelog: avoid slicing raw data until needed...
r28495 off = self._offsets
Augie Fackler
formatting: blacken the codebase...
r43346 dateextra = self._text[off[1] + 1 : off[2]]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fields = dateextra.split(b' ', 2)
Gregory Szorc
changelog: lazily parse date/extra field...
r28492 if len(fields) != 3:
return None
return fields[2]
@property
def date(self):
raw = self._rawdate
time = float(raw[0])
# Various tools did silly things with the timezone.
try:
timezone = int(raw[1])
except ValueError:
timezone = 0
return time, timezone
@property
def extra(self):
raw = self._rawextra
if raw is None:
return _defaultextra
return decodeextra(raw)
@property
changelog: add a `changes` property on `changelogrevision`...
r46144 def changes(self):
if self._changes is not None:
return self._changes
sidedata: add a `decode_files_sidedata` function...
r46145 if self._cpsd:
changing-files: drop the now useless changelogrevision argument...
r46212 changes = metadata.decode_files_sidedata(self._sidedata)
sidedata: add a `decode_files_sidedata` function...
r46145 else:
changes = metadata.ChangingFiles(
touched=self.files or (),
added=self.filesadded or (),
removed=self.filesremoved or (),
p1_copies=self.p1copies or {},
p2_copies=self.p2copies or {},
)
changelog: add a `changes` property on `changelogrevision`...
r46144 self._changes = changes
return changes
@property
Gregory Szorc
changelog: lazily parse files...
r28493 def files(self):
changing-files: retrieve changelogrevision.files from the sidedata block...
r46213 if self._cpsd:
return sorted(self.changes.touched)
Gregory Szorc
changelog: avoid slicing raw data until needed...
r28495 off = self._offsets
if off[2] == off[3]:
Gregory Szorc
changelog: lazily parse files...
r28493 return []
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return self._text[off[2] + 1 : off[3]].split(b'\n')
Gregory Szorc
changelog: lazily parse files...
r28493
@property
Martin von Zweigbergk
context: get filesadded() and filesremoved() from changeset if configured...
r42599 def filesadded(self):
sidedatacopies: only read from copies when in this mode...
r43504 if self._cpsd:
sidedata: simply read added files from the `ChangingFiles` object
r46146 return self.changes.added
sidedatacopies: read rename information from sidedata...
r43416 else:
rawindices = self.extra.get(b'filesadded')
changelog: make copies related function return None or a valid value...
r43415 if rawindices is None:
return None
metadata: move computation related to files touched in a dedicated module...
r45466 return metadata.decodefileindices(self.files, rawindices)
Martin von Zweigbergk
context: get filesadded() and filesremoved() from changeset if configured...
r42599
@property
def filesremoved(self):
sidedatacopies: only read from copies when in this mode...
r43504 if self._cpsd:
sidedata: simply read removed files from the `ChangingFiles` object
r46147 return self.changes.removed
sidedatacopies: read rename information from sidedata...
r43416 else:
rawindices = self.extra.get(b'filesremoved')
changelog: make copies related function return None or a valid value...
r43415 if rawindices is None:
return None
metadata: move computation related to files touched in a dedicated module...
r45466 return metadata.decodefileindices(self.files, rawindices)
Martin von Zweigbergk
context: get filesadded() and filesremoved() from changeset if configured...
r42599
@property
Martin von Zweigbergk
changelog: parse copy metadata if available in extras...
r42318 def p1copies(self):
sidedatacopies: only read from copies when in this mode...
r43504 if self._cpsd:
sidedata: simply read p1copies files from the `ChangingFiles` object
r46148 return self.changes.copied_from_p1
sidedatacopies: read rename information from sidedata...
r43416 else:
rawcopies = self.extra.get(b'p1copies')
changelog: make copies related function return None or a valid value...
r43415 if rawcopies is None:
return None
metadata: move computation related to files touched in a dedicated module...
r45466 return metadata.decodecopies(self.files, rawcopies)
Martin von Zweigbergk
changelog: parse copy metadata if available in extras...
r42318
@property
def p2copies(self):
sidedatacopies: only read from copies when in this mode...
r43504 if self._cpsd:
sidedata: simply read p2copies files from the `ChangingFiles` object
r46149 return self.changes.copied_from_p2
sidedatacopies: read rename information from sidedata...
r43416 else:
rawcopies = self.extra.get(b'p2copies')
changelog: make copies related function return None or a valid value...
r43415 if rawcopies is None:
return None
metadata: move computation related to files touched in a dedicated module...
r45466 return metadata.decodecopies(self.files, rawcopies)
Martin von Zweigbergk
changelog: parse copy metadata if available in extras...
r42318
@property
Gregory Szorc
changelog: lazily parse description...
r28489 def description(self):
Augie Fackler
formatting: blacken the codebase...
r43346 return encoding.tolocal(self._text[self._offsets[3] + 2 :])
Joerg Sonnenberger
changelog: move branchinfo to changelogrevision...
r47082 @property
def branchinfo(self):
extra = self.extra
return encoding.tolocal(extra.get(b"branch")), b'close' in extra
Gregory Szorc
changelog: lazily parse description...
r28489
Matt Mackall
revlog: kill from-style imports...
r7634 class changelog(revlog.revlog):
Kyle Lippincott
revlog: add a mechanism to verify expected file position before appending...
r47349 def __init__(self, opener, trypending=False, concurrencychecker=None):
Gregory Szorc
changelog: load pending file directly...
r32292 """Load a changelog revlog using an opener.
If ``trypending`` is true, we attempt to load the index from a
``00changelog.i.a`` file instead of the default ``00changelog.i``.
The ``00changelog.i.a`` file contains index (and possibly inline
revision) data for a transaction that hasn't been finalized yet.
It exists in a separate file to facilitate readers (such as
hooks processes) accessing data before a transaction is finalized.
Kyle Lippincott
revlog: add a mechanism to verify expected file position before appending...
r47349
``concurrencychecker`` will be passed to the revlog init function, see
the documentation there.
Gregory Szorc
changelog: load pending file directly...
r32292 """
Augie Fackler
formatting: blacken the codebase...
r43346 revlog.revlog.__init__(
self,
opener,
revlog: introduce an explicit tracking of what the revlog is about...
r47838 target=(revlog_constants.KIND_CHANGELOG, None),
revlog: use a "radix" to address revlog...
r47921 radix=b'00changelog',
Augie Fackler
formatting: blacken the codebase...
r43346 checkambig=True,
mmaplargeindex=True,
nodemap: drop the 'exp-' prefix for internal opener option...
r45296 persistentnodemap=opener.options.get(b'persistent-nodemap', False),
Kyle Lippincott
revlog: add a mechanism to verify expected file position before appending...
r47349 concurrencychecker=concurrencychecker,
revlog: move the `trypending` logic from the `changelog` to the `revlog`...
r48014 trypending=trypending,
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
changelog: load pending file directly...
r32292
revlog: split the `version` attribute into its two components...
r47910 if self._initempty and (self._format_version == revlog.REVLOGV1):
Gregory Szorc
revlog: always enable generaldelta on version 2 revlogs...
r41238 # changelogs don't benefit from generaldelta.
revlog: split the `version` attribute into its two components...
r47910 self._format_flags &= ~revlog.FLAG_GENERALDELTA
Sune Foldager
changelog: don't use generaldelta
r14334 self._generaldelta = False
Gregory Szorc
changelog: disable delta chains...
r30155
# Delta chains for changelogs tend to be very small because entries
# tend to be small and don't delta well with each. So disable delta
# chains.
Gregory Szorc
repository: remove storedeltachains from ifilestorage...
r39268 self._storedeltachains = False
Gregory Szorc
changelog: disable delta chains...
r30155
Matt Mackall
changelog: make delayopener less intrusive
r8644 self._realopener = opener
self._delayed = False
Pierre-Yves David
changelog: rework the delayupdate mechanism...
r23201 self._delaybuf = None
Matt Mackall
changelog: _delaycount -> _divert
r9163 self._divert = False
Kyle Lippincott
branchmap: add a cache validation cache, avoid expensive re-hash on every use...
r46088 self._filteredrevs = frozenset()
self._filteredrevs_hashcache = {}
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._copiesstorage = opener.options.get(b'copies-storage')
Pierre-Yves David
clfilter: introduce `filteredrevs` attribute on changelog...
r17677
Kyle Lippincott
branchmap: add a cache validation cache, avoid expensive re-hash on every use...
r46088 @property
def filteredrevs(self):
return self._filteredrevs
@filteredrevs.setter
def filteredrevs(self, val):
# Ensure all updates go through this function
assert isinstance(val, frozenset)
self._filteredrevs = val
self._filteredrevs_hashcache = {}
revlogv2: delay the update of the changelog docket to transaction end...
r48013 def _write_docket(self, tr):
if not self._delayed:
super(changelog, self)._write_docket(tr)
Pierre-Yves David
changelog: handle writepending in the transaction...
r23203 def delayupdate(self, tr):
Matt Harbison
cleanup: fix docstring formatting...
r44226 """delay visibility of index updates to other readers"""
revlogv2: delay the update of the changelog docket to transaction end...
r48013 if self._docket is None and not self._delayed:
Pierre-Yves David
changelog: rework the delayupdate mechanism...
r23201 if len(self) == 0:
self._divert = True
revlog: rename `indexfile` to `_indexfile`...
r47919 if self._realopener.exists(self._indexfile + b'.a'):
self._realopener.unlink(self._indexfile + b'.a')
self.opener = _divertopener(self._realopener, self._indexfile)
Pierre-Yves David
changelog: rework the delayupdate mechanism...
r23201 else:
self._delaybuf = []
Augie Fackler
formatting: blacken the codebase...
r43346 self.opener = _delayopener(
revlog: rename `indexfile` to `_indexfile`...
r47919 self._realopener, self._indexfile, self._delaybuf
Augie Fackler
formatting: blacken the codebase...
r43346 )
Simon Sapin
revlog: Extract low-level random-access file read caching logic...
r48218 self._segmentfile.opener = self.opener
Simon Sapin
revlog: use file read caching for sidedata...
r48219 self._segmentfile_sidedata.opener = self.opener
Matt Mackall
changelog: make delayopener less intrusive
r8644 self._delayed = True
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 tr.addpending(b'cl-%i' % id(self), self._writepending)
tr.addfinalize(b'cl-%i' % id(self), self._finalize)
Matt Mackall
restructure changelog file appending...
r4261
Pierre-Yves David
changelog: rely on transaction for finalization...
r23205 def _finalize(self, tr):
Matt Harbison
cleanup: fix docstring formatting...
r44226 """finalize index updates"""
Matt Mackall
changelog: make delayopener less intrusive
r8644 self._delayed = False
Matt Mackall
changelog: swap opener to switch delay modes
r9165 self.opener = self._realopener
Simon Sapin
revlog: Extract low-level random-access file read caching logic...
r48218 self._segmentfile.opener = self.opener
Simon Sapin
revlog: use file read caching for sidedata...
r48219 self._segmentfile_sidedata.opener = self.opener
Matt Mackall
changelog: optimize delayed updates for clone vs pull...
r4269 # move redirected index data back into place
revlogv2: delay the update of the changelog docket to transaction end...
r48013 if self._docket is not None:
self._write_docket(tr)
elif self._divert:
Pierre-Yves David
changelog: rework the delayupdate mechanism...
r23201 assert not self._delaybuf
revlog: rename `indexfile` to `_indexfile`...
r47919 tmpname = self._indexfile + b".a"
FUJIWARA Katsunori
changelog: use "vfs.rename()" instead of "util.rename()"
r19898 nfile = self.opener.open(tmpname)
Zachary Gramana
changelog: fixes leaked file handle
r14207 nfile.close()
revlog: rename `indexfile` to `_indexfile`...
r47919 self.opener.rename(tmpname, self._indexfile, checkambig=True)
Matt Mackall
changelog: optimize delayed updates for clone vs pull...
r4269 elif self._delaybuf:
revlog: rename `indexfile` to `_indexfile`...
r47919 fp = self.opener(self._indexfile, b'a', checkambig=True)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fp.write(b"".join(self._delaybuf))
Matt Mackall
restructure changelog file appending...
r4261 fp.close()
Pierre-Yves David
changelog: rework the delayupdate mechanism...
r23201 self._delaybuf = None
self._divert = False
Matt Mackall
changelog: optimize delayed updates for clone vs pull...
r4269 # split when we're done
Boris Feld
revlog: rename 'self.checkinlinesize' into '_enforceinlinesize'...
r35992 self._enforceinlinesize(tr)
Matt Mackall
restructure changelog file appending...
r4261
Pierre-Yves David
transaction: pass the transaction to 'pending' callback...
r23280 def _writepending(self, tr):
Matt Harbison
cleanup: fix docstring formatting...
r44226 """create a file containing the unfinalized state for
pretxnchangegroup"""
revlogv2: track pending write in the docket and expose it to hooks...
r48015 if self._docket:
return self._docket.write(tr, pending=True)
Matt Mackall
Introduce HG_PREPEND to solve pretxn races...
r7787 if self._delaybuf:
# make a temporary copy of the index
revlog: rename `indexfile` to `_indexfile`...
r47919 fp1 = self._realopener(self._indexfile)
pendingfilename = self._indexfile + b".a"
Pierre-Yves David
changelog: register changelog.i.a as a temporary file...
r23292 # register as a temp file to ensure cleanup on failure
tr.registertmp(pendingfilename)
# write existing data
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fp2 = self._realopener(pendingfilename, b"w")
Matt Mackall
Introduce HG_PREPEND to solve pretxn races...
r7787 fp2.write(fp1.read())
# add pending data
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fp2.write(b"".join(self._delaybuf))
Matt Mackall
Introduce HG_PREPEND to solve pretxn races...
r7787 fp2.close()
# switch modes so finalize can simply rename
Pierre-Yves David
changelog: rework the delayupdate mechanism...
r23201 self._delaybuf = None
Matt Mackall
changelog: factor out _delayname
r9164 self._divert = True
revlog: rename `indexfile` to `_indexfile`...
r47919 self.opener = _divertopener(self._realopener, self._indexfile)
Simon Sapin
revlog: Extract low-level random-access file read caching logic...
r48218 self._segmentfile.opener = self.opener
Simon Sapin
revlog: use file read caching for sidedata...
r48219 self._segmentfile_sidedata.opener = self.opener
Matt Mackall
Introduce HG_PREPEND to solve pretxn races...
r7787
Matt Mackall
changelog: factor out _delayname
r9164 if self._divert:
Matt Mackall
Introduce HG_PREPEND to solve pretxn races...
r7787 return True
return False
revlog: introduce a mandatory `_writing` context to update revlog content...
r47988 def _enforceinlinesize(self, tr):
Matt Mackall
changelog: swap opener to switch delay modes
r9165 if not self._delayed:
revlog: introduce a mandatory `_writing` context to update revlog content...
r47988 revlog.revlog._enforceinlinesize(self, tr)
Matt Mackall
restructure changelog file appending...
r4261
Joerg Sonnenberger
changelog: rename parameters to reflect semantics...
r47376 def read(self, nodeorrev):
Gregory Szorc
changelog: add class to represent parsed changelog revisions...
r28487 """Obtain data from a parsed changelog revision.
Returns a 6-tuple of:
Benoit Boissinot
[extendedchangelog] add extra metadata in the changelog entry...
r3233
Gregory Szorc
changelog: add class to represent parsed changelog revisions...
r28487 - manifest node in binary
- author/user as a localstr
- date as a 2-tuple of (time, timezone)
- list of files
- commit message as a localstr
- dict of extra metadata
Unless you need to access all fields, consider calling
``changelogrevision`` instead, as it is faster for partial object
access.
Benoit Boissinot
document changelog format
r3077 """
revlog: no longer return sidedata from `_revisiondata`...
r48177 d = self._revisiondata(nodeorrev)
revlog: use `self.sidedata` directly to construct changelogrevision...
r48175 sidedata = self.sidedata(nodeorrev)
copy_sd = self._copiesstorage == b'changeset-sidedata'
c = changelogrevision(self, d, sidedata, copy_sd)
Augie Fackler
formatting: blacken the codebase...
r43346 return (c.manifest, c.user, c.date, c.files, c.description, c.extra)
Benoit Boissinot
[extendedchangelog] add extra metadata in the changelog entry...
r3233
Gregory Szorc
changelog: add class to represent parsed changelog revisions...
r28487 def changelogrevision(self, nodeorrev):
"""Obtain a ``changelogrevision`` for a node or revision."""
revlog: no longer return sidedata from `_revisiondata`...
r48177 text = self._revisiondata(nodeorrev)
revlog: use `self.sidedata` directly to construct changelogrevision...
r48175 sidedata = self.sidedata(nodeorrev)
sidedatacopies: only read from copies when in this mode...
r43504 return changelogrevision(
Joerg Sonnenberger
node: introduce nodeconstants class...
r47538 self, text, sidedata, self._copiesstorage == b'changeset-sidedata'
sidedatacopies: only read from copies when in this mode...
r43504 )
mpm@selenic.com
Break apart hg.py...
r1089
Joerg Sonnenberger
changelog: rename parameters to reflect semantics...
r47376 def readfiles(self, nodeorrev):
Laurent Charignon
changelog: add a new method to get files modified by a changeset...
r27439 """
short version of read that only returns the files modified by the cset
"""
Joerg Sonnenberger
changelog: rename parameters to reflect semantics...
r47376 text = self.revision(nodeorrev)
Laurent Charignon
changelog: add a new method to get files modified by a changeset...
r27439 if not text:
return []
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 last = text.index(b"\n\n")
l = text[:last].split(b'\n')
Laurent Charignon
changelog: add a new method to get files modified by a changeset...
r27439 return l[3:]
Augie Fackler
formatting: blacken the codebase...
r43346 def add(
self,
manifest,
files,
desc,
transaction,
p1,
p2,
user,
date=None,
extra=None,
):
Martin Geisler
changelog: convert user and desc from local encoding early...
r14379 # Convert to UTF-8 encoded bytestrings as the very first
# thing: calling any method on a localstr object will turn it
# into a str object and the cached UTF-8 string is thus lost.
user, desc = encoding.fromlocal(user), encoding.fromlocal(desc)
Benoit Boissinot
forbid username with '\n' at the changelog level...
r7035 user = user.strip()
Martin Geisler
changelog: refuse to add revisions with empty usernames...
r8424 # An empty username or a username with a "\n" will make the
# revision text contain two "\n\n" sequences -> corrupt
# repository since read cannot unpack the revision.
if not user:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.StorageError(_(b"empty username"))
if b"\n" in user:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.StorageError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"username %r contains a newline") % pycompat.bytestr(user)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Matt Mackall
commit: move description trimming into changelog
r8499
Pierre-Yves David
changelog: extract description cleaning logic in a dedicated function...
r17810 desc = stripdesc(desc)
Matt Mackall
commit: move description trimming into changelog
r8499
Bryan O'Sullivan
Validate user input of dates when adding a changelog entry.
r1195 if date:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 parseddate = b"%d %d" % dateutil.parsedate(date)
Bryan O'Sullivan
Validate user input of dates when adding a changelog entry.
r1195 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 parseddate = b"%d %d" % dateutil.makedate()
Wagner Bruna
branch: avoid using reserved tag names...
r10417 if extra:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 branch = extra.get(b"branch")
if branch in (b"default", b""):
del extra[b"branch"]
elif branch in (b".", b"null", b"tip"):
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.StorageError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'the name \'%s\' is reserved') % branch
Augie Fackler
formatting: blacken the codebase...
r43346 )
commitctx: directly pass a ChangingFiles object to changelog.add...
r45884 sortedfiles = sorted(files.touched)
copies: add a HASCOPIESINFO flag to highlight rev with useful data...
r46263 flags = 0
sidedatacopies: write copies information in sidedata when applicable...
r43412 sidedata = None
commitctx: extract copy information encoding into extra into commit.py...
r45809 if self._copiesstorage == b'changeset-sidedata':
changing-files: add a shorthand property to check for copy relevant info...
r46320 if files.has_copies_info:
copies: add a HASCOPIESINFO flag to highlight rev with useful data...
r46263 flags |= flagutil.REVIDX_HASCOPIESINFO
sidedata: rename `encode_copies_sidedata` to `encode_files_sidedata`...
r46143 sidedata = metadata.encode_files_sidedata(files)
Martin von Zweigbergk
copies: add config option for writing copy metadata to file and/or changset...
r42317
Benoit Boissinot
[extendedchangelog] add extra metadata in the changelog entry...
r3233 if extra:
Martin Geisler
changelog: turn {de,en}code_extra methods into functions...
r8443 extra = encodeextra(extra)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 parseddate = b"%s %s" % (parseddate, extra)
l = [hex(manifest), user, parseddate] + sortedfiles + [b"", desc]
text = b"\n".join(l)
Joerg Sonnenberger
revlog: change addrevision to return the new revision, not node...
r47258 rev = self.addrevision(
copies: return None instead of ChangingFiles when relevant...
r46264 text, transaction, len(self), p1, p2, sidedata=sidedata, flags=flags
sidedatacopies: write copies information in sidedata when applicable...
r43412 )
Joerg Sonnenberger
revlog: change addrevision to return the new revision, not node...
r47258 return self.node(rev)
Pierre-Yves David
changelog: add a `branch` method, bypassing changectx...
r18306
Brodie Rao
branchmap: cache open/closed branch head information...
r20185 def branchinfo(self, rev):
"""return the branch name and open/close state of a revision
Pierre-Yves David
changelog: add a `branch` method, bypassing changectx...
r18306
Mads Kiilerich
changelog: please check-code and remove tabs...
r18308 This function exists because creating a changectx object
just to access this is costly."""
Joerg Sonnenberger
changelog: move branchinfo to changelogrevision...
r47082 return self.changelogrevision(rev).branchinfo
Boris Feld
changelog: keep track of duplicated node in the transaction adding them...
r39923
Joerg Sonnenberger
revlog: change addgroup callbacks to take revision numbers...
r47259 def _nodeduplicatecallback(self, transaction, rev):
Boris Feld
changelog: keep track of duplicated node in the transaction adding them...
r39923 # keep track of revisions that got "re-added", eg: unbunde of know rev.
#
# We track them in a list to preserve their order from the source bundle
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 duplicates = transaction.changes.setdefault(b'revduplicates', [])
Joerg Sonnenberger
revlog: change addgroup callbacks to take revision numbers...
r47259 duplicates.append(rev)