# fbmetaedit.py - improved metaedit functionality # # Copyright 2017 Facebook, Inc. # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. """extends the existing metaedit functionality Speeds up metaedit by reusing manifestes and allows to metaedit multiple commit at once. """ from mercurial import ( cmdutil, context, error, extensions, lock as lockmod, scmutil, obsolete, ) from mercurial.i18n import _ wrapcommand = extensions.wrapcommand wrapfunction = extensions.wrapfunction cmdtable = {} command = cmdutil.command(cmdtable) testedwith = 'ships-with-fb-hgext' def uisetup(ui): order = extensions._order order.remove('fbmetaedit') order.append('fbmetaedit') def extsetup(ui): try: evolve = extensions.find('evolve') def metarewrite(repo, old, newbases, commitopts): """Return (nodeid, created) where nodeid is the identifier of the changeset generated by the rewrite process, and created is True if nodeid was actually created. If created is False, nodeid references a changeset existing before the rewrite call. """ wlock = lock = tr = None try: wlock = repo.wlock() lock = repo.lock() tr = repo.transaction('rewrite') updatebookmarks = evolve._bookmarksupdater(repo, old.node(), tr) message = cmdutil.logmessage(repo.ui, commitopts) if not message: message = old.description() user = commitopts.get('user') or old.user() date = commitopts.get('date') or None # old.date() extra = dict(commitopts.get('extra', old.extra())) extra['branch'] = old.branch() new = context.metadataonlyctx(repo, old, parents=newbases, text=message, user=user, date=date, extra=extra) if commitopts.get('edit'): new._text = cmdutil.commitforceeditor(repo, new, []) revcount = len(repo) newid = repo.commitctx(new) new = repo[newid] created = len(repo) != revcount updatebookmarks(newid) tr.close() return newid, created finally: lockmod.release(tr, lock, wlock) def _metaedit(orig, ui, repo, *revs, **opts): if opts['fold']: return orig(ui, repo, *revs, **opts) revs = list(revs) revs.extend(opts['rev']) if not revs: revs = ['.'] wlock = lock = None try: wlock = repo.wlock() lock = repo.lock() revs = scmutil.revrange(repo, revs) if repo.revs("%ld and public()", revs): raise error.Abort(_('cannot edit commit information for ' 'public revisions')) wctx = repo[None] p1 = wctx.p1() tr = repo.transaction('metaedit') try: commitopts = opts.copy() allctx = [repo[r] for r in revs] if commitopts.get('message') or commitopts.get('logfile'): commitopts['edit'] = False else: commitopts['edit'] = True replacemap = {} # we need topological order allctx = sorted(allctx, key=lambda c: c.rev()) # all descendats that can be safely rewritten newunstable = evolve._disallowednewunstable(repo, revs) newunstablectx = sorted([repo[r] for r in newunstable], key=lambda c: c.rev()) def _rewritesingle(c, _commitopts): if _commitopts.get('edit', False): _commitopts['message'] = \ "HG: Commit message of changeset %s\n%s" %\ (str(c), c.description()) bases = [ replacemap.get(c.p1().node(), c.p1().node()), replacemap.get(c.p2().node(), c.p2().node()), ] newid, created = metarewrite(repo, c, bases, commitopts=_commitopts) if created: replacemap[c.node()] = newid for c in allctx: _rewritesingle(c, commitopts) for c in newunstablectx: _rewritesingle(c, {'date': commitopts.get('date') or None}) if p1.node() in replacemap: repo.setparents(replacemap[p1.node()]) if len(replacemap) > 0: obsolete.createmarkers( repo, [(repo[old], (repo[new],)) for old, new in replacemap.iteritems()], ) # TODO: set poroper phase boundaries (affects secret # phase only) else: ui.status(_("nothing changed\n")) tr.close() finally: tr.release() finally: lockmod.release(lock, wlock) wrapcommand(evolve.cmdtable, 'metaedit', _metaedit) except KeyError: pass # evolve not found