comparison hgsubversion/hg_delta_editor.py @ 411:d71972428fce

editor: move current revision state into a separate class This makes it more obvious and needs fewer faux namespacing.
author Dirkjan Ochtman <dirkjan@ochtman.nl>
date Thu, 11 Jun 2009 10:12:16 +0200
parents eb524b957345
children ac0cc3c9ea63
comparison
equal deleted inserted replaced
410:eb524b957345 411:d71972428fce
61 except: #pragma: no cover 61 except: #pragma: no cover
62 if not hasattr(self, '_exception_info'): 62 if not hasattr(self, '_exception_info'):
63 self._exception_info = sys.exc_info() 63 self._exception_info = sys.exc_info()
64 raise 64 raise
65 return fun 65 return fun
66
67
68 class RevisionData(object):
69
70 __slots__ = [
71 'file', 'files', 'deleted', 'rev', 'execfiles', 'symlinks', 'batons',
72 'copies', 'missing', 'emptybranches', 'base', 'closebranches',
73 'externals',
74 ]
75
76 def __init__(self):
77 self.clear()
78
79 def clear(self):
80 self.file = None
81 self.files = {}
82 self.deleted = {}
83 self.rev = None
84 self.execfiles = {}
85 self.symlinks = {}
86 self.batons = {}
87 # Map fully qualified destination file paths to module source path
88 self.copies = {}
89 self.missing = set()
90 self.emptybranches = {}
91 self.base = None
92 self.closebranches = set()
93 self.externals = {}
66 94
67 95
68 class HgChangeReceiver(delta.Editor): 96 class HgChangeReceiver(delta.Editor):
69 97
70 def last_known_revision(self): 98 def last_known_revision(self):
121 self.meta_data_dir) 149 self.meta_data_dir)
122 # ensure nested paths are handled properly 150 # ensure nested paths are handled properly
123 self.tag_locations.sort() 151 self.tag_locations.sort()
124 self.tag_locations.reverse() 152 self.tag_locations.reverse()
125 153
126 self.clear_current_info() 154 self.current = RevisionData()
127 self.authors = maps.AuthorMap(self.ui, self.authors_file, 155 self.authors = maps.AuthorMap(self.ui, self.authors_file,
128 defaulthost=author_host) 156 defaulthost=author_host)
129 if authors: self.authors.load(authors) 157 if authors: self.authors.load(authors)
130 158
131 self.lastdate = '1970-01-01 00:00:00 -0000' 159 self.lastdate = '1970-01-01 00:00:00 -0000'
140 date += ' -0000' 168 date += ' -0000'
141 self.lastdate = date 169 self.lastdate = date
142 else: 170 else:
143 date = self.lastdate 171 date = self.lastdate
144 return date 172 return date
145
146 def clear_current_info(self):
147 '''Clear the info relevant to a replayed revision so that the next
148 revision can be replayed.
149 '''
150 # Map files to raw svn data (symlink prefix is preserved)
151 self.current_files = {}
152 self.deleted_files = {}
153 self.current_rev = None
154 self.current_files_exec = {}
155 self.current_files_symlink = {}
156 self.dir_batons = {}
157 # Map fully qualified destination file paths to module source path
158 self.copies = {}
159 self.missing_plaintexts = set()
160 self.commit_branches_empty = {}
161 self.base_revision = None
162 self.branches_to_delete = set()
163 self.externals = {}
164 173
165 def _save_metadata(self): 174 def _save_metadata(self):
166 '''Save the Subversion metadata. This should really be called after 175 '''Save the Subversion metadata. This should really be called after
167 every revision is created. 176 every revision is created.
168 ''' 177 '''
228 ln = self._localname(test) 237 ln = self._localname(test)
229 if ln and ln.startswith('../'): 238 if ln and ln.startswith('../'):
230 return None, None, None 239 return None, None, None
231 return path, ln, test 240 return path, ln, test
232 241
233 def set_current_rev(self, rev):
234 """Set the revision we're currently converting.
235 """
236 self.current_rev = rev
237
238 def set_file(self, path, data, isexec=False, islink=False): 242 def set_file(self, path, data, isexec=False, islink=False):
239 if islink: 243 if islink:
240 data = 'link ' + data 244 data = 'link ' + data
241 self.current_files[path] = data 245 self.current.files[path] = data
242 self.current_files_exec[path] = isexec 246 self.current.execfiles[path] = isexec
243 self.current_files_symlink[path] = islink 247 self.current.symlinks[path] = islink
244 if path in self.deleted_files: 248 if path in self.current.deleted:
245 del self.deleted_files[path] 249 del self.current.deleted[path]
246 if path in self.missing_plaintexts: 250 if path in self.current.missing:
247 self.missing_plaintexts.remove(path) 251 self.current.missing.remove(path)
248 252
249 def delete_file(self, path): 253 def delete_file(self, path):
250 self.deleted_files[path] = True 254 self.current.deleted[path] = True
251 if path in self.current_files: 255 if path in self.current.files:
252 del self.current_files[path] 256 del self.current.files[path]
253 self.current_files_exec[path] = False 257 self.current.execfiles[path] = False
254 self.current_files_symlink[path] = False 258 self.current.symlinks[path] = False
255 self.ui.note('D %s\n' % path) 259 self.ui.note('D %s\n' % path)
256 260
257 def _normalize_path(self, path): 261 def _normalize_path(self, path):
258 '''Normalize a path to strip of leading slashes and our subdir if we 262 '''Normalize a path to strip of leading slashes and our subdir if we
259 have one. 263 have one.
353 357
354 def update_branch_tag_map_for_rev(self, revision): 358 def update_branch_tag_map_for_rev(self, revision):
355 paths = revision.paths 359 paths = revision.paths
356 added_branches = {} 360 added_branches = {}
357 added_tags = {} 361 added_tags = {}
358 self.branches_to_delete = set() 362 self.current.closebranches = set()
359 tags_to_delete = set() 363 tags_to_delete = set()
360 for p in sorted(paths): 364 for p in sorted(paths):
361 t_name = self._is_path_tag(p) 365 t_name = self._is_path_tag(p)
362 if t_name != False: 366 if t_name != False:
363 src_p, src_rev = paths[p].copyfrom_path, paths[p].copyfrom_rev 367 src_p, src_rev = paths[p].copyfrom_path, paths[p].copyfrom_rev
406 # action will be 'R'. 410 # action will be 'R'.
407 fi, br = self._path_and_branch_for_path(p) 411 fi, br = self._path_and_branch_for_path(p)
408 if fi is not None: 412 if fi is not None:
409 if fi == '': 413 if fi == '':
410 if paths[p].action == 'D': 414 if paths[p].action == 'D':
411 self.branches_to_delete.add(br) # case 4 415 self.current.closebranches.add(br) # case 4
412 elif paths[p].action == 'R': 416 elif paths[p].action == 'R':
413 parent = self._determine_parent_branch( 417 parent = self._determine_parent_branch(
414 p, paths[p].copyfrom_path, paths[p].copyfrom_rev, 418 p, paths[p].copyfrom_path, paths[p].copyfrom_rev,
415 revision.revnum) 419 revision.revnum)
416 added_branches.update(parent) 420 added_branches.update(parent)
417 continue # case 1 421 continue # case 1
418 if paths[p].action == 'D': 422 if paths[p].action == 'D':
419 for known in self.branches: 423 for known in self.branches:
420 if self._svnpath(known).startswith(p): 424 if self._svnpath(known).startswith(p):
421 self.branches_to_delete.add(known) # case 5 425 self.current.closebranches.add(known) # case 5
422 parent = self._determine_parent_branch( 426 parent = self._determine_parent_branch(
423 p, paths[p].copyfrom_path, paths[p].copyfrom_rev, revision.revnum) 427 p, paths[p].copyfrom_path, paths[p].copyfrom_rev, revision.revnum)
424 if not parent and paths[p].copyfrom_path: 428 if not parent and paths[p].copyfrom_path:
425 bpath, branch = self._path_and_branch_for_path(p, False) 429 bpath, branch = self._path_and_branch_for_path(p, False)
426 if (bpath is not None 430 if (bpath is not None
429 parent = {branch: (None, 0, revision.revnum)} 433 parent = {branch: (None, 0, revision.revnum)}
430 added_branches.update(parent) 434 added_branches.update(parent)
431 rmtags = dict((t, self.tags[t][0]) for t in tags_to_delete) 435 rmtags = dict((t, self.tags[t][0]) for t in tags_to_delete)
432 return { 436 return {
433 'tags': (added_tags, rmtags), 437 'tags': (added_tags, rmtags),
434 'branches': (added_branches, self.branches_to_delete), 438 'branches': (added_branches, self.current.closebranches),
435 } 439 }
436 440
437 def save_tbdelta(self, tbdelta): 441 def save_tbdelta(self, tbdelta):
438 for t in tbdelta['tags'][1]: 442 for t in tbdelta['tags'][1]:
439 del self.tags[t] 443 del self.tags[t]
444 (info[0] or 'trunk', info[1], t)) 448 (info[0] or 'trunk', info[1], t))
445 self.tags.update(tbdelta['tags'][0]) 449 self.tags.update(tbdelta['tags'][0])
446 self.branches.update(tbdelta['branches'][0]) 450 self.branches.update(tbdelta['branches'][0])
447 451
448 def _updateexternals(self): 452 def _updateexternals(self):
449 if not self.externals: 453 if not self.current.externals:
450 return 454 return
451 # Accumulate externals records for all branches 455 # Accumulate externals records for all branches
452 revnum = self.current_rev.revnum 456 revnum = self.current.rev.revnum
453 branches = {} 457 branches = {}
454 for path, entry in self.externals.iteritems(): 458 for path, entry in self.current.externals.iteritems():
455 if not self._is_path_valid(path): 459 if not self._is_path_valid(path):
456 self.ui.warn('WARNING: Invalid path %s in externals\n' % path) 460 self.ui.warn('WARNING: Invalid path %s in externals\n' % path)
457 continue 461 continue
458 p, b, bp = self._split_branch_path(path) 462 p, b, bp = self._split_branch_path(path)
459 if bp not in branches: 463 if bp not in branches:
534 538
535 def commit_current_delta(self, tbdelta): 539 def commit_current_delta(self, tbdelta):
536 if hasattr(self, '_exception_info'): #pragma: no cover 540 if hasattr(self, '_exception_info'): #pragma: no cover
537 traceback.print_exception(*self._exception_info) 541 traceback.print_exception(*self._exception_info)
538 raise ReplayException() 542 raise ReplayException()
539 if self.missing_plaintexts: 543 if self.current.missing:
540 raise MissingPlainTextError() 544 raise MissingPlainTextError()
541 self._updateexternals() 545 self._updateexternals()
542 # paranoidly generate the list of files to commit 546 # paranoidly generate the list of files to commit
543 files_to_commit = set(self.current_files.keys()) 547 files_to_commit = set(self.current.files.keys())
544 files_to_commit.update(self.current_files_symlink.keys()) 548 files_to_commit.update(self.current.symlinks.keys())
545 files_to_commit.update(self.current_files_exec.keys()) 549 files_to_commit.update(self.current.execfiles.keys())
546 files_to_commit.update(self.deleted_files.keys()) 550 files_to_commit.update(self.current.deleted.keys())
547 # back to a list and sort so we get sane behavior 551 # back to a list and sort so we get sane behavior
548 files_to_commit = list(files_to_commit) 552 files_to_commit = list(files_to_commit)
549 files_to_commit.sort() 553 files_to_commit.sort()
550 branch_batches = {} 554 branch_batches = {}
551 rev = self.current_rev 555 rev = self.current.rev
552 date = self.fixdate(rev.date) 556 date = self.fixdate(rev.date)
553 557
554 # build up the branches that have files on them 558 # build up the branches that have files on them
555 for f in files_to_commit: 559 for f in files_to_commit:
556 if not self._is_path_valid(f): 560 if not self._is_path_valid(f):
570 closebranches[branch] = ha 574 closebranches[branch] = ha
571 575
572 # 1. handle normal commits 576 # 1. handle normal commits
573 closedrevs = closebranches.values() 577 closedrevs = closebranches.values()
574 for branch, files in branch_batches.iteritems(): 578 for branch, files in branch_batches.iteritems():
575 if branch in self.commit_branches_empty and files: 579 if branch in self.current.emptybranches and files:
576 del self.commit_branches_empty[branch] 580 del self.current.emptybranches[branch]
577 files = dict(files) 581 files = dict(files)
578 582
579 parents = (self.get_parent_revision(rev.revnum, branch), 583 parents = (self.get_parent_revision(rev.revnum, branch),
580 revlog.nullid) 584 revlog.nullid)
581 if parents[0] in closedrevs and branch in self.branches_to_delete: 585 if parents[0] in closedrevs and branch in self.current.closebranches:
582 continue 586 continue
583 extra = util.build_extra(rev.revnum, branch, self.uuid, self.subdir) 587 extra = util.build_extra(rev.revnum, branch, self.uuid, self.subdir)
584 if branch is not None: 588 if branch is not None:
585 if (branch not in self.branches 589 if (branch not in self.branches
586 and branch not in self.repo.branchtags()): 590 and branch not in self.repo.branchtags()):
587 continue 591 continue
588 parent_ctx = self.repo.changectx(parents[0]) 592 parent_ctx = self.repo.changectx(parents[0])
589 if '.hgsvnexternals' not in parent_ctx and '.hgsvnexternals' in files: 593 if '.hgsvnexternals' not in parent_ctx and '.hgsvnexternals' in files:
590 # Do not register empty externals files 594 # Do not register empty externals files
591 if (files['.hgsvnexternals'] in self.current_files 595 if (files['.hgsvnexternals'] in self.current.files
592 and not self.current_files[files['.hgsvnexternals']]): 596 and not self.current.files[files['.hgsvnexternals']]):
593 del files['.hgsvnexternals'] 597 del files['.hgsvnexternals']
594 598
595 def filectxfn(repo, memctx, path): 599 def filectxfn(repo, memctx, path):
596 current_file = files[path] 600 current_file = files[path]
597 if current_file in self.deleted_files: 601 if current_file in self.current.deleted:
598 raise IOError() 602 raise IOError()
599 copied = self.copies.get(current_file) 603 copied = self.current.copies.get(current_file)
600 flags = parent_ctx.flags(path) 604 flags = parent_ctx.flags(path)
601 is_exec = self.current_files_exec.get(current_file, 'x' in flags) 605 is_exec = self.current.execfiles.get(current_file, 'x' in flags)
602 is_link = self.current_files_symlink.get(current_file, 'l' in flags) 606 is_link = self.current.symlinks.get(current_file, 'l' in flags)
603 if current_file in self.current_files: 607 if current_file in self.current.files:
604 data = self.current_files[current_file] 608 data = self.current.files[current_file]
605 if is_link and data.startswith('link '): 609 if is_link and data.startswith('link '):
606 data = data[len('link '):] 610 data = data[len('link '):]
607 elif is_link: 611 elif is_link:
608 self.ui.warn('file marked as link, but contains data: ' 612 self.ui.warn('file marked as link, but contains data: '
609 '%s (%r)\n' % (current_file, flags)) 613 '%s (%r)\n' % (current_file, flags))
627 util.describe_commit(self.ui, new_hash, branch) 631 util.describe_commit(self.ui, new_hash, branch)
628 if (rev.revnum, branch) not in self.revmap: 632 if (rev.revnum, branch) not in self.revmap:
629 self.revmap[rev.revnum, branch] = new_hash 633 self.revmap[rev.revnum, branch] = new_hash
630 634
631 # 2. handle branches that need to be committed without any files 635 # 2. handle branches that need to be committed without any files
632 for branch in self.commit_branches_empty: 636 for branch in self.current.emptybranches:
633 ha = self.get_parent_revision(rev.revnum, branch) 637 ha = self.get_parent_revision(rev.revnum, branch)
634 if ha == node.nullid: 638 if ha == node.nullid:
635 continue 639 continue
636 parent_ctx = self.repo.changectx(ha) 640 parent_ctx = self.repo.changectx(ha)
637 def del_all_files(*args): 641 def del_all_files(*args):
638 raise IOError 642 raise IOError
639 # True here meant nuke all files, shouldn't happen with branch closing 643 # True here meant nuke all files, shouldn't happen with branch closing
640 if self.commit_branches_empty[branch]: #pragma: no cover 644 if self.current.emptybranches[branch]: #pragma: no cover
641 raise hgutil.Abort('Empty commit to an open branch attempted. ' 645 raise hgutil.Abort('Empty commit to an open branch attempted. '
642 'Please report this issue.') 646 'Please report this issue.')
643 extra = util.build_extra(rev.revnum, branch, self.uuid, self.subdir) 647 extra = util.build_extra(rev.revnum, branch, self.uuid, self.subdir)
644 if not self.usebranchnames: 648 if not self.usebranchnames:
645 extra.pop('branch', None) 649 extra.pop('branch', None)
665 if parent is None: 669 if parent is None:
666 continue 670 continue
667 self.delbranch(branch, parent, rev) 671 self.delbranch(branch, parent, rev)
668 672
669 self._save_metadata() 673 self._save_metadata()
670 self.clear_current_info() 674 self.current.clear()
671 675
672 def delbranch(self, branch, node, rev): 676 def delbranch(self, branch, node, rev):
673 pctx = self.repo[node] 677 pctx = self.repo[node]
674 files = pctx.manifest().keys() 678 files = pctx.manifest().keys()
675 extra = {'close': 1} 679 extra = {'close': 1}
729 733
730 @ieditor 734 @ieditor
731 def delete_entry(self, path, revision_bogus, parent_baton, pool=None): 735 def delete_entry(self, path, revision_bogus, parent_baton, pool=None):
732 br_path, branch = self._path_and_branch_for_path(path) 736 br_path, branch = self._path_and_branch_for_path(path)
733 if br_path == '': 737 if br_path == '':
734 self.branches_to_delete.add(branch) 738 self.current.closebranches.add(branch)
735 if br_path is not None: 739 if br_path is not None:
736 ha = self.get_parent_revision(self.current_rev.revnum, branch) 740 ha = self.get_parent_revision(self.current.rev.revnum, branch)
737 if ha == revlog.nullid: 741 if ha == revlog.nullid:
738 return 742 return
739 ctx = self.repo.changectx(ha) 743 ctx = self.repo.changectx(ha)
740 if br_path not in ctx: 744 if br_path not in ctx:
741 br_path2 = '' 745 br_path2 = ''
742 if br_path != '': 746 if br_path != '':
743 br_path2 = br_path + '/' 747 br_path2 = br_path + '/'
744 # assuming it is a directory 748 # assuming it is a directory
745 self.externals[path] = None 749 self.current.externals[path] = None
746 map(self.delete_file, [pat for pat in self.current_files.iterkeys() 750 map(self.delete_file, [pat for pat in self.current.files.iterkeys()
747 if pat.startswith(path+'/')]) 751 if pat.startswith(path+'/')])
748 for f in ctx.walk(util.PrefixMatch(br_path2)): 752 for f in ctx.walk(util.PrefixMatch(br_path2)):
749 f_p = '%s/%s' % (path, f[len(br_path2):]) 753 f_p = '%s/%s' % (path, f[len(br_path2):])
750 if f_p not in self.current_files: 754 if f_p not in self.current.files:
751 self.delete_file(f_p) 755 self.delete_file(f_p)
752 self.delete_file(path) 756 self.delete_file(path)
753 757
754 @ieditor 758 @ieditor
755 def open_file(self, path, parent_baton, base_revision, p=None): 759 def open_file(self, path, parent_baton, base_revision, p=None):
756 self.current_file = None 760 self.current.file = None
757 fpath, branch = self._path_and_branch_for_path(path) 761 fpath, branch = self._path_and_branch_for_path(path)
758 if not fpath: 762 if not fpath:
759 self.ui.debug('WARNING: Opening non-existant file %s\n' % path) 763 self.ui.debug('WARNING: Opening non-existant file %s\n' % path)
760 return 764 return
761 765
762 self.current_file = path 766 self.current.file = path
763 self.ui.note('M %s\n' % path) 767 self.ui.note('M %s\n' % path)
764 if base_revision != -1: 768 if base_revision != -1:
765 self.base_revision = base_revision 769 self.current.base = base_revision
766 else: 770 else:
767 self.base_revision = None 771 self.current.base = None
768 772
769 if self.current_file in self.current_files: 773 if self.current.file in self.current.files:
770 return 774 return
771 775
772 baserev = base_revision 776 baserev = base_revision
773 if baserev is None or baserev == -1: 777 if baserev is None or baserev == -1:
774 baserev = self.current_rev.revnum - 1 778 baserev = self.current.rev.revnum - 1
775 parent = self.get_parent_revision(baserev + 1, branch) 779 parent = self.get_parent_revision(baserev + 1, branch)
776 780
777 ctx = self.repo[parent] 781 ctx = self.repo[parent]
778 if not self._is_path_valid(path): 782 if not self._is_path_valid(path):
779 return 783 return
780 784
781 if fpath not in ctx: 785 if fpath not in ctx:
782 self.missing_plaintexts.add(path) 786 self.current.missing.add(path)
783 787
784 fctx = ctx.filectx(fpath) 788 fctx = ctx.filectx(fpath)
785 base = fctx.data() 789 base = fctx.data()
786 if 'l' in fctx.flags(): 790 if 'l' in fctx.flags():
787 base = 'link ' + base 791 base = 'link ' + base
788 self.set_file(path, base, 'x' in fctx.flags(), 'l' in fctx.flags()) 792 self.set_file(path, base, 'x' in fctx.flags(), 'l' in fctx.flags())
789 793
790 @ieditor 794 @ieditor
791 def add_file(self, path, parent_baton=None, copyfrom_path=None, 795 def add_file(self, path, parent_baton=None, copyfrom_path=None,
792 copyfrom_revision=None, file_pool=None): 796 copyfrom_revision=None, file_pool=None):
793 self.current_file = None 797 self.current.file = None
794 self.base_revision = None 798 self.current.base = None
795 if path in self.deleted_files: 799 if path in self.current.deleted:
796 del self.deleted_files[path] 800 del self.current.deleted[path]
797 fpath, branch = self._path_and_branch_for_path(path, existing=False) 801 fpath, branch = self._path_and_branch_for_path(path, existing=False)
798 if not fpath: 802 if not fpath:
799 return 803 return
800 if branch not in self.branches: 804 if branch not in self.branches:
801 # we know this branch will exist now, because it has at least one file. Rock. 805 # we know this branch will exist now, because it has at least one file. Rock.
802 self.branches[branch] = None, 0, self.current_rev.revnum 806 self.branches[branch] = None, 0, self.current.rev.revnum
803 self.current_file = path 807 self.current.file = path
804 if not copyfrom_path: 808 if not copyfrom_path:
805 self.ui.note('A %s\n' % path) 809 self.ui.note('A %s\n' % path)
806 self.set_file(path, '', False, False) 810 self.set_file(path, '', False, False)
807 return 811 return
808 self.ui.note('A+ %s\n' % path) 812 self.ui.note('A+ %s\n' % path)
809 (from_file, 813 (from_file,
810 from_branch) = self._path_and_branch_for_path(copyfrom_path) 814 from_branch) = self._path_and_branch_for_path(copyfrom_path)
811 if not from_file: 815 if not from_file:
812 self.missing_plaintexts.add(path) 816 self.current.missing.add(path)
813 return 817 return
814 ha = self.get_parent_revision(copyfrom_revision + 1, 818 ha = self.get_parent_revision(copyfrom_revision + 1,
815 from_branch) 819 from_branch)
816 ctx = self.repo.changectx(ha) 820 ctx = self.repo.changectx(ha)
817 if from_file in ctx: 821 if from_file in ctx:
818 fctx = ctx.filectx(from_file) 822 fctx = ctx.filectx(from_file)
819 flags = fctx.flags() 823 flags = fctx.flags()
820 self.set_file(path, fctx.data(), 'x' in flags, 'l' in flags) 824 self.set_file(path, fctx.data(), 'x' in flags, 'l' in flags)
821 if from_branch == branch: 825 if from_branch == branch:
822 parentid = self.get_parent_revision(self.current_rev.revnum, 826 parentid = self.get_parent_revision(self.current.rev.revnum,
823 branch) 827 branch)
824 if parentid != revlog.nullid: 828 if parentid != revlog.nullid:
825 parentctx = self.repo.changectx(parentid) 829 parentctx = self.repo.changectx(parentid)
826 if util.aresamefiles(parentctx, ctx, [from_file]): 830 if util.aresamefiles(parentctx, ctx, [from_file]):
827 self.copies[path] = from_file 831 self.current.copies[path] = from_file
828 832
829 @ieditor 833 @ieditor
830 def add_directory(self, path, parent_baton, copyfrom_path, 834 def add_directory(self, path, parent_baton, copyfrom_path,
831 copyfrom_revision, dir_pool=None): 835 copyfrom_revision, dir_pool=None):
832 self.dir_batons[path] = path 836 self.current.batons[path] = path
833 br_path, branch = self._path_and_branch_for_path(path) 837 br_path, branch = self._path_and_branch_for_path(path)
834 if br_path is not None: 838 if br_path is not None:
835 if not copyfrom_path and not br_path: 839 if not copyfrom_path and not br_path:
836 self.commit_branches_empty[branch] = True 840 self.current.emptybranches[branch] = True
837 else: 841 else:
838 self.commit_branches_empty[branch] = False 842 self.current.emptybranches[branch] = False
839 if br_path is None or not copyfrom_path: 843 if br_path is None or not copyfrom_path:
840 return path 844 return path
841 if copyfrom_path: 845 if copyfrom_path:
842 tag = self._is_path_tag(copyfrom_path) 846 tag = self._is_path_tag(copyfrom_path)
843 if tag not in self.tags: 847 if tag not in self.tags:
844 tag = None 848 tag = None
845 if not self._is_path_valid(copyfrom_path) and not tag: 849 if not self._is_path_valid(copyfrom_path) and not tag:
846 self.missing_plaintexts.add('%s/' % path) 850 self.current.missing.add('%s/' % path)
847 return path 851 return path
848 if tag: 852 if tag:
849 source_branch, source_rev = self.tags[tag] 853 source_branch, source_rev = self.tags[tag]
850 cp_f = '' 854 cp_f = ''
851 else: 855 else:
852 source_rev = copyfrom_revision 856 source_rev = copyfrom_revision
853 cp_f, source_branch = self._path_and_branch_for_path(copyfrom_path) 857 cp_f, source_branch = self._path_and_branch_for_path(copyfrom_path)
854 if cp_f == '' and br_path == '': 858 if cp_f == '' and br_path == '':
855 assert br_path is not None 859 assert br_path is not None
856 self.branches[branch] = source_branch, source_rev, self.current_rev.revnum 860 self.branches[branch] = source_branch, source_rev, self.current.rev.revnum
857 new_hash = self.get_parent_revision(source_rev + 1, 861 new_hash = self.get_parent_revision(source_rev + 1,
858 source_branch) 862 source_branch)
859 if new_hash == node.nullid: 863 if new_hash == node.nullid:
860 self.missing_plaintexts.add('%s/' % path) 864 self.current.missing.add('%s/' % path)
861 return path 865 return path
862 cp_f_ctx = self.repo.changectx(new_hash) 866 cp_f_ctx = self.repo.changectx(new_hash)
863 if cp_f != '/' and cp_f != '': 867 if cp_f != '/' and cp_f != '':
864 cp_f = '%s/' % cp_f 868 cp_f = '%s/' % cp_f
865 else: 869 else:
870 continue 874 continue
871 f2 = f[len(cp_f):] 875 f2 = f[len(cp_f):]
872 fctx = cp_f_ctx.filectx(f) 876 fctx = cp_f_ctx.filectx(f)
873 fp_c = path + '/' + f2 877 fp_c = path + '/' + f2
874 self.set_file(fp_c, fctx.data(), 'x' in fctx.flags(), 'l' in fctx.flags()) 878 self.set_file(fp_c, fctx.data(), 'x' in fctx.flags(), 'l' in fctx.flags())
875 if fp_c in self.deleted_files: 879 if fp_c in self.current.deleted:
876 del self.deleted_files[fp_c] 880 del self.current.deleted[fp_c]
877 if branch == source_branch: 881 if branch == source_branch:
878 copies[fp_c] = f 882 copies[fp_c] = f
879 if copies: 883 if copies:
880 # Preserve the directory copy records if no file was changed between 884 # Preserve the directory copy records if no file was changed between
881 # the source and destination revisions, or discard it completely. 885 # the source and destination revisions, or discard it completely.
882 parentid = self.get_parent_revision(self.current_rev.revnum, branch) 886 parentid = self.get_parent_revision(self.current.rev.revnum, branch)
883 if parentid != revlog.nullid: 887 if parentid != revlog.nullid:
884 parentctx = self.repo.changectx(parentid) 888 parentctx = self.repo.changectx(parentid)
885 if util.aresamefiles(parentctx, cp_f_ctx, copies.values()): 889 if util.aresamefiles(parentctx, cp_f_ctx, copies.values()):
886 self.copies.update(copies) 890 self.current.copies.update(copies)
887 return path 891 return path
888 892
889 @ieditor 893 @ieditor
890 def change_file_prop(self, file_baton, name, value, pool=None): 894 def change_file_prop(self, file_baton, name, value, pool=None):
891 if name == 'svn:executable': 895 if name == 'svn:executable':
892 self.current_files_exec[self.current_file] = bool(value is not None) 896 self.current.execfiles[self.current.file] = bool(value is not None)
893 elif name == 'svn:special': 897 elif name == 'svn:special':
894 self.current_files_symlink[self.current_file] = bool(value is not None) 898 self.current.symlinks[self.current.file] = bool(value is not None)
895 899
896 @ieditor 900 @ieditor
897 def change_dir_prop(self, dir_baton, name, value, pool=None): 901 def change_dir_prop(self, dir_baton, name, value, pool=None):
898 if dir_baton is None: 902 if dir_baton is None:
899 return 903 return
900 path = self.dir_batons[dir_baton] 904 path = self.current.batons[dir_baton]
901 if name == 'svn:externals': 905 if name == 'svn:externals':
902 self.externals[path] = value 906 self.current.externals[path] = value
903 907
904 @ieditor 908 @ieditor
905 def open_directory(self, path, parent_baton, base_revision, dir_pool=None): 909 def open_directory(self, path, parent_baton, base_revision, dir_pool=None):
906 self.dir_batons[path] = path 910 self.current.batons[path] = path
907 p_, branch = self._path_and_branch_for_path(path) 911 p_, branch = self._path_and_branch_for_path(path)
908 if p_ == '': 912 if p_ == '':
909 self.commit_branches_empty[branch] = False 913 self.current.emptybranches[branch] = False
910 return path 914 return path
911 915
912 @ieditor 916 @ieditor
913 def close_directory(self, dir_baton, dir_pool=None): 917 def close_directory(self, dir_baton, dir_pool=None):
914 if dir_baton is not None: 918 if dir_baton is not None:
915 del self.dir_batons[dir_baton] 919 del self.current.batons[dir_baton]
916 920
917 @ieditor 921 @ieditor
918 def apply_textdelta(self, file_baton, base_checksum, pool=None): 922 def apply_textdelta(self, file_baton, base_checksum, pool=None):
919 # We know coming in here the file must be one of the following options: 923 # We know coming in here the file must be one of the following options:
920 # 1) Deleted (invalid, fail an assertion) 924 # 1) Deleted (invalid, fail an assertion)
921 # 2) Missing a base text (bail quick since we have to fetch a full plaintext) 925 # 2) Missing a base text (bail quick since we have to fetch a full plaintext)
922 # 3) Has a base text in self.current_files, apply deltas 926 # 3) Has a base text in self.current.files, apply deltas
923 base = '' 927 base = ''
924 if not self._is_path_valid(self.current_file): 928 if not self._is_path_valid(self.current.file):
925 return lambda x: None 929 return lambda x: None
926 assert self.current_file not in self.deleted_files, ( 930 assert self.current.file not in self.current.deleted, (
927 'Cannot apply_textdelta to a deleted file: %s' % self.current_file) 931 'Cannot apply_textdelta to a deleted file: %s' % self.current.file)
928 assert (self.current_file in self.current_files 932 assert (self.current.file in self.current.files
929 or self.current_file in self.missing_plaintexts), '%s not found' % self.current_file 933 or self.current.file in self.current.missing), '%s not found' % self.current.file
930 if self.current_file in self.missing_plaintexts: 934 if self.current.file in self.current.missing:
931 return lambda x: None 935 return lambda x: None
932 base = self.current_files[self.current_file] 936 base = self.current.files[self.current.file]
933 source = cStringIO.StringIO(base) 937 source = cStringIO.StringIO(base)
934 target = cStringIO.StringIO() 938 target = cStringIO.StringIO()
935 self.stream = target 939 self.stream = target
936 940
937 handler, baton = delta.svn_txdelta_apply(source, target, None) 941 handler, baton = delta.svn_txdelta_apply(source, target, None)
938 if not callable(handler): #pragma: no cover 942 if not callable(handler): #pragma: no cover
939 raise hgutil.Abort('Error in Subversion bindings: ' 943 raise hgutil.Abort('Error in Subversion bindings: '
940 'cannot call handler!') 944 'cannot call handler!')
941 def txdelt_window(window): 945 def txdelt_window(window):
942 try: 946 try:
943 if not self._is_path_valid(self.current_file): 947 if not self._is_path_valid(self.current.file):
944 return 948 return
945 handler(window, baton) 949 handler(window, baton)
946 # window being None means commit this file 950 # window being None means commit this file
947 if not window: 951 if not window:
948 self.current_files[self.current_file] = target.getvalue() 952 self.current.files[self.current.file] = target.getvalue()
949 except core.SubversionException, e: #pragma: no cover 953 except core.SubversionException, e: #pragma: no cover
950 if e.apr_err == core.SVN_ERR_INCOMPLETE_DATA: 954 if e.apr_err == core.SVN_ERR_INCOMPLETE_DATA:
951 self.missing_plaintexts.add(self.current_file) 955 self.current.missing.add(self.current.file)
952 else: #pragma: no cover 956 else: #pragma: no cover
953 raise hgutil.Abort(*e.args) 957 raise hgutil.Abort(*e.args)
954 except: #pragma: no cover 958 except: #pragma: no cover
955 print len(base), self.current_file 959 print len(base), self.current.file
956 self._exception_info = sys.exc_info() 960 self._exception_info = sys.exc_info()
957 raise 961 raise
958 return txdelt_window 962 return txdelt_window