Mercurial > hgsubversion
comparison hg_delta_editor.py @ 211:05243ec295e1
fetch: Fix a bug that caused plaintexts to be interpreted as missing more often than they should be.
Also refactored a large part of hg_delta_editor to make it carry slightly less state around.
author | Augie Fackler <durin42@gmail.com> |
---|---|
date | Mon, 16 Mar 2009 23:19:00 -0500 |
parents | b20a6c149021 |
children | 1416429584b2 2165461d2dd8 |
comparison
equal
deleted
inserted
replaced
210:b81e7f2f7818 | 211:05243ec295e1 |
---|---|
290 self.current_files[path] = data | 290 self.current_files[path] = data |
291 self.current_files_exec[path] = isexec | 291 self.current_files_exec[path] = isexec |
292 self.current_files_symlink[path] = islink | 292 self.current_files_symlink[path] = islink |
293 if path in self.deleted_files: | 293 if path in self.deleted_files: |
294 del self.deleted_files[path] | 294 del self.deleted_files[path] |
295 if path in self.missing_plaintexts: | |
296 self.missing_plaintexts.remove(path) | |
295 | 297 |
296 def delete_file(self, path): | 298 def delete_file(self, path): |
297 self.deleted_files[path] = True | 299 self.deleted_files[path] = True |
298 self.current_files[path] = '' | 300 if path in self.current_files: |
301 del self.current_files[path] | |
299 self.current_files_exec[path] = False | 302 self.current_files_exec[path] = False |
300 self.current_files_symlink[path] = False | 303 self.current_files_symlink[path] = False |
301 self.ui.note('D %s\n' % path) | 304 self.ui.note('D %s\n' % path) |
302 | 305 |
303 def _normalize_path(self, path): | 306 def _normalize_path(self, path): |
520 | 523 |
521 # Register the file changes | 524 # Register the file changes |
522 for bp, external in branches.iteritems(): | 525 for bp, external in branches.iteritems(): |
523 path = bp + '/.hgsvnexternals' | 526 path = bp + '/.hgsvnexternals' |
524 if external: | 527 if external: |
525 self.current_files[path] = external.write() | 528 self.set_file(path, external.write(), False, False) |
526 self.current_files_symlink[path] = False | |
527 self.current_files_exec[path] = False | |
528 else: | 529 else: |
529 self.delete_file(path) | 530 self.delete_file(path) |
530 | 531 |
531 def commit_current_delta(self): | 532 def commit_current_delta(self): |
532 if hasattr(self, '_exception_info'): #pragma: no cover | 533 if hasattr(self, '_exception_info'): #pragma: no cover |
533 traceback.print_exception(*self._exception_info) | 534 traceback.print_exception(*self._exception_info) |
534 raise ReplayException() | 535 raise ReplayException() |
535 if self.missing_plaintexts: | 536 if self.missing_plaintexts: |
536 raise MissingPlainTextError() | 537 raise MissingPlainTextError() |
537 self._updateexternals() | 538 self._updateexternals() |
538 files_to_commit = self.current_files.keys() | 539 # paranoidly generate the list of files to commit |
539 files_to_commit.extend(self.current_files_symlink.keys()) | 540 files_to_commit = set(self.current_files.keys()) |
540 files_to_commit.extend(self.current_files_exec.keys()) | 541 files_to_commit.update(self.current_files_symlink.keys()) |
541 files_to_commit = sorted(set(files_to_commit)) | 542 files_to_commit.update(self.current_files_exec.keys()) |
543 files_to_commit.update(self.deleted_files.keys()) | |
544 # back to a list and sort so we get sane behavior | |
545 files_to_commit = list(files_to_commit) | |
546 files_to_commit.sort() | |
542 branch_batches = {} | 547 branch_batches = {} |
543 rev = self.current_rev | 548 rev = self.current_rev |
544 date = rev.date.replace('T', ' ').replace('Z', '').split('.')[0] | 549 date = rev.date.replace('T', ' ').replace('Z', '').split('.')[0] |
545 date += ' -0000' | 550 date += ' -0000' |
546 | 551 |
600 and branch not in self.repo.branchtags()): | 605 and branch not in self.repo.branchtags()): |
601 continue | 606 continue |
602 parent_ctx = self.repo.changectx(parents[0]) | 607 parent_ctx = self.repo.changectx(parents[0]) |
603 if '.hgsvnexternals' not in parent_ctx and '.hgsvnexternals' in files: | 608 if '.hgsvnexternals' not in parent_ctx and '.hgsvnexternals' in files: |
604 # Do not register empty externals files | 609 # Do not register empty externals files |
605 if not self.current_files[files['.hgsvnexternals']]: | 610 if (files['.hgsvnexternals'] in self.current_files |
611 and not self.current_files[files['.hgsvnexternals']]): | |
606 del files['.hgsvnexternals'] | 612 del files['.hgsvnexternals'] |
607 | 613 |
608 def filectxfn(repo, memctx, path): | 614 def filectxfn(repo, memctx, path): |
609 current_file = files[path] | 615 current_file = files[path] |
610 if current_file in self.deleted_files: | 616 if current_file in self.deleted_files: |
631 filectxfn, | 637 filectxfn, |
632 self.authorforsvnauthor(rev.author), | 638 self.authorforsvnauthor(rev.author), |
633 date, | 639 date, |
634 extra) | 640 extra) |
635 new_hash = self.repo.commitctx(current_ctx) | 641 new_hash = self.repo.commitctx(current_ctx) |
636 | |
637 our_util.describe_commit(self.ui, new_hash, branch) | 642 our_util.describe_commit(self.ui, new_hash, branch) |
638 if (rev.revnum, branch) not in self.revmap: | 643 if (rev.revnum, branch) not in self.revmap: |
639 self.add_to_revmap(rev.revnum, branch, new_hash) | 644 self.add_to_revmap(rev.revnum, branch, new_hash) |
640 # now we handle branches that need to be committed without any files | 645 # now we handle branches that need to be committed without any files |
641 for branch in self.commit_branches_empty: | 646 for branch in self.commit_branches_empty: |
779 | 784 |
780 def authors_file(self): | 785 def authors_file(self): |
781 return self.meta_file_named('authors') | 786 return self.meta_file_named('authors') |
782 authors_file = property(authors_file) | 787 authors_file = property(authors_file) |
783 | 788 |
789 def load_base_from_ctx(self, svnpath, path, ctx): | |
790 if not self._is_path_valid(svnpath): | |
791 return | |
792 if path in ctx: | |
793 fctx = ctx.filectx(path) | |
794 base = fctx.data() | |
795 if 'l' in fctx.flags(): | |
796 base = 'link ' + base | |
797 self.set_file(svnpath, base, 'x' in fctx.flags(), 'l' in fctx.flags()) | |
798 else: | |
799 self.missing_plaintexts.add(path) | |
800 | |
784 def delete_entry(self, path, revision_bogus, parent_baton, pool=None): | 801 def delete_entry(self, path, revision_bogus, parent_baton, pool=None): |
785 br_path, branch = self._path_and_branch_for_path(path) | 802 br_path, branch = self._path_and_branch_for_path(path) |
786 if br_path == '': | 803 if br_path == '': |
787 self.branches_to_delete.add(branch) | 804 self.branches_to_delete.add(branch) |
788 if br_path is not None: | 805 if br_path is not None: |
794 br_path2 = '' | 811 br_path2 = '' |
795 if br_path != '': | 812 if br_path != '': |
796 br_path2 = br_path + '/' | 813 br_path2 = br_path + '/' |
797 # assuming it is a directory | 814 # assuming it is a directory |
798 self.externals[path] = None | 815 self.externals[path] = None |
799 def delete_x(x): | 816 map(self.delete_file, [pat for pat in self.current_files.iterkeys() |
800 self.deleted_files[x] = True | 817 if pat.startswith(path)]) |
801 map(delete_x, [pat for pat in self.current_files.iterkeys() | |
802 if pat.startswith(path)]) | |
803 for f in ctx.walk(our_util.PrefixMatch(br_path2)): | 818 for f in ctx.walk(our_util.PrefixMatch(br_path2)): |
804 f_p = '%s/%s' % (path, f[len(br_path2):]) | 819 f_p = '%s/%s' % (path, f[len(br_path2):]) |
805 if f_p not in self.current_files: | 820 if f_p not in self.current_files: |
806 self.delete_file(f_p) | 821 self.delete_file(f_p) |
807 self.delete_file(path) | 822 self.delete_file(path) |
815 self.ui.note('M %s\n' % path) | 830 self.ui.note('M %s\n' % path) |
816 if base_revision != -1: | 831 if base_revision != -1: |
817 self.base_revision = base_revision | 832 self.base_revision = base_revision |
818 else: | 833 else: |
819 self.base_revision = None | 834 self.base_revision = None |
820 self.should_edit_most_recent_plaintext = True | 835 if self.current_file not in self.current_files: |
836 baserev = base_revision | |
837 if baserev is None or baserev == -1: | |
838 baserev = self.current_rev.revnum - 1 | |
839 parent = self.get_parent_revision(baserev + 1, branch) | |
840 self.load_base_from_ctx(path, fpath, self.repo.changectx(parent)) | |
821 open_file = stash_exception_on_self(open_file) | 841 open_file = stash_exception_on_self(open_file) |
822 | 842 |
823 def aresamefiles(self, parentctx, childctx, files): | 843 def aresamefiles(self, parentctx, childctx, files): |
824 """Assuming all files exist in childctx and parentctx, return True | 844 """Assuming all files exist in childctx and parentctx, return True |
825 if none of them was changed in-between. | 845 if none of them was changed in-between. |
855 return | 875 return |
856 if branch not in self.branches: | 876 if branch not in self.branches: |
857 # we know this branch will exist now, because it has at least one file. Rock. | 877 # we know this branch will exist now, because it has at least one file. Rock. |
858 self.branches[branch] = None, 0, self.current_rev.revnum | 878 self.branches[branch] = None, 0, self.current_rev.revnum |
859 self.current_file = path | 879 self.current_file = path |
860 self.should_edit_most_recent_plaintext = False | |
861 if not copyfrom_path: | 880 if not copyfrom_path: |
862 self.ui.note('A %s\n' % path) | 881 self.ui.note('A %s\n' % path) |
882 self.set_file(path, '', False, False) | |
863 return | 883 return |
864 self.ui.note('A+ %s\n' % path) | 884 self.ui.note('A+ %s\n' % path) |
865 (from_file, | 885 (from_file, |
866 from_branch) = self._path_and_branch_for_path(copyfrom_path) | 886 from_branch) = self._path_and_branch_for_path(copyfrom_path) |
867 if not from_file: | 887 if not from_file: |
871 from_branch) | 891 from_branch) |
872 ctx = self.repo.changectx(ha) | 892 ctx = self.repo.changectx(ha) |
873 if from_file in ctx: | 893 if from_file in ctx: |
874 fctx = ctx.filectx(from_file) | 894 fctx = ctx.filectx(from_file) |
875 flags = fctx.flags() | 895 flags = fctx.flags() |
876 cur_file = self.current_file | 896 self.set_file(path, fctx.data(), 'x' in flags, 'l' in flags) |
877 self.set_file(cur_file, fctx.data(), 'x' in flags, 'l' in flags) | |
878 if from_branch == branch: | 897 if from_branch == branch: |
879 parentid = self.get_parent_revision(self.current_rev.revnum, | 898 parentid = self.get_parent_revision(self.current_rev.revnum, |
880 branch) | 899 branch) |
881 if parentid != revlog.nullid: | 900 if parentid != revlog.nullid: |
882 parentctx = self.repo.changectx(parentid) | 901 parentctx = self.repo.changectx(parentid) |
970 if dir_baton is not None: | 989 if dir_baton is not None: |
971 del self.dir_batons[dir_baton] | 990 del self.dir_batons[dir_baton] |
972 close_directory = stash_exception_on_self(close_directory) | 991 close_directory = stash_exception_on_self(close_directory) |
973 | 992 |
974 def apply_textdelta(self, file_baton, base_checksum, pool=None): | 993 def apply_textdelta(self, file_baton, base_checksum, pool=None): |
994 # We know coming in here the file must be one of the following options: | |
995 # 1) Deleted (invalid, fail an assertion) | |
996 # 2) Missing a base text (bail quick since we have to fetch a full plaintext) | |
997 # 3) Has a base text in self.current_files, apply deltas | |
975 base = '' | 998 base = '' |
976 if not self._is_path_valid(self.current_file): | 999 if not self._is_path_valid(self.current_file): |
977 return lambda x: None | 1000 return lambda x: None |
978 if (self.current_file in self.current_files | 1001 assert self.current_file not in self.deleted_files, ( |
979 and not self.should_edit_most_recent_plaintext): | 1002 'Cannot apply_textdelta to a deleted file: %s' % self.current_file) |
980 base = self.current_files[self.current_file] | 1003 assert (self.current_file in self.current_files |
981 elif (base_checksum is not None or | 1004 or self.current_file in self.missing_plaintexts), '%s not found' % self.current_file |
982 self.should_edit_most_recent_plaintext): | 1005 if self.current_file in self.missing_plaintexts: |
983 p_, br = self._path_and_branch_for_path(self.current_file) | 1006 return lambda x: None |
984 par_rev = self.current_rev.revnum | 1007 base = self.current_files[self.current_file] |
985 if self.base_revision: | |
986 par_rev = self.base_revision + 1 | |
987 ha = self.get_parent_revision(par_rev, br) | |
988 if ha != revlog.nullid: | |
989 ctx = self.repo.changectx(ha) | |
990 if not p_ in ctx: | |
991 self.missing_plaintexts.add(self.current_file) | |
992 # short circuit exit since we can't do anything anyway | |
993 return lambda x: None | |
994 fctx = ctx[p_] | |
995 base = fctx.data() | |
996 if 'l' in fctx.flags(): | |
997 base = 'link ' + base | |
998 source = cStringIO.StringIO(base) | 1008 source = cStringIO.StringIO(base) |
999 target = cStringIO.StringIO() | 1009 target = cStringIO.StringIO() |
1000 self.stream = target | 1010 self.stream = target |
1001 | 1011 |
1002 handler, baton = delta.svn_txdelta_apply(source, target, None) | 1012 handler, baton = delta.svn_txdelta_apply(source, target, None) |