##// END OF EJS Templates
manifest: avoid corruption by dropping removed files with pure (issue5801)...
manifest: avoid corruption by dropping removed files with pure (issue5801) Previously, removed files would simply be marked by overwriting the first byte with NUL and dropping their entry in `self.position`. But no effort was made to ignore them when compacting the dictionary into text form. This allowed them to slip into the manifest revision, since the code seems to be trying to minimize the string operations by copying as large a chunk as possible. As part of this, compact() walks the existing text based on entries in the `positions` list, and consumed everything up to the next position entry. This typically resulted in a ValueError complaining about unsorted manifest entries. Sometimes it seems that files do get dropped in large repos- it seems to correspond to there being a new entry that would take the same slot. A much more trivial problem is that if the only changes were removals, `_compact()` didn't even run because `__delitem__` doesn't add anything to `self.extradata`. Now there's an explicit variable to flag this, both to allow `_compact()` to run, and to avoid searching the manifest in cases where there are no removals. In practice, this behavior was mostly obscured by the check in fastdelta() which takes a different path that explicitly drops removed files if there are fewer than 1000 changes. However, timeless has a repo where after rebasing tens of commits, a totally different path[1] is taken that bypasses the change count check and hits this problem. [1] https://www.mercurial-scm.org/repo/hg/file/2338bdea4474/mercurial/manifest.py#l1511

File last commit:

