Mercurial > hgsubversion
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: |