Mercurial > hgsubversion
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) |