comparison hg_delta_editor.py @ 69:63ece4ea25c9

hg_delta_editor: register copies only if files are unchanged between source and dest Handle copies of items from revision X into revision Y where X is not the parent of Y. This cannot happen in Mercurial because copies always happen between parents and children. A file copy is recorded if: 1- Source and destination revs are in the same branch. 2- The file is unchanged (content, type, removal) through all revisions between destination and source, not including source and destination. Directory copies are registered only if the previous rules apply on all copied items. [1] is there because file copies across branches are meaningless in Mercurial world. We could have tried to remap the source rev to a similar one in the correct branch, but anyway the intent is wrong. [2] is more questionable but I think it's better this way for we live in a non-perfect svn world. In theory, 99% of copies out there should come from the direct parent. But the direct parent is a fuzzy notion when you can have a working directory composed of different directory at different revisions. So we assume that stuff copied from past revisions exactly matching the content of the direct parent revision is really copied from the parent revision. The alternative would be to discard the copy, which would always happen unless people kept updating the working directory after every commit (see tests).
author Patrick Mezard <pmezard@gmail.com>
date Wed, 05 Nov 2008 13:37:08 +0100
parents e319c9168910
children 9ec2a12c12ae
comparison
equal deleted inserted replaced
68:e0c86ebe05e3 69:63ece4ea25c9
493 self.base_revision = base_revision 493 self.base_revision = base_revision
494 else: 494 else:
495 self.base_revision = None 495 self.base_revision = None
496 self.should_edit_most_recent_plaintext = True 496 self.should_edit_most_recent_plaintext = True
497 497
498 def _aresamefiles(self, parentctx, childctx, files):
499 """Assuming all files exist in childctx and parentctx, return True
500 if none of them was changed in-between.
501 """
502 if parentctx == childctx:
503 return True
504 if parentctx.rev() > childctx.rev():
505 parentctx, childctx = childctx, parentctx
506
507 def selfandancestors(selfctx):
508 yield selfctx
509 for ctx in selfctx.ancestors():
510 yield ctx
511
512 files = dict.fromkeys(files)
513 for pctx in selfandancestors(childctx):
514 if pctx.rev() <= parentctx.rev():
515 return True
516 for f in pctx.files():
517 if f in files:
518 return False
519 # parentctx is not an ancestor of childctx, files are unrelated
520 return False
521
498 @stash_exception_on_self 522 @stash_exception_on_self
499 def add_file(self, path, parent_baton, copyfrom_path, 523 def add_file(self, path, parent_baton, copyfrom_path,
500 copyfrom_revision, file_pool=None): 524 copyfrom_revision, file_pool=None):
501 self.current_file = 'foobaz' 525 self.current_file = 'foobaz'
502 self.base_revision = None 526 self.base_revision = None
526 cur_file = self.current_file 550 cur_file = self.current_file
527 self.current_files[cur_file] = fctx.data() 551 self.current_files[cur_file] = fctx.data()
528 self.current_files_symlink[cur_file] = 'l' in fctx.flags() 552 self.current_files_symlink[cur_file] = 'l' in fctx.flags()
529 self.current_files_exec[cur_file] = 'x' in fctx.flags() 553 self.current_files_exec[cur_file] = 'x' in fctx.flags()
530 if from_branch == branch: 554 if from_branch == branch:
531 self.copies[path] = from_file 555 parentid = self.get_parent_revision(self.current_rev.revnum,
556 branch)
557 if parentid != revlog.nullid:
558 parentctx = self.repo.changectx(parentid)
559 if self._aresamefiles(parentctx, ctx, [from_file]):
560 self.copies[path] = from_file
532 561
533 @stash_exception_on_self 562 @stash_exception_on_self
534 def add_directory(self, path, parent_baton, copyfrom_path, 563 def add_directory(self, path, parent_baton, copyfrom_path,
535 copyfrom_revision, dir_pool=None): 564 copyfrom_revision, dir_pool=None):
536 if self._is_path_valid(path): 565 if self._is_path_valid(path):
563 cp_f_ctx = self.repo.changectx(new_hash) 592 cp_f_ctx = self.repo.changectx(new_hash)
564 if cp_f != '/' and cp_f != '': 593 if cp_f != '/' and cp_f != '':
565 cp_f = '%s/' % cp_f 594 cp_f = '%s/' % cp_f
566 else: 595 else:
567 cp_f = '' 596 cp_f = ''
597 copies = {}
568 for f in cp_f_ctx: 598 for f in cp_f_ctx:
569 if f.startswith(cp_f): 599 if not f.startswith(cp_f):
570 f2 = f[len(cp_f):] 600 continue
571 fctx = cp_f_ctx.filectx(f) 601 f2 = f[len(cp_f):]
572 fp_c = path + '/' + f2 602 fctx = cp_f_ctx.filectx(f)
573 self.current_files[fp_c] = fctx.data() 603 fp_c = path + '/' + f2
574 self.current_files_exec[fp_c] = 'x' in fctx.flags() 604 self.current_files[fp_c] = fctx.data()
575 self.current_files_symlink[fp_c] = 'l' in fctx.flags() 605 self.current_files_exec[fp_c] = 'x' in fctx.flags()
576 if fp_c in self.deleted_files: 606 self.current_files_symlink[fp_c] = 'l' in fctx.flags()
577 del self.deleted_files[fp_c] 607 if fp_c in self.deleted_files:
578 if branch == source_branch: 608 del self.deleted_files[fp_c]
579 self.copies[fp_c] = f 609 if branch == source_branch:
610 copies[fp_c] = f
611 if copies:
612 # Preserve the directory copy records if no file was changed between
613 # the source and destination revisions, or discard it completely.
614 parentid = self.get_parent_revision(self.current_rev.revnum, branch)
615 if parentid != revlog.nullid:
616 parentctx = self.repo.changectx(parentid)
617 if self._aresamefiles(parentctx, cp_f_ctx, copies.values()):
618 self.copies.update(copies)
580 619
581 @stash_exception_on_self 620 @stash_exception_on_self
582 def change_file_prop(self, file_baton, name, value, pool=None): 621 def change_file_prop(self, file_baton, name, value, pool=None):
583 if name == 'svn:executable': 622 if name == 'svn:executable':
584 self.current_files_exec[self.current_file] = bool(value) 623 self.current_files_exec[self.current_file] = bool(value)