comparison 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
comparison
equal deleted inserted replaced
174:f80132c5fea5 175:2412800b1258
3 from mercurial import node 3 from mercurial import node
4 from svn import core 4 from svn import core
5 5
6 import util 6 import util
7 import hg_delta_editor 7 import hg_delta_editor
8 import svnexternals
8 import svnwrap 9 import svnwrap
9 import fetch_command 10 import fetch_command
10 import utility_commands 11 import utility_commands
11 12
12 13
96 svn.list_dir('%s/%s' % (branchpath, svndir)) 97 svn.list_dir('%s/%s' % (branchpath, svndir))
97 return True 98 return True
98 except core.SubversionException: 99 except core.SubversionException:
99 return False 100 return False
100 101
101 def _getdirchanges(svn, branchpath, parentctx, ctx, changedfiles): 102 def _getdirchanges(svn, branchpath, parentctx, ctx, changedfiles, extchanges):
102 """Compute directories to add or delete when moving from parentctx 103 """Compute directories to add or delete when moving from parentctx
103 to ctx, assuming only 'changedfiles' files changed. 104 to ctx, assuming only 'changedfiles' files changed, and 'extchanges'
105 external references changed (as returned by svnexternals.diff()).
104 106
105 Return (added, deleted) where 'added' is the list of all added 107 Return (added, deleted) where 'added' is the list of all added
106 directories and 'deleted' the list of deleted directories. 108 directories and 'deleted' the list of deleted directories.
107 Intermediate directories are included: if a/b/c is new and requires 109 Intermediate directories are included: if a/b/c is new and requires
108 the addition of a/b and a, those will be listed too. Intermediate 110 the addition of a/b and a, those will be listed too. Intermediate
109 deleted directories are also listed, but item order of undefined 111 deleted directories are also listed, but item order of undefined
110 in either list. 112 in either list.
111 """ 113 """
112 def finddirs(path): 114 def finddirs(path, includeself=False):
115 if includeself:
116 yield path
113 pos = path.rfind('/') 117 pos = path.rfind('/')
114 while pos != -1: 118 while pos != -1:
115 yield path[:pos] 119 yield path[:pos]
116 pos = path.rfind('/', 0, pos) 120 pos = path.rfind('/', 0, pos)
117 121
118 def getctxdirs(ctx, keptdirs): 122 def getctxdirs(ctx, keptdirs, extdirs):
119 dirs = {} 123 dirs = {}
120 for f in ctx.manifest(): 124 for f in ctx.manifest():
121 for d in finddirs(f): 125 for d in finddirs(f):
122 if d in dirs: 126 if d in dirs:
123 break 127 break
124 if d in keptdirs: 128 if d in keptdirs:
125 dirs[d] = 1 129 dirs[d] = 1
130 for extdir in extdirs:
131 for d in finddirs(extdir, True):
132 dirs[d] = 1
126 return dirs 133 return dirs
127 134
128 deleted, added = [], [] 135 deleted, added = [], []
129 changeddirs = {} 136 changeddirs = {}
130 for f in changedfiles: 137 for f in changedfiles:
132 # Updated files cannot cause directories to be created 139 # Updated files cannot cause directories to be created
133 # or removed. 140 # or removed.
134 continue 141 continue
135 for d in finddirs(f): 142 for d in finddirs(f):
136 changeddirs[d] = 1 143 changeddirs[d] = 1
144 for e in extchanges:
145 if not e[1] or not e[2]:
146 for d in finddirs(e[0], True):
147 changeddirs[d] = 1
137 if not changeddirs: 148 if not changeddirs:
138 return added, deleted 149 return added, deleted
139 olddirs = getctxdirs(parentctx, changeddirs) 150 olddirs = getctxdirs(parentctx, changeddirs,
140 newdirs = getctxdirs(ctx, changeddirs) 151 [e[0] for e in extchanges if e[1]])
152 newdirs = getctxdirs(ctx, changeddirs,
153 [e[0] for e in extchanges if e[2]])
141 154
142 for d in newdirs: 155 for d in newdirs:
143 if d not in olddirs and not _isdir(svn, branchpath, d): 156 if d not in olddirs and not _isdir(svn, branchpath, d):
144 added.append(d) 157 added.append(d)
145 158
147 if d not in newdirs and _isdir(svn, branchpath, d): 160 if d not in newdirs and _isdir(svn, branchpath, d):
148 deleted.append(d) 161 deleted.append(d)
149 162
150 return added, deleted 163 return added, deleted
151 164
165 def _externals(ctx):
166 ext = svnexternals.externalsfile()
167 if '.hgsvnexternals' in ctx:
168 ext.read(ctx['.hgsvnexternals'].data())
169 return ext
152 170
153 def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision): 171 def commit_from_rev(ui, repo, rev_ctx, hg_editor, svn_url, base_revision):
154 """Build and send a commit from Mercurial to Subversion. 172 """Build and send a commit from Mercurial to Subversion.
155 """ 173 """
156 file_data = {} 174 file_data = {}
160 branch_path = 'trunk' 178 branch_path = 'trunk'
161 179
162 if parent_branch and parent_branch != 'default': 180 if parent_branch and parent_branch != 'default':
163 branch_path = 'branches/%s' % parent_branch 181 branch_path = 'branches/%s' % parent_branch
164 182
165 addeddirs, deleteddirs = _getdirchanges(svn, branch_path, parent, 183 extchanges = list(svnexternals.diff(_externals(parent),
166 rev_ctx, rev_ctx.files()) 184 _externals(rev_ctx)))
185 addeddirs, deleteddirs = _getdirchanges(svn, branch_path, parent, rev_ctx,
186 rev_ctx.files(), extchanges)
167 deleteddirs = set(deleteddirs) 187 deleteddirs = set(deleteddirs)
168 188
169 props = {} 189 props = {}
170 copies = {} 190 copies = {}
171 for file in rev_ctx.files(): 191 for file in rev_ctx.files():
192 if file == '.hgsvnexternals':
193 continue
172 new_data = base_data = '' 194 new_data = base_data = ''
173 action = '' 195 action = ''
174 if file in rev_ctx: 196 if file in rev_ctx:
175 fctx = rev_ctx.filectx(file) 197 fctx = rev_ctx.filectx(file)
176 new_data = fctx.data() 198 new_data = fctx.data()
206 # This file will be removed when its directory is removed 228 # This file will be removed when its directory is removed
207 continue 229 continue
208 action = 'delete' 230 action = 'delete'
209 file_data[file] = base_data, new_data, action 231 file_data[file] = base_data, new_data, action
210 232
233 def svnpath(p):
234 return '%s/%s' % (branch_path, p)
235
236 changeddirs = []
237 for d, v1, v2 in extchanges:
238 props.setdefault(svnpath(d), {})['svn:externals'] = v2
239 if d not in deleteddirs and d not in addeddirs:
240 changeddirs.append(svnpath(d))
241
211 # Now we are done with files, we can prune deleted directories 242 # Now we are done with files, we can prune deleted directories
212 # against themselves: ignore a/b if a/ is already removed 243 # against themselves: ignore a/b if a/ is already removed
213 deleteddirs2 = list(deleteddirs) 244 deleteddirs2 = list(deleteddirs)
214 deleteddirs2.sort(reverse=True) 245 deleteddirs2.sort(reverse=True)
215 for d in deleteddirs2: 246 for d in deleteddirs2:
216 pos = d.rfind('/') 247 pos = d.rfind('/')
217 if pos >= 0 and d[:pos] in deleteddirs: 248 if pos >= 0 and d[:pos] in deleteddirs:
218 deleteddirs.remove(d[:pos]) 249 deleteddirs.remove(d[:pos])
219
220 def svnpath(p):
221 return '%s/%s' % (branch_path, p)
222 250
223 newcopies = {} 251 newcopies = {}
224 for source, dest in copies.iteritems(): 252 for source, dest in copies.iteritems():
225 newcopies[svnpath(source)] = (svnpath(dest), base_revision) 253 newcopies[svnpath(source)] = (svnpath(dest), base_revision)
226 254
236 props.setdefault(ntf, {})['svn:mime-type'] = 'application/octet-stream' 264 props.setdefault(ntf, {})['svn:mime-type'] = 'application/octet-stream'
237 del file_data[tf] 265 del file_data[tf]
238 266
239 addeddirs = [svnpath(d) for d in addeddirs] 267 addeddirs = [svnpath(d) for d in addeddirs]
240 deleteddirs = [svnpath(d) for d in deleteddirs] 268 deleteddirs = [svnpath(d) for d in deleteddirs]
241 new_target_files += addeddirs + deleteddirs 269 new_target_files += addeddirs + deleteddirs + changeddirs
242 try: 270 try:
243 svn.commit(new_target_files, rev_ctx.description(), file_data, 271 svn.commit(new_target_files, rev_ctx.description(), file_data,
244 base_revision, set(addeddirs), set(deleteddirs), 272 base_revision, set(addeddirs), set(deleteddirs),
245 props, newcopies) 273 props, newcopies)
246 except core.SubversionException, e: 274 except core.SubversionException, e: