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