##// END OF EJS Templates
initial version of monotone source for convert extension
Mikkel Fahnøe Jørgensen -
r6306:2f9de4aa default
parent child Browse files
Show More
@@ -0,0 +1,214 b''
1 # monotone support for the convert extension
2
3 import os
4 import re
5 import time
6 from mercurial import util
7
8 from common import NoRepo, commit, converter_source, checktool
9
10 class monotone_source(converter_source):
11 def __init__(self, ui, path=None, rev=None):
12 converter_source.__init__(self, ui, path, rev)
13
14 self.ui = ui
15 self.path = path
16
17
18 # regular expressions for parsing monotone output
19
20 space = r'\s*'
21 name = r'\s+"((?:[^"]|\\")*)"\s*'
22 value = name
23 revision = r'\s+\[(\w+)\]\s*'
24 lines = r'(?:.|\n)+'
25
26 self.dir_re = re.compile(space + "dir" + name)
27 self.file_re = re.compile(space + "file" + name + "content" + revision)
28 self.add_file_re = re.compile(space + "add_file" + name + "content" + revision)
29 self.patch_re = re.compile(space + "patch" + name + "from" + revision + "to" + revision)
30 self.rename_re = re.compile(space + "rename" + name + "to" + name)
31 self.tag_re = re.compile(space + "tag" + name + "revision" + revision)
32 self.cert_re = re.compile(lines + space + "name" + name + "value" + value)
33
34 attr = space + "file" + lines + space + "attr" + space
35 self.attr_execute_re = re.compile(attr + '"mtn:execute"' + space + '"true"')
36
37 # cached data
38
39 self.manifest_rev = None
40 self.manifest = None
41 self.files = None
42 self.dirs = None
43
44 norepo = NoRepo("%s does not look like a monotone repo" % path)
45 if not os.path.exists(path):
46 raise norepo
47
48 checktool('mtn')
49
50 # test if there are are any revisions
51 self.rev = None
52 try :
53 self.getheads()
54 except :
55 raise norepo
56
57 self.rev = rev
58
59
60 def mtncmd(self, arg):
61 cmdline = "mtn -d %s automate %s" % (util.shellquote(self.path), arg)
62 self.ui.debug(cmdline, '\n')
63 p = util.popen(cmdline)
64 result = p.read()
65 if p.close():
66 raise IOError()
67 return result
68
69 def mtnloadmanifest(self, rev):
70 if self.manifest_rev == rev:
71 return
72 self.manifest_rev = rev
73 self.manifest = self.mtncmd("get_manifest_of %s" % rev).split("\n\n")
74
75 manifest = self.manifest
76 files = {}
77 dirs = {}
78
79 for e in manifest:
80 m = self.file_re.match(e)
81 if m:
82 attr = ""
83 name = m.group(1)
84 node = m.group(2)
85 if self.attr_execute_re.match(e):
86 attr += "x"
87 files[name] = (node, attr)
88 m = self.dir_re.match(e)
89 if m:
90 dirs[m.group(1)] = True
91
92 self.files = files
93 self.dirs = dirs
94
95 def mtnisfile(self, name, rev):
96 # a non-file could be a directory or a deleted or renamed file
97 self.mtnloadmanifest(rev)
98 try :
99 self.files[name]
100 return True
101 except KeyError:
102 return False
103
104 def mtnisdir(self, name, rev):
105 self.mtnloadmanifest(rev)
106 try :
107 self.dirs[name]
108 return True
109 except KeyError:
110 return False
111
112 def mtngetcerts(self, rev):
113 certs = {"author":"<missing>", "date":"<missing>",
114 "changelog":"<missing>", "branch":"<missing>"}
115 cert_list = self.mtncmd("certs %s" % rev).split("\n\n")
116 for e in cert_list:
117 m = self.cert_re.match(e)
118 if m:
119 certs[m.group(1)] = m.group(2)
120 return certs
121
122 def mtngetparents(self, rev):
123 parents = self.mtncmd("parents %s" % rev).strip("\n").split("\n")
124 p = []
125 for x in parents:
126 if len(x) >= 40: # blank revs have been seen otherwise
127 p.append(x)
128 return p
129
130 def mtnrenamefiles(self, files, fromdir, todir):
131 renamed = {}
132 for tofile in files:
133 suffix = tofile.lstrip(todir)
134 if todir + suffix == tofile:
135 renamed[tofile] = (fromdir + suffix).lstrip("/")
136 return renamed
137
138
139 # implement the converter_source interface:
140
141 def getheads(self):
142 if not self.rev or self.rev == "":
143 return self.mtncmd("leaves").splitlines()
144 else:
145 return [self.rev]
146
147 def getchanges(self, rev):
148 revision = self.mtncmd("get_revision %s" % rev).split("\n\n")
149 files = {}
150 copies = {}
151 for e in revision:
152 m = self.add_file_re.match(e)
153 if m:
154 files[m.group(1)] = rev
155 m = self.patch_re.match(e)
156 if m:
157 files[m.group(1)] = rev
158
159 # Delete/rename is handled later when the convert engine
160 # discovers an IOError exception from getfile,
161 # but only if we add the "from" file to the list of changes.
162 m = self.rename_re.match(e)
163 if m:
164 toname = m.group(2)
165 fromname = m.group(1)
166 if self.mtnisfile(toname, rev):
167 copies[toname] = fromname
168 files[toname] = rev
169 files[fromname] = rev
170 if self.mtnisdir(toname, rev):
171 renamed = self.mtnrenamefiles(self.files, fromname, toname)
172 for tofile, fromfile in renamed.items():
173 self.ui.debug (("copying file in renamed dir from '%s' to '%s'" % (fromfile, tofile)), "\n")
174 files[tofile] = rev
175 for fromfile in renamed.values():
176 files[fromfile] = rev
177
178 return (files.items(), copies)
179
180 def getmode(self, name, rev):
181 self.mtnloadmanifest(rev)
182 try :
183 node, attr = self.files[name]
184 return attr
185 except KeyError:
186 return ""
187
188 def getfile(self, name, rev):
189 if not self.mtnisfile(name, rev):
190 raise IOError() # file was deleted or renamed
191 return self.mtncmd("get_file_of %s -r %s" % (util.shellquote(name), rev))
192
193 def getcommit(self, rev):
194 certs = self.mtngetcerts(rev)
195 return commit(
196 author=certs["author"],
197 date=util.datestr(util.strdate(certs["date"], "%Y-%m-%dT%H:%M:%S")),
198 desc=certs["changelog"],
199 rev=rev,
200 parents=self.mtngetparents(rev),
201 branch=certs["branch"])
202
203 def gettags(self):
204 tags = {}
205 for e in self.mtncmd("tags").split("\n\n"):
206 m = self.tag_re.match(e)
207 if m:
208 tags[m.group(1)] = m.group(2)
209 return tags
210
211 def getchangedfiles(self, rev, i):
212 # This function is only needed to support --filemap
213 # ... and we don't support that
214 raise NotImplementedError()
@@ -19,6 +19,7 b' def convert(ui, src, dest=None, revmapfi'
19 - Darcs
19 - Darcs
20 - git
20 - git
21 - Subversion
21 - Subversion
22 - Monotone
22 - GNU Arch
23 - GNU Arch
23
24
24 Accepted destination formats:
25 Accepted destination formats:
@@ -11,6 +11,7 b' from darcs import darcs_source'
11 from git import convert_git
11 from git import convert_git
12 from hg import mercurial_source, mercurial_sink
12 from hg import mercurial_source, mercurial_sink
13 from subversion import debugsvnlog, svn_source, svn_sink
13 from subversion import debugsvnlog, svn_source, svn_sink
14 from monotone import monotone_source
14 from gnuarch import gnuarch_source
15 from gnuarch import gnuarch_source
15 import filemap
16 import filemap
16
17
@@ -32,6 +33,7 b' source_converters = ['
32 ('svn', svn_source),
33 ('svn', svn_source),
33 ('hg', mercurial_source),
34 ('hg', mercurial_source),
34 ('darcs', darcs_source),
35 ('darcs', darcs_source),
36 ('mtn', monotone_source),
35 ('gnuarch', gnuarch_source),
37 ('gnuarch', gnuarch_source),
36 ]
38 ]
37
39
General Comments 0
You need to be logged in to leave comments. Login now