##// END OF EJS Templates
convert/gnuarch: wrap long line, format kwargs without spaces
Martin Geisler -
r8662:eaee3491 default
parent child Browse files
Show More
@@ -1,341 +1,342 b''
1 # gnuarch.py - GNU Arch support for the convert extension
1 # gnuarch.py - GNU Arch support for the convert extension
2 #
2 #
3 # Copyright 2008, 2009 Aleix Conchillo Flaque <aleix@member.fsf.org>
3 # Copyright 2008, 2009 Aleix Conchillo Flaque <aleix@member.fsf.org>
4 # and others
4 # and 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 from common import NoRepo, commandline, commit, converter_source
9 from common import NoRepo, commandline, commit, converter_source
10 from mercurial.i18n import _
10 from mercurial.i18n import _
11 from mercurial import util
11 from mercurial import util
12 import os, shutil, tempfile, stat, locale
12 import os, shutil, tempfile, stat, locale
13 from email.Parser import Parser
13 from email.Parser import Parser
14
14
15 class gnuarch_source(converter_source, commandline):
15 class gnuarch_source(converter_source, commandline):
16
16
17 class gnuarch_rev:
17 class gnuarch_rev:
18 def __init__(self, rev):
18 def __init__(self, rev):
19 self.rev = rev
19 self.rev = rev
20 self.summary = ''
20 self.summary = ''
21 self.date = None
21 self.date = None
22 self.author = ''
22 self.author = ''
23 self.continuationof = None
23 self.continuationof = None
24 self.add_files = []
24 self.add_files = []
25 self.mod_files = []
25 self.mod_files = []
26 self.del_files = []
26 self.del_files = []
27 self.ren_files = {}
27 self.ren_files = {}
28 self.ren_dirs = {}
28 self.ren_dirs = {}
29
29
30 def __init__(self, ui, path, rev=None):
30 def __init__(self, ui, path, rev=None):
31 super(gnuarch_source, self).__init__(ui, path, rev=rev)
31 super(gnuarch_source, self).__init__(ui, path, rev=rev)
32
32
33 if not os.path.exists(os.path.join(path, '{arch}')):
33 if not os.path.exists(os.path.join(path, '{arch}')):
34 raise NoRepo(_("%s does not look like a GNU Arch repo") % path)
34 raise NoRepo(_("%s does not look like a GNU Arch repo") % path)
35
35
36 # Could use checktool, but we want to check for baz or tla.
36 # Could use checktool, but we want to check for baz or tla.
37 self.execmd = None
37 self.execmd = None
38 if util.find_exe('baz'):
38 if util.find_exe('baz'):
39 self.execmd = 'baz'
39 self.execmd = 'baz'
40 else:
40 else:
41 if util.find_exe('tla'):
41 if util.find_exe('tla'):
42 self.execmd = 'tla'
42 self.execmd = 'tla'
43 else:
43 else:
44 raise util.Abort(_('cannot find a GNU Arch tool'))
44 raise util.Abort(_('cannot find a GNU Arch tool'))
45
45
46 commandline.__init__(self, ui, self.execmd)
46 commandline.__init__(self, ui, self.execmd)
47
47
48 self.path = os.path.realpath(path)
48 self.path = os.path.realpath(path)
49 self.tmppath = None
49 self.tmppath = None
50
50
51 self.treeversion = None
51 self.treeversion = None
52 self.lastrev = None
52 self.lastrev = None
53 self.changes = {}
53 self.changes = {}
54 self.parents = {}
54 self.parents = {}
55 self.tags = {}
55 self.tags = {}
56 self.modecache = {}
56 self.modecache = {}
57 self.catlogparser = Parser()
57 self.catlogparser = Parser()
58 self.locale = locale.getpreferredencoding()
58 self.locale = locale.getpreferredencoding()
59 self.archives = []
59 self.archives = []
60
60
61 def before(self):
61 def before(self):
62 # Get registered archives
62 # Get registered archives
63 self.archives = [i.rstrip('\n')
63 self.archives = [i.rstrip('\n')
64 for i in self.runlines0('archives', '-n')]
64 for i in self.runlines0('archives', '-n')]
65
65
66 if self.execmd == 'tla':
66 if self.execmd == 'tla':
67 output = self.run0('tree-version', self.path)
67 output = self.run0('tree-version', self.path)
68 else:
68 else:
69 output = self.run0('tree-version', '-d', self.path)
69 output = self.run0('tree-version', '-d', self.path)
70 self.treeversion = output.strip()
70 self.treeversion = output.strip()
71
71
72 # Get name of temporary directory
72 # Get name of temporary directory
73 version = self.treeversion.split('/')
73 version = self.treeversion.split('/')
74 self.tmppath = os.path.join(tempfile.gettempdir(),
74 self.tmppath = os.path.join(tempfile.gettempdir(),
75 'hg-%s' % version[1])
75 'hg-%s' % version[1])
76
76
77 # Generate parents dictionary
77 # Generate parents dictionary
78 self.parents[None] = []
78 self.parents[None] = []
79 treeversion = self.treeversion
79 treeversion = self.treeversion
80 child = None
80 child = None
81 while treeversion:
81 while treeversion:
82 self.ui.status(_('analyzing tree version %s...\n') % treeversion)
82 self.ui.status(_('analyzing tree version %s...\n') % treeversion)
83
83
84 archive = treeversion.split('/')[0]
84 archive = treeversion.split('/')[0]
85 if archive not in self.archives:
85 if archive not in self.archives:
86 self.ui.status(_('tree analysis stopped because it points to an unregistered archive %s...\n') % archive)
86 self.ui.status(_('tree analysis stopped because it points to '
87 'an unregistered archive %s...\n') % archive)
87 break
88 break
88
89
89 # Get the complete list of revisions for that tree version
90 # Get the complete list of revisions for that tree version
90 output, status = self.runlines('revisions', '-r', '-f', treeversion)
91 output, status = self.runlines('revisions', '-r', '-f', treeversion)
91 self.checkexit(status, 'failed retrieveing revisions for %s' % treeversion)
92 self.checkexit(status, 'failed retrieveing revisions for %s' % treeversion)
92
93
93 # No new iteration unless a revision has a continuation-of header
94 # No new iteration unless a revision has a continuation-of header
94 treeversion = None
95 treeversion = None
95
96
96 for l in output:
97 for l in output:
97 rev = l.strip()
98 rev = l.strip()
98 self.changes[rev] = self.gnuarch_rev(rev)
99 self.changes[rev] = self.gnuarch_rev(rev)
99 self.parents[rev] = []
100 self.parents[rev] = []
100
101
101 # Read author, date and summary
102 # Read author, date and summary
102 catlog, status = self.run('cat-log', '-d', self.path, rev)
103 catlog, status = self.run('cat-log', '-d', self.path, rev)
103 if status:
104 if status:
104 catlog = self.run0('cat-archive-log', rev)
105 catlog = self.run0('cat-archive-log', rev)
105 self._parsecatlog(catlog, rev)
106 self._parsecatlog(catlog, rev)
106
107
107 # Populate the parents map
108 # Populate the parents map
108 self.parents[child].append(rev)
109 self.parents[child].append(rev)
109
110
110 # Keep track of the current revision as the child of the next
111 # Keep track of the current revision as the child of the next
111 # revision scanned
112 # revision scanned
112 child = rev
113 child = rev
113
114
114 # Check if we have to follow the usual incremental history
115 # Check if we have to follow the usual incremental history
115 # or if we have to 'jump' to a different treeversion given
116 # or if we have to 'jump' to a different treeversion given
116 # by the continuation-of header.
117 # by the continuation-of header.
117 if self.changes[rev].continuationof:
118 if self.changes[rev].continuationof:
118 treeversion = '--'.join(self.changes[rev].continuationof.split('--')[:-1])
119 treeversion = '--'.join(self.changes[rev].continuationof.split('--')[:-1])
119 break
120 break
120
121
121 # If we reached a base-0 revision w/o any continuation-of
122 # If we reached a base-0 revision w/o any continuation-of
122 # header, it means the tree history ends here.
123 # header, it means the tree history ends here.
123 if rev[-6:] == 'base-0':
124 if rev[-6:] == 'base-0':
124 break
125 break
125
126
126 def after(self):
127 def after(self):
127 self.ui.debug(_('cleaning up %s\n') % self.tmppath)
128 self.ui.debug(_('cleaning up %s\n') % self.tmppath)
128 shutil.rmtree(self.tmppath, ignore_errors=True)
129 shutil.rmtree(self.tmppath, ignore_errors=True)
129
130
130 def getheads(self):
131 def getheads(self):
131 return self.parents[None]
132 return self.parents[None]
132
133
133 def getfile(self, name, rev):
134 def getfile(self, name, rev):
134 if rev != self.lastrev:
135 if rev != self.lastrev:
135 raise util.Abort(_('internal calling inconsistency'))
136 raise util.Abort(_('internal calling inconsistency'))
136
137
137 # Raise IOError if necessary (i.e. deleted files).
138 # Raise IOError if necessary (i.e. deleted files).
138 if not os.path.exists(os.path.join(self.tmppath, name)):
139 if not os.path.exists(os.path.join(self.tmppath, name)):
139 raise IOError
140 raise IOError
140
141
141 data, mode = self._getfile(name, rev)
142 data, mode = self._getfile(name, rev)
142 self.modecache[(name, rev)] = mode
143 self.modecache[(name, rev)] = mode
143
144
144 return data
145 return data
145
146
146 def getmode(self, name, rev):
147 def getmode(self, name, rev):
147 return self.modecache[(name, rev)]
148 return self.modecache[(name, rev)]
148
149
149 def getchanges(self, rev):
150 def getchanges(self, rev):
150 self.modecache = {}
151 self.modecache = {}
151 self._update(rev)
152 self._update(rev)
152 changes = []
153 changes = []
153 copies = {}
154 copies = {}
154
155
155 for f in self.changes[rev].add_files:
156 for f in self.changes[rev].add_files:
156 changes.append((f, rev))
157 changes.append((f, rev))
157
158
158 for f in self.changes[rev].mod_files:
159 for f in self.changes[rev].mod_files:
159 changes.append((f, rev))
160 changes.append((f, rev))
160
161
161 for f in self.changes[rev].del_files:
162 for f in self.changes[rev].del_files:
162 changes.append((f, rev))
163 changes.append((f, rev))
163
164
164 for src in self.changes[rev].ren_files:
165 for src in self.changes[rev].ren_files:
165 to = self.changes[rev].ren_files[src]
166 to = self.changes[rev].ren_files[src]
166 changes.append((src, rev))
167 changes.append((src, rev))
167 changes.append((to, rev))
168 changes.append((to, rev))
168 copies[to] = src
169 copies[to] = src
169
170
170 for src in self.changes[rev].ren_dirs:
171 for src in self.changes[rev].ren_dirs:
171 to = self.changes[rev].ren_dirs[src]
172 to = self.changes[rev].ren_dirs[src]
172 chgs, cps = self._rendirchanges(src, to);
173 chgs, cps = self._rendirchanges(src, to);
173 changes += [(f, rev) for f in chgs]
174 changes += [(f, rev) for f in chgs]
174 copies.update(cps)
175 copies.update(cps)
175
176
176 self.lastrev = rev
177 self.lastrev = rev
177 return sorted(set(changes)), copies
178 return sorted(set(changes)), copies
178
179
179 def getcommit(self, rev):
180 def getcommit(self, rev):
180 changes = self.changes[rev]
181 changes = self.changes[rev]
181 return commit(author = changes.author, date = changes.date,
182 return commit(author=changes.author, date=changes.date,
182 desc = changes.summary, parents = self.parents[rev], rev=rev)
183 desc=changes.summary, parents=self.parents[rev], rev=rev)
183
184
184 def gettags(self):
185 def gettags(self):
185 return self.tags
186 return self.tags
186
187
187 def _execute(self, cmd, *args, **kwargs):
188 def _execute(self, cmd, *args, **kwargs):
188 cmdline = [self.execmd, cmd]
189 cmdline = [self.execmd, cmd]
189 cmdline += args
190 cmdline += args
190 cmdline = [util.shellquote(arg) for arg in cmdline]
191 cmdline = [util.shellquote(arg) for arg in cmdline]
191 cmdline += ['>', util.nulldev, '2>', util.nulldev]
192 cmdline += ['>', util.nulldev, '2>', util.nulldev]
192 cmdline = util.quotecommand(' '.join(cmdline))
193 cmdline = util.quotecommand(' '.join(cmdline))
193 self.ui.debug(cmdline, '\n')
194 self.ui.debug(cmdline, '\n')
194 return os.system(cmdline)
195 return os.system(cmdline)
195
196
196 def _update(self, rev):
197 def _update(self, rev):
197 self.ui.debug(_('applying revision %s...\n') % rev)
198 self.ui.debug(_('applying revision %s...\n') % rev)
198 changeset, status = self.runlines('replay', '-d', self.tmppath,
199 changeset, status = self.runlines('replay', '-d', self.tmppath,
199 rev)
200 rev)
200 if status:
201 if status:
201 # Something went wrong while merging (baz or tla
202 # Something went wrong while merging (baz or tla
202 # issue?), get latest revision and try from there
203 # issue?), get latest revision and try from there
203 shutil.rmtree(self.tmppath, ignore_errors=True)
204 shutil.rmtree(self.tmppath, ignore_errors=True)
204 self._obtainrevision(rev)
205 self._obtainrevision(rev)
205 else:
206 else:
206 old_rev = self.parents[rev][0]
207 old_rev = self.parents[rev][0]
207 self.ui.debug(_('computing changeset between %s and %s...\n')
208 self.ui.debug(_('computing changeset between %s and %s...\n')
208 % (old_rev, rev))
209 % (old_rev, rev))
209 self._parsechangeset(changeset, rev)
210 self._parsechangeset(changeset, rev)
210
211
211 def _getfile(self, name, rev):
212 def _getfile(self, name, rev):
212 mode = os.lstat(os.path.join(self.tmppath, name)).st_mode
213 mode = os.lstat(os.path.join(self.tmppath, name)).st_mode
213 if stat.S_ISLNK(mode):
214 if stat.S_ISLNK(mode):
214 data = os.readlink(os.path.join(self.tmppath, name))
215 data = os.readlink(os.path.join(self.tmppath, name))
215 mode = mode and 'l' or ''
216 mode = mode and 'l' or ''
216 else:
217 else:
217 data = open(os.path.join(self.tmppath, name), 'rb').read()
218 data = open(os.path.join(self.tmppath, name), 'rb').read()
218 mode = (mode & 0111) and 'x' or ''
219 mode = (mode & 0111) and 'x' or ''
219 return data, mode
220 return data, mode
220
221
221 def _exclude(self, name):
222 def _exclude(self, name):
222 exclude = [ '{arch}', '.arch-ids', '.arch-inventory' ]
223 exclude = [ '{arch}', '.arch-ids', '.arch-inventory' ]
223 for exc in exclude:
224 for exc in exclude:
224 if name.find(exc) != -1:
225 if name.find(exc) != -1:
225 return True
226 return True
226 return False
227 return False
227
228
228 def _readcontents(self, path):
229 def _readcontents(self, path):
229 files = []
230 files = []
230 contents = os.listdir(path)
231 contents = os.listdir(path)
231 while len(contents) > 0:
232 while len(contents) > 0:
232 c = contents.pop()
233 c = contents.pop()
233 p = os.path.join(path, c)
234 p = os.path.join(path, c)
234 # os.walk could be used, but here we avoid internal GNU
235 # os.walk could be used, but here we avoid internal GNU
235 # Arch files and directories, thus saving a lot time.
236 # Arch files and directories, thus saving a lot time.
236 if not self._exclude(p):
237 if not self._exclude(p):
237 if os.path.isdir(p):
238 if os.path.isdir(p):
238 contents += [os.path.join(c, f) for f in os.listdir(p)]
239 contents += [os.path.join(c, f) for f in os.listdir(p)]
239 else:
240 else:
240 files.append(c)
241 files.append(c)
241 return files
242 return files
242
243
243 def _rendirchanges(self, src, dest):
244 def _rendirchanges(self, src, dest):
244 changes = []
245 changes = []
245 copies = {}
246 copies = {}
246 files = self._readcontents(os.path.join(self.tmppath, dest))
247 files = self._readcontents(os.path.join(self.tmppath, dest))
247 for f in files:
248 for f in files:
248 s = os.path.join(src, f)
249 s = os.path.join(src, f)
249 d = os.path.join(dest, f)
250 d = os.path.join(dest, f)
250 changes.append(s)
251 changes.append(s)
251 changes.append(d)
252 changes.append(d)
252 copies[d] = s
253 copies[d] = s
253 return changes, copies
254 return changes, copies
254
255
255 def _obtainrevision(self, rev):
256 def _obtainrevision(self, rev):
256 self.ui.debug(_('obtaining revision %s...\n') % rev)
257 self.ui.debug(_('obtaining revision %s...\n') % rev)
257 output = self._execute('get', rev, self.tmppath)
258 output = self._execute('get', rev, self.tmppath)
258 self.checkexit(output)
259 self.checkexit(output)
259 self.ui.debug(_('analysing revision %s...\n') % rev)
260 self.ui.debug(_('analysing revision %s...\n') % rev)
260 files = self._readcontents(self.tmppath)
261 files = self._readcontents(self.tmppath)
261 self.changes[rev].add_files += files
262 self.changes[rev].add_files += files
262
263
263 def _stripbasepath(self, path):
264 def _stripbasepath(self, path):
264 if path.startswith('./'):
265 if path.startswith('./'):
265 return path[2:]
266 return path[2:]
266 return path
267 return path
267
268
268 def _parsecatlog(self, data, rev):
269 def _parsecatlog(self, data, rev):
269 try:
270 try:
270 catlog = self.catlogparser.parsestr(data)
271 catlog = self.catlogparser.parsestr(data)
271
272
272 # Commit date
273 # Commit date
273 self.changes[rev].date = util.datestr(
274 self.changes[rev].date = util.datestr(
274 util.strdate(catlog['Standard-date'],
275 util.strdate(catlog['Standard-date'],
275 '%Y-%m-%d %H:%M:%S'))
276 '%Y-%m-%d %H:%M:%S'))
276
277
277 # Commit author
278 # Commit author
278 self.changes[rev].author = self.recode(catlog['Creator'])
279 self.changes[rev].author = self.recode(catlog['Creator'])
279
280
280 # Commit description
281 # Commit description
281 self.changes[rev].summary = '\n\n'.join((catlog['Summary'],
282 self.changes[rev].summary = '\n\n'.join((catlog['Summary'],
282 catlog.get_payload()))
283 catlog.get_payload()))
283 self.changes[rev].summary = self.recode(self.changes[rev].summary)
284 self.changes[rev].summary = self.recode(self.changes[rev].summary)
284
285
285 # Commit revision origin when dealing with a branch or tag
286 # Commit revision origin when dealing with a branch or tag
286 if catlog.has_key('Continuation-of'):
287 if catlog.has_key('Continuation-of'):
287 self.changes[rev].continuationof = self.recode(catlog['Continuation-of'])
288 self.changes[rev].continuationof = self.recode(catlog['Continuation-of'])
288 except Exception:
289 except Exception:
289 raise util.Abort(_('could not parse cat-log of %s') % rev)
290 raise util.Abort(_('could not parse cat-log of %s') % rev)
290
291
291 def _parsechangeset(self, data, rev):
292 def _parsechangeset(self, data, rev):
292 for l in data:
293 for l in data:
293 l = l.strip()
294 l = l.strip()
294 # Added file (ignore added directory)
295 # Added file (ignore added directory)
295 if l.startswith('A') and not l.startswith('A/'):
296 if l.startswith('A') and not l.startswith('A/'):
296 file = self._stripbasepath(l[1:].strip())
297 file = self._stripbasepath(l[1:].strip())
297 if not self._exclude(file):
298 if not self._exclude(file):
298 self.changes[rev].add_files.append(file)
299 self.changes[rev].add_files.append(file)
299 # Deleted file (ignore deleted directory)
300 # Deleted file (ignore deleted directory)
300 elif l.startswith('D') and not l.startswith('D/'):
301 elif l.startswith('D') and not l.startswith('D/'):
301 file = self._stripbasepath(l[1:].strip())
302 file = self._stripbasepath(l[1:].strip())
302 if not self._exclude(file):
303 if not self._exclude(file):
303 self.changes[rev].del_files.append(file)
304 self.changes[rev].del_files.append(file)
304 # Modified binary file
305 # Modified binary file
305 elif l.startswith('Mb'):
306 elif l.startswith('Mb'):
306 file = self._stripbasepath(l[2:].strip())
307 file = self._stripbasepath(l[2:].strip())
307 if not self._exclude(file):
308 if not self._exclude(file):
308 self.changes[rev].mod_files.append(file)
309 self.changes[rev].mod_files.append(file)
309 # Modified link
310 # Modified link
310 elif l.startswith('M->'):
311 elif l.startswith('M->'):
311 file = self._stripbasepath(l[3:].strip())
312 file = self._stripbasepath(l[3:].strip())
312 if not self._exclude(file):
313 if not self._exclude(file):
313 self.changes[rev].mod_files.append(file)
314 self.changes[rev].mod_files.append(file)
314 # Modified file
315 # Modified file
315 elif l.startswith('M'):
316 elif l.startswith('M'):
316 file = self._stripbasepath(l[1:].strip())
317 file = self._stripbasepath(l[1:].strip())
317 if not self._exclude(file):
318 if not self._exclude(file):
318 self.changes[rev].mod_files.append(file)
319 self.changes[rev].mod_files.append(file)
319 # Renamed file (or link)
320 # Renamed file (or link)
320 elif l.startswith('=>'):
321 elif l.startswith('=>'):
321 files = l[2:].strip().split(' ')
322 files = l[2:].strip().split(' ')
322 if len(files) == 1:
323 if len(files) == 1:
323 files = l[2:].strip().split('\t')
324 files = l[2:].strip().split('\t')
324 src = self._stripbasepath(files[0])
325 src = self._stripbasepath(files[0])
325 dst = self._stripbasepath(files[1])
326 dst = self._stripbasepath(files[1])
326 if not self._exclude(src) and not self._exclude(dst):
327 if not self._exclude(src) and not self._exclude(dst):
327 self.changes[rev].ren_files[src] = dst
328 self.changes[rev].ren_files[src] = dst
328 # Conversion from file to link or from link to file (modified)
329 # Conversion from file to link or from link to file (modified)
329 elif l.startswith('ch'):
330 elif l.startswith('ch'):
330 file = self._stripbasepath(l[2:].strip())
331 file = self._stripbasepath(l[2:].strip())
331 if not self._exclude(file):
332 if not self._exclude(file):
332 self.changes[rev].mod_files.append(file)
333 self.changes[rev].mod_files.append(file)
333 # Renamed directory
334 # Renamed directory
334 elif l.startswith('/>'):
335 elif l.startswith('/>'):
335 dirs = l[2:].strip().split(' ')
336 dirs = l[2:].strip().split(' ')
336 if len(dirs) == 1:
337 if len(dirs) == 1:
337 dirs = l[2:].strip().split('\t')
338 dirs = l[2:].strip().split('\t')
338 src = self._stripbasepath(dirs[0])
339 src = self._stripbasepath(dirs[0])
339 dst = self._stripbasepath(dirs[1])
340 dst = self._stripbasepath(dirs[1])
340 if not self._exclude(src) and not self._exclude(dst):
341 if not self._exclude(src) and not self._exclude(dst):
341 self.changes[rev].ren_dirs[src] = dst
342 self.changes[rev].ren_dirs[src] = dst
General Comments 0
You need to be logged in to leave comments. Login now