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