##// END OF EJS Templates
convert: remove superfluous pass statements
Augie Fackler -
r34368:5adbd380 default
parent child Browse files
Show More
@@ -1,496 +1,491 b''
1 1 # common.py - common code for the convert extension
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 from __future__ import absolute_import
8 8
9 9 import base64
10 10 import datetime
11 11 import errno
12 12 import os
13 13 import re
14 14 import subprocess
15 15
16 16 from mercurial.i18n import _
17 17 from mercurial import (
18 18 encoding,
19 19 error,
20 20 phases,
21 21 util,
22 22 )
23 23
24 24 pickle = util.pickle
25 25 propertycache = util.propertycache
26 26
27 27 def encodeargs(args):
28 28 def encodearg(s):
29 29 lines = base64.encodestring(s)
30 30 lines = [l.splitlines()[0] for l in lines]
31 31 return ''.join(lines)
32 32
33 33 s = pickle.dumps(args)
34 34 return encodearg(s)
35 35
36 36 def decodeargs(s):
37 37 s = base64.decodestring(s)
38 38 return pickle.loads(s)
39 39
40 40 class MissingTool(Exception):
41 41 pass
42 42
43 43 def checktool(exe, name=None, abort=True):
44 44 name = name or exe
45 45 if not util.findexe(exe):
46 46 if abort:
47 47 exc = error.Abort
48 48 else:
49 49 exc = MissingTool
50 50 raise exc(_('cannot find required "%s" tool') % name)
51 51
52 52 class NoRepo(Exception):
53 53 pass
54 54
55 55 SKIPREV = 'SKIP'
56 56
57 57 class commit(object):
58 58 def __init__(self, author, date, desc, parents, branch=None, rev=None,
59 59 extra=None, sortkey=None, saverev=True, phase=phases.draft,
60 60 optparents=None):
61 61 self.author = author or 'unknown'
62 62 self.date = date or '0 0'
63 63 self.desc = desc
64 64 self.parents = parents # will be converted and used as parents
65 65 self.optparents = optparents or [] # will be used if already converted
66 66 self.branch = branch
67 67 self.rev = rev
68 68 self.extra = extra or {}
69 69 self.sortkey = sortkey
70 70 self.saverev = saverev
71 71 self.phase = phase
72 72
73 73 class converter_source(object):
74 74 """Conversion source interface"""
75 75
76 76 def __init__(self, ui, path=None, revs=None):
77 77 """Initialize conversion source (or raise NoRepo("message")
78 78 exception if path is not a valid repository)"""
79 79 self.ui = ui
80 80 self.path = path
81 81 self.revs = revs
82 82
83 83 self.encoding = 'utf-8'
84 84
85 85 def checkhexformat(self, revstr, mapname='splicemap'):
86 86 """ fails if revstr is not a 40 byte hex. mercurial and git both uses
87 87 such format for their revision numbering
88 88 """
89 89 if not re.match(r'[0-9a-fA-F]{40,40}$', revstr):
90 90 raise error.Abort(_('%s entry %s is not a valid revision'
91 91 ' identifier') % (mapname, revstr))
92 92
93 93 def before(self):
94 94 pass
95 95
96 96 def after(self):
97 97 pass
98 98
99 99 def targetfilebelongstosource(self, targetfilename):
100 100 """Returns true if the given targetfile belongs to the source repo. This
101 101 is useful when only a subdirectory of the target belongs to the source
102 102 repo."""
103 103 # For normal full repo converts, this is always True.
104 104 return True
105 105
106 106 def setrevmap(self, revmap):
107 107 """set the map of already-converted revisions"""
108 pass
109 108
110 109 def getheads(self):
111 110 """Return a list of this repository's heads"""
112 111 raise NotImplementedError
113 112
114 113 def getfile(self, name, rev):
115 114 """Return a pair (data, mode) where data is the file content
116 115 as a string and mode one of '', 'x' or 'l'. rev is the
117 116 identifier returned by a previous call to getchanges().
118 117 Data is None if file is missing/deleted in rev.
119 118 """
120 119 raise NotImplementedError
121 120
122 121 def getchanges(self, version, full):
123 122 """Returns a tuple of (files, copies, cleanp2).
124 123
125 124 files is a sorted list of (filename, id) tuples for all files
126 125 changed between version and its first parent returned by
127 126 getcommit(). If full, all files in that revision is returned.
128 127 id is the source revision id of the file.
129 128
130 129 copies is a dictionary of dest: source
131 130
132 131 cleanp2 is the set of files filenames that are clean against p2.
133 132 (Files that are clean against p1 are already not in files (unless
134 133 full). This makes it possible to handle p2 clean files similarly.)
135 134 """
136 135 raise NotImplementedError
137 136
138 137 def getcommit(self, version):
139 138 """Return the commit object for version"""
140 139 raise NotImplementedError
141 140
142 141 def numcommits(self):
143 142 """Return the number of commits in this source.
144 143
145 144 If unknown, return None.
146 145 """
147 146 return None
148 147
149 148 def gettags(self):
150 149 """Return the tags as a dictionary of name: revision
151 150
152 151 Tag names must be UTF-8 strings.
153 152 """
154 153 raise NotImplementedError
155 154
156 155 def recode(self, s, encoding=None):
157 156 if not encoding:
158 157 encoding = self.encoding or 'utf-8'
159 158
160 159 if isinstance(s, unicode):
161 160 return s.encode("utf-8")
162 161 try:
163 162 return s.decode(encoding).encode("utf-8")
164 163 except UnicodeError:
165 164 try:
166 165 return s.decode("latin-1").encode("utf-8")
167 166 except UnicodeError:
168 167 return s.decode(encoding, "replace").encode("utf-8")
169 168
170 169 def getchangedfiles(self, rev, i):
171 170 """Return the files changed by rev compared to parent[i].
172 171
173 172 i is an index selecting one of the parents of rev. The return
174 173 value should be the list of files that are different in rev and
175 174 this parent.
176 175
177 176 If rev has no parents, i is None.
178 177
179 178 This function is only needed to support --filemap
180 179 """
181 180 raise NotImplementedError
182 181
183 182 def converted(self, rev, sinkrev):
184 183 '''Notify the source that a revision has been converted.'''
185 pass
186 184
187 185 def hasnativeorder(self):
188 186 """Return true if this source has a meaningful, native revision
189 187 order. For instance, Mercurial revisions are store sequentially
190 188 while there is no such global ordering with Darcs.
191 189 """
192 190 return False
193 191
194 192 def hasnativeclose(self):
195 193 """Return true if this source has ability to close branch.
196 194 """
197 195 return False
198 196
199 197 def lookuprev(self, rev):
200 198 """If rev is a meaningful revision reference in source, return
201 199 the referenced identifier in the same format used by getcommit().
202 200 return None otherwise.
203 201 """
204 202 return None
205 203
206 204 def getbookmarks(self):
207 205 """Return the bookmarks as a dictionary of name: revision
208 206
209 207 Bookmark names are to be UTF-8 strings.
210 208 """
211 209 return {}
212 210
213 211 def checkrevformat(self, revstr, mapname='splicemap'):
214 212 """revstr is a string that describes a revision in the given
215 213 source control system. Return true if revstr has correct
216 214 format.
217 215 """
218 216 return True
219 217
220 218 class converter_sink(object):
221 219 """Conversion sink (target) interface"""
222 220
223 221 def __init__(self, ui, path):
224 222 """Initialize conversion sink (or raise NoRepo("message")
225 223 exception if path is not a valid repository)
226 224
227 225 created is a list of paths to remove if a fatal error occurs
228 226 later"""
229 227 self.ui = ui
230 228 self.path = path
231 229 self.created = []
232 230
233 231 def revmapfile(self):
234 232 """Path to a file that will contain lines
235 233 source_rev_id sink_rev_id
236 234 mapping equivalent revision identifiers for each system."""
237 235 raise NotImplementedError
238 236
239 237 def authorfile(self):
240 238 """Path to a file that will contain lines
241 239 srcauthor=dstauthor
242 240 mapping equivalent authors identifiers for each system."""
243 241 return None
244 242
245 243 def putcommit(self, files, copies, parents, commit, source, revmap, full,
246 244 cleanp2):
247 245 """Create a revision with all changed files listed in 'files'
248 246 and having listed parents. 'commit' is a commit object
249 247 containing at a minimum the author, date, and message for this
250 248 changeset. 'files' is a list of (path, version) tuples,
251 249 'copies' is a dictionary mapping destinations to sources,
252 250 'source' is the source repository, and 'revmap' is a mapfile
253 251 of source revisions to converted revisions. Only getfile() and
254 252 lookuprev() should be called on 'source'. 'full' means that 'files'
255 253 is complete and all other files should be removed.
256 254 'cleanp2' is a set of the filenames that are unchanged from p2
257 255 (only in the common merge case where there two parents).
258 256
259 257 Note that the sink repository is not told to update itself to
260 258 a particular revision (or even what that revision would be)
261 259 before it receives the file data.
262 260 """
263 261 raise NotImplementedError
264 262
265 263 def puttags(self, tags):
266 264 """Put tags into sink.
267 265
268 266 tags: {tagname: sink_rev_id, ...} where tagname is an UTF-8 string.
269 267 Return a pair (tag_revision, tag_parent_revision), or (None, None)
270 268 if nothing was changed.
271 269 """
272 270 raise NotImplementedError
273 271
274 272 def setbranch(self, branch, pbranches):
275 273 """Set the current branch name. Called before the first putcommit
276 274 on the branch.
277 275 branch: branch name for subsequent commits
278 276 pbranches: (converted parent revision, parent branch) tuples"""
279 pass
280 277
281 278 def setfilemapmode(self, active):
282 279 """Tell the destination that we're using a filemap
283 280
284 281 Some converter_sources (svn in particular) can claim that a file
285 282 was changed in a revision, even if there was no change. This method
286 283 tells the destination that we're using a filemap and that it should
287 284 filter empty revisions.
288 285 """
289 pass
290 286
291 287 def before(self):
292 288 pass
293 289
294 290 def after(self):
295 291 pass
296 292
297 293 def putbookmarks(self, bookmarks):
298 294 """Put bookmarks into sink.
299 295
300 296 bookmarks: {bookmarkname: sink_rev_id, ...}
301 297 where bookmarkname is an UTF-8 string.
302 298 """
303 pass
304 299
305 300 def hascommitfrommap(self, rev):
306 301 """Return False if a rev mentioned in a filemap is known to not be
307 302 present."""
308 303 raise NotImplementedError
309 304
310 305 def hascommitforsplicemap(self, rev):
311 306 """This method is for the special needs for splicemap handling and not
312 307 for general use. Returns True if the sink contains rev, aborts on some
313 308 special cases."""
314 309 raise NotImplementedError
315 310
316 311 class commandline(object):
317 312 def __init__(self, ui, command):
318 313 self.ui = ui
319 314 self.command = command
320 315
321 316 def prerun(self):
322 317 pass
323 318
324 319 def postrun(self):
325 320 pass
326 321
327 322 def _cmdline(self, cmd, *args, **kwargs):
328 323 cmdline = [self.command, cmd] + list(args)
329 324 for k, v in kwargs.iteritems():
330 325 if len(k) == 1:
331 326 cmdline.append('-' + k)
332 327 else:
333 328 cmdline.append('--' + k.replace('_', '-'))
334 329 try:
335 330 if len(k) == 1:
336 331 cmdline.append('' + v)
337 332 else:
338 333 cmdline[-1] += '=' + v
339 334 except TypeError:
340 335 pass
341 336 cmdline = [util.shellquote(arg) for arg in cmdline]
342 337 if not self.ui.debugflag:
343 338 cmdline += ['2>', os.devnull]
344 339 cmdline = ' '.join(cmdline)
345 340 return cmdline
346 341
347 342 def _run(self, cmd, *args, **kwargs):
348 343 def popen(cmdline):
349 344 p = subprocess.Popen(cmdline, shell=True, bufsize=-1,
350 345 close_fds=util.closefds,
351 346 stdout=subprocess.PIPE)
352 347 return p
353 348 return self._dorun(popen, cmd, *args, **kwargs)
354 349
355 350 def _run2(self, cmd, *args, **kwargs):
356 351 return self._dorun(util.popen2, cmd, *args, **kwargs)
357 352
358 353 def _run3(self, cmd, *args, **kwargs):
359 354 return self._dorun(util.popen3, cmd, *args, **kwargs)
360 355
361 356 def _dorun(self, openfunc, cmd, *args, **kwargs):
362 357 cmdline = self._cmdline(cmd, *args, **kwargs)
363 358 self.ui.debug('running: %s\n' % (cmdline,))
364 359 self.prerun()
365 360 try:
366 361 return openfunc(cmdline)
367 362 finally:
368 363 self.postrun()
369 364
370 365 def run(self, cmd, *args, **kwargs):
371 366 p = self._run(cmd, *args, **kwargs)
372 367 output = p.communicate()[0]
373 368 self.ui.debug(output)
374 369 return output, p.returncode
375 370
376 371 def runlines(self, cmd, *args, **kwargs):
377 372 p = self._run(cmd, *args, **kwargs)
378 373 output = p.stdout.readlines()
379 374 p.wait()
380 375 self.ui.debug(''.join(output))
381 376 return output, p.returncode
382 377
383 378 def checkexit(self, status, output=''):
384 379 if status:
385 380 if output:
386 381 self.ui.warn(_('%s error:\n') % self.command)
387 382 self.ui.warn(output)
388 383 msg = util.explainexit(status)[0]
389 384 raise error.Abort('%s %s' % (self.command, msg))
390 385
391 386 def run0(self, cmd, *args, **kwargs):
392 387 output, status = self.run(cmd, *args, **kwargs)
393 388 self.checkexit(status, output)
394 389 return output
395 390
396 391 def runlines0(self, cmd, *args, **kwargs):
397 392 output, status = self.runlines(cmd, *args, **kwargs)
398 393 self.checkexit(status, ''.join(output))
399 394 return output
400 395
401 396 @propertycache
402 397 def argmax(self):
403 398 # POSIX requires at least 4096 bytes for ARG_MAX
404 399 argmax = 4096
405 400 try:
406 401 argmax = os.sysconf("SC_ARG_MAX")
407 402 except (AttributeError, ValueError):
408 403 pass
409 404
410 405 # Windows shells impose their own limits on command line length,
411 406 # down to 2047 bytes for cmd.exe under Windows NT/2k and 2500 bytes
412 407 # for older 4nt.exe. See http://support.microsoft.com/kb/830473 for
413 408 # details about cmd.exe limitations.
414 409
415 410 # Since ARG_MAX is for command line _and_ environment, lower our limit
416 411 # (and make happy Windows shells while doing this).
417 412 return argmax // 2 - 1
418 413
419 414 def _limit_arglist(self, arglist, cmd, *args, **kwargs):
420 415 cmdlen = len(self._cmdline(cmd, *args, **kwargs))
421 416 limit = self.argmax - cmdlen
422 417 bytes = 0
423 418 fl = []
424 419 for fn in arglist:
425 420 b = len(fn) + 3
426 421 if bytes + b < limit or len(fl) == 0:
427 422 fl.append(fn)
428 423 bytes += b
429 424 else:
430 425 yield fl
431 426 fl = [fn]
432 427 bytes = b
433 428 if fl:
434 429 yield fl
435 430
436 431 def xargs(self, arglist, cmd, *args, **kwargs):
437 432 for l in self._limit_arglist(arglist, cmd, *args, **kwargs):
438 433 self.run0(cmd, *(list(args) + l), **kwargs)
439 434
440 435 class mapfile(dict):
441 436 def __init__(self, ui, path):
442 437 super(mapfile, self).__init__()
443 438 self.ui = ui
444 439 self.path = path
445 440 self.fp = None
446 441 self.order = []
447 442 self._read()
448 443
449 444 def _read(self):
450 445 if not self.path:
451 446 return
452 447 try:
453 448 fp = open(self.path, 'r')
454 449 except IOError as err:
455 450 if err.errno != errno.ENOENT:
456 451 raise
457 452 return
458 453 for i, line in enumerate(util.iterfile(fp)):
459 454 line = line.splitlines()[0].rstrip()
460 455 if not line:
461 456 # Ignore blank lines
462 457 continue
463 458 try:
464 459 key, value = line.rsplit(' ', 1)
465 460 except ValueError:
466 461 raise error.Abort(
467 462 _('syntax error in %s(%d): key/value pair expected')
468 463 % (self.path, i + 1))
469 464 if key not in self:
470 465 self.order.append(key)
471 466 super(mapfile, self).__setitem__(key, value)
472 467 fp.close()
473 468
474 469 def __setitem__(self, key, value):
475 470 if self.fp is None:
476 471 try:
477 472 self.fp = open(self.path, 'a')
478 473 except IOError as err:
479 474 raise error.Abort(
480 475 _('could not open map file %r: %s') %
481 476 (self.path, encoding.strtolocal(err.strerror)))
482 477 self.fp.write('%s %s\n' % (key, value))
483 478 self.fp.flush()
484 479 super(mapfile, self).__setitem__(key, value)
485 480
486 481 def close(self):
487 482 if self.fp:
488 483 self.fp.close()
489 484 self.fp = None
490 485
491 486 def makedatetimestamp(t):
492 487 """Like util.makedate() but for time t instead of current time"""
493 488 delta = (datetime.datetime.utcfromtimestamp(t) -
494 489 datetime.datetime.fromtimestamp(t))
495 490 tz = delta.days * 86400 + delta.seconds
496 491 return t, tz
General Comments 0
You need to be logged in to leave comments. Login now