r39865:28626957 default
r42569:0546ead3 stable
Show More
gnuarch.py
357 lines | 12.8 KiB | text/x-python | PythonLexer
Martin Geisler
convert: add copyright and license headers to back-ends
r8250 # gnuarch.py - GNU Arch support for the convert extension
#
# Copyright 2008, 2009 Aleix Conchillo Flaque <aleix@member.fsf.org>
# and others
#
# 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.
timeless
convert: gnuarch use absolute_import
r28366 from __future__ import absolute_import
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
Pulkit Goyal
py3: use email.parser module to parse email messages...
r35651 import email.parser as emailparser
timeless
convert: gnuarch use absolute_import
r28366 import os
import shutil
import stat
import tempfile
Yuya Nishihara
py3: move up symbol imports to enforce import-checker rules...
r29205
from mercurial.i18n import _
timeless
convert: gnuarch use absolute_import
r28366 from mercurial import (
encoding,
error,
Matt Harbison
py3: convert arguments, cwd and env to native strings when spawning subprocess...
r39851 pycompat,
Matt Harbison
convert: fix a file descriptor leak...
r39865 util,
timeless
convert: gnuarch use absolute_import
r28366 )
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 from mercurial.utils import (
dateutil,
procutil,
)
timeless
convert: gnuarch use absolute_import
r28366 from . import common
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
timeless
convert: gnuarch use absolute_import
r28366 class gnuarch_source(common.converter_source, common.commandline):
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
Benoit Boissinot
use new style classes
r8778 class gnuarch_rev(object):
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 def __init__(self, rev):
self.rev = rev
self.summary = ''
self.date = None
self.author = ''
Edouard Gomez
convert/gnuarch: parse continuation-of revisions in gnuarch source...
r7583 self.continuationof = None
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 self.add_files = []
self.mod_files = []
self.del_files = []
self.ren_files = {}
self.ren_dirs = {}
Matt Harbison
convert: save an indicator of the repo type for sources and sinks...
r35168 def __init__(self, ui, repotype, path, revs=None):
super(gnuarch_source, self).__init__(ui, repotype, path, revs=revs)
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
if not os.path.exists(os.path.join(path, '{arch}')):
timeless
convert: gnuarch use absolute_import
r28366 raise common.NoRepo(_("%s does not look like a GNU Arch repository")
Martin Geisler
convert: write "repository" instead of "repo"...
r10938 % path)
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
# Could use checktool, but we want to check for baz or tla.
self.execmd = None
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 if procutil.findexe('baz'):
Patrick Mezard
convert: detect baz before tla...
r6083 self.execmd = 'baz'
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 else:
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 if procutil.findexe('tla'):
Patrick Mezard
convert: detect baz before tla...
r6083 self.execmd = 'tla'
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 else:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('cannot find a GNU Arch tool'))
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
timeless
convert: gnuarch use absolute_import
r28366 common.commandline.__init__(self, ui, self.execmd)
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
Matt Mackall
backout dbdb777502dc (issue3077) (issue3071)...
r15381 self.path = os.path.realpath(path)
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 self.tmppath = None
self.treeversion = None
self.lastrev = None
self.changes = {}
self.parents = {}
self.tags = {}
Pulkit Goyal
py3: use email.parser module to parse email messages...
r35651 self.catlogparser = emailparser.Parser()
Brodie Rao
convert: use encoding.encoding instead of locale.getpreferredencoding()...
r11987 self.encoding = encoding.encoding
Edouard Gomez
convert/gnuarch: retrieve known archive names list...
r7584 self.archives = []
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
def before(self):
Edouard Gomez
convert/gnuarch: retrieve known archive names list...
r7584 # Get registered archives
self.archives = [i.rstrip('\n')
for i in self.runlines0('archives', '-n')]
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 if self.execmd == 'tla':
output = self.run0('tree-version', self.path)
else:
output = self.run0('tree-version', '-d', self.path)
self.treeversion = output.strip()
# Get name of temporary directory
version = self.treeversion.split('/')
self.tmppath = os.path.join(tempfile.gettempdir(),
'hg-%s' % version[1])
# Generate parents dictionary
Edouard Gomez
convert/gnuarch: follow continuation-of revisions...
r7585 self.parents[None] = []
treeversion = self.treeversion
child = None
while treeversion:
self.ui.status(_('analyzing tree version %s...\n') % treeversion)
archive = treeversion.split('/')[0]
if archive not in self.archives:
Martin Geisler
convert/gnuarch: wrap long line, format kwargs without spaces
r8662 self.ui.status(_('tree analysis stopped because it points to '
'an unregistered archive %s...\n') % archive)
Edouard Gomez
convert/gnuarch: follow continuation-of revisions...
r7585 break
# Get the complete list of revisions for that tree version
output, status = self.runlines('revisions', '-r', '-f', treeversion)
Mads Kiilerich
fix trivial spelling errors
r17424 self.checkexit(status, 'failed retrieving revisions for %s'
Matt Mackall
many, many trivial check-code fixups
r10282 % treeversion)
Edouard Gomez
convert/gnuarch: follow continuation-of revisions...
r7585
# No new iteration unless a revision has a continuation-of header
treeversion = None
for l in output:
rev = l.strip()
self.changes[rev] = self.gnuarch_rev(rev)
self.parents[rev] = []
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
Edouard Gomez
convert/gnuarch: follow continuation-of revisions...
r7585 # Read author, date and summary
catlog, status = self.run('cat-log', '-d', self.path, rev)
if status:
catlog = self.run0('cat-archive-log', rev)
self._parsecatlog(catlog, rev)
# Populate the parents map
self.parents[child].append(rev)
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
Edouard Gomez
convert/gnuarch: follow continuation-of revisions...
r7585 # Keep track of the current revision as the child of the next
# revision scanned
child = rev
# Check if we have to follow the usual incremental history
# or if we have to 'jump' to a different treeversion given
# by the continuation-of header.
if self.changes[rev].continuationof:
Matt Mackall
many, many trivial check-code fixups
r10282 treeversion = '--'.join(
self.changes[rev].continuationof.split('--')[:-1])
Edouard Gomez
convert/gnuarch: follow continuation-of revisions...
r7585 break
# If we reached a base-0 revision w/o any continuation-of
# header, it means the tree history ends here.
if rev[-6:] == 'base-0':
break
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
def after(self):
Martin Geisler
do not attempt to translate ui.debug output
r9467 self.ui.debug('cleaning up %s\n' % self.tmppath)
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 shutil.rmtree(self.tmppath, ignore_errors=True)
def getheads(self):
return self.parents[None]
def getfile(self, name, rev):
if rev != self.lastrev:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('internal calling inconsistency'))
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
Patrick Mezard
Use lexists() instead of exists() where appropriate
r12344 if not os.path.lexists(os.path.join(self.tmppath, name)):
Mads Kiilerich
convert: use None value for missing files instead of overloading IOError...
r22296 return None, None
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
Patrick Mezard
convert: merge sources getmode() into getfile()
r11134 return self._getfile(name, rev)
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
Mads Kiilerich
convert: introduce --full for converting all files...
r22300 def getchanges(self, rev, full):
if full:
timeless@mozdev.org
grammar: use does instead of do where appropriate
r26779 raise error.Abort(_("convert from arch does not support --full"))
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 self._update(rev)
changes = []
copies = {}
for f in self.changes[rev].add_files:
changes.append((f, rev))
for f in self.changes[rev].mod_files:
changes.append((f, rev))
for f in self.changes[rev].del_files:
changes.append((f, rev))
for src in self.changes[rev].ren_files:
to = self.changes[rev].ren_files[src]
changes.append((src, rev))
changes.append((to, rev))
Patrick Mezard
convert/gnuarch: fix switched copy source and destination...
r7567 copies[to] = src
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
for src in self.changes[rev].ren_dirs:
to = self.changes[rev].ren_dirs[src]
Benoit Boissinot
fix coding style (reported by pylint)
r10394 chgs, cps = self._rendirchanges(src, to)
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 changes += [(f, rev) for f in chgs]
Patrick Mezard
convert/gnuarch: fix switched copy source and destination...
r7567 copies.update(cps)
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
self.lastrev = rev
Mads Kiilerich
convert: optimize convert of files that are unmodified from p2 in merges...
r24395 return sorted(set(changes)), copies, set()
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
def getcommit(self, rev):
changes = self.changes[rev]
timeless
convert: gnuarch use absolute_import
r28366 return common.commit(author=changes.author, date=changes.date,
desc=changes.summary, parents=self.parents[rev],
rev=rev)
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
def gettags(self):
return self.tags
def _execute(self, cmd, *args, **kwargs):
cmdline = [self.execmd, cmd]
cmdline += args
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 cmdline = [procutil.shellquote(arg) for arg in cmdline]
Ross Lagerwall
util: replace util.nulldev with os.devnull...
r17391 cmdline += ['>', os.devnull, '2>', os.devnull]
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 cmdline = procutil.quotecommand(' '.join(cmdline))
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 self.ui.debug(cmdline, '\n')
Matt Harbison
py3: convert arguments, cwd and env to native strings when spawning subprocess...
r39851 return os.system(pycompat.rapply(procutil.tonativestr, cmdline))
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
def _update(self, rev):
Martin Geisler
do not attempt to translate ui.debug output
r9467 self.ui.debug('applying revision %s...\n' % rev)
Edouard Gomez
convert/gnuarch: follow continuation-of revisions...
r7585 changeset, status = self.runlines('replay', '-d', self.tmppath,
rev)
if status:
# Something went wrong while merging (baz or tla
# issue?), get latest revision and try from there
shutil.rmtree(self.tmppath, ignore_errors=True)
Aleix Conchillo Flaque
convert: improve gnu arch source performance and other fixes...
r6049 self._obtainrevision(rev)
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 else:
Edouard Gomez
convert/gnuarch: follow continuation-of revisions...
r7585 old_rev = self.parents[rev][0]
Martin Geisler
do not attempt to translate ui.debug output
r9467 self.ui.debug('computing changeset between %s and %s...\n'
Edouard Gomez
convert/gnuarch: follow continuation-of revisions...
r7585 % (old_rev, rev))
self._parsechangeset(changeset, rev)
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035
def _getfile(self, name, rev):
mode = os.lstat(os.path.join(self.tmppath, name)).st_mode
if stat.S_ISLNK(mode):
Matt Harbison
py3: convert os.readlink() path to native strings on Windows...
r39940 data = util.readlink(os.path.join(self.tmppath, name))
Jordi Gutiérrez Hermoso
style: kill ersatz if-else ternary operators...
r24306 if mode:
mode = 'l'
else:
mode = ''
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 else:
Matt Harbison
convert: fix a file descriptor leak...
r39865 data = util.readfile(os.path.join(self.tmppath, name))
Gregory Szorc
global: mass rewrite to use modern octal syntax...
r25658 mode = (mode & 0o111) and 'x' or ''
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 return data, mode
def _exclude(self, name):
Matt Mackall
many, many trivial check-code fixups
r10282 exclude = ['{arch}', '.arch-ids', '.arch-inventory']
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 for exc in exclude:
if name.find(exc) != -1:
return True
return False
def _readcontents(self, path):
files = []
contents = os.listdir(path)
while len(contents) > 0:
c = contents.pop()
p = os.path.join(path, c)
Aleix Conchillo Flaque
convert: do not skip some lines in gnu arch summaries
r6044 # os.walk could be used, but here we avoid internal GNU
# Arch files and directories, thus saving a lot time.
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 if not self._exclude(p):
if os.path.isdir(p):
contents += [os.path.join(c, f) for f in os.listdir(p)]
else:
files.append(c)
return files
def _rendirchanges(self, src, dest):
changes = []
copies = {}
files = self._readcontents(os.path.join(self.tmppath, dest))
for f in files:
s = os.path.join(src, f)
d = os.path.join(dest, f)
changes.append(s)
changes.append(d)
Patrick Mezard
convert/gnuarch: fix switched copy source and destination...
r7567 copies[d] = s
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 return changes, copies
Aleix Conchillo Flaque
convert: improve gnu arch source performance and other fixes...
r6049 def _obtainrevision(self, rev):
Martin Geisler
do not attempt to translate ui.debug output
r9467 self.ui.debug('obtaining revision %s...\n' % rev)
Edouard Gomez
convert/gnuarch: use fully qualified revisions...
r7582 output = self._execute('get', rev, self.tmppath)
Aleix Conchillo Flaque
convert: improve gnu arch source performance and other fixes...
r6049 self.checkexit(output)
Martin Geisler
do not attempt to translate ui.debug output
r9467 self.ui.debug('analyzing revision %s...\n' % rev)
Aleix Conchillo Flaque
convert: improve gnu arch source performance and other fixes...
r6049 files = self._readcontents(self.tmppath)
self.changes[rev].add_files += files
Aleix Conchillo Flaque
convert: added GNU Arch (tla) tests and related fixes
r6079 def _stripbasepath(self, path):
if path.startswith('./'):
return path[2:]
return path
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 def _parsecatlog(self, data, rev):
Edouard Gomez
convert/gnuarch: fix cat-log parsing...
r7578 try:
catlog = self.catlogparser.parsestr(data)
Edouard Gomez
convert/gnuarch: recode cat-log parts to utf-8 to be hg.description friendly
r7592
# Commit date
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 self.changes[rev].date = dateutil.datestr(
dateutil.strdate(catlog['Standard-date'],
Edouard Gomez
convert/gnuarch: fix cat-log parsing...
r7578 '%Y-%m-%d %H:%M:%S'))
Edouard Gomez
convert/gnuarch: recode cat-log parts to utf-8 to be hg.description friendly
r7592
# Commit author
self.changes[rev].author = self.recode(catlog['Creator'])
# Commit description
self.changes[rev].summary = '\n\n'.join((catlog['Summary'],
catlog.get_payload()))
self.changes[rev].summary = self.recode(self.changes[rev].summary)
# Commit revision origin when dealing with a branch or tag
Nicolas Dumazet
use 'x in dict' instead of 'dict.has_key(x)'...
r9391 if 'Continuation-of' in catlog:
Matt Mackall
many, many trivial check-code fixups
r10282 self.changes[rev].continuationof = self.recode(
catlog['Continuation-of'])
Peter Arrenbrecht
cleanup: drop unused assignments
r7875 except Exception:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('could not parse cat-log of %s') % rev)
Aleix Conchillo Flaque
convert: add full description for gnu arch revisions
r6037
Aleix Conchillo Flaque
convert: improve gnu arch source performance and other fixes...
r6049 def _parsechangeset(self, data, rev):
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 for l in data:
l = l.strip()
Aleix Conchillo Flaque
convert: support binary files, link to files (viceversa) in gnu arch
r6055 # Added file (ignore added directory)
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 if l.startswith('A') and not l.startswith('A/'):
Aleix Conchillo Flaque
convert: added GNU Arch (tla) tests and related fixes
r6079 file = self._stripbasepath(l[1:].strip())
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 if not self._exclude(file):
self.changes[rev].add_files.append(file)
Aleix Conchillo Flaque
convert: support binary files, link to files (viceversa) in gnu arch
r6055 # Deleted file (ignore deleted directory)
elif l.startswith('D') and not l.startswith('D/'):
Aleix Conchillo Flaque
convert: added GNU Arch (tla) tests and related fixes
r6079 file = self._stripbasepath(l[1:].strip())
Aleix Conchillo Flaque
convert: support binary files, link to files (viceversa) in gnu arch
r6055 if not self._exclude(file):
self.changes[rev].del_files.append(file)
# Modified binary file
elif l.startswith('Mb'):
Aleix Conchillo Flaque
convert: added GNU Arch (tla) tests and related fixes
r6079 file = self._stripbasepath(l[2:].strip())
Aleix Conchillo Flaque
convert: support binary files, link to files (viceversa) in gnu arch
r6055 if not self._exclude(file):
self.changes[rev].mod_files.append(file)
# Modified link
elif l.startswith('M->'):
Aleix Conchillo Flaque
convert: added GNU Arch (tla) tests and related fixes
r6079 file = self._stripbasepath(l[3:].strip())
Aleix Conchillo Flaque
convert: support binary files, link to files (viceversa) in gnu arch
r6055 if not self._exclude(file):
self.changes[rev].mod_files.append(file)
# Modified file
elif l.startswith('M'):
Aleix Conchillo Flaque
convert: added GNU Arch (tla) tests and related fixes
r6079 file = self._stripbasepath(l[1:].strip())
Aleix Conchillo Flaque
convert: support binary files, link to files (viceversa) in gnu arch
r6055 if not self._exclude(file):
self.changes[rev].mod_files.append(file)
# Renamed file (or link)
elif l.startswith('=>'):
files = l[2:].strip().split(' ')
if len(files) == 1:
files = l[2:].strip().split('\t')
Aleix Conchillo Flaque
convert: added GNU Arch (tla) tests and related fixes
r6079 src = self._stripbasepath(files[0])
dst = self._stripbasepath(files[1])
if not self._exclude(src) and not self._exclude(dst):
self.changes[rev].ren_files[src] = dst
Aleix Conchillo Flaque
convert: support binary files, link to files (viceversa) in gnu arch
r6055 # Conversion from file to link or from link to file (modified)
elif l.startswith('ch'):
Aleix Conchillo Flaque
convert: added GNU Arch (tla) tests and related fixes
r6079 file = self._stripbasepath(l[2:].strip())
Aleix Conchillo Flaque
convert: support binary files, link to files (viceversa) in gnu arch
r6055 if not self._exclude(file):
self.changes[rev].mod_files.append(file)
# Renamed directory
Aleix Conchillo Flaque
convert: added GNU Arch source converter
r6035 elif l.startswith('/>'):
dirs = l[2:].strip().split(' ')
if len(dirs) == 1:
dirs = l[2:].strip().split('\t')
Aleix Conchillo Flaque
convert: added GNU Arch (tla) tests and related fixes
r6079 src = self._stripbasepath(dirs[0])
dst = self._stripbasepath(dirs[1])
if not self._exclude(src) and not self._exclude(dst):
self.changes[rev].ren_dirs[src] = dst