Patchwork [4,of,9] qrefresh: use "editor" argument of "commit()" instead of explicit "ui.edit()"

login
register
mail settings
Submitter Katsunori FUJIWARA
Date May 5, 2014, 12:33 p.m.
Message ID <e4659ade0bed4e6f9fef.1399293184@feefifofum>
Download mbox | patch
Permalink /patch/4635/
State Accepted
Commit 49148d7868df9151652dafa0b622d8ba2a0b7d3e
Headers show

Comments

Katsunori FUJIWARA - May 5, 2014, 12:33 p.m.
# HG changeset patch
# User FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
# Date 1399292800 -32400
#      Mon May 05 21:26:40 2014 +0900
# Branch stable
# Node ID e4659ade0bed4e6f9fef51839b101479852c4863
# Parent  e22cce613e295c1c67d74c8b5b19df9674f67067
qrefresh: use "editor" argument of "commit()" instead of explicit "ui.edit()"

Before this patch, "hg qrefresh" and "hg qfold" invoke "ui.edit()"
explicitly to get commit message edited manually.

This requires explicit "localrepository.savecommitmessage()"
invocation to save edited commit message into ".hg/last-message.txt",
because unexpected exception raising may abort command execution
before saving it in "localrepository.commit()".

This patch uses "editor" argument of "localrepository.commit()"
instead of explicit "ui.edit()" invocation for "hg qnew" and "hg qfold"

"localrepository.commit()" will invoke "desceditor()" function newly
added by this patch, and save edited commit message into
".hg/last-message.txt" automatically.

This patch passes not "editor" but "desceditor" to "commit()", because
"hg qnew" and "hg qfold" require editor function to return edited
message (and invoke "patchheader.setmessage()" with it) if not empty,
or default message otherwise.

This patch also avoids "not q.applied" check at "hg qrefresh --edit",
because it is also checked in "queue.refresh()", and it is not needed
to get commit message from patch header before "queue.refresh()".

Patch

diff --git a/hgext/mq.py b/hgext/mq.py
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -1484,6 +1484,7 @@ 
             self.ui.write(_("no patches applied\n"))
             return 1
         msg = opts.get('msg', '').rstrip()
+        editor = opts.get('editor')
         newuser = opts.get('user')
         newdate = opts.get('date')
         if newdate:
@@ -1652,9 +1653,20 @@ 
             try:
                 # might be nice to attempt to roll back strip after this
 
-                if not msg:
+                defaultmsg = "[mq]: %s" % patchfn
+                if editor:
+                    origeditor = editor
+                    def desceditor(repo, ctx, subs):
+                        desc = origeditor(repo, ctx, subs)
+                        if desc.rstrip():
+                            ph.setmessage(desc)
+                            return desc
+                        return defaultmsg
+                    message = msg or "\n".join(ph.message)
+                    editor = desceditor
+                elif not msg:
                     if not ph.message:
-                        message = "[mq]: %s\n" % patchfn
+                        message = defaultmsg
                     else:
                         message = "\n".join(ph.message)
                 else:
@@ -1664,7 +1676,7 @@ 
                 # Ensure we create a new changeset in the same phase than
                 # the old one.
                 n = newcommit(repo, oldphase, message, user, ph.date,
-                              match=match, force=True)
+                              match=match, force=True, editor=editor)
                 # only write patch after a successful commit
                 c = [list(x) for x in refreshchanges]
                 if inclsubs:
@@ -2478,20 +2490,16 @@ 
     q = repo.mq
     message = cmdutil.logmessage(ui, opts)
     if opts.get('edit'):
-        if not q.applied:
-            ui.write(_("no patches applied\n"))
-            return 1
         if message:
             raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
-        patch = q.applied[-1].name
-        ph = patchheader(q.join(patch), q.plainmode)
-        message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
-        # We don't want to lose the patch message if qrefresh fails (issue2062)
-        repo.savecommitmessage(message)
+        def editor(repo, ctx, subs):
+            return ui.edit(ctx.description() + "\n", ctx.user())
+    else:
+        editor = False
     setupheaderopts(ui, opts)
     wlock = repo.wlock()
     try:
-        ret = q.refresh(repo, pats, msg=message, **opts)
+        ret = q.refresh(repo, pats, msg=message, editor=editor, **opts)
         q.savedirty()
         return ret
     finally:
@@ -2582,13 +2590,15 @@ 
         message = '\n'.join(message)
 
     if opts.get('edit'):
-        message = ui.edit(message, user or ui.username())
-        repo.savecommitmessage(message)
+        def editor(repo, ctx, subs):
+            return ui.edit(ctx.description() + "\n", ctx.user())
+    else:
+        editor = False
 
     diffopts = q.patchopts(q.diffopts(), *patches)
     wlock = repo.wlock()
     try:
-        q.refresh(repo, msg=message, git=diffopts.git)
+        q.refresh(repo, msg=message, git=diffopts.git, editor=editor)
         q.delete(repo, patches, opts)
         q.savedirty()
     finally:
diff --git a/tests/test-mq-qfold.t b/tests/test-mq-qfold.t
--- a/tests/test-mq-qfold.t
+++ b/tests/test-mq-qfold.t
@@ -153,8 +153,9 @@ 
   >     repo.__class__ = commitfailure
   > EOF
 
