diff 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
line wrap: on
line diff
--- a/hg_delta_editor.py
+++ b/hg_delta_editor.py
@@ -14,6 +14,7 @@ from mercurial import node
 from svn import delta
 from svn import core
 
+import svnexternals
 import util as our_util
 
 def pickle_atomic(data, file_path, dir=None):
@@ -144,12 +145,14 @@ class HgChangeReceiver(delta.Editor):
         self.current_rev = None
         self.current_files_exec = {}
         self.current_files_symlink = {}
+        self.dir_batons = {}
         # Map fully qualified destination file paths to module source path
         self.copies = {}
         self.missing_plaintexts = set()
         self.commit_branches_empty = {}
         self.base_revision = None
         self.branches_to_delete = set()
+        self.externals = {}
 
     def _save_metadata(self):
         '''Save the Subversion metadata. This should really be called after
@@ -349,12 +352,41 @@ class HgChangeReceiver(delta.Editor):
         self.branches.update(added_branches)
         self._save_metadata()
 
+    def _updateexternals(self):
+        if not self.externals:
+            return
+        # Accumulate externals records for all branches
+        revnum = self.current_rev.revnum
+        branches = {}
+        for path, entry in self.externals.iteritems():
+            if not self._is_path_valid(path):
+                continue
+            p, b, bp = self._split_branch_path(path)
+            if bp not in branches:
+                external = svnexternals.externalsfile()
+                parent = self.get_parent_revision(revnum, b)
+                pctx = self.repo[parent]
+                if '.hgsvnexternals' in pctx:
+                    external.read(pctx['.hgsvnexternals'].data())
+                branches[bp] = external
+            else:
+                external = branches[bp]
+            external[p] = entry
+
+        # Register the file changes
+        for bp, external in branches.iteritems():
+            path = bp + '/.hgsvnexternals'
+            self.current_files[path] = external.write()
+            self.current_files_symlink[path] = False
+            self.current_files_exec[path] = False
+
     def commit_current_delta(self):
         if hasattr(self, '_exception_info'):  #pragma: no cover
             traceback.print_exception(*self._exception_info)
             raise ReplayException()
         if self.missing_plaintexts:
             raise MissingPlainTextError()
+        self._updateexternals()
         files_to_commit = self.current_files.keys()
         files_to_commit.extend(self.current_files_symlink.keys())
         files_to_commit.extend(self.current_files_exec.keys())
@@ -420,6 +452,11 @@ class HgChangeReceiver(delta.Editor):
                     and branch not in self.repo.branchtags()):
                     continue
             parent_ctx = self.repo.changectx(parents[0])
+            if '.hgsvnexternals' not in parent_ctx and '.hgsvnexternals' in files:
+                # Do not register empty externals files
+                if not self.current_files[files['.hgsvnexternals']]:
+                    del files['.hgsvnexternals']
+
             def filectxfn(repo, memctx, path):
                 current_file = files[path]
                 if current_file in self.deleted_files:
@@ -579,6 +616,7 @@ class HgChangeReceiver(delta.Editor):
                 if br_path != '':
                     br_path2 = br_path + '/'
                 # assuming it is a directory
+                self.externals[path] = None
                 def delete_x(x):
                     self.deleted_files[x] = True
                 map(delete_x, [pat for pat in self.current_files.iterkeys()
@@ -666,6 +704,7 @@ class HgChangeReceiver(delta.Editor):
     @stash_exception_on_self
     def add_directory(self, path, parent_baton, copyfrom_path,
                       copyfrom_revision, dir_pool=None):
+        self.dir_batons[path] = path
         br_path, branch = self._path_and_branch_for_path(path)
         if br_path is not None:
             if not copyfrom_path and not br_path:
@@ -673,14 +712,14 @@ class HgChangeReceiver(delta.Editor):
             else:
                 self.commit_branches_empty[branch] = False
         if br_path is None or not copyfrom_path:
-            return
+            return path
         if copyfrom_path:
             tag = self._is_path_tag(copyfrom_path)
             if tag not in self.tags:
                 tag = None
             if not self._is_path_valid(copyfrom_path) and not tag:
                 self.missing_plaintexts.add('%s/' % path)
-                return
+                return path
 
         if tag:
             source_branch, source_rev = self.tags[tag]
@@ -692,7 +731,7 @@ class HgChangeReceiver(delta.Editor):
                                             source_branch)
         if new_hash == node.nullid:
             self.missing_plaintexts.add('%s/' % path)
-            return
+            return path
         cp_f_ctx = self.repo.changectx(new_hash)
         if cp_f != '/' and cp_f != '':
             cp_f = '%s/' % cp_f
@@ -718,6 +757,7 @@ class HgChangeReceiver(delta.Editor):
                 parentctx = self.repo.changectx(parentid)
                 if self.aresamefiles(parentctx, cp_f_ctx, copies.values()):
                     self.copies.update(copies)
+        return path
 
     @stash_exception_on_self
     def change_file_prop(self, file_baton, name, value, pool=None):
@@ -726,11 +766,26 @@ class HgChangeReceiver(delta.Editor):
         elif name == 'svn:special':
             self.current_files_symlink[self.current_file] = bool(value is not None)
 
+    @stash_exception_on_self
+    def change_dir_prop(self, dir_baton, name, value, pool=None):
+        if dir_baton is None:
+            return
+        path = self.dir_batons[dir_baton]
+        if name == 'svn:externals':
+            self.externals[path] = value
+
     @stash_exception_on_self
     def open_directory(self, path, parent_baton, base_revision, dir_pool=None):
+        self.dir_batons[path] = path
         p_, branch = self._path_and_branch_for_path(path)
         if p_ == '':
             self.commit_branches_empty[branch] = False
+        return path
+
+    @stash_exception_on_self
+    def close_directory(self, dir_baton, dir_pool=None):
+        if dir_baton is not None:
+            del self.dir_batons[dir_baton]
 
     @stash_exception_on_self
     def apply_textdelta(self, file_baton, base_checksum, pool=None):