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)