-  $ cat > .hg/hgrc <<EOF
+  $ cat >> .hg/hgrc <<EOF
   > [extensions]
+  > # this failure occurs before editor invocation
   > commitfailure = $TESTTMP/commitfailure.py
   > EOF
 
@@ -165,15 +166,65 @@ 
   > (echo; echo "test saving last-message.txt") >> \$1
   > EOF
 
+  $ hg qapplied
+  p1
+  git
+  $ hg tip --template "{files}\n"
+  aa
+
+(test that editor is not invoked before transaction starting)
+
   $ rm -f .hg/last-message.txt
   $ HGEDITOR="sh $TESTTMP/editor.sh" hg qfold -e p3
-  ==== before editing
-  original message====
   refresh interrupted while patch was popped! (revert --all, qpush to recover)
   abort: emulating unexpected abort
   [255]
   $ cat .hg/last-message.txt
+  cat: .hg/last-message.txt: No such file or directory
+  [1]
+
+(reset applied patches and directory status)
+
+  $ cat >> .hg/hgrc <<EOF
+  > [extensions]
+  > # this failure occurs after editor invocation
+  > commitfailure = !
+  > EOF
+
+  $ hg qapplied
+  p1
+  $ hg status -A aa
+  ? aa
+  $ rm aa
+  $ hg status -m
+  M a
+  $ hg revert --no-backup -q a
+  $ hg qpush -q git
+  now at: git
+
+(test that editor is invoked and commit message is saved into
+"last-message.txt")
+
+  $ cat >> .hg/hgrc <<EOF
+  > [hooks]
+  > # this failure occurs after editor invocation
+  > pretxncommit.unexpectedabort = false
+  > EOF
+
+  $ rm -f .hg/last-message.txt
+  $ HGEDITOR="sh $TESTTMP/editor.sh" hg qfold -e p3
+  ==== before editing
   original message
+  ====
+  transaction abort!
+  rollback completed
+  note: commit message saved in .hg/last-message.txt
+  refresh interrupted while patch was popped! (revert --all, qpush to recover)
+  abort: pretxncommit.unexpectedabort hook exited with status 1
+  [255]
+  $ cat .hg/last-message.txt
+  original message
+  
   test saving last-message.txt
 
   $ cd ..
diff --git a/tests/test-mq-qrefresh-replace-log-message.t b/tests/test-mq-qrefresh-replace-log-message.t
--- a/tests/test-mq-qrefresh-replace-log-message.t
+++ b/tests/test-mq-qrefresh-replace-log-message.t
@@ -59,3 +59,86 @@ 
   $ hg log -l1 --template "{desc}\n"
   Fifth commit message
    This is the 5th log message
+
+Test saving last-message.txt:
+
+  $ cat > $TESTTMP/editor.sh << EOF
+  > echo "==== before editing"
+  > cat \$1
+  > echo "===="
+  > (echo; echo "test saving last-message.txt") >> \$1
+  > EOF
+
+  $ cat > $TESTTMP/commitfailure.py <<EOF
+  > from mercurial import util
+  > def reposetup(ui, repo):
+  >     class commitfailure(repo.__class__):
+  >         def commit(self, *args, **kwargs):
+  >             raise util.Abort('emulating unexpected abort')
+  >     repo.__class__ = commitfailure
+  > EOF
+
+  $ cat >> .hg/hgrc <<EOF
+  > [extensions]
+  > # this failure occurs before editor invocation
+  > commitfailure = $TESTTMP/commitfailure.py
+  > EOF
+
+  $ hg qapplied
+  first-patch
+  second-patch
+  $ hg tip --template "{files}\n"
+  file2
+
+(test that editor is not invoked before transaction starting)
+
+  $ rm -f .hg/last-message.txt
+  $ HGEDITOR="sh $TESTTMP/editor.sh" hg qrefresh -e
+  refresh interrupted while patch was popped! (revert --all, qpush to recover)
+  abort: emulating unexpected abort
+  [255]
+  $ cat .hg/last-message.txt
+  cat: .hg/last-message.txt: No such file or directory
+  [1]
+
+(reset applied patches and directory status)
+
+  $ cat >> .hg/hgrc <<EOF
+  > [extensions]
+  > commitfailure = !
+  > EOF
+
+  $ hg qapplied
+  first-patch
+  $ hg status -A file2
+  ? file2
+  $ rm file2
+  $ hg qpush -q second-patch
+  now at: second-patch
+
+(test that editor is invoked and commit message is saved into
+"last-message.txt")
+
+  $ cat >> .hg/hgrc <<EOF
+  > [hooks]
+  > # this failure occurs after editor invocation
+  > pretxncommit.unexpectedabort = false
+  > EOF
+
+  $ rm -f .hg/last-message.txt
+  $ HGEDITOR="sh $TESTTMP/editor.sh" hg qrefresh -e
+  ==== before editing
+  Fifth commit message
+   This is the 5th log message
+  ====
+  transaction abort!
+  rollback completed
+  note: commit message saved in .hg/last-message.txt
+  refresh interrupted while patch was popped! (revert --all, qpush to recover)
+  abort: pretxncommit.unexpectedabort hook exited with status 1
+  [255]
+  $ cat .hg/last-message.txt
+  Fifth commit message
+   This is the 5th log message
+  
+  test saving last-message.txt