Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/patch.py @ 22296:650b5b6e75ed
convert: use None value for missing files instead of overloading IOError
The internal API used IOError to indicate that a file should be marked as
removed.
There is some correlation between IOError (especially with ENOENT) and files
that should be removed, but using IOErrors to represent file removal internally
required some hacks.
Instead, use the value None to indicate that the file not is present.
Before, spurious IO errors could cause commits that silently removed files.
They will now be reported like all other IO errors so the root cause can be
fixed.
author | Mads Kiilerich <madski@unity3d.com> |
---|---|
date | Tue, 26 Aug 2014 22:03:32 +0200 |
parents | c1ceec0c8cb4 |
children | c343557a8442 |
comparison
equal
deleted
inserted
replaced
22295:926bc0d3b595 | 22296:650b5b6e75ed |
---|---|
380 def __init__(self, ui): | 380 def __init__(self, ui): |
381 self.ui = ui | 381 self.ui = ui |
382 | 382 |
383 def getfile(self, fname): | 383 def getfile(self, fname): |
384 """Return target file data and flags as a (data, (islink, | 384 """Return target file data and flags as a (data, (islink, |
385 isexec)) tuple. | 385 isexec)) tuple. Data is None if file is missing/deleted. |
386 """ | 386 """ |
387 raise NotImplementedError | 387 raise NotImplementedError |
388 | 388 |
389 def setfile(self, fname, data, mode, copysource): | 389 def setfile(self, fname, data, mode, copysource): |
390 """Write data to target file fname and set its mode. mode is a | 390 """Write data to target file fname and set its mode. mode is a |
424 try: | 424 try: |
425 isexec = self.opener.lstat(fname).st_mode & 0100 != 0 | 425 isexec = self.opener.lstat(fname).st_mode & 0100 != 0 |
426 except OSError, e: | 426 except OSError, e: |
427 if e.errno != errno.ENOENT: | 427 if e.errno != errno.ENOENT: |
428 raise | 428 raise |
429 return (self.opener.read(fname), (False, isexec)) | 429 try: |
430 return (self.opener.read(fname), (False, isexec)) | |
431 except IOError, e: | |
432 if e.errno != errno.ENOENT: | |
433 raise | |
434 return None, None | |
430 | 435 |
431 def setfile(self, fname, data, mode, copysource): | 436 def setfile(self, fname, data, mode, copysource): |
432 islink, isexec = mode | 437 islink, isexec = mode |
433 if data is None: | 438 if data is None: |
434 self.opener.setflags(fname, islink, isexec) | 439 self.opener.setflags(fname, islink, isexec) |
526 | 531 |
527 def getfile(self, fname): | 532 def getfile(self, fname): |
528 if fname in self.data: | 533 if fname in self.data: |
529 return self.data[fname] | 534 return self.data[fname] |
530 if not self.opener or fname not in self.files: | 535 if not self.opener or fname not in self.files: |
531 raise IOError | 536 return None, None, None |
532 fn, mode, copied = self.files[fname] | 537 fn, mode, copied = self.files[fname] |
533 return self.opener.read(fn), mode, copied | 538 return self.opener.read(fn), mode, copied |
534 | 539 |
535 def close(self): | 540 def close(self): |
536 if self.opener: | 541 if self.opener: |
552 | 557 |
553 def getfile(self, fname): | 558 def getfile(self, fname): |
554 try: | 559 try: |
555 fctx = self.ctx[fname] | 560 fctx = self.ctx[fname] |
556 except error.LookupError: | 561 except error.LookupError: |
557 raise IOError | 562 return None, None |
558 flags = fctx.flags() | 563 flags = fctx.flags() |
559 return fctx.data(), ('l' in flags, 'x' in flags) | 564 return fctx.data(), ('l' in flags, 'x' in flags) |
560 | 565 |
561 def setfile(self, fname, data, mode, copysource): | 566 def setfile(self, fname, data, mode, copysource): |
562 if copysource: | 567 if copysource: |
595 self.missing = True | 600 self.missing = True |
596 self.mode = gp.mode | 601 self.mode = gp.mode |
597 self.copysource = gp.oldpath | 602 self.copysource = gp.oldpath |
598 self.create = gp.op in ('ADD', 'COPY', 'RENAME') | 603 self.create = gp.op in ('ADD', 'COPY', 'RENAME') |
599 self.remove = gp.op == 'DELETE' | 604 self.remove = gp.op == 'DELETE' |
600 try: | 605 if self.copysource is None: |
601 if self.copysource is None: | 606 data, mode = backend.getfile(self.fname) |
602 data, mode = backend.getfile(self.fname) | 607 else: |
603 self.exists = True | 608 data, mode = store.getfile(self.copysource)[:2] |
604 else: | 609 if data is not None: |
605 data, mode = store.getfile(self.copysource)[:2] | 610 self.exists = self.copysource is None or backend.exists(self.fname) |
606 self.exists = backend.exists(self.fname) | |
607 self.missing = False | 611 self.missing = False |
608 if data: | 612 if data: |
609 self.lines = mdiff.splitnewlines(data) | 613 self.lines = mdiff.splitnewlines(data) |
610 if self.mode is None: | 614 if self.mode is None: |
611 self.mode = mode | 615 self.mode = mode |
620 for l in self.lines: | 624 for l in self.lines: |
621 if l.endswith('\r\n'): | 625 if l.endswith('\r\n'): |
622 l = l[:-2] + '\n' | 626 l = l[:-2] + '\n' |
623 nlines.append(l) | 627 nlines.append(l) |
624 self.lines = nlines | 628 self.lines = nlines |
625 except IOError: | 629 else: |
626 if self.create: | 630 if self.create: |
627 self.missing = False | 631 self.missing = False |
628 if self.mode is None: | 632 if self.mode is None: |
629 self.mode = (False, False) | 633 self.mode = (False, False) |
630 if self.missing: | 634 if self.missing: |
1378 backend.unlink(gp.path) | 1382 backend.unlink(gp.path) |
1379 continue | 1383 continue |
1380 data, mode = None, None | 1384 data, mode = None, None |
1381 if gp.op in ('RENAME', 'COPY'): | 1385 if gp.op in ('RENAME', 'COPY'): |
1382 data, mode = store.getfile(gp.oldpath)[:2] | 1386 data, mode = store.getfile(gp.oldpath)[:2] |
1387 # FIXME: failing getfile has never been handled here | |
1388 assert data is not None | |
1383 if gp.mode: | 1389 if gp.mode: |
1384 mode = gp.mode | 1390 mode = gp.mode |
1385 if gp.op == 'ADD': | 1391 if gp.op == 'ADD': |
1386 # Added files without content have no hunk and | 1392 # Added files without content have no hunk and |
1387 # must be created | 1393 # must be created |
1402 rejects += 1 | 1408 rejects += 1 |
1403 continue | 1409 continue |
1404 elif state == 'git': | 1410 elif state == 'git': |
1405 for gp in values: | 1411 for gp in values: |
1406 path = pstrip(gp.oldpath) | 1412 path = pstrip(gp.oldpath) |
1407 try: | 1413 data, mode = backend.getfile(path) |
1408 data, mode = backend.getfile(path) | 1414 if data is None: |
1409 except IOError, e: | |
1410 if e.errno != errno.ENOENT: | |
1411 raise | |
1412 # The error ignored here will trigger a getfile() | 1415 # The error ignored here will trigger a getfile() |
1413 # error in a place more appropriate for error | 1416 # error in a place more appropriate for error |
1414 # handling, and will not interrupt the patching | 1417 # handling, and will not interrupt the patching |
1415 # process. | 1418 # process. |
1419 pass | |
1416 else: | 1420 else: |
1417 store.setfile(path, data, mode) | 1421 store.setfile(path, data, mode) |
1418 else: | 1422 else: |
1419 raise util.Abort(_('unsupported parser state: %s') % state) | 1423 raise util.Abort(_('unsupported parser state: %s') % state) |
1420 | 1424 |