35 import transport |
36 import transport |
36 except ImportError: |
37 except ImportError: |
37 pass |
38 pass |
38 |
39 |
39 class CompatibilityException(Exception): pass |
40 class CompatibilityException(Exception): pass |
|
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 |
40 |
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): |
44 super(convert_svn, self).__init__(ui, url, rev=rev) |
51 super(convert_svn, self).__init__(ui, url, rev=rev) |
172 commit = self.commits[rev] |
179 commit = self.commits[rev] |
173 # caller caches the result, so free it here to release memory |
180 # caller caches the result, so free it here to release memory |
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, pool = arg |
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 |
184 ent = orig_paths[path] |
246 ent = orig_paths[path] |
185 source = ent.copyfrom_path |
247 source = ent.copyfrom_path |
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 |
201 def revid(self, revnum, module=None): |
255 def revid(self, revnum, module=None): |
202 if not module: |
256 if not module: |
274 |
328 |
275 # The path is outside our tracked tree... |
329 # The path is outside our tracked tree... |
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((revnum, orig_paths, author, date, message)): |
334 def parselogentry(orig_paths, revnum, author, date, message): |
295 self.ui.debug("parsing revision %d\n" % revnum) |
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] |
299 if new_module != self.module: |
340 if new_module != self.module: |
300 self.module = new_module |
341 self.module = new_module |
525 (self.module, from_revnum, to_revnum)) |
567 (self.module, from_revnum, to_revnum)) |
526 |
568 |
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 svn.ra.get_log(self.ra, [self.module], from_revnum, to_revnum, 0, |
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, |
540 revision="Revision number %d" % to_revnum) |
584 revision="Revision number %d" % to_revnum) |
541 raise |
585 raise |