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