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