183 return [t] |
187 return [t] |
184 return [] |
188 return [] |
185 |
189 |
186 def uisetup(ui): |
190 def uisetup(ui): |
187 destination.modsetup(ui) |
191 destination.modsetup(ui) |
188 topicrevset.modsetup(ui) |
|
189 discovery.modsetup(ui) |
192 discovery.modsetup(ui) |
190 topicmap.modsetup(ui) |
193 topicmap.modsetup(ui) |
191 setupimportexport(ui) |
194 setupimportexport(ui) |
192 |
195 |
193 extensions.afterloaded('rebase', _fixrebase) |
196 extensions.afterloaded('rebase', _fixrebase) |
194 |
197 |
195 entry = extensions.wrapcommand(commands.table, 'commit', commitwrap) |
198 entry = extensions.wrapcommand(commands.table, 'commit', commitwrap) |
196 entry[1].append(('t', 'topic', '', |
199 entry[1].append(('t', 'topic', '', |
197 _("use specified topic"), _('TOPIC'))) |
200 _("use specified topic"), _('TOPIC'))) |
|
201 |
|
202 entry = extensions.wrapcommand(commands.table, 'push', pushoutgoingwrap) |
|
203 entry[1].append(('t', 'topic', '', |
|
204 _("topic to push"), _('TOPIC'))) |
|
205 |
|
206 entry = extensions.wrapcommand(commands.table, 'outgoing', |
|
207 pushoutgoingwrap) |
|
208 entry[1].append(('t', 'topic', '', |
|
209 _("topic to push"), _('TOPIC'))) |
198 |
210 |
199 extensions.wrapfunction(cmdutil, 'buildcommittext', committextwrap) |
211 extensions.wrapfunction(cmdutil, 'buildcommittext', committextwrap) |
200 extensions.wrapfunction(merge, 'update', mergeupdatewrap) |
212 extensions.wrapfunction(merge, 'update', mergeupdatewrap) |
201 # We need to check whether t0 or b0 is passed to override the default update |
213 # We need to check whether t0 or b0 is passed to override the default update |
202 # behaviour of changing topic and I can't find a better way |
214 # behaviour of changing topic and I can't find a better way |
298 usetopic = not self._repo.publishing() |
310 usetopic = not self._repo.publishing() |
299 return self._repo.branchmap(topic=usetopic) |
311 return self._repo.branchmap(topic=usetopic) |
300 peer.__class__ = topicpeer |
312 peer.__class__ = topicpeer |
301 return peer |
313 return peer |
302 |
314 |
|
315 def transaction(self, desc, *a, **k): |
|
316 ctr = self.currenttransaction() |
|
317 tr = super(topicrepo, self).transaction(desc, *a, **k) |
|
318 if desc in ('strip', 'repair') or ctr is not None: |
|
319 return tr |
|
320 |
|
321 # real transaction start |
|
322 ct = self.currenttopic |
|
323 if not ct: |
|
324 return tr |
|
325 ctwasempty = stack.stackdata(self, topic=ct)['changesetcount'] == 0 |
|
326 |
|
327 reporef = weakref.ref(self) |
|
328 |
|
329 def currenttopicempty(tr): |
|
330 # check active topic emptyness |
|
331 repo = reporef() |
|
332 csetcount = stack.stackdata(repo, topic=ct)['changesetcount'] |
|
333 empty = csetcount == 0 |
|
334 if empty and not ctwasempty: |
|
335 ui.status('active topic %r is now empty\n' % ct) |
|
336 if ctwasempty and not empty: |
|
337 if csetcount == 1: |
|
338 msg = _('active topic %r grew its first changeset\n') |
|
339 ui.status(msg % ct) |
|
340 else: |
|
341 msg = _('active topic %r grew its %s first changesets\n') |
|
342 ui.status(msg % (ct, csetcount)) |
|
343 |
|
344 tr.addpostclose('signalcurrenttopicempty', currenttopicempty) |
|
345 return tr |
|
346 |
303 repo.__class__ = topicrepo |
347 repo.__class__ = topicrepo |
304 repo._topics = None |
348 repo._topics = None |
305 if util.safehasattr(repo, 'names'): |
349 if util.safehasattr(repo, 'names'): |
306 repo.names.addnamespace(namespaces.namespace( |
350 repo.names.addnamespace(namespaces.namespace( |
307 'topics', 'topic', namemap=_namemap, nodemap=_nodemap, |
351 'topics', 'topic', namemap=_namemap, nodemap=_nodemap, |
308 listnames=lambda repo: repo.topics)) |
352 listnames=lambda repo: repo.topics)) |
|
353 # Wrap workingctx extra to return the topic name |
|
354 extensions.wrapfunction(context.workingctx, '__init__', wrapinit) |
|
355 # Wrap changelog.add to drop empty topic |
|
356 extensions.wrapfunction(changelog.changelog, 'add', wrapadd) |
|
357 |
|
358 def wrapinit(orig, self, repo, *args, **kwargs): |
|
359 orig(self, repo, *args, **kwargs) |
|
360 if repo.currenttopic: |
|
361 self._extra[constants.extrakey] = repo.currenttopic |
|
362 else: |
|
363 # Empty key will be dropped from extra by another hack at the changegroup level |
|
364 self._extra[constants.extrakey] = '' |
|
365 |
|
366 def wrapadd(orig, cl, manifest, files, desc, transaction, p1, p2, user, |
|
367 date=None, extra=None): |
|
368 if constants.extrakey in extra and not extra[constants.extrakey]: |
|
369 extra = extra.copy() |
|
370 del extra[constants.extrakey] |
|
371 return orig(cl, manifest, files, desc, transaction, p1, p2, user, |
|
372 date=date, extra=extra) |
|
373 |
|
374 # revset predicates are automatically registered at loading via this symbol |
|
375 revsetpredicate = topicrevset.revsetpredicate |
309 |
376 |
310 @command('topics', [ |
377 @command('topics', [ |
311 ('', 'clear', False, 'clear active topic if any'), |
378 ('', 'clear', False, 'clear active topic if any'), |
312 ('r', 'rev', '', 'revset of existing revisions', _('REV')), |
379 ('r', 'rev', [], 'revset of existing revisions', _('REV')), |
313 ('l', 'list', False, 'show the stack of changeset in the topic'), |
380 ('l', 'list', False, 'show the stack of changeset in the topic'), |
314 ('', 'age', False, 'show when you last touched the topics') |
381 ('', 'age', False, 'show when you last touched the topics'), |
|
382 ('', 'current', None, 'display the current topic only'), |
315 ] + commands.formatteropts, |
383 ] + commands.formatteropts, |
316 _('hg topics [TOPIC]')) |
384 _('hg topics [TOPIC]')) |
317 def topics(ui, repo, topic='', clear=False, rev=None, list=False, **opts): |
385 def topics(ui, repo, topic=None, **opts): |
318 """View current topic, set current topic, change topic for a set of revisions, or see all topics. |
386 """View current topic, set current topic, change topic for a set of revisions, or see all topics. |
319 |
387 |
320 Clear topic on existing topiced revisions: |
388 Clear topic on existing topiced revisions:: |
321 `hg topic --rev <related revset> --clear` |
389 |
322 |
390 hg topics --rev <related revset> --clear |
323 Change topic on some revisions: |
391 |
324 `hg topic <newtopicname> --rev <related revset>` |
392 Change topic on some revisions:: |
325 |
393 |
326 Clear current topic: |
394 hg topics <newtopicname> --rev <related revset> |
327 `hg topic --clear` |
395 |
328 |
396 Clear current topic:: |
329 Set current topic: |
397 |
330 `hg topic <topicname>` |
398 hg topics --clear |
331 |
399 |
332 List of topics: |
400 Set current topic:: |
333 `hg topics` |
401 |
334 |
402 hg topics <topicname> |
335 List of topics with their last touched time sorted according to it: |
403 |
336 `hg topic --age` |
404 List of topics:: |
|
405 |
|
406 hg topics |
|
407 |
|
408 List of topics sorted according to their last touched time displaying last |
|
409 touched time and the user who last touched the topic:: |
|
410 |
|
411 hg topics --age |
337 |
412 |
338 The active topic (if any) will be prepended with a "*". |
413 The active topic (if any) will be prepended with a "*". |
339 |
414 |
|
415 The `--current` flag helps to take active topic into account. For |
|
416 example, if you want to set the topic on all the draft changesets to the |
|
417 active topic, you can do: |
|
418 `hg topics -r "draft()" --current` |
|
419 |
340 The --verbose version of this command display various information on the state of each topic.""" |
420 The --verbose version of this command display various information on the state of each topic.""" |
|
421 |
|
422 clear = opts.get('clear') |
|
423 list = opts.get('list') |
|
424 rev = opts.get('rev') |
|
425 current = opts.get('current') |
|
426 age = opts.get('age') |
|
427 |
|
428 if current and topic: |
|
429 raise error.Abort(_("cannot use --current when setting a topic")) |
|
430 if current and clear: |
|
431 raise error.Abort(_("cannot use --current and --clear")) |
|
432 if clear and topic: |
|
433 raise error.Abort(_("cannot use --clear when setting a topic")) |
|
434 if age and topic: |
|
435 raise error.Abort(_("cannot use --age while setting a topic")) |
|
436 |
|
437 touchedrevs = set() |
|
438 if rev: |
|
439 touchedrevs = scmutil.revrange(repo, rev) |
|
440 |
|
441 if topic: |
|
442 topic = topic.strip() |
|
443 if not topic: |
|
444 raise error.Abort(_("topic name cannot consist entirely of whitespaces")) |
|
445 # Have some restrictions on the topic name just like bookmark name |
|
446 scmutil.checknewlabel(repo, topic, 'topic') |
|
447 |
341 if list: |
448 if list: |
342 if clear or rev: |
449 if clear or rev: |
343 raise error.Abort(_("cannot use --clear or --rev with --list")) |
450 raise error.Abort(_("cannot use --clear or --rev with --list")) |
344 if not topic: |
451 if not topic: |
345 topic = repo.currenttopic |
452 topic = repo.currenttopic |
346 if not topic: |
453 if not topic: |
347 raise error.Abort(_('no active topic to list')) |
454 raise error.Abort(_('no active topic to list')) |
348 return stack.showstack(ui, repo, topic=topic, opts=opts) |
455 return stack.showstack(ui, repo, topic=topic, opts=opts) |
349 |
456 |
350 if rev: |
457 if touchedrevs: |
351 if not obsolete.isenabled(repo, obsolete.createmarkersopt): |
458 if not obsolete.isenabled(repo, obsolete.createmarkersopt): |
352 raise error.Abort(_('must have obsolete enabled to change topics')) |
459 raise error.Abort(_('must have obsolete enabled to change topics')) |
353 if clear: |
460 if clear: |
354 topic = None |
461 topic = None |
|
462 elif opts.get('current'): |
|
463 topic = repo.currenttopic |
355 elif not topic: |
464 elif not topic: |
356 raise error.Abort('changing topic requires a topic name or --clear') |
465 raise error.Abort('changing topic requires a topic name or --clear') |
357 if any(not c.mutable() for c in repo.set('%r and public()', rev)): |
466 if repo.revs('%ld and public()', touchedrevs): |
358 raise error.Abort("can't change topic of a public change") |
467 raise error.Abort("can't change topic of a public change") |
359 return _changetopics(ui, repo, rev, topic) |
468 wl = l = txn = None |
360 |
469 try: |
|
470 wl = repo.wlock() |
|
471 l = repo.lock() |
|
472 txn = repo.transaction('rewrite-topics') |
|
473 rewrote = _changetopics(ui, repo, touchedrevs, topic) |
|
474 txn.close() |
|
475 ui.status('changed topic on %d changes\n' % rewrote) |
|
476 finally: |
|
477 lockmod.release(txn, l, wl) |
|
478 repo.invalidate() |
|
479 return |
|
480 |
|
481 ct = repo.currenttopic |
361 if clear: |
482 if clear: |
|
483 empty = stack.stackdata(repo, topic=ct)['changesetcount'] == 0 |
|
484 if empty: |
|
485 if ct: |
|
486 ui.status(_('clearing empty topic "%s"\n') % ct) |
362 return _changecurrenttopic(repo, None) |
487 return _changecurrenttopic(repo, None) |
363 |
488 |
364 if topic: |
489 if topic: |
|
490 if not ct: |
|
491 ui.status(_('marked working directory as topic: %s\n') % topic) |
365 return _changecurrenttopic(repo, topic) |
492 return _changecurrenttopic(repo, topic) |
366 |
493 |
367 _listtopics(ui, repo, opts) |
494 # `hg topic --current` |
|
495 ret = 0 |
|
496 if current and not ct: |
|
497 ui.write_err(_('no active topic\n')) |
|
498 ret = 1 |
|
499 elif current: |
|
500 fm = ui.formatter('topic', opts) |
|
501 namemask = '%s\n' |
|
502 label = 'topic.active' |
|
503 fm.startitem() |
|
504 fm.write('topic', namemask, ct, label=label) |
|
505 fm.end() |
|
506 else: |
|
507 _listtopics(ui, repo, opts) |
|
508 return ret |
368 |
509 |
369 @command('stack', [ |
510 @command('stack', [ |
370 ] + commands.formatteropts, |
511 ] + commands.formatteropts, |
371 _('hg stack [TOPIC]')) |
512 _('hg stack [TOPIC]')) |
372 def cmdstack(ui, repo, topic='', **opts): |
513 def cmdstack(ui, repo, topic='', **opts): |
383 topic = repo.currenttopic |
524 topic = repo.currenttopic |
384 if topic is None: |
525 if topic is None: |
385 branch = repo[None].branch() |
526 branch = repo[None].branch() |
386 return stack.showstack(ui, repo, branch=branch, topic=topic, opts=opts) |
527 return stack.showstack(ui, repo, branch=branch, topic=topic, opts=opts) |
387 |
528 |
|
529 @command('debugcb|debugconvertbookmark', [ |
|
530 ('b', 'bookmark', '', _('bookmark to convert to topic')), |
|
531 ('', 'all', None, _('convert all bookmarks to topics')), |
|
532 ], |
|
533 _('[-b BOOKMARK] [--all]')) |
|
534 def debugconvertbookmark(ui, repo, **opts): |
|
535 """Converts a bookmark to a topic with the same name. |
|
536 """ |
|
537 |
|
538 bookmark = opts.get('bookmark') |
|
539 convertall = opts.get('all') |
|
540 |
|
541 if convertall and bookmark: |
|
542 raise error.Abort(_("cannot use '--all' and '-b' together")) |
|
543 if not (convertall or bookmark): |
|
544 raise error.Abort(_("you must specify either '--all' or '-b'")) |
|
545 |
|
546 bmstore = repo._bookmarks |
|
547 |
|
548 nodetobook = {} |
|
549 for book, revnode in bmstore.iteritems(): |
|
550 if nodetobook.get(revnode): |
|
551 nodetobook[revnode].append(book) |
|
552 else: |
|
553 nodetobook[revnode] = [book] |
|
554 |
|
555 # a list of nodes which we have skipped so that we don't print the skip |
|
556 # warning repeatedly |
|
557 skipped = [] |
|
558 |
|
559 actions = {} |
|
560 |
|
561 lock = wlock = tr = None |
|
562 try: |
|
563 wlock = repo.wlock() |
|
564 lock = repo.lock() |
|
565 if bookmark: |
|
566 try: |
|
567 node = bmstore[bookmark] |
|
568 except KeyError: |
|
569 raise error.Abort(_("no such bookmark exists: '%s'") % bookmark) |
|
570 |
|
571 revnum = repo[node].rev() |
|
572 if len(nodetobook[node]) > 1: |
|
573 ui.status(_("skipping revision '%d' as it has multiple bookmarks " |
|
574 "on it\n") % revnum) |
|
575 return |
|
576 targetrevs = _findconvertbmarktopic(repo, bookmark) |
|
577 if targetrevs: |
|
578 actions[(bookmark, revnum)] = targetrevs |
|
579 |
|
580 elif convertall: |
|
581 for bmark, revnode in sorted(bmstore.iteritems()): |
|
582 revnum = repo[revnode].rev() |
|
583 if revnum in skipped: |
|
584 continue |
|
585 if len(nodetobook[revnode]) > 1: |
|
586 ui.status(_("skipping '%d' as it has multiple bookmarks on" |
|
587 " it\n") % revnum) |
|
588 skipped.append(revnum) |
|
589 continue |
|
590 if bmark == '@': |
|
591 continue |
|
592 targetrevs = _findconvertbmarktopic(repo, bmark) |
|
593 if targetrevs: |
|
594 actions[(bmark, revnum)] = targetrevs |
|
595 |
|
596 if actions: |
|
597 try: |
|
598 tr = repo.transaction('debugconvertbookmark') |
|
599 for ((bmark, revnum), targetrevs) in sorted(actions.iteritems()): |
|
600 _applyconvertbmarktopic(ui, repo, targetrevs, revnum, bmark, tr) |
|
601 tr.close() |
|
602 finally: |
|
603 tr.release() |
|
604 finally: |
|
605 lockmod.release(lock, wlock) |
|
606 |
|
607 # inspired from mercurial.repair.stripbmrevset |
|
608 CONVERTBOOKREVSET = """ |
|
609 not public() and ( |
|
610 ancestors(bookmark(%s)) |
|
611 and not ancestors( |
|
612 ( |
|
613 (head() and not bookmark(%s)) |
|
614 or (bookmark() - bookmark(%s)) |
|
615 ) - ( |
|
616 descendants(bookmark(%s)) |
|
617 - bookmark(%s) |
|
618 ) |
|
619 ) |
|
620 ) |
|
621 """ |
|
622 |
|
623 def _findconvertbmarktopic(repo, bmark): |
|
624 """find revisions unambigiously defined by a bookmark |
|
625 |
|
626 find all changesets under the bookmark and under that bookmark only. |
|
627 """ |
|
628 return repo.revs(CONVERTBOOKREVSET, bmark, bmark, bmark, bmark, bmark) |
|
629 |
|
630 def _applyconvertbmarktopic(ui, repo, revs, old, bmark, tr): |
|
631 """apply bookmark convertion to topic |
|
632 |
|
633 Sets a topic as same as bname to all the changesets under the bookmark |
|
634 and delete the bookmark, if topic is set to any changeset |
|
635 |
|
636 old is the revision on which bookmark bmark is and tr is transaction object. |
|
637 """ |
|
638 |
|
639 rewrote = _changetopics(ui, repo, revs, bmark) |
|
640 # We didn't changed topic to any changesets because the revset |
|
641 # returned an empty set of revisions, so let's skip deleting the |
|
642 # bookmark corresponding to which we didn't put a topic on any |
|
643 # changeset |
|
644 if rewrote == 0: |
|
645 return |
|
646 ui.status(_('changed topic to "%s" on %d revisions\n') % (bmark, |
|
647 rewrote)) |
|
648 ui.debug('removing bookmark "%s" from "%d"' % (bmark, old)) |
|
649 bookmarks.delete(repo, tr, [bmark]) |
|
650 |
388 def _changecurrenttopic(repo, newtopic): |
651 def _changecurrenttopic(repo, newtopic): |
389 """changes the current topic.""" |
652 """changes the current topic.""" |
390 |
653 |
391 if newtopic: |
654 if newtopic: |
392 with repo.wlock(): |
655 with repo.wlock(): |
394 f.write(newtopic) |
657 f.write(newtopic) |
395 else: |
658 else: |
396 if repo.vfs.exists('topic'): |
659 if repo.vfs.exists('topic'): |
397 repo.vfs.unlink('topic') |
660 repo.vfs.unlink('topic') |
398 |
661 |
399 def _changetopics(ui, repo, revset, newtopic): |
662 def _changetopics(ui, repo, revs, newtopic): |
|
663 """ Changes topic to newtopic of all the revisions in the revset and return |
|
664 the count of revisions whose topic has been changed. |
|
665 """ |
400 rewrote = 0 |
666 rewrote = 0 |
401 wl = l = txn = None |
667 p1 = None |
402 try: |
668 p2 = None |
403 wl = repo.wlock() |
669 successors = {} |
404 l = repo.lock() |
670 for r in revs: |
405 txn = repo.transaction('rewrite-topics') |
671 c = repo[r] |
406 p1 = None |
672 |
407 p2 = None |
673 def filectxfn(repo, ctx, path): |
408 successors = {} |
674 try: |
409 for c in repo.set('%r', revset): |
675 return c[path] |
410 def filectxfn(repo, ctx, path): |
676 except error.ManifestLookupError: |
411 try: |
677 return None |
412 return c[path] |
678 fixedextra = dict(c.extra()) |
413 except error.ManifestLookupError: |
679 ui.debug('old node id is %s\n' % node.hex(c.node())) |
414 return None |
680 ui.debug('origextra: %r\n' % fixedextra) |
415 fixedextra = dict(c.extra()) |
681 oldtopic = fixedextra.get(constants.extrakey, None) |
416 ui.debug('old node id is %s\n' % node.hex(c.node())) |
682 if oldtopic == newtopic: |
417 ui.debug('origextra: %r\n' % fixedextra) |
683 continue |
418 oldtopic = fixedextra.get(constants.extrakey, None) |
684 if newtopic is None: |
419 if oldtopic == newtopic: |
685 del fixedextra[constants.extrakey] |
420 continue |
686 else: |
421 if newtopic is None: |
687 fixedextra[constants.extrakey] = newtopic |
422 del fixedextra[constants.extrakey] |
688 fixedextra[constants.changekey] = c.hex() |
423 else: |
689 if 'amend_source' in fixedextra: |
424 fixedextra[constants.extrakey] = newtopic |
690 # TODO: right now the commitctx wrapper in |
425 fixedextra[constants.changekey] = c.hex() |
691 # topicrepo overwrites the topic in extra if |
426 if 'amend_source' in fixedextra: |
692 # amend_source is set to support 'hg commit |
427 # TODO: right now the commitctx wrapper in |
693 # --amend'. Support for amend should be adjusted |
428 # topicrepo overwrites the topic in extra if |
694 # to not be so invasive. |
429 # amend_source is set to support 'hg commit |
695 del fixedextra['amend_source'] |
430 # --amend'. Support for amend should be adjusted |
696 ui.debug('changing topic of %s from %s to %s\n' % ( |
431 # to not be so invasive. |
697 c, oldtopic, newtopic)) |
432 del fixedextra['amend_source'] |
698 ui.debug('fixedextra: %r\n' % fixedextra) |
433 ui.debug('changing topic of %s from %s to %s\n' % ( |
699 # While changing topic of set of linear commits, make sure that |
434 c, oldtopic, newtopic)) |
700 # we base our commits on new parent rather than old parent which |
435 ui.debug('fixedextra: %r\n' % fixedextra) |
701 # was obsoleted while changing the topic |
436 # While changing topic of set of linear commits, make sure that |
702 p1 = c.p1().node() |
437 # we base our commits on new parent rather than old parent which |
703 p2 = c.p2().node() |
438 # was obsoleted while changing the topic |
704 if p1 in successors: |
439 p1 = c.p1().node() |
705 p1 = successors[p1] |
440 p2 = c.p2().node() |
706 if p2 in successors: |
441 if p1 in successors: |
707 p2 = successors[p2] |
442 p1 = successors[p1] |
708 mc = context.memctx( |
443 if p2 in successors: |
709 repo, (p1, p2), c.description(), |
444 p2 = successors[p2] |
710 c.files(), filectxfn, |
445 mc = context.memctx( |
711 user=c.user(), date=c.date(), extra=fixedextra) |
446 repo, (p1, p2), c.description(), |
712 newnode = repo.commitctx(mc) |
447 c.files(), filectxfn, |
713 successors[c.node()] = newnode |
448 user=c.user(), date=c.date(), extra=fixedextra) |
714 ui.debug('new node id is %s\n' % node.hex(newnode)) |
449 newnode = repo.commitctx(mc) |
715 obsolete.createmarkers(repo, [(c, (repo[newnode],))]) |
450 successors[c.node()] = newnode |
716 rewrote += 1 |
451 ui.debug('new node id is %s\n' % node.hex(newnode)) |
717 # move the working copy too |
452 obsolete.createmarkers(repo, [(c, (repo[newnode],))]) |
718 wctx = repo[None] |
453 rewrote += 1 |
719 # in-progress merge is a bit too complex for now. |
454 # move the working copy too |
720 if len(wctx.parents()) == 1: |
455 wctx = repo[None] |
721 newid = successors.get(wctx.p1().node()) |
456 # in-progress merge is a bit too complex for now. |
722 if newid is not None: |
457 if len(wctx.parents()) == 1: |
723 hg.update(repo, newid, quietempty=True) |
458 newid = successors.get(wctx.p1().node()) |
724 return rewrote |
459 if newid is not None: |
|
460 hg.update(repo, newid, quietempty=True) |
|
461 txn.close() |
|
462 finally: |
|
463 lock.release(txn, l, wl) |
|
464 repo.invalidate() |
|
465 ui.status('changed topic on %d changes\n' % rewrote) |
|
466 |
725 |
467 def _listtopics(ui, repo, opts): |
726 def _listtopics(ui, repo, opts): |
468 fm = ui.formatter('topics', opts) |
727 fm = ui.formatter('topics', opts) |
469 showlast = opts.get('age') |
728 showlast = opts.get('age') |
470 if showlast: |
729 if showlast: |
561 passed from current time for a topic as keys and topic name as values. |
822 passed from current time for a topic as keys and topic name as values. |
562 """ |
823 """ |
563 topicstime = {} |
824 topicstime = {} |
564 curtime = time.time() |
825 curtime = time.time() |
565 for t in topics: |
826 for t in topics: |
|
827 secspassed = -1 |
|
828 user = None |
566 maxtime = (0, 0) |
829 maxtime = (0, 0) |
567 trevs = repo.revs("topic(%s)", t) |
830 trevs = repo.revs("topic(%s)", t) |
568 # Need to check for the time of all changesets in the topic, whether |
831 # Need to check for the time of all changesets in the topic, whether |
569 # they are obsolete of non-heads |
832 # they are obsolete of non-heads |
570 # XXX: can we just rely on the max rev number for this |
833 # XXX: can we just rely on the max rev number for this |
571 for revs in trevs: |
834 for revs in trevs: |
572 rt = repo[revs].date() |
835 rt = repo[revs].date() |
573 if rt[0] > maxtime[0]: |
836 if rt[0] >= maxtime[0]: |
574 # Can store the rev to gather more info |
837 # Can store the rev to gather more info |
575 # latesthead = revs |
838 # latesthead = revs |
576 maxtime = rt |
839 maxtime = rt |
|
840 user = repo[revs].user() |
577 # looking on the markers also to get more information and accurate |
841 # looking on the markers also to get more information and accurate |
578 # last touch time. |
842 # last touch time. |
579 obsmarkers = obsutil.getmarkers(repo, [repo[revs].node()]) |
843 obsmarkers = compat.getmarkers(repo, [repo[revs].node()]) |
580 for marker in obsmarkers: |
844 for marker in obsmarkers: |
581 rt = marker.date() |
845 rt = marker.date() |
582 if rt[0] > maxtime[0]: |
846 if rt[0] > maxtime[0]: |
|
847 user = marker.metadata().get('user', user) |
583 maxtime = rt |
848 maxtime = rt |
584 # is the topic still yet untouched |
849 |
585 if not trevs: |
850 # Making the username more better |
586 secspassed = -1 |
851 username = None |
587 else: |
852 if user: |
|
853 # user is of form "abc <abc@xyz.com>" |
|
854 username = user.split('<')[0] |
|
855 if not username: |
|
856 # user is of form "<abc@xyz.com>" |
|
857 username = user[1:-1] |
|
858 username = username.strip() |
|
859 |
|
860 topicuser = (t, username) |
|
861 |
|
862 if trevs: |
588 secspassed = (curtime - maxtime[0]) |
863 secspassed = (curtime - maxtime[0]) |
589 try: |
864 try: |
590 topicstime[secspassed][1].append(t) |
865 topicstime[secspassed][1].append(topicuser) |
591 except KeyError: |
866 except KeyError: |
592 topicstime[secspassed] = (maxtime, [t]) |
867 topicstime[secspassed] = (maxtime, [topicuser]) |
593 |
868 |
594 return topicstime |
869 return topicstime |
595 |
870 |
596 def summaryhook(ui, repo): |
871 def summaryhook(ui, repo): |
597 t = repo.currenttopic |
872 t = repo.currenttopic |