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