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