##// END OF EJS Templates
l10n: use %d instead of %s for numbers
l10n: use %d instead of %s for numbers

File last commit:

r26587:56b2bcea default
r26778:a95c975f default
Show More
monotone.py
364 lines | 12.9 KiB | text/x-python | PythonLexer
Martin Geisler
convert: add copyright and license headers to back-ends
r8250 # monotone.py - monotone support for the convert extension
#
# Copyright 2008, 2009 Mikkel Fahnoe Jorgensen <mikkel@dvide.com> 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.
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306
Peter Arrenbrecht
cleanup: drop unused imports
r7873 import os, re
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 from mercurial import util, error
Peter Arrenbrecht
cleanup: drop unused imports
r7873 from common import NoRepo, commit, converter_source, checktool
Patrick Mezard
convert: allow missing tools not to stop source type detection
r6332 from common import commandline
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 from mercurial.i18n import _
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 class monotone_source(converter_source, commandline):
Durham Goode
convert: add support for specifying multiple revs...
r25748 def __init__(self, ui, path=None, revs=None):
converter_source.__init__(self, ui, path, revs)
if revs and len(revs) > 1:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('monotone source does not support specifying '
Durham Goode
convert: add support for specifying multiple revs...
r25748 'multiple revs'))
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 commandline.__init__(self, ui, 'mtn')
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 self.ui = ui
self.path = path
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760 self.automatestdio = False
Durham Goode
convert: add support for specifying multiple revs...
r25748 self.revs = revs
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306
Martin Geisler
convert: write "repository" instead of "repo"...
r10938 norepo = NoRepo(_("%s does not look like a monotone repository")
% path)
Matt Mackall
convert: attempt to check repo type before checking for tool
r7973 if not os.path.exists(os.path.join(path, '_MTN')):
Patrick Mezard
convert/mtn: allow monotone database files as sources
r8052 # Could be a monotone repository (SQLite db file)
try:
Matt Mackall
hgext: fixup a couple missed file().read() instances
r14179 f = file(path, 'rb')
header = f.read(16)
f.close()
Brodie Rao
cleanup: replace naked excepts with more specific ones
r16688 except IOError:
Patrick Mezard
convert/mtn: allow monotone database files as sources
r8052 header = ''
if header != 'SQLite format 3\x00':
raise norepo
Matt Mackall
convert: attempt to check repo type before checking for tool
r7973
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 # regular expressions for parsing monotone output
space = r'\s*'
David Reiss
convert.monotone: fix quotes and backslashes in change descriptions.
r6632 name = r'\s+"((?:\\"|[^"])*)"\s*'
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 value = name
revision = r'\s+\[(\w+)\]\s*'
lines = r'(?:.|\n)+'
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307
self.dir_re = re.compile(space + "dir" + name)
Matt Mackall
many, many trivial check-code fixups
r10282 self.file_re = re.compile(space + "file" + name +
"content" + revision)
self.add_file_re = re.compile(space + "add_file" + name +
"content" + revision)
self.patch_re = re.compile(space + "patch" + name +
"from" + revision + "to" + revision)
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 self.rename_re = re.compile(space + "rename" + name + "to" + name)
Patrick Mezard
convert: record deleted files in monotone source
r6376 self.delete_re = re.compile(space + "delete" + name)
Matt Mackall
many, many trivial check-code fixups
r10282 self.tag_re = re.compile(space + "tag" + name + "revision" +
revision)
self.cert_re = re.compile(lines + space + "name" + name +
"value" + value)
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306
attr = space + "file" + lines + space + "attr" + space
Matt Mackall
many, many trivial check-code fixups
r10282 self.attr_execute_re = re.compile(attr + '"mtn:execute"' +
space + '"true"')
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306
# cached data
self.manifest_rev = None
self.manifest = None
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 self.files = None
self.dirs = None
Patrick Mezard
convert: allow missing tools not to stop source type detection
r6332 checktool('mtn', abort=False)
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307
def mtnrun(self, *args, **kwargs):
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760 if self.automatestdio:
return self.mtnrunstdio(*args, **kwargs)
else:
return self.mtnrunsingle(*args, **kwargs)
def mtnrunsingle(self, *args, **kwargs):
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 kwargs['d'] = self.path
return self.run0('automate', *args, **kwargs)
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760 def mtnrunstdio(self, *args, **kwargs):
# Prepare the command in automate stdio format
command = []
for k, v in kwargs.iteritems():
command.append("%s:%s" % (len(k), k))
if v:
command.append("%s:%s" % (len(v), v))
if command:
command.insert(0, 'o')
command.append('e')
command.append('l')
for arg in args:
command += "%s:%s" % (len(arg), arg)
command.append('e')
command = ''.join(command)
self.ui.debug("mtn: sending '%s'\n" % command)
self.mtnwritefp.write(command)
self.mtnwritefp.flush()
return self.mtnstdioreadcommandoutput(command)
def mtnstdioreadpacket(self):
read = None
commandnbr = ''
while read != ':':
read = self.mtnreadfp.read(1)
if not read:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('bad mtn packet - no end of commandnbr'))
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760 commandnbr += read
commandnbr = commandnbr[:-1]
stream = self.mtnreadfp.read(1)
if stream not in 'mewptl':
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('bad mtn packet - bad stream type %s') % stream)
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760
read = self.mtnreadfp.read(1)
if read != ':':
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('bad mtn packet - no divider before size'))
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760
read = None
lengthstr = ''
while read != ':':
read = self.mtnreadfp.read(1)
if not read:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('bad mtn packet - no end of packet size'))
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760 lengthstr += read
try:
length = long(lengthstr[:-1])
except TypeError:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('bad mtn packet - bad packet size %s')
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760 % lengthstr)
read = self.mtnreadfp.read(length)
if len(read) != length:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("bad mtn packet - unable to read full packet "
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760 "read %s of %s") % (len(read), length))
return (commandnbr, stream, length, read)
def mtnstdioreadcommandoutput(self, command):
Daniel Atallah
convert/mtn: Fix conversion of large files from mtn (broken in ed97955e0c04)...
r13792 retval = []
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760 while True:
commandnbr, stream, length, output = self.mtnstdioreadpacket()
self.ui.debug('mtn: read packet %s:%s:%s\n' %
(commandnbr, stream, length))
if stream == 'l':
# End of command
if output != '0':
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("mtn command '%s' returned %s") %
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760 (command, output))
break
elif stream in 'ew':
# Error, warning output
self.ui.warn(_('%s error:\n') % self.command)
self.ui.warn(output)
elif stream == 'p':
# Progress messages
self.ui.debug('mtn: ' + output)
elif stream == 'm':
# Main stream - command output
Daniel Atallah
convert/mtn: Fix conversion of large files from mtn (broken in ed97955e0c04)...
r13792 retval.append(output)
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760
Daniel Atallah
convert/mtn: Fix conversion of large files from mtn (broken in ed97955e0c04)...
r13792 return ''.join(retval)
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 def mtnloadmanifest(self, rev):
if self.manifest_rev == rev:
return
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 self.manifest = self.mtnrun("get_manifest_of", rev).split("\n\n")
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 self.manifest_rev = rev
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 self.files = {}
self.dirs = {}
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 for e in self.manifest:
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 m = self.file_re.match(e)
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 if m:
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 attr = ""
name = m.group(1)
node = m.group(2)
if self.attr_execute_re.match(e):
attr += "x"
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 self.files[name] = (node, attr)
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 m = self.dir_re.match(e)
if m:
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 self.dirs[m.group(1)] = True
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306
def mtnisfile(self, name, rev):
# a non-file could be a directory or a deleted or renamed file
self.mtnloadmanifest(rev)
Benoit Boissinot
convert: cleanups in monotone converter
r8458 return name in self.files
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 def mtnisdir(self, name, rev):
self.mtnloadmanifest(rev)
Benoit Boissinot
convert: cleanups in monotone converter
r8458 return name in self.dirs
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 def mtngetcerts(self, rev):
certs = {"author":"<missing>", "date":"<missing>",
"changelog":"<missing>", "branch":"<missing>"}
Patrick Mezard
convert/mtn: handle change in mtn 0.45 certs output
r9823 certlist = self.mtnrun("certs", rev)
# mtn < 0.45:
# key "test@selenic.com"
# mtn >= 0.45:
# key [ff58a7ffb771907c4ff68995eada1c4da068d328]
certlist = re.split('\n\n key ["\[]', certlist)
for e in certlist:
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 m = self.cert_re.match(e)
if m:
David Reiss
convert.monotone: fix quotes and backslashes in change descriptions.
r6632 name, value = m.groups()
value = value.replace(r'\"', '"')
value = value.replace(r'\\', '\\')
certs[name] = value
Paul Aurich
convert/mtn: handle subsecond commit dates (issue1616)
r8101 # Monotone may have subsecond dates: 2005-02-05T09:39:12.364306
Paul Aurich
convert/mtn: mtn does not record timezones, mark dates as UTC (issue1624)
r8125 # and all times are stored in UTC
certs["date"] = certs["date"].split('.')[0] + " UTC"
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 return certs
# implement the converter_source interface:
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 def getheads(self):
Durham Goode
convert: add support for specifying multiple revs...
r25748 if not self.revs:
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 return self.mtnrun("leaves").splitlines()
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 else:
Durham Goode
convert: add support for specifying multiple revs...
r25748 return self.revs
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306
Mads Kiilerich
convert: introduce --full for converting all files...
r22300 def getchanges(self, rev, full):
if full:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("convert from monotone do not support --full"))
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 revision = self.mtnrun("get_revision", rev).split("\n\n")
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 files = {}
Patrick Mezard
convert/mtn: handle files moved in a moved directory (issue1619/2)
r8123 ignoremove = {}
Patrick Mezard
convert/mtn: handle new files in moved directories (issue1619)...
r8099 renameddirs = []
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 copies = {}
for e in revision:
m = self.add_file_re.match(e)
if m:
files[m.group(1)] = rev
Patrick Mezard
convert/mtn: handle files moved in a moved directory (issue1619/2)
r8123 ignoremove[m.group(1)] = rev
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 m = self.patch_re.match(e)
if m:
files[m.group(1)] = rev
# Delete/rename is handled later when the convert engine
# discovers an IOError exception from getfile,
# but only if we add the "from" file to the list of changes.
Patrick Mezard
convert: record deleted files in monotone source
r6376 m = self.delete_re.match(e)
if m:
files[m.group(1)] = rev
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 m = self.rename_re.match(e)
if m:
toname = m.group(2)
fromname = m.group(1)
if self.mtnisfile(toname, rev):
Patrick Mezard
convert/mtn: handle files moved in a moved directory (issue1619/2)
r8123 ignoremove[toname] = 1
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 copies[toname] = fromname
files[toname] = rev
files[fromname] = rev
Patrick Mezard
convert/mtn: handle new files in moved directories (issue1619)...
r8099 elif self.mtnisdir(toname, rev):
renameddirs.append((fromname, toname))
# Directory renames can be handled only once we have recorded
# all new files
for fromdir, todir in renameddirs:
renamed = {}
for tofile in self.files:
Patrick Mezard
convert/mtn: handle files moved in a moved directory (issue1619/2)
r8123 if tofile in ignoremove:
Patrick Mezard
convert/mtn: handle new files in moved directories (issue1619)...
r8099 continue
if tofile.startswith(todir + '/'):
renamed[tofile] = fromdir + tofile[len(todir):]
Patrick Mezard
convert/mtn: handle directory move into moved directory (issue1619/3)
r8124 # Avoid chained moves like:
# d1(/a) => d3/d1(/a)
# d2 => d3
ignoremove[tofile] = 1
Patrick Mezard
convert/mtn: handle new files in moved directories (issue1619)...
r8099 for tofile, fromfile in renamed.items():
Patrick Mezard
Merge with crew-stable
r8100 self.ui.debug (_("copying file in renamed directory "
"from '%s' to '%s'")
Patrick Mezard
convert/mtn: handle new files in moved directories (issue1619)...
r8099 % (fromfile, tofile), '\n')
files[tofile] = rev
copies[tofile] = fromfile
for fromfile in renamed.values():
files[fromfile] = rev
Mads Kiilerich
convert: optimize convert of files that are unmodified from p2 in merges...
r24395 return (files.items(), copies, set())
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306
def getfile(self, name, rev):
if not self.mtnisfile(name, rev):
Mads Kiilerich
convert: use None value for missing files instead of overloading IOError...
r22296 return None, None
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 try:
Patrick Mezard
convert: merge sources getmode() into getfile()
r11134 data = self.mtnrun("get_file_of", name, r=rev)
Brodie Rao
cleanup: replace naked excepts with except Exception: ...
r16689 except Exception:
Mads Kiilerich
convert: use None value for missing files instead of overloading IOError...
r22296 return None, None
Patrick Mezard
convert: merge sources getmode() into getfile()
r11134 self.mtnloadmanifest(rev)
node, attr = self.files.get(name, (None, ""))
return data, attr
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307
def getcommit(self, rev):
Daniel Atallah
convert/mtn: convert suspended branches as closed branches...
r13779 extra = {}
certs = self.mtngetcerts(rev)
if certs.get('suspend') == certs["branch"]:
Mads Kiilerich
convert: when converting from monotone, use the number 1 for close in extras...
r24178 extra['close'] = 1
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 return commit(
author=certs["author"],
date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
desc=certs["changelog"],
rev=rev,
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 parents=self.mtnrun("parents", rev).splitlines(),
Daniel Atallah
convert/mtn: convert suspended branches as closed branches...
r13779 branch=certs["branch"],
extra=extra)
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306
def gettags(self):
tags = {}
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 for e in self.mtnrun("tags").split("\n\n"):
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 m = self.tag_re.match(e)
if m:
tags[m.group(1)] = m.group(2)
return tags
def getchangedfiles(self, rev, i):
# This function is only needed to support --filemap
# ... and we don't support that
Brodie Rao
cleanup: "raise SomeException()" -> "raise SomeException"
r16687 raise NotImplementedError
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760
def before(self):
# Check if we have a new enough version to use automate stdio
version = 0.0
try:
versionstr = self.mtnrunsingle("interface_version")
version = float(versionstr)
except Exception:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("unable to determine mtn automate interface "
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760 "version"))
if version >= 12.0:
self.automatestdio = True
self.ui.debug("mtn automate version %s - using automate stdio\n" %
version)
# launch the long-running automate stdio process
self.mtnwritefp, self.mtnreadfp = self._run2('automate', 'stdio',
'-d', self.path)
# read the headers
read = self.mtnreadfp.readline()
if read != 'format-version: 2\n':
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('mtn automate stdio header unexpected: %s')
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760 % read)
while read != '\n':
read = self.mtnreadfp.readline()
if not read:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("failed to reach end of mtn automate "
Daniel Atallah
convert/mtn: add support for using monotone's "automate stdio" when available...
r13760 "stdio headers"))
else:
self.ui.debug("mtn automate version %s - not using automate stdio "
"(automate >= 12.0 - mtn >= 0.46 is needed)\n" % version)
def after(self):
if self.automatestdio:
self.mtnwritefp.close()
self.mtnwritefp = None
self.mtnreadfp.close()
self.mtnreadfp = None