Show More
@@ -15,7 +15,8 b'' | |||||
15 |
|
15 | |||
16 | import pprint |
|
16 | import pprint | |
17 | import locale |
|
17 | import locale | |
18 |
|
18 | import os | ||
|
19 | import cPickle as pickle | |||
19 | from mercurial import util |
|
20 | from mercurial import util | |
20 |
|
21 | |||
21 | # Subversion stuff. Works best with very recent Python SVN bindings |
|
22 | # Subversion stuff. Works best with very recent Python SVN bindings | |
@@ -38,6 +39,12 b' except ImportError:' | |||||
38 |
|
39 | |||
39 | class CompatibilityException(Exception): pass |
|
40 | class CompatibilityException(Exception): pass | |
40 |
|
41 | |||
|
42 | class changedpath(object): | |||
|
43 | def __init__(self, p): | |||
|
44 | self.copyfrom_path = p.copyfrom_path | |||
|
45 | self.copyfrom_rev = p.copyfrom_rev | |||
|
46 | self.action = p.action | |||
|
47 | ||||
41 | # SVN conversion code stolen from bzr-svn and tailor |
|
48 | # SVN conversion code stolen from bzr-svn and tailor | |
42 | class convert_svn(converter_source): |
|
49 | class convert_svn(converter_source): | |
43 | def __init__(self, ui, url, rev=None): |
|
50 | def __init__(self, ui, url, rev=None): | |
@@ -71,9 +78,9 b' class convert_svn(converter_source):' | |||||
71 | self.url = url |
|
78 | self.url = url | |
72 | self.encoding = 'UTF-8' # Subversion is always nominal UTF-8 |
|
79 | self.encoding = 'UTF-8' # Subversion is always nominal UTF-8 | |
73 | try: |
|
80 | try: | |
74 |
self.transport = transport.SvnRaTransport(url |
|
81 | self.transport = transport.SvnRaTransport(url=url) | |
75 | self.ra = self.transport.ra |
|
82 | self.ra = self.transport.ra | |
76 |
self.ctx = s |
|
83 | self.ctx = self.transport.client | |
77 | self.base = svn.ra.get_repos_root(self.ra) |
|
84 | self.base = svn.ra.get_repos_root(self.ra) | |
78 | self.module = self.url[len(self.base):] |
|
85 | self.module = self.url[len(self.base):] | |
79 | self.modulemap = {} # revision, module |
|
86 | self.modulemap = {} # revision, module | |
@@ -174,10 +181,65 b' class convert_svn(converter_source):' | |||||
174 | del self.commits[rev] |
|
181 | del self.commits[rev] | |
175 | return commit |
|
182 | return commit | |
176 |
|
183 | |||
|
184 | def get_log(self, paths, start, end, limit=0, discover_changed_paths=True, | |||
|
185 | strict_node_history=False): | |||
|
186 | '''wrapper for svn.ra.get_log. | |||
|
187 | on a large repository, svn.ra.get_log pins huge amounts of | |||
|
188 | memory that cannot be recovered. work around it by forking | |||
|
189 | and writing results over a pipe.''' | |||
|
190 | ||||
|
191 | def child(fp): | |||
|
192 | protocol = -1 | |||
|
193 | def receiver(orig_paths, revnum, author, date, message, pool): | |||
|
194 | if orig_paths is not None: | |||
|
195 | for k, v in orig_paths.iteritems(): | |||
|
196 | orig_paths[k] = changedpath(v) | |||
|
197 | pickle.dump((orig_paths, revnum, author, date, message), | |||
|
198 | fp, protocol) | |||
|
199 | ||||
|
200 | try: | |||
|
201 | # Use an ra of our own so that our parent can consume | |||
|
202 | # our results without confusing the server. | |||
|
203 | t = transport.SvnRaTransport(url=self.url) | |||
|
204 | svn.ra.get_log(t.ra, paths, start, end, limit, | |||
|
205 | discover_changed_paths, | |||
|
206 | strict_node_history, | |||
|
207 | receiver) | |||
|
208 | except SubversionException, (_, num): | |||
|
209 | pickle.dump(num, fp, protocol) | |||
|
210 | else: | |||
|
211 | pickle.dump(None, fp, protocol) | |||
|
212 | fp.close() | |||
|
213 | ||||
|
214 | def parent(fp): | |||
|
215 | while True: | |||
|
216 | entry = pickle.load(fp) | |||
|
217 | try: | |||
|
218 | orig_paths, revnum, author, date, message = entry | |||
|
219 | except: | |||
|
220 | if entry is None: | |||
|
221 | break | |||
|
222 | raise SubversionException("child raised exception", entry) | |||
|
223 | yield entry | |||
|
224 | ||||
|
225 | rfd, wfd = os.pipe() | |||
|
226 | pid = os.fork() | |||
|
227 | if pid: | |||
|
228 | os.close(wfd) | |||
|
229 | for p in parent(os.fdopen(rfd, 'rb')): | |||
|
230 | yield p | |||
|
231 | ret = os.waitpid(pid, 0)[1] | |||
|
232 | if ret: | |||
|
233 | raise util.Abort(_('get_log %s') % util.explain_exit(ret)) | |||
|
234 | else: | |||
|
235 | os.close(rfd) | |||
|
236 | child(os.fdopen(wfd, 'wb')) | |||
|
237 | os._exit(0) | |||
|
238 | ||||
177 | def gettags(self): |
|
239 | def gettags(self): | |
178 | tags = {} |
|
240 | tags = {} | |
179 | def parselogentry(*arg, **args): |
|
241 | for entry in self.get_log(['/tags'], 0, self.revnum(self.head)): | |
180 |
orig_paths, revnum, author, date, message |
|
242 | orig_paths, revnum, author, date, message = entry | |
181 | for path in orig_paths: |
|
243 | for path in orig_paths: | |
182 | if not path.startswith('/tags/'): |
|
244 | if not path.startswith('/tags/'): | |
183 | continue |
|
245 | continue | |
@@ -186,15 +248,7 b' class convert_svn(converter_source):' | |||||
186 | rev = ent.copyfrom_rev |
|
248 | rev = ent.copyfrom_rev | |
187 | tag = path.split('/', 2)[2] |
|
249 | tag = path.split('/', 2)[2] | |
188 | tags[tag] = self.revid(rev, module=source) |
|
250 | tags[tag] = self.revid(rev, module=source) | |
189 |
|
251 | return tags | ||
190 | start = self.revnum(self.head) |
|
|||
191 | try: |
|
|||
192 | svn.ra.get_log(self.ra, ['/tags'], 0, start, 0, True, False, |
|
|||
193 | parselogentry) |
|
|||
194 | return tags |
|
|||
195 | except SubversionException: |
|
|||
196 | self.ui.note('no tags found at revision %d\n' % start) |
|
|||
197 | return {} |
|
|||
198 |
|
252 | |||
199 | # -- helper functions -- |
|
253 | # -- helper functions -- | |
200 |
|
254 | |||
@@ -276,23 +330,10 b' class convert_svn(converter_source):' | |||||
276 | self.ui.debug('Ignoring %r since it is not under %r\n' % (path, module)) |
|
330 | self.ui.debug('Ignoring %r since it is not under %r\n' % (path, module)) | |
277 | return None |
|
331 | return None | |
278 |
|
332 | |||
279 | received = [] |
|
|||
280 | # svn.ra.get_log requires no other calls to the ra until it completes, |
|
|||
281 | # so we just collect the log entries and parse them afterwards |
|
|||
282 | def receivelog(orig_paths, revnum, author, date, message, pool): |
|
|||
283 | if self.is_blacklisted(revnum): |
|
|||
284 | self.ui.note('skipping blacklisted revision %d\n' % revnum) |
|
|||
285 | return |
|
|||
286 |
|
||||
287 | if orig_paths is None: |
|
|||
288 | self.ui.debug('revision %d has no entries\n' % revnum) |
|
|||
289 | return |
|
|||
290 |
|
||||
291 | received.append((revnum, orig_paths.items(), author, date, message)) |
|
|||
292 |
|
||||
293 | self.child_cset = None |
|
333 | self.child_cset = None | |
294 |
def parselogentry( |
|
334 | def parselogentry(orig_paths, revnum, author, date, message): | |
295 |
self.ui.debug("parsing revision %d\n" % |
|
335 | self.ui.debug("parsing revision %d (%d changes)\n" % | |
|
336 | (revnum, len(orig_paths))) | |||
296 |
|
337 | |||
297 | if revnum in self.modulemap: |
|
338 | if revnum in self.modulemap: | |
298 | new_module = self.modulemap[revnum] |
|
339 | new_module = self.modulemap[revnum] | |
@@ -318,6 +359,7 b' class convert_svn(converter_source):' | |||||
318 | except IndexError: |
|
359 | except IndexError: | |
319 | branch = None |
|
360 | branch = None | |
320 |
|
361 | |||
|
362 | orig_paths = orig_paths.items() | |||
321 | orig_paths.sort() |
|
363 | orig_paths.sort() | |
322 | for path, ent in orig_paths: |
|
364 | for path, ent in orig_paths: | |
323 | # self.ui.write("path %s\n" % path) |
|
365 | # self.ui.write("path %s\n" % path) | |
@@ -527,13 +569,15 b' class convert_svn(converter_source):' | |||||
527 | try: |
|
569 | try: | |
528 | discover_changed_paths = True |
|
570 | discover_changed_paths = True | |
529 | strict_node_history = False |
|
571 | strict_node_history = False | |
530 |
|
|
572 | for entry in self.get_log([self.module], from_revnum, to_revnum): | |
531 | discover_changed_paths, strict_node_history, |
|
573 | orig_paths, revnum, author, date, message = entry | |
532 | receivelog) |
|
574 | if self.is_blacklisted(revnum): | |
533 | self.ui.note('parsing %d log entries for "%s"\n' % |
|
575 | self.ui.note('skipping blacklisted revision %d\n' % revnum) | |
534 | (len(received), self.module)) |
|
576 | continue | |
535 | for entry in received: |
|
577 | if orig_paths is None: | |
536 | parselogentry(entry) |
|
578 | self.ui.debug('revision %d has no entries\n' % revnum) | |
|
579 | continue | |||
|
580 | parselogentry(orig_paths, revnum, author, date, message) | |||
537 | except SubversionException, (_, num): |
|
581 | except SubversionException, (_, num): | |
538 | if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION: |
|
582 | if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION: | |
539 | raise NoSuchRevision(branch=self, |
|
583 | raise NoSuchRevision(branch=self, |
General Comments 0
You need to be logged in to leave comments.
Login now