Patchwork D122: phabricator: add --amend option to phabsend

login
register
mail settings
Submitter phabricator
Date July 18, 2017, 4:20 a.m.
Message ID <differential-rev-PHID-DREV-dh4zicpobka3useuhxo6-req@phab.mercurial-scm.org>
Download mbox | patch
Permalink /patch/22470/
State Superseded, archived
Headers show

Comments

phabricator - July 18, 2017, 4:20 a.m.
quark created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Previously `hg phabsend` was imitating `hg email` and won't mutate
  changesets. That works fine with reviewer-push workflow, reviewers run
  `phabread`, `import`.
  
  However, it does not work well with author-push workflow. Namely, the author
  needs to run extra commands to get the right commit message, and remove the
  local tag after push.
  
  This patch solves those issues by adding the `--amend` option, so local
  changesets will have the right commit message, and tags become unnecessary.

TEST PLAN
  Given the following DAG:
  
    o  17
    o  16
    | o  15
    | @  14
    |/
    o  13
    o  12
  
  Run `hg phabsend '(13::)-17'  --amend`, check the new DAG looks like:
  
    o  21
    | o  20
    | @  19
    |/
    o  18
    | o  17
    | x  16
    | x  13
    |/
    o  12
  
  And commit messages are updated to contain the `Differential Revision` lines.
  
  Also check `phabsend .` followed by a `phabsend . --amend`, the commit
  message will be updated and the tag will be removed.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D122

AFFECTED FILES
  contrib/phabricator.py

CHANGE DETAILS




EMAIL PREFERENCES
  https://phab.mercurial-scm.org/settings/panel/emailpreferences/

To: quark, #hg-reviewers
Cc: mercurial-devel
phabricator - July 18, 2017, 2:32 p.m.
mitrandir accepted this revision.
mitrandir added inline comments.

INLINE COMMENTS

> phabricator.py:413
> +                drev = [d for d in drevs if int(d[r'id']) == drevid][0]
> +                newdesc = getdescfromdrev(drev)
> +                # Make sure commit message contain "Differential Revision"

I think it would be better to use differential.getcommitmessage for this. For example "reviewed by" field is a really nice thing to have in the commit message.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D122

EMAIL PREFERENCES
  https://phab.mercurial-scm.org/settings/panel/emailpreferences/

To: quark, #hg-reviewers, mitrandir
Cc: mercurial-devel
phabricator - July 18, 2017, 4:40 p.m.
quark added inline comments.

INLINE COMMENTS

> mitrandir wrote in phabricator.py:413
> I think it would be better to use differential.getcommitmessage for this. For example "reviewed by" field is a really nice thing to have in the commit message.

I was trying to make it behave closer to `hg email`. IIRC reviewers have some other plans to show "who accepts" information (that works for both Phabricator and email submits) on hgweb, so I didn't pay too much attention on "Reviewed By" here.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D122

EMAIL PREFERENCES
  https://phab.mercurial-scm.org/settings/panel/emailpreferences/

To: quark, #hg-reviewers, mitrandir
Cc: mercurial-devel

Patch

diff --git a/contrib/phabricator.py b/contrib/phabricator.py
--- a/contrib/phabricator.py
+++ b/contrib/phabricator.py
@@ -38,6 +38,8 @@ 
 from mercurial.node import bin, nullid
 from mercurial.i18n import _
 from mercurial import (
+    cmdutil,
+    context,
     encoding,
     error,
     mdiff,
@@ -325,6 +327,7 @@ 
 
 @command('phabsend',
          [('r', 'rev', [], _('revisions to send'), _('REV')),
+          ('', 'amend', False, _('update commit messages')),
           ('', 'reviewer', [], _('specify reviewers'))],
          _('REV [OPTIONS]'))
 def phabsend(ui, repo, *revs, **opts):
@@ -334,16 +337,21 @@ 
     with a linear dependencies relationship using the order specified by the
     revset.
 
-    For the first time uploading changesets, local tags will be created to
-    maintain the association. After the first time, phabsend will check
-    obsstore and tags information so it can figure out whether to update an
-    existing Differential Revision, or create a new one.
+    If --amend is set, update commit messages so they have the
+    ``Differential Revision`` URL, remove related tags. This is similar to what
+    arcanist will do, and is more desired in author-push workflows. Otherwise,
+    use local tags to record the ``Differential Revision`` association.
+
+    phabsend will check obsstore and the above association to decide whether to
+    update an existing Differential Revision, or create a new one.
     """
     revs = list(revs) + opts.get('rev', [])
     revs = scmutil.revrange(repo, revs)
 
     if not revs:
         raise error.Abort(_('phabsend requires at least one changeset'))
+    if opts.get('amend'):
+        cmdutil.checkunfinished(repo)
 
     actions = []
     reviewers = opts.get('reviewer', [])
@@ -355,6 +363,7 @@ 
 
     # Send patches one by one so we know their Differential Revision IDs and
     # can provide dependency relationship
+    drevids = []
     lastrevid = None
     for rev in revs:
         ui.debug('sending rev %d\n' % rev)
@@ -372,20 +381,55 @@ 
             else:
                 action = _('created')
 
-            # Create a local tag to note the association
-            tagname = 'D%d' % newrevid
-            tags.tag(repo, tagname, ctx.node(), message=None, user=None,
-                     date=None, local=True)
+            # Create a local tag to note the association, if commit message
+            # does not have it already
+            m = _differentialrevisiondescre.search(ctx.description())
+            if not m or int(m.group(1)) != newrevid:
+                tagname = 'D%d' % newrevid
+                tags.tag(repo, tagname, ctx.node(), message=None, user=None,
+                         date=None, local=True)
         else:
             # Nothing changed. But still set "newrevid" so the next revision
             # could depend on this one.
             newrevid = revid
             action = _('skipped')
 
         ui.write(_('D%s: %s - %s: %s\n') % (newrevid, action, ctx,
                                             ctx.description().split('\n')[0]))
+        drevids.append(newrevid)
         lastrevid = newrevid
 
+    # Update commit messages and remove tags
+    if opts.get('amend'):
+        unfi = repo.unfiltered()
+        drevs = callconduit(repo, 'differential.query', {'ids': drevids})
+        with repo.wlock(), repo.lock(), repo.transaction('phabsend'):
+            wnode = unfi['.'].node()
+            mapping = {} # {oldnode: newnode}
+            for i, rev in enumerate(revs):
+                old = unfi[rev]
+                drevid = drevids[i]
+                drev = [d for d in drevs if int(d[r'id']) == drevid][0]
+                newdesc = getdescfromdrev(drev)
+                # Make sure commit message contain "Differential Revision"
+                if old.description() != newdesc:
+                    parents = [
+                        mapping.get(old.p1().node(), (old.p1(),))[0],
+                        mapping.get(old.p2().node(), (old.p2(),))[0],
+                    ]
+                    new = context.metadataonlyctx(
+                        repo, old, parents=parents, text=newdesc,
+                        user=old.user(), date=old.date(), extra=old.extra())
+                    mapping[old.node()] = [new.commit()]
+                # Remove local tags since it's no longer necessary
+                tagname = 'D%d' % drevid
+                if tagname in repo.tags():
+                    tags.tag(repo, tagname, nullid, message=None, user=None,
+                             date=None, local=True)
+            scmutil.cleanupnodes(repo, mapping, 'phabsend')
+            if wnode in mapping:
+                unfi.setparents(mapping[wnode][0])
+
 # Map from "hg:meta" keys to header understood by "hg import". The order is
 # consistent with "hg export" output.
 _metanamemap = util.sortdict([(r'user', 'User'), (r'date', 'Date'),