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