Mercurial > hgsubversion
comparison wrappers.py @ 331:75f082b5897e
Switch to using url scheme wrappers instead of duplicating each command we wrap.
The 'hg svn url' command has been killed; the replacement is
'.hg/hgrc'. More stuff related to its disappearance has been stripped,
including two tests.
HgChangeReceiver now takes a UUID argument, which it uses to ensure
that remote repositories remain unchanged. This is a temporary
solution, and I'm not entirely satisfied with how it's done either.
Access to the UUID file has been isolated in a HgChangeReceiver
property.
Some more tests have been updated to use ui.pushbuffer()/popbuffer(),
and to pass through the Mercurial API.
Moved the arguments to wrappers.pull() to the UI configuration.
Also, remove HgChangeReceiver.opts in favour of a 'usebranchnames'
instance & configuration variable. The name is taken from the
ConvertExtension.
| author | Dan Villiom Podlaski Christiansen <danchr@gmail.com> |
|---|---|
| date | Fri, 15 May 2009 19:18:43 +0200 |
| parents | 235022089da6 |
| children |
comparison
equal
deleted
inserted
replaced
| 330:5f8f2fd4fd54 | 331:75f082b5897e |
|---|---|
| 85 'unified': True, | 85 'unified': True, |
| 86 'text': False, | 86 'text': False, |
| 87 })) | 87 })) |
| 88 ui.write(cmdutil.filterdiff(''.join(it), baserev, newrev)) | 88 ui.write(cmdutil.filterdiff(''.join(it), baserev, newrev)) |
| 89 | 89 |
| 90 | 90 def push(repo, dest="default", force=False, revs=None): |
| 91 def push(orig, ui, repo, dest=None, *args, **opts): | |
| 92 """push revisions starting at a specified head back to Subversion. | 91 """push revisions starting at a specified head back to Subversion. |
| 93 """ | 92 """ |
| 94 opts.pop('svn', None) # unused in this case | 93 assert not revs, 'designated revisions for push remains unimplemented.' |
| 95 svnurl = repo.ui.expandpath(dest or 'default-push', dest or 'default') | 94 print dest |
| 96 if not cmdutil.issvnurl(svnurl): | 95 ui = repo.ui |
| 97 return orig(ui, repo, dest=dest, *args, **opts) | 96 svnurl = util.normalize_url(repo.ui.expandpath(dest)) |
| 98 old_encoding = util.swap_out_encoding() | 97 old_encoding = util.swap_out_encoding() |
| 99 hge = hg_delta_editor.HgChangeReceiver(repo=repo) | |
| 100 svnurl = util.normalize_url(svnurl) | |
| 101 # split of #rev; TODO: implement --rev/#rev support | 98 # split of #rev; TODO: implement --rev/#rev support |
| 102 svnurl, revs, checkout = hg.parseurl(svnurl, opts.get('rev')) | 99 svnurl, revs, checkout = hg.parseurl(svnurl, revs) |
| 103 if svnurl != hge.url: | 100 # TODO: do credentials specified in the URL still work? |
| 104 raise hgutil.Abort('wrong subversion url!') | 101 user = repo.ui.config('hgsubversion', 'username') |
| 105 svn_commit_hashes = dict(zip(hge.revmap.itervalues(), | 102 passwd = repo.ui.config('hgsubversion', 'password') |
| 106 hge.revmap.iterkeys())) | 103 svn = svnwrap.SubversionRepo(svnurl, user, passwd) |
| 107 user, passwd = util.getuserpass(opts) | 104 hge = hg_delta_editor.HgChangeReceiver(repo=repo, uuid=svn.uuid) |
| 105 | |
| 106 # Check if we are up-to-date with the Subversion repository. | |
| 107 if hge.last_known_revision() != svn.last_changed_rev: | |
| 108 # Based on localrepository.push() in localrepo.py:1559. | |
| 109 # TODO: Ideally, we would behave exactly like other repositories: | |
| 110 # - push everything by default | |
| 111 # - handle additional heads in the same way | |
| 112 # - allow pushing single revisions, branches, tags or heads using | |
| 113 # the -r/--rev flag. | |
| 114 if force: | |
| 115 ui.warn("note: unsynced remote changes!\n") | |
| 116 else: | |
| 117 ui.warn("abort: unsynced remote changes!\n") | |
| 118 return None, 0 | |
| 119 | |
| 108 # Strategy: | 120 # Strategy: |
| 109 # 1. Find all outgoing commits from this head | 121 # 1. Find all outgoing commits from this head |
| 110 if len(repo.parents()) != 1: | 122 if len(repo.parents()) != 1: |
| 111 ui.status('Cowardly refusing to push branch merge\n') | 123 ui.status('Cowardly refusing to push branch merge\n') |
| 112 return 1 | 124 return 1 |
| 113 workingrev = repo.parents()[0] | 125 workingrev = repo.parents()[0] |
| 114 ui.status('searching for changes\n') | 126 ui.status('searching for changes\n') |
| 127 svn_commit_hashes = dict(zip(hge.revmap.itervalues(), | |
| 128 hge.revmap.iterkeys())) | |
| 115 outgoing = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingrev.node()) | 129 outgoing = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingrev.node()) |
| 116 if not (outgoing and len(outgoing)): | 130 if not (outgoing and len(outgoing)): |
| 117 ui.status('no changes found\n') | 131 ui.status('no changes found\n') |
| 118 return 0 | 132 return 0 |
| 119 while outgoing: | 133 while outgoing: |
| 141 except cmdutil.NoFilesException: | 155 except cmdutil.NoFilesException: |
| 142 ui.warn("Could not push revision %s because it had no changes in svn.\n" % | 156 ui.warn("Could not push revision %s because it had no changes in svn.\n" % |
| 143 old_ctx) | 157 old_ctx) |
| 144 return 1 | 158 return 1 |
| 145 # 3. Fetch revisions from svn | 159 # 3. Fetch revisions from svn |
| 146 # TODO this probably should pass in the source explicitly | 160 # TODO: this probably should pass in the source explicitly - rev too? |
| 147 r = pull(None, ui, repo, svn=True, stupid=opts.get('svn_stupid', False), | 161 r = pull(repo, source=dest, force=force) |
| 148 username=user, password=passwd) | |
| 149 assert not r or r == 0 | 162 assert not r or r == 0 |
| 150 # 4. Find the new head of the target branch | 163 # 4. Find the new head of the target branch |
| 151 repo = hg.repository(ui, hge.path) | |
| 152 oldtipctx = repo[oldtip] | 164 oldtipctx = repo[oldtip] |
| 153 replacement = [c for c in oldtipctx.children() if c not in old_children | 165 replacement = [c for c in oldtipctx.children() if c not in old_children |
| 154 and c.branch() == oldtipctx.branch()] | 166 and c.branch() == oldtipctx.branch()] |
| 155 assert len(replacement) == 1, 'Replacement node came back as: %r' % replacement | 167 assert len(replacement) == 1, 'Replacement node came back as: %r' % replacement |
| 156 replacement = replacement[0] | 168 replacement = replacement[0] |
| 159 for needs_transplant in heads: | 171 for needs_transplant in heads: |
| 160 def extrafn(ctx, extra): | 172 def extrafn(ctx, extra): |
| 161 if ctx.node() == oldest: | 173 if ctx.node() == oldest: |
| 162 return | 174 return |
| 163 extra['branch'] = ctx.branch() | 175 extra['branch'] = ctx.branch() |
| 176 # TODO: can we avoid calling our own rebase wrapper here? | |
| 164 rebase(hgrebase.rebase, ui, repo, svn=True, svnextrafn=extrafn, | 177 rebase(hgrebase.rebase, ui, repo, svn=True, svnextrafn=extrafn, |
| 165 svnsourcerev=needs_transplant, **opts) | 178 svnsourcerev=needs_transplant) |
| 166 repo = hg.repository(ui, hge.path) | 179 repo = hg.repository(ui, hge.path) |
| 167 for child in repo[replacement.node()].children(): | 180 for child in repo[replacement.node()].children(): |
| 168 rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid))) | 181 rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid))) |
| 169 if rebasesrc in outgoing: | 182 if rebasesrc in outgoing: |
| 170 while rebasesrc in outgoing: | 183 while rebasesrc in outgoing: |
| 173 [child.node(), ] + outgoing[rebsrcindex+1:]) | 186 [child.node(), ] + outgoing[rebsrcindex+1:]) |
| 174 children = [c for c in child.children() if c.branch() == child.branch()] | 187 children = [c for c in child.children() if c.branch() == child.branch()] |
| 175 if children: | 188 if children: |
| 176 child = children[0] | 189 child = children[0] |
| 177 rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid))) | 190 rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid))) |
| 178 hge = hg_delta_editor.HgChangeReceiver(hge.path, ui_=ui) | 191 # TODO: stop constantly creating the HgChangeReceiver instances. |
| 192 hge = hg_delta_editor.HgChangeReceiver(hge.repo, ui_=ui, uuid=svn.uuid) | |
| 179 svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys())) | 193 svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys())) |
| 180 util.swap_out_encoding(old_encoding) | 194 util.swap_out_encoding(old_encoding) |
| 181 return 0 | 195 return 0 |
| 182 | 196 |
| 183 | 197 |
| 232 commands.update(ui, repo, uprev) | 246 commands.update(ui, repo, uprev) |
| 233 | 247 |
| 234 return res | 248 return res |
| 235 | 249 |
| 236 | 250 |
| 237 def pull(orig, ui, repo, source="default", *args, **opts): | 251 def pull(repo, source="default", rev=None, force=False): |
| 238 """pull new revisions from Subversion | 252 """pull new revisions from Subversion |
| 239 | 253 |
| 240 Also takes svn, svn_stupid, and create_new_dest kwargs. | 254 Also takes svn, svn_stupid, and create_new_dest kwargs. |
| 241 """ | 255 """ |
| 242 svn = opts.pop('svn', None) | 256 url = repo.ui.expandpath(source) |
| 243 svn_stupid = opts.pop('svn_stupid', False) | 257 svn_url = util.normalize_url(url) |
| 244 create_new_dest = opts.pop('create_new_dest', False) | 258 |
| 245 url = ((repo and repo.ui) or ui).expandpath(source) | |
| 246 if orig and not (cmdutil.issvnurl(url) or svn or create_new_dest): | |
| 247 return orig(ui, repo, source=source, *args, **opts) | |
| 248 svn_url = url | |
| 249 svn_url = util.normalize_url(svn_url) | |
| 250 # Split off #rev; TODO: implement --rev/#rev support limiting the pulled/cloned revisions | 259 # Split off #rev; TODO: implement --rev/#rev support limiting the pulled/cloned revisions |
| 251 svn_url, revs, checkout = hg.parseurl(svn_url, opts.get('rev')) | 260 svn_url, revs, checkout = hg.parseurl(svn_url, rev) |
| 252 old_encoding = util.swap_out_encoding() | 261 old_encoding = util.swap_out_encoding() |
| 253 # TODO implement skipto support | 262 # TODO implement skipto support |
| 254 skipto_rev = 0 | 263 skipto_rev = 0 |
| 255 have_replay = not svn_stupid | 264 have_replay = not repo.ui.configbool('hgsubversion', 'stupid') |
| 256 if have_replay and not callable( | 265 if have_replay and not callable( |
| 257 delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover | 266 delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover |
| 258 ui.status('You are using old Subversion SWIG bindings. Replay will not' | 267 ui.status('You are using old Subversion SWIG bindings. Replay will not' |
| 259 ' work until you upgrade to 1.5.0 or newer. Falling back to' | 268 ' work until you upgrade to 1.5.0 or newer. Falling back to' |
| 260 ' a slower method that may be buggier. Please upgrade, or' | 269 ' a slower method that may be buggier. Please upgrade, or' |
| 261 ' contribute a patch to use the ctypes bindings instead' | 270 ' contribute a patch to use the ctypes bindings instead' |
| 262 ' of SWIG.\n') | 271 ' of SWIG.\n') |
| 263 have_replay = False | 272 have_replay = False |
| 264 initializing_repo = False | 273 |
| 265 user, passwd = util.getuserpass(opts) | 274 # FIXME: enable this |
| 275 user = repo.ui.config('hgsubversion', 'username') | |
| 276 passwd = repo.ui.config('hgsubversion', 'password') | |
| 266 svn = svnwrap.SubversionRepo(svn_url, user, passwd) | 277 svn = svnwrap.SubversionRepo(svn_url, user, passwd) |
| 267 author_host = ui.config('hgsubversion', 'defaulthost', svn.uuid) | 278 hg_editor = hg_delta_editor.HgChangeReceiver(repo=repo, subdir=svn.subdir, |
| 268 tag_locations = ['tags', ] | 279 uuid=svn.uuid) |
| 269 authors = opts.pop('svn_authors', None) | 280 |
| 270 filemap = opts.pop('svn_filemap', None) | 281 start = max(hg_editor.last_known_revision(), skipto_rev) |
| 271 if repo: | 282 initializing_repo = (hg_editor.last_known_revision() <= 0) |
| 272 hg_editor = hg_delta_editor.HgChangeReceiver(repo=repo, | 283 ui = repo.ui |
| 273 subdir=svn.subdir, | |
| 274 author_host=author_host, | |
| 275 tag_locations=tag_locations, | |
| 276 authors=authors, | |
| 277 filemap=filemap) | |
| 278 else: | |
| 279 hg_editor = hg_delta_editor.HgChangeReceiver(ui_=ui, | |
| 280 path=create_new_dest, | |
| 281 subdir=svn.subdir, | |
| 282 author_host=author_host, | |
| 283 tag_locations=tag_locations, | |
| 284 authors=authors, | |
| 285 filemap=filemap) | |
| 286 hg_editor.opts = opts | |
| 287 if os.path.exists(hg_editor.uuid_file): | |
| 288 uuid = open(hg_editor.uuid_file).read() | |
| 289 assert uuid == svn.uuid | |
| 290 start = hg_editor.last_known_revision() | |
| 291 else: | |
| 292 open(hg_editor.uuid_file, 'w').write(svn.uuid) | |
| 293 open(hg_editor.svn_url_file, 'w').write(svn_url) | |
| 294 initializing_repo = True | |
| 295 start = skipto_rev | |
| 296 | 284 |
| 297 if initializing_repo and start > 0: | 285 if initializing_repo and start > 0: |
| 298 raise hgutil.Abort('Revision skipping at repository initialization ' | 286 raise hgutil.Abort('Revision skipping at repository initialization ' |
| 299 'remains unimplemented.') | 287 'remains unimplemented.') |
| 300 | 288 |
| 301 revisions = 0 | 289 revisions = 0 |
| 302 if not initializing_repo: | |
| 303 oldheads = len(repo.changelog.heads()) | |
| 304 | 290 |
| 305 # start converting revisions | 291 # start converting revisions |
| 306 for r in svn.revisions(start=start): | 292 for r in svn.revisions(start=start): |
| 307 valid = True | 293 valid = True |
| 308 hg_editor.update_branch_tag_map_for_rev(r) | 294 hg_editor.update_branch_tag_map_for_rev(r) |
| 342 if revisions == 0: | 328 if revisions == 0: |
| 343 ui.status(i18n._("no changes found\n")) | 329 ui.status(i18n._("no changes found\n")) |
| 344 return | 330 return |
| 345 else: | 331 else: |
| 346 ui.status("added %d svn revisions\n" % revisions) | 332 ui.status("added %d svn revisions\n" % revisions) |
| 347 if not initializing_repo: | |
| 348 newheads = len(repo.changelog.heads()) | |
| 349 # postincoming needs to know if heads were added or removed | |
| 350 # calculation based on mercurial.localrepo.addchangegroup | |
| 351 # 0 means no changes, 1 no new heads, > 1 new heads, < 0 heads removed | |
| 352 modheads = newheads - oldheads + (newheads < oldheads and -1 or 1) | |
| 353 commands.postincoming(ui, repo, modheads, opts.get('update'), checkout) | |
| 354 | |
| 355 | 333 |
| 356 def rebase(orig, ui, repo, **opts): | 334 def rebase(orig, ui, repo, **opts): |
| 357 """rebase current unpushed revisions onto the Subversion head | 335 """rebase current unpushed revisions onto the Subversion head |
| 358 | 336 |
| 359 This moves a line of development from making its own head to the top of | 337 This moves a line of development from making its own head to the top of |
