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