##// END OF EJS Templates
contrib: add OS X p4merge to mergetools.hgrc
contrib: add OS X p4merge to mergetools.hgrc

File last commit:

r22300:35ab037d default
r22461:864bc2f4 default
Show More
monotone.py
361 lines | 12.7 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
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 from mercurial import util
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):
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 def __init__(self, ui, path=None, rev=None):
converter_source.__init__(self, ui, path, rev)
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
Daniel Atallah
convert/mtn: avoid unnecessary initial test of mtn repo...
r13761 self.rev = rev
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:
raise util.Abort(_('bad mtn packet - no end of commandnbr'))
commandnbr += read
commandnbr = commandnbr[:-1]
stream = self.mtnreadfp.read(1)
if stream not in 'mewptl':
Matt Mackall
i18n: fix all remaining uses of % inside _()
r16231 raise util.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 != ':':
raise util.Abort(_('bad mtn packet - no divider before size'))
read = None
lengthstr = ''
while read != ':':
read = self.mtnreadfp.read(1)
if not read:
raise util.Abort(_('bad mtn packet - no end of packet size'))
lengthstr += read
try:
length = long(lengthstr[:-1])
except TypeError:
raise util.Abort(_('bad mtn packet - bad packet size %s')
% lengthstr)
read = self.mtnreadfp.read(length)
if len(read) != length:
raise util.Abort(_("bad mtn packet - unable to read full packet "
"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':
raise util.Abort(_("mtn command '%s' returned %s") %
(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):
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 if not self.rev:
return self.mtnrun("leaves").splitlines()
Mikkel Fahnøe Jørgensen
initial version of monotone source for convert extension
r6306 else:
return [self.rev]
Mads Kiilerich
convert: introduce --full for converting all files...
r22300 def getchanges(self, rev, full):
if full:
raise util.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
Mikkel Fahnøe Jørgensen
cleanup monotone conversion and use commandline class
r6307 return (files.items(), copies)
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"]:
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:
raise util.Abort(_("unable to determine mtn automate interface "
"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':
raise util.Abort(_('mtn automate stdio header unexpected: %s')
% read)
while read != '\n':
read = self.mtnreadfp.readline()
if not read:
raise util.Abort(_("failed to reach end of mtn automate "
"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