diff push_cmd.py @ 175:2412800b1258

Support svn:externals changes via .hgsvnexternals updates
author Patrick Mezard <pmezard@gmail.com>
date Fri, 02 Jan 2009 15:54:05 -0600
parents f244eaee5069
children e37f9d3fd5e7
line wrap: on
line diff
--- a/push_cmd.py
+++ b/push_cmd.py
@@ -5,6 +5,7 @@ from svn import core
 
 import util
 import hg_delta_editor
+import svnexternals
 import svnwrap
 import fetch_command
 import utility_commands
@@ -98,9 +99,10 @@ def _isdir(svn, branchpath, svndir):
     except core.SubversionException:
         return False
 
-def _getdirchanges(svn, branchpath, parentctx, ctx, changedfiles):
+def _getdirchanges(svn, branchpath, parentctx, ctx, changedfiles, extchanges):
     """Compute directories to add or delete when moving from parentctx
-    to ctx, assuming only 'changedfiles' files changed.
+    to ctx, assuming only 'changedfiles' files changed, and 'extchanges'
+    external references changed (as returned by svnexternals.diff()).
 
     Return (added, deleted) where 'added' is the list of all added
     directories and 'deleted' the list of deleted directories.
@@ -109,13 +111,15 @@ def _getdirchanges(svn, branchpath, pare
     deleted directories are also listed, but item order of undefined
     in either list.
     """
-    def finddirs(path):
+    def finddirs(path, includeself=False):
+        if includeself:
+            yield path
         pos = path.rfind('/')
         while pos != -1:
             yield path[:pos]
             pos = path.rfind('/', 0, pos)
 
-    def getctxdirs(ctx, keptdirs):
+    def getctxdirs(ctx, keptdirs, extdirs):
         dirs = {}
         for f in ctx.manifest():
             for d in finddirs(f):
@@ -123,6 +127,9 @@ def _getdirchanges(svn, branchpath, pare
                     break
                 if d in keptdirs:
                     dirs[d] = 1
+        for extdir in extdirs:
+            for d in finddirs(extdir, True):
+                dirs[d] = 1
         return dirs
 
     deleted, added = [], []
@@ -134,10 +141,16 @@ def _getdirchanges(svn, branchpath, pare
             continue
         for d in finddirs(f):
             changeddirs[d] = 1
+    for e in extchanges:
+        if not e[1] or not e[2]:
+            for d in finddirs(e[0], True):
+                changeddirs[d] = 1
     if not changeddirs:
         return added, deleted
-    olddirs = getctxdirs(parentctx, changeddirs)
-    newdirs = getctxdirs(ctx, changeddirs)
+    olddirs = getctxdirs(parentctx, changeddirs, 
+                         [e[0] for e in extchanges if e[1]])
+    newdirs = getctxdirs(ctx, changeddirs,
+                         [e[0] for e in extchanges if e[2]])
 
     for d in newdirs:
         if d not in olddirs and not _isdir(svn, branchpath, d):
@@ -149,6 +162,11 @@ def _getdirchanges(svn, branchpath, pare
 
     return added, deleted
 
+def _externals(ctx):
+    ext = svnexternals.externalsfile()
+    if '.hgsvnexternals' in ctx:
+        ext.read(ctx['.hgsvnexternals'].data())
+    return ext
 
 def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision):
     """Build and send a commit from Mercurial to Subversion.
@@ -162,13 +180,17 @@ def commit_from_rev(ui, repo, rev_ctx, h
     if parent_branch and parent_branch != 'default':
         branch_path = 'branches/%s' % parent_branch
 
-    addeddirs, deleteddirs = _getdirchanges(svn, branch_path, parent,
-                                            rev_ctx, rev_ctx.files())
+    extchanges = list(svnexternals.diff(_externals(parent), 
+                                        _externals(rev_ctx)))
+    addeddirs, deleteddirs = _getdirchanges(svn, branch_path, parent, rev_ctx,
+                                            rev_ctx.files(), extchanges)
     deleteddirs = set(deleteddirs)
 
     props = {}
     copies = {}
     for file in rev_ctx.files():
+        if file == '.hgsvnexternals':
+            continue
         new_data = base_data = ''
         action = ''
         if file in rev_ctx:
@@ -208,6 +230,15 @@ def commit_from_rev(ui, repo, rev_ctx, h
             action = 'delete'
         file_data[file] = base_data, new_data, action
 
+    def svnpath(p):
+        return '%s/%s' % (branch_path, p)
+
+    changeddirs = []
+    for d, v1, v2 in extchanges:
+        props.setdefault(svnpath(d), {})['svn:externals'] = v2
+        if d not in deleteddirs and d not in addeddirs:
+            changeddirs.append(svnpath(d))
+
     # Now we are done with files, we can prune deleted directories
     # against themselves: ignore a/b if a/ is already removed
     deleteddirs2 = list(deleteddirs)
@@ -217,9 +248,6 @@ def commit_from_rev(ui, repo, rev_ctx, h
         if pos >= 0 and d[:pos] in deleteddirs:
             deleteddirs.remove(d[:pos])
 
-    def svnpath(p):
-        return '%s/%s' % (branch_path, p)
-
     newcopies = {}
     for source, dest in copies.iteritems():
         newcopies[svnpath(source)] = (svnpath(dest), base_revision)
@@ -238,7 +266,7 @@ def commit_from_rev(ui, repo, rev_ctx, h
 
     addeddirs = [svnpath(d) for d in addeddirs]
     deleteddirs = [svnpath(d) for d in deleteddirs]
-    new_target_files += addeddirs + deleteddirs
+    new_target_files += addeddirs + deleteddirs + changeddirs
     try:
         svn.commit(new_target_files, rev_ctx.description(), file_data,
                    base_revision, set(addeddirs), set(deleteddirs),