# HG changeset patch # User Siddharth Agarwal # Date 1461626682 25200 # Node ID 4fd0db2f6d849562699337e24fac6a377158cad0 # Parent 40d7b0c4abb121b3e325008060fa4eb9029eb4a2 commands: introduce a new command to edit commit metadata This patch introduces metaedit, a command to metadata of a set of revisions without updating the working copy. This is particularly relevant for repositories where changing the parent of the working copy is time-consuming. We could add more stack manipulation operations to metaedit in the future. diff -r 40d7b0c4abb1 -r 4fd0db2f6d84 hgext/evolve.py --- a/hgext/evolve.py Mon Apr 25 16:24:42 2016 -0700 +++ b/hgext/evolve.py Mon Apr 25 16:24:42 2016 -0700 @@ -3158,6 +3158,101 @@ finally: lockmod.release(lock, wlock) +@command('^metaedit', + [('r', 'rev', [], _("revision to edit")), + ] + commitopts + commitopts2, + _('hg metaedit [OPTION]... [-r] [REV]')) +def metaedit(ui, repo, *revs, **opts): + """edit commit information + + Edits the commit information for the specified revision. By default, edits + commit information for the working directory parent. + + .. container:: verbose + + Some examples: + + - Edit the commit message for the working directory parent:: + + hg metaedit + + - Change the username for the working directory parent:: + + hg metaedit --user 'New User ' + + """ + 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 len(revs) > 1: + # TODO: handle multiple revisions. This is somewhat tricky because + # if we want to edit a series of commits: + # + # a ---- b ---- c + # + # we need to rewrite a first, then directly rewrite b on top of the + # new a, then rewrite c on top of the new b. So we need to handle + # revisions in topological order. + raise error.Abort(_('editing multiple revisions is not ' + 'currently supported')) + + newunstable = _disallowednewunstable(repo, revs) + if newunstable: + raise error.Abort( + _('cannot edit commit information in the middle of a stack'), + hint=_('%s will be affected') % repo[newunstable.first()]) + if repo.revs("%ld and public()", revs): + raise error.Abort(_('cannot edit commit information for public ' + 'revisions')) + root = head = repo[revs.first()] + + wctx = repo[None] + p1 = wctx.p1() + tr = repo.transaction('metaedit') + newp1 = None + try: + commitopts = opts.copy() + allctx = [repo[r] for r in revs] + targetphase = max(c.phase() for c in allctx) + + if commitopts.get('message') or commitopts.get('logfile'): + commitopts['edit'] = False + else: + msgs = [head.description()] + commitopts['message'] = "\n".join(msgs) + commitopts['edit'] = True + + # TODO: if the author and message are the same, don't create a new + # hash. Right now we create a new hash because the date can be + # different. + newid, created = rewrite(repo, root, allctx, head, + [root.p1().node(), root.p2().node()], + commitopts=commitopts) + if created: + if p1.rev() in revs: + newp1 = newid + phases.retractboundary(repo, tr, targetphase, [newid]) + obsolete.createmarkers(repo, [(ctx, (repo[newid],)) + for ctx in allctx]) + else: + ui.status(_("nothing changed\n")) + tr.close() + finally: + tr.release() + + if newp1 is not None: + hg.update(repo, newp1) + finally: + lockmod.release(lock, wlock) + def _foldcheck(repo, revs): roots = repo.revs('roots(%ld)', revs) if len(roots) > 1: diff -r 40d7b0c4abb1 -r 4fd0db2f6d84 tests/test-evolve.t --- a/tests/test-evolve.t Mon Apr 25 16:24:42 2016 -0700 +++ b/tests/test-evolve.t Mon Apr 25 16:24:42 2016 -0700 @@ -2,6 +2,7 @@ > [defaults] > amend=-d "0 0" > fold=-d "0 0" + > metaedit=-d "0 0" > [web] > push_ssl = false > allow_push = * @@ -1457,3 +1458,67 @@ $ hg status newlyadded A newlyadded + +hg metaedit +----------- + + $ hg update --clean . + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ rm newlyadded + $ hg metaedit -r 0 + abort: cannot edit commit information for public revisions + [255] +check that metaedit respects allowunstable + $ hg metaedit '.^' --config 'experimental.evolution=createmarkers, allnewcommands' + abort: cannot edit commit information in the middle of a stack + (c904da5245b0 will be affected) + [255] + $ hg metaedit --user foobar + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log --template '{rev}: {author}\n' -r '42:' --hidden + 42: test + 43: foobar + $ hg log --template '{rev}: {author}\n' -r . + 43: foobar + +TODO: support this + $ hg metaedit '.^::.' + abort: editing multiple revisions is not currently supported + [255] + +no new commit is created here because the date is the same + $ HGEDITOR=cat hg metaedit + will be evolved safely + + + HG: Enter commit message. Lines beginning with 'HG:' are removed. + HG: Leave message empty to abort commit. + HG: -- + HG: user: foobar + HG: branch 'default' + HG: changed a + nothing changed + + $ glog -r '.^::.' + @ 43:62353add3dfb@default(draft) will be evolved safely + | + o 41:34ae045ec400@default(draft) amended + | + ~ + +TODO: don't create a new commit in this case + $ hg metaedit --config defaults.metaedit= + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg log -r '.^::.' --template '{rev}: {desc|firstline}\n' + 41: amended + 44: will be evolved safely + + $ hg up .^ + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg metaedit --user foobar2 44 + $ hg log --template '{rev}: {author}\n' -r '42:' --hidden + 42: test + 43: foobar + 44: foobar + 45: foobar2 + $ hg diff -r 44 -r 45 --hidden