Mercurial > hgsubversion
comparison hg_delta_editor.py @ 174:f80132c5fea5
Convert svn:externals properties into a .hgsvnexternals file
| author | Patrick Mezard <pmezard@gmail.com> |
|---|---|
| date | Fri, 02 Jan 2009 15:54:05 -0600 |
| parents | 4f26fa049452 |
| children | c4115b3918e9 |
comparison
equal
deleted
inserted
replaced
| 173:f244eaee5069 | 174:f80132c5fea5 |
|---|---|
| 12 from mercurial import revlog | 12 from mercurial import revlog |
| 13 from mercurial import node | 13 from mercurial import node |
| 14 from svn import delta | 14 from svn import delta |
| 15 from svn import core | 15 from svn import core |
| 16 | 16 |
| 17 import svnexternals | |
| 17 import util as our_util | 18 import util as our_util |
| 18 | 19 |
| 19 def pickle_atomic(data, file_path, dir=None): | 20 def pickle_atomic(data, file_path, dir=None): |
| 20 """pickle some data to a path atomically. | 21 """pickle some data to a path atomically. |
| 21 | 22 |
| 142 self.current_files = {} | 143 self.current_files = {} |
| 143 self.deleted_files = {} | 144 self.deleted_files = {} |
| 144 self.current_rev = None | 145 self.current_rev = None |
| 145 self.current_files_exec = {} | 146 self.current_files_exec = {} |
| 146 self.current_files_symlink = {} | 147 self.current_files_symlink = {} |
| 148 self.dir_batons = {} | |
| 147 # Map fully qualified destination file paths to module source path | 149 # Map fully qualified destination file paths to module source path |
| 148 self.copies = {} | 150 self.copies = {} |
| 149 self.missing_plaintexts = set() | 151 self.missing_plaintexts = set() |
| 150 self.commit_branches_empty = {} | 152 self.commit_branches_empty = {} |
| 151 self.base_revision = None | 153 self.base_revision = None |
| 152 self.branches_to_delete = set() | 154 self.branches_to_delete = set() |
| 155 self.externals = {} | |
| 153 | 156 |
| 154 def _save_metadata(self): | 157 def _save_metadata(self): |
| 155 '''Save the Subversion metadata. This should really be called after | 158 '''Save the Subversion metadata. This should really be called after |
| 156 every revision is created. | 159 every revision is created. |
| 157 ''' | 160 ''' |
| 347 (info[0] or 'trunk', info[1], t)) | 350 (info[0] or 'trunk', info[1], t)) |
| 348 self.tags.update(added_tags) | 351 self.tags.update(added_tags) |
| 349 self.branches.update(added_branches) | 352 self.branches.update(added_branches) |
| 350 self._save_metadata() | 353 self._save_metadata() |
| 351 | 354 |
| 355 def _updateexternals(self): | |
| 356 if not self.externals: | |
| 357 return | |
| 358 # Accumulate externals records for all branches | |
| 359 revnum = self.current_rev.revnum | |
| 360 branches = {} | |
| 361 for path, entry in self.externals.iteritems(): | |
| 362 if not self._is_path_valid(path): | |
| 363 continue | |
| 364 p, b, bp = self._split_branch_path(path) | |
| 365 if bp not in branches: | |
| 366 external = svnexternals.externalsfile() | |
| 367 parent = self.get_parent_revision(revnum, b) | |
| 368 pctx = self.repo[parent] | |
| 369 if '.hgsvnexternals' in pctx: | |
| 370 external.read(pctx['.hgsvnexternals'].data()) | |
| 371 branches[bp] = external | |
| 372 else: | |
| 373 external = branches[bp] | |
| 374 external[p] = entry | |
| 375 | |
| 376 # Register the file changes | |
| 377 for bp, external in branches.iteritems(): | |
| 378 path = bp + '/.hgsvnexternals' | |
| 379 self.current_files[path] = external.write() | |
| 380 self.current_files_symlink[path] = False | |
| 381 self.current_files_exec[path] = False | |
| 382 | |
| 352 def commit_current_delta(self): | 383 def commit_current_delta(self): |
| 353 if hasattr(self, '_exception_info'): #pragma: no cover | 384 if hasattr(self, '_exception_info'): #pragma: no cover |
| 354 traceback.print_exception(*self._exception_info) | 385 traceback.print_exception(*self._exception_info) |
| 355 raise ReplayException() | 386 raise ReplayException() |
| 356 if self.missing_plaintexts: | 387 if self.missing_plaintexts: |
| 357 raise MissingPlainTextError() | 388 raise MissingPlainTextError() |
| 389 self._updateexternals() | |
| 358 files_to_commit = self.current_files.keys() | 390 files_to_commit = self.current_files.keys() |
| 359 files_to_commit.extend(self.current_files_symlink.keys()) | 391 files_to_commit.extend(self.current_files_symlink.keys()) |
| 360 files_to_commit.extend(self.current_files_exec.keys()) | 392 files_to_commit.extend(self.current_files_exec.keys()) |
| 361 files_to_commit = sorted(set(files_to_commit)) | 393 files_to_commit = sorted(set(files_to_commit)) |
| 362 branch_batches = {} | 394 branch_batches = {} |
| 418 if branch is not None: | 450 if branch is not None: |
| 419 if (branch not in self.branches | 451 if (branch not in self.branches |
| 420 and branch not in self.repo.branchtags()): | 452 and branch not in self.repo.branchtags()): |
| 421 continue | 453 continue |
| 422 parent_ctx = self.repo.changectx(parents[0]) | 454 parent_ctx = self.repo.changectx(parents[0]) |
| 455 if '.hgsvnexternals' not in parent_ctx and '.hgsvnexternals' in files: | |
| 456 # Do not register empty externals files | |
| 457 if not self.current_files[files['.hgsvnexternals']]: | |
| 458 del files['.hgsvnexternals'] | |
| 459 | |
| 423 def filectxfn(repo, memctx, path): | 460 def filectxfn(repo, memctx, path): |
| 424 current_file = files[path] | 461 current_file = files[path] |
| 425 if current_file in self.deleted_files: | 462 if current_file in self.deleted_files: |
| 426 raise IOError() | 463 raise IOError() |
| 427 copied = self.copies.get(current_file) | 464 copied = self.copies.get(current_file) |
| 577 if br_path not in ctx: | 614 if br_path not in ctx: |
| 578 br_path2 = '' | 615 br_path2 = '' |
| 579 if br_path != '': | 616 if br_path != '': |
| 580 br_path2 = br_path + '/' | 617 br_path2 = br_path + '/' |
| 581 # assuming it is a directory | 618 # assuming it is a directory |
| 619 self.externals[path] = None | |
| 582 def delete_x(x): | 620 def delete_x(x): |
| 583 self.deleted_files[x] = True | 621 self.deleted_files[x] = True |
| 584 map(delete_x, [pat for pat in self.current_files.iterkeys() | 622 map(delete_x, [pat for pat in self.current_files.iterkeys() |
| 585 if pat.startswith(path)]) | 623 if pat.startswith(path)]) |
| 586 for f in ctx.walk(our_util.PrefixMatch(br_path2)): | 624 for f in ctx.walk(our_util.PrefixMatch(br_path2)): |
| 664 self.copies[path] = from_file | 702 self.copies[path] = from_file |
| 665 | 703 |
| 666 @stash_exception_on_self | 704 @stash_exception_on_self |
| 667 def add_directory(self, path, parent_baton, copyfrom_path, | 705 def add_directory(self, path, parent_baton, copyfrom_path, |
| 668 copyfrom_revision, dir_pool=None): | 706 copyfrom_revision, dir_pool=None): |
| 707 self.dir_batons[path] = path | |
| 669 br_path, branch = self._path_and_branch_for_path(path) | 708 br_path, branch = self._path_and_branch_for_path(path) |
| 670 if br_path is not None: | 709 if br_path is not None: |
| 671 if not copyfrom_path and not br_path: | 710 if not copyfrom_path and not br_path: |
| 672 self.commit_branches_empty[branch] = True | 711 self.commit_branches_empty[branch] = True |
| 673 else: | 712 else: |
| 674 self.commit_branches_empty[branch] = False | 713 self.commit_branches_empty[branch] = False |
| 675 if br_path is None or not copyfrom_path: | 714 if br_path is None or not copyfrom_path: |
| 676 return | 715 return path |
| 677 if copyfrom_path: | 716 if copyfrom_path: |
| 678 tag = self._is_path_tag(copyfrom_path) | 717 tag = self._is_path_tag(copyfrom_path) |
| 679 if tag not in self.tags: | 718 if tag not in self.tags: |
| 680 tag = None | 719 tag = None |
| 681 if not self._is_path_valid(copyfrom_path) and not tag: | 720 if not self._is_path_valid(copyfrom_path) and not tag: |
| 682 self.missing_plaintexts.add('%s/' % path) | 721 self.missing_plaintexts.add('%s/' % path) |
| 683 return | 722 return path |
| 684 | 723 |
| 685 if tag: | 724 if tag: |
| 686 source_branch, source_rev = self.tags[tag] | 725 source_branch, source_rev = self.tags[tag] |
| 687 cp_f = '' | 726 cp_f = '' |
| 688 else: | 727 else: |
| 690 cp_f, source_branch = self._path_and_branch_for_path(copyfrom_path) | 729 cp_f, source_branch = self._path_and_branch_for_path(copyfrom_path) |
| 691 new_hash = self.get_parent_revision(source_rev + 1, | 730 new_hash = self.get_parent_revision(source_rev + 1, |
| 692 source_branch) | 731 source_branch) |
| 693 if new_hash == node.nullid: | 732 if new_hash == node.nullid: |
| 694 self.missing_plaintexts.add('%s/' % path) | 733 self.missing_plaintexts.add('%s/' % path) |
| 695 return | 734 return path |
| 696 cp_f_ctx = self.repo.changectx(new_hash) | 735 cp_f_ctx = self.repo.changectx(new_hash) |
| 697 if cp_f != '/' and cp_f != '': | 736 if cp_f != '/' and cp_f != '': |
| 698 cp_f = '%s/' % cp_f | 737 cp_f = '%s/' % cp_f |
| 699 else: | 738 else: |
| 700 cp_f = '' | 739 cp_f = '' |
| 716 parentid = self.get_parent_revision(self.current_rev.revnum, branch) | 755 parentid = self.get_parent_revision(self.current_rev.revnum, branch) |
| 717 if parentid != revlog.nullid: | 756 if parentid != revlog.nullid: |
| 718 parentctx = self.repo.changectx(parentid) | 757 parentctx = self.repo.changectx(parentid) |
| 719 if self.aresamefiles(parentctx, cp_f_ctx, copies.values()): | 758 if self.aresamefiles(parentctx, cp_f_ctx, copies.values()): |
| 720 self.copies.update(copies) | 759 self.copies.update(copies) |
| 760 return path | |
| 721 | 761 |
| 722 @stash_exception_on_self | 762 @stash_exception_on_self |
| 723 def change_file_prop(self, file_baton, name, value, pool=None): | 763 def change_file_prop(self, file_baton, name, value, pool=None): |
| 724 if name == 'svn:executable': | 764 if name == 'svn:executable': |
| 725 self.current_files_exec[self.current_file] = bool(value is not None) | 765 self.current_files_exec[self.current_file] = bool(value is not None) |
| 726 elif name == 'svn:special': | 766 elif name == 'svn:special': |
| 727 self.current_files_symlink[self.current_file] = bool(value is not None) | 767 self.current_files_symlink[self.current_file] = bool(value is not None) |
| 728 | 768 |
| 729 @stash_exception_on_self | 769 @stash_exception_on_self |
| 770 def change_dir_prop(self, dir_baton, name, value, pool=None): | |
| 771 if dir_baton is None: | |
| 772 return | |
| 773 path = self.dir_batons[dir_baton] | |
| 774 if name == 'svn:externals': | |
| 775 self.externals[path] = value | |
| 776 | |
| 777 @stash_exception_on_self | |
| 730 def open_directory(self, path, parent_baton, base_revision, dir_pool=None): | 778 def open_directory(self, path, parent_baton, base_revision, dir_pool=None): |
| 779 self.dir_batons[path] = path | |
| 731 p_, branch = self._path_and_branch_for_path(path) | 780 p_, branch = self._path_and_branch_for_path(path) |
| 732 if p_ == '': | 781 if p_ == '': |
| 733 self.commit_branches_empty[branch] = False | 782 self.commit_branches_empty[branch] = False |
| 783 return path | |
| 784 | |
| 785 @stash_exception_on_self | |
| 786 def close_directory(self, dir_baton, dir_pool=None): | |
| 787 if dir_baton is not None: | |
| 788 del self.dir_batons[dir_baton] | |
| 734 | 789 |
| 735 @stash_exception_on_self | 790 @stash_exception_on_self |
| 736 def apply_textdelta(self, file_baton, base_checksum, pool=None): | 791 def apply_textdelta(self, file_baton, base_checksum, pool=None): |
| 737 base = '' | 792 base = '' |
| 738 if not self._is_path_valid(self.current_file): | 793 if not self._is_path_valid(self.current_file): |
