comparison mercurial/patch.py @ 14391:1e64e1e12195

patch: unify backend file access interface - Rename readlines() into getfile(), return data and mode - Make setfile() write a data buffer instead of lines, make mode mandatory.
author Patrick Mezard <pmezard@gmail.com>
date Thu, 19 May 2011 22:49:43 +0200
parents ce77c275bec3
children bb5cbc16349e
comparison
equal deleted inserted replaced
14390:ce77c275bec3 14391:1e64e1e12195
364 364
365 class abstractbackend(object): 365 class abstractbackend(object):
366 def __init__(self, ui): 366 def __init__(self, ui):
367 self.ui = ui 367 self.ui = ui
368 368
369 def readlines(self, fname): 369 def getfile(self, fname):
370 """Return target file lines, or its content as a single line 370 """Return target file data and flags as a (data, (islink,
371 for symlinks. 371 isexec)) tuple.
372 """ 372 """
373 raise NotImplementedError 373 raise NotImplementedError
374 374
375 def setfile(self, fname, lines, mode): 375 def setfile(self, fname, data, mode):
376 """Write lines to target file. mode is a (islink, isexec) 376 """Write data to target file fname and set its mode. mode is a
377 tuple, or None if there is no mode information. If lines is None, 377 (islink, isexec) tuple. If data is None, the file content should
378 the file must exists and its content is left unchanged. 378 be left unchanged.
379 """ 379 """
380 raise NotImplementedError 380 raise NotImplementedError
381 381
382 def unlink(self, fname): 382 def unlink(self, fname):
383 """Unlink target file.""" 383 """Unlink target file."""
406 self.opener = scmutil.opener(basedir) 406 self.opener = scmutil.opener(basedir)
407 407
408 def _join(self, f): 408 def _join(self, f):
409 return os.path.join(self.opener.base, f) 409 return os.path.join(self.opener.base, f)
410 410
411 def readlines(self, fname): 411 def getfile(self, fname):
412 if os.path.islink(self._join(fname)): 412 path = self._join(fname)
413 return [os.readlink(self._join(fname))] 413 if os.path.islink(path):
414 fp = self.opener(fname, 'r') 414 return (os.readlink(path), (True, False))
415 isexec, islink = False, False
415 try: 416 try:
416 return list(fp) 417 isexec = os.lstat(path).st_mode & 0100 != 0
417 finally: 418 islink = os.path.islink(path)
418 fp.close() 419 except OSError, e:
419 420 if e.errno != errno.ENOENT:
420 def setfile(self, fname, lines, mode): 421 raise
421 if lines is None: 422 return (self.opener.read(fname), (islink, isexec))
422 if mode: 423
423 util.setflags(self._join(fname), mode[0], mode[1]) 424 def setfile(self, fname, data, mode):
425 islink, isexec = mode
426 if data is None:
427 util.setflags(self._join(fname), islink, isexec)
424 return 428 return
425 if not mode: 429 if islink:
426 # Preserve mode information 430 self.opener.symlink(data, fname)
427 isexec, islink = False, False
428 try:
429 isexec = os.lstat(self._join(fname)).st_mode & 0100 != 0
430 islink = os.path.islink(self._join(fname))
431 except OSError, e:
432 if e.errno != errno.ENOENT:
433 raise
434 else: 431 else:
435 islink, isexec = mode 432 self.opener.write(fname, data)
436 if islink:
437 self.opener.symlink(''.join(lines), fname)
438 else:
439 self.opener(fname, 'w').writelines(lines)
440 if isexec: 433 if isexec:
441 util.setflags(self._join(fname), False, True) 434 util.setflags(self._join(fname), False, True)
442 435
443 def unlink(self, fname): 436 def unlink(self, fname):
444 try: 437 try:
483 self.similarity = similarity 476 self.similarity = similarity
484 self.removed = set() 477 self.removed = set()
485 self.changed = set() 478 self.changed = set()
486 self.copied = [] 479 self.copied = []
487 480
488 def setfile(self, fname, lines, mode): 481 def setfile(self, fname, data, mode):
489 super(workingbackend, self).setfile(fname, lines, mode) 482 super(workingbackend, self).setfile(fname, data, mode)
490 self.changed.add(fname) 483 self.changed.add(fname)
491 484
492 def unlink(self, fname): 485 def unlink(self, fname):
493 super(workingbackend, self).unlink(fname) 486 super(workingbackend, self).unlink(fname)
494 self.removed.add(fname) 487 self.removed.add(fname)
532 self.exists = False 525 self.exists = False
533 self.missing = missing 526 self.missing = missing
534 self.mode = mode 527 self.mode = mode
535 if not missing: 528 if not missing:
536 try: 529 try:
537 self.lines = self.backend.readlines(fname) 530 data, mode = self.backend.getfile(fname)
531 if data:
532 self.lines = data.splitlines(True)
533 if self.mode is None:
534 self.mode = mode
538 if self.lines: 535 if self.lines:
539 # Normalize line endings 536 # Normalize line endings
540 if self.lines[0].endswith('\r\n'): 537 if self.lines[0].endswith('\r\n'):
541 self.eol = '\r\n' 538 self.eol = '\r\n'
542 elif self.lines[0].endswith('\n'): 539 elif self.lines[0].endswith('\n'):
548 l = l[:-2] + '\n' 545 l = l[:-2] + '\n'
549 nlines.append(l) 546 nlines.append(l)
550 self.lines = nlines 547 self.lines = nlines
551 self.exists = True 548 self.exists = True
552 except IOError: 549 except IOError:
553 pass 550 if self.mode is None:
551 self.mode = (False, False)
554 else: 552 else:
555 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname) 553 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
556 554
557 self.hash = {} 555 self.hash = {}
558 self.dirty = 0 556 self.dirty = 0
577 if l and l[-1] == '\n': 575 if l and l[-1] == '\n':
578 l = l[:-1] + eol 576 l = l[:-1] + eol
579 rawlines.append(l) 577 rawlines.append(l)
580 lines = rawlines 578 lines = rawlines
581 579
582 self.backend.setfile(fname, lines, mode) 580 self.backend.setfile(fname, ''.join(lines), mode)
583 581
584 def printfile(self, warn): 582 def printfile(self, warn):
585 if self.fileprinted: 583 if self.fileprinted:
586 return 584 return
587 if warn or self.ui.verbose: 585 if warn or self.ui.verbose:
1248 if gp.mode and not first_hunk: 1246 if gp.mode and not first_hunk:
1249 data = None 1247 data = None
1250 if gp.op == 'ADD': 1248 if gp.op == 'ADD':
1251 # Added files without content have no hunk and 1249 # Added files without content have no hunk and
1252 # must be created 1250 # must be created
1253 data = [] 1251 data = ''
1254 backend.setfile(path, data, gp.mode) 1252 backend.setfile(path, data, gp.mode)
1255 if not first_hunk: 1253 if not first_hunk:
1256 continue 1254 continue
1257 try: 1255 try:
1258 mode = gp and gp.mode or None 1256 mode = gp and gp.mode or None