comparison svncommands.py @ 257:ffccf0080e54

Move wrappers for hg commands to their own module.
author Augie Fackler <durin42@gmail.com>
date Fri, 10 Apr 2009 22:38:29 -0500
parents 7932d098cb5f
children 9f0738587f94
comparison
equal deleted inserted replaced
256:7932d098cb5f 257:ffccf0080e54
1 import os 1 import os
2 import cPickle as pickle 2 import cPickle as pickle
3 3
4 from mercurial import cmdutil as hgcmdutil
5 from mercurial import commands
6 from mercurial import hg 4 from mercurial import hg
7 from mercurial import node 5 from mercurial import node
8 from mercurial import patch
9 from mercurial import util as hgutil 6 from mercurial import util as hgutil
10 7
11 from svn import core
12 from svn import delta
13 8
14 import hg_delta_editor 9 import hg_delta_editor
15 import svnwrap 10 import svnwrap
16 import stupid as stupidmod
17 import cmdutil
18 import util 11 import util
19 import utility_commands 12 import utility_commands
20
21
22 def clone(orig, ui, source, dest=None, **opts):
23 '''clone Subversion repository to a local Mercurial repository.
24
25 If no destination directory name is specified, it defaults to the
26 basename of the source plus "-hg".
27
28 You can specify multiple paths for the location of tags using comma
29 separated values.
30 '''
31 svnurl = ui.expandpath(source)
32 if not cmdutil.issvnurl(svnurl):
33 orig(ui, repo, source=source, dest=dest, *args, **opts)
34
35 if not dest:
36 dest = hg.defaultdest(source) + '-hg'
37 ui.status("Assuming destination %s\n" % dest)
38
39 if os.path.exists(dest):
40 raise hgutil.Abort("destination '%s' already exists" % dest)
41 url = util.normalize_url(svnurl)
42 res = -1
43 try:
44 try:
45 res = pull(None, ui, None, True, opts.pop('svn_stupid', False),
46 source=url, create_new_dest=dest, **opts)
47 except core.SubversionException, e:
48 if e.apr_err == core.SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED:
49 raise hgutil.Abort('It appears svn does not trust the ssl cert for this site.\n'
50 'Please try running svn ls on that url first.')
51 raise
52 finally:
53 if os.path.exists(dest):
54 repo = hg.repository(ui, dest)
55 fp = repo.opener("hgrc", "w", text=True)
56 fp.write("[paths]\n")
57 # percent needs to be escaped for ConfigParser
58 fp.write("default = %(url)s\nsvn = %(url)s\n" % {'url': svnurl.replace('%', '%%')})
59 fp.close()
60 if res is None or res == 0:
61 commands.update(ui, repo, repo['tip'].node())
62
63 return res
64
65
66 def pull(orig, ui, repo, svn=None, svn_stupid=False, source="default", create_new_dest=False,
67 *args, **opts):
68 """pull new revisions from Subversion
69 """
70 url = ui.expandpath(source)
71 if not (cmdutil.issvnurl(url) or svn or create_new_dest):
72 orig(ui, repo, source=source, *args, **opts)
73 svn_url = url
74 svn_url = util.normalize_url(svn_url)
75 old_encoding = util.swap_out_encoding()
76 # TODO implement skipto support
77 skipto_rev = 0
78 have_replay = not svn_stupid
79 if have_replay and not callable(
80 delta.svn_txdelta_apply(None, None, None)[0]): #pragma: no cover
81 ui.status('You are using old Subversion SWIG bindings. Replay will not'
82 ' work until you upgrade to 1.5.0 or newer. Falling back to'
83 ' a slower method that may be buggier. Please upgrade, or'
84 ' contribute a patch to use the ctypes bindings instead'
85 ' of SWIG.\n')
86 have_replay = False
87 initializing_repo = False
88 user = opts.get('username', hgutil.getuser())
89 passwd = opts.get('password', '')
90 svn = svnwrap.SubversionRepo(svn_url, user, passwd)
91 author_host = "@%s" % svn.uuid
92 # TODO these should be configurable again, but I'm torn on how.
93 # Maybe this should be configured in .hg/hgrc for each repo? Seems vaguely reasonable.
94 tag_locations = ['tags', ]
95 authors = None
96 filemap = None
97 if repo:
98 hg_editor = hg_delta_editor.HgChangeReceiver(repo=repo)
99 else:
100 hg_editor = hg_delta_editor.HgChangeReceiver(ui_=ui,
101 path=create_new_dest,
102 subdir=svn.subdir,
103 author_host=author_host,
104 tag_locations=tag_locations,
105 authors=authors,
106 filemap=filemap)
107 if os.path.exists(hg_editor.uuid_file):
108 uuid = open(hg_editor.uuid_file).read()
109 assert uuid == svn.uuid
110 start = hg_editor.last_known_revision()
111 else:
112 open(hg_editor.uuid_file, 'w').write(svn.uuid)
113 open(hg_editor.svn_url_file, 'w').write(svn_url)
114 initializing_repo = True
115 start = skipto_rev
116
117 if initializing_repo and start > 0:
118 raise hgutil.Abort('Revision skipping at repository initialization '
119 'remains unimplemented.')
120
121 # start converting revisions
122 for r in svn.revisions(start=start):
123 valid = True
124 hg_editor.update_branch_tag_map_for_rev(r)
125 for p in r.paths:
126 if hg_editor._is_path_valid(p):
127 valid = True
128 break
129 if valid:
130 # got a 502? Try more than once!
131 tries = 0
132 converted = False
133 while not converted:
134 try:
135 util.describe_revision(ui, r)
136 if have_replay:
137 try:
138 cmdutil.replay_convert_rev(hg_editor, svn, r)
139 except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover
140 ui.status('%s\n' % e.message)
141 stupidmod.print_your_svn_is_old_message(ui)
142 have_replay = False
143 stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
144 else:
145 stupidmod.svn_server_pull_rev(ui, svn, hg_editor, r)
146 converted = True
147 except core.SubversionException, e: #pragma: no cover
148 if (e.apr_err == core.SVN_ERR_RA_DAV_REQUEST_FAILED
149 and '502' in str(e)
150 and tries < 3):
151 tries += 1
152 ui.status('Got a 502, retrying (%s)\n' % tries)
153 else:
154 raise hgutil.Abort(*e.args)
155 util.swap_out_encoding(old_encoding)
156 13
157 14
158 def incoming(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None, 15 def incoming(ui, svn_url, hg_repo_path, skipto_rev=0, stupid=None,
159 tag_locations='tags', authors=None, filemap=None, **opts): 16 tag_locations='tags', authors=None, filemap=None, **opts):
160 """show incoming revisions from Subversion 17 """show incoming revisions from Subversion
200 ui.status('\n') 57 ui.status('\n')
201 for label, attr in rev_stuff: 58 for label, attr in rev_stuff:
202 l1 = label+':' 59 l1 = label+':'
203 ui.status('%s%s\n' % (l1.ljust(13), 60 ui.status('%s%s\n' % (l1.ljust(13),
204 str(r.__getattribute__(attr)).strip(), )) 61 str(r.__getattribute__(attr)).strip(), ))
205
206
207 def push(orig, ui, repo, dest=None, **opts):
208 """push revisions starting at a specified head back to Subversion.
209 """
210 svnurl = ui.expandpath(dest or 'default-push', dest or 'default')
211 if not cmdutil.issvnurl(svnurl):
212 orig(ui, repo, dest=dest, *args, **opts)
213 old_encoding = util.swap_out_encoding()
214 hge = hg_delta_editor.HgChangeReceiver(repo=repo)
215 assert svnurl == hge.url
216 svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
217 hge.revmap.iterkeys()))
218 user = opts.get('username', hgutil.getuser())
219 passwd = opts.get('password', '')
220
221 # Strategy:
222 # 1. Find all outgoing commits from this head
223 if len(repo.parents()) != 1:
224 ui.status('Cowardly refusing to push branch merge')
225 return 1
226 workingrev = repo.parents()[0]
227 outgoing = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes, workingrev.node())
228 if not (outgoing and len(outgoing)):
229 ui.status('No revisions to push.')
230 return 0
231 while outgoing:
232 oldest = outgoing.pop(-1)
233 old_ctx = repo[oldest]
234 if len(old_ctx.parents()) != 1:
235 ui.status('Found a branch merge, this needs discussion and '
236 'implementation.')
237 return 1
238 base_n = old_ctx.parents()[0].node()
239 old_children = repo[base_n].children()
240 svnbranch = repo[base_n].branch()
241 oldtip = base_n
242 samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
243 and c.node() in svn_commit_hashes]
244 while samebranchchildren:
245 oldtip = samebranchchildren[0].node()
246 samebranchchildren = [c for c in repo[oldtip].children() if c.branch() == svnbranch
247 and c.node() in svn_commit_hashes]
248 # 2. Commit oldest revision that needs to be pushed
249 base_revision = svn_commit_hashes[base_n][0]
250 try:
251 cmdutil.commit_from_rev(ui, repo, old_ctx, hge, svnurl,
252 base_revision, user, passwd)
253 except cmdutil.NoFilesException:
254 ui.warn("Could not push revision %s because it had no changes in svn.\n" %
255 old_ctx)
256 return 1
257 # 3. Fetch revisions from svn
258 r = pull(None, ui, repo, True, stupid=opts.get('svn_stupid', False),
259 username=user, password=passwd)
260 assert not r or r == 0
261 # 4. Find the new head of the target branch
262 repo = hg.repository(ui, hge.path)
263 oldtipctx = repo[oldtip]
264 replacement = [c for c in oldtipctx.children() if c not in old_children
265 and c.branch() == oldtipctx.branch()]
266 assert len(replacement) == 1, 'Replacement node came back as: %r' % replacement
267 replacement = replacement[0]
268 # 5. Rebase all children of the currently-pushing rev to the new branch
269 heads = repo.heads(old_ctx.node())
270 for needs_transplant in heads:
271 def extrafn(ctx, extra):
272 if ctx.node() == oldest:
273 return
274 extra['branch'] = ctx.branch()
275 utility_commands.rebase(ui, repo, extrafn=extrafn,
276 sourcerev=needs_transplant, **opts)
277 repo = hg.repository(ui, hge.path)
278 for child in repo[replacement.node()].children():
279 rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
280 if rebasesrc in outgoing:
281 while rebasesrc in outgoing:
282 rebsrcindex = outgoing.index(rebasesrc)
283 outgoing = (outgoing[0:rebsrcindex] +
284 [child.node(), ] + outgoing[rebsrcindex+1:])
285 children = [c for c in child.children() if c.branch() == child.branch()]
286 if children:
287 child = children[0]
288 rebasesrc = node.bin(child.extra().get('rebase_source', node.hex(node.nullid)))
289 hge = hg_delta_editor.HgChangeReceiver(hge.path, ui_=ui)
290 svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys()))
291 util.swap_out_encoding(old_encoding)
292 return 0
293
294
295 def diff(orig, ui, repo, *args, **opts):
296 """show a diff of the most recent revision against its parent from svn
297 """
298 if not opts.get('svn', False) or opts.get('change', None):
299 return orig(ui, repo, *args, **opts)
300 svn_commit_hashes = {}
301 hge = hg_delta_editor.HgChangeReceiver(repo=repo)
302 svn_commit_hashes = dict(zip(hge.revmap.itervalues(),
303 hge.revmap.iterkeys()))
304 if not opts.get('rev', None):
305 parent = repo.parents()[0]
306 o_r = util.outgoing_revisions(ui, repo, hge, svn_commit_hashes,
307 parent.node())
308 if o_r:
309 parent = repo[o_r[-1]].parents()[0]
310 opts['rev'] = ['%s:.' % node.hex(parent.node()), ]
311 node1, node2 = hgcmdutil.revpair(repo, opts['rev'])
312 baserev, _junk = svn_commit_hashes.get(node1, (-1, 'junk', ))
313 newrev, _junk = svn_commit_hashes.get(node2, (-1, 'junk', ))
314 it = patch.diff(repo, node1, node2,
315 opts=patch.diffopts(ui, opts={'git': True,
316 'show_function': False,
317 'ignore_all_space': False,
318 'ignore_space_change': False,
319 'ignore_blank_lines': False,
320 'unified': True,
321 'text': False,
322 }))
323 ui.write(cmdutil.filterdiff(''.join(it), baserev, newrev))
324 62
325 63
326 def rebuildmeta(ui, repo, hg_repo_path, args, **opts): 64 def rebuildmeta(ui, repo, hg_repo_path, args, **opts):
327 """rebuild hgsubversion metadata using values stored in revisions 65 """rebuild hgsubversion metadata using values stored in revisions
328 """ 66 """