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