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