morestatus: move fb extension to core by plugging to `hg status --verbose`
authorPulkit Goyal <7895pulkit@gmail.com>
Thu, 03 Aug 2017 05:12:35 +0530
changeset 33771 96f43981c1c4
parent 33770 119e1c6be1ce
child 33772 d434a7f0685c
morestatus: move fb extension to core by plugging to `hg status --verbose` morestatus extension in fbext use to show more context about the state of the repo like the repository is in a unfinished merge state, or a rebase is going on, or histedit is going on, listing the files which need to be resolved and also suggesting ways to handle the situation. This patch moves the extension directly to core by plugging it into the --verbose flag of the status command. So now if you are in any unfinished state and you do hg status -v, it will show you details and help related to the state. The extension in fbext also shows context about unfinished update state which is not ported to core as that plug in hooks to update command which need to be tackled somewhat differently. The following configuration will turn the behaviour on by default [commands] status.verbose = 1 You can also skip considering some states like bisect as follows: [commands] status.skipstates=bisect This patch also adds test for the feature. .. feature:: ``hg status -v`` can now show unfinished state. For example, when in an unfinished rebase state, ``hg status -v`` might show:: # The repository is in an unfinished *rebase* state. # No unresolved merge conflicts. # To continue: hg rebase --continue # To abort: hg rebase --abort Differential Revision: https://phab.mercurial-scm.org/D219
mercurial/cmdutil.py
mercurial/commands.py
mercurial/configitems.py
tests/test-bisect.t
tests/test-conflict.t
tests/test-graft.t
tests/test-histedit-fold.t
tests/test-rebase-conflicts.t
tests/test-shelve.t
--- a/mercurial/cmdutil.py	Wed Aug 09 17:01:21 2017 +0200
+++ b/mercurial/cmdutil.py	Thu Aug 03 05:12:35 2017 +0530
@@ -573,6 +573,111 @@
 
     return finalrs
 
+def _commentlines(raw):
+    '''Surround lineswith a comment char and a new line'''
+    lines = raw.splitlines()
+    commentedlines = ['# %s' % line for line in lines]
+    return '\n'.join(commentedlines) + '\n'
+
+def _conflictsmsg(repo):
+    # avoid merge cycle
+    from . import merge as mergemod
+    mergestate = mergemod.mergestate.read(repo)
+    if not mergestate.active():
+        return
+
+    m = scmutil.match(repo[None])
+    unresolvedlist = [f for f in mergestate if m(f) and mergestate[f] == 'u']
+    if unresolvedlist:
+        mergeliststr = '\n'.join(
+            ['    %s' % os.path.relpath(
+                os.path.join(repo.root, path),
+                pycompat.getcwd()) for path in unresolvedlist])
+        msg = _('''Unresolved merge conflicts:
+
+%s
+
+To mark files as resolved:  hg resolve --mark FILE''') % mergeliststr
+    else:
+        msg = _('No unresolved merge conflicts.')
+
+    return _commentlines(msg)
+
+def _helpmessage(continuecmd, abortcmd):
+    msg = _('To continue:                %s\n'
+            'To abort:                   %s') % (continuecmd, abortcmd)
+    return _commentlines(msg)
+
+def _rebasemsg():
+    return _helpmessage('hg rebase --continue', 'hg rebase --abort')
+
+def _histeditmsg():
+    return _helpmessage('hg histedit --continue', 'hg histedit --abort')
+
+def _unshelvemsg():
+    return _helpmessage('hg unshelve --continue', 'hg unshelve --abort')
+
+def _updatecleanmsg(dest=None):
+    warning = _('warning: this will discard uncommitted changes')
+    return 'hg update --clean %s    (%s)' % (dest or '.', warning)
+
+def _graftmsg():
+    # tweakdefaults requires `update` to have a rev hence the `.`
+    return _helpmessage('hg graft --continue', _updatecleanmsg())
+
+def _mergemsg():
+    # tweakdefaults requires `update` to have a rev hence the `.`
+     return _helpmessage('hg commit', _updatecleanmsg())
+
+def _bisectmsg():
+    msg = _('To mark the changeset good:    hg bisect --good\n'
+            'To mark the changeset bad:     hg bisect --bad\n'
+            'To abort:                      hg bisect --reset\n')
+    return _commentlines(msg)
+
+def fileexistspredicate(filename):
+    return lambda repo: repo.vfs.exists(filename)
+
+def _mergepredicate(repo):
+    return len(repo[None].parents()) > 1
+
+STATES = (
+    # (state, predicate to detect states, helpful message function)
+    ('histedit', fileexistspredicate('histedit-state'), _histeditmsg),
+    ('bisect', fileexistspredicate('bisect.state'), _bisectmsg),
+    ('graft', fileexistspredicate('graftstate'), _graftmsg),
+    ('unshelve', fileexistspredicate('unshelverebasestate'), _unshelvemsg),
+    ('rebase', fileexistspredicate('rebasestate'), _rebasemsg),
+    # The merge state is part of a list that will be iterated over.
+    # They need to be last because some of the other unfinished states may also
+    # be in a merge or update state (eg. rebase, histedit, graft, etc).
+    # We want those to have priority.
+    ('merge', _mergepredicate, _mergemsg),
+)
+
+def _getrepostate(repo):
+    # experimental config: commands.status.skipstates
+    skip = set(repo.ui.configlist('commands', 'status.skipstates'))
+    for state, statedetectionpredicate, msgfn in STATES:
+        if state in skip:
+            continue
+        if statedetectionpredicate(repo):
+            return (state, statedetectionpredicate, msgfn)
+
+def morestatus(repo, fm):
+    statetuple = _getrepostate(repo)
+    label = 'status.morestatus'
+    if statetuple:
+        fm.startitem()
+        state, statedetectionpredicate, helpfulmsg = statetuple
+        statemsg = _('The repository is in an unfinished *%s* state.') % state
+        fm.write('statemsg', '%s\n',  _commentlines(statemsg), label=label)
+        conmsg = _conflictsmsg(repo)
+        fm.write('conflictsmsg', '%s\n', conmsg, label=label)
+        if helpfulmsg:
+            helpmsg = helpfulmsg()
+            fm.write('helpmsg', '%s\n', helpmsg, label=label)
+
 def findpossible(cmd, table, strict=False):
     """
     Return cmd -> (aliases, command table entry)
--- a/mercurial/commands.py	Wed Aug 09 17:01:21 2017 +0200
+++ b/mercurial/commands.py	Thu Aug 03 05:12:35 2017 +0530
@@ -4710,6 +4710,19 @@
       files are not considered while tersing until 'i' is there in --terse value
       or the --ignored option is used.
 
+      --verbose option shows more context about the state of the repo
+      like the repository is in unfinised merge, shelve, rebase state etc.
+      You can have this behaviour turned on by default by following config:
+
+      [commands]
+      status.verbose = true
+
+      You can also skip some states like bisect by adding following in
+      configuration file.
+
+      [commands]
+      status.skipstates = bisect
+
       Examples:
 
       - show changes in the working directory relative to a
@@ -4799,6 +4812,10 @@
                 if f in copy:
                     fm.write("copy", '  %s' + end, repo.pathto(copy[f], cwd),
                              label='status.copied')
+
+    if ((ui.verbose or ui.configbool('commands', 'status.verbose'))
+        and not ui.plain()):
+        cmdutil.morestatus(repo, fm)
     fm.end()
 
 @command('^summary|sum',
--- a/mercurial/configitems.py	Wed Aug 09 17:01:21 2017 +0200
+++ b/mercurial/configitems.py	Thu Aug 03 05:12:35 2017 +0530
@@ -97,6 +97,12 @@
 coreconfigitem('commands', 'status.relative',
     default=False,
 )
+coreconfigitem('commands', 'status.skipstates',
+    default=[],
+)
+coreconfigitem('commands', 'status.verbose',
+    default=False,
+)
 coreconfigitem('commands', 'update.requiredest',
     default=False,
 )
--- a/tests/test-bisect.t	Wed Aug 09 17:01:21 2017 +0200
+++ b/tests/test-bisect.t	Thu Aug 03 05:12:35 2017 +0530
@@ -184,6 +184,15 @@
 
   $ hg bisect -r
   $ hg bisect -b
+  $ hg status -v
+  # The repository is in an unfinished *bisect* state.
+  
+  None
+  # To mark the changeset good:    hg bisect --good
+  # To mark the changeset bad:     hg bisect --bad
+  # To abort:                      hg bisect --reset
+  
+  $ hg status -v --config commands.status.skipstates=bisect
   $ hg summary
   parent: 31:58c80a7c8a40 tip
    msg 31
--- a/tests/test-conflict.t	Wed Aug 09 17:01:21 2017 +0200
+++ b/tests/test-conflict.t	Thu Aug 03 05:12:35 2017 +0530
@@ -44,6 +44,23 @@
   $ hg id
   618808747361+c0c68e4fe667+ tip
 
+  $ echo "[commands]" >> $HGRCPATH
+  $ echo "status.verbose=true" >> $HGRCPATH
+  $ hg status
+  M a
+  ? a.orig
+  # The repository is in an unfinished *merge* state.
+  
+  # Unresolved merge conflicts:
+  # 
+  #     a
+  # 
+  # To mark files as resolved:  hg resolve --mark FILE
+  
+  # To continue:                hg commit
+  # To abort:                   hg update --clean .    (warning: this will discard uncommitted changes)
+  
+
   $ cat a
   Small Mathematical Series.
   1
@@ -58,7 +75,7 @@
   >>>>>>> merge rev:    c0c68e4fe667 - test: branch1
   Hop we are done.
 
-  $ hg status
+  $ hg status --config commands.status.verbose=0
   M a
   ? a.orig
 
--- a/tests/test-graft.t	Wed Aug 09 17:01:21 2017 +0200
+++ b/tests/test-graft.t	Thu Aug 03 05:12:35 2017 +0530
@@ -221,6 +221,25 @@
   $ hg summary |grep graft
   commit: 2 modified, 2 unknown, 1 unresolved (graft in progress)
 
+Using status to get more context
+
+  $ hg status --verbose
+  M d
+  M e
+  ? a.orig
+  ? e.orig
+  # The repository is in an unfinished *graft* state.
+  
+  # Unresolved merge conflicts:
+  # 
+  #     e
+  # 
+  # To mark files as resolved:  hg resolve --mark FILE
+  
+  # To continue:                hg graft --continue
+  # To abort:                   hg update --clean .    (warning: this will discard uncommitted changes)
+  
+
 Commit while interrupted should fail:
 
   $ hg ci -m 'commit interrupted graft'
--- a/tests/test-histedit-fold.t	Wed Aug 09 17:01:21 2017 +0200
+++ b/tests/test-histedit-fold.t	Thu Aug 03 05:12:35 2017 +0530
@@ -294,9 +294,21 @@
   [1]
 There were conflicts, we keep P1 content. This
 should effectively drop the changes from +6.
-  $ hg status
+
+  $ hg status -v
   M file
   ? file.orig
+  # The repository is in an unfinished *histedit* state.
+  
+  # Unresolved merge conflicts:
+  # 
+  #     file
+  # 
+  # To mark files as resolved:  hg resolve --mark FILE
+  
+  # To continue:                hg histedit --continue
+  # To abort:                   hg histedit --abort
+  
   $ hg resolve -l
   U file
   $ hg revert -r 'p1()' file
--- a/tests/test-rebase-conflicts.t	Wed Aug 09 17:01:21 2017 +0200
+++ b/tests/test-rebase-conflicts.t	Thu Aug 03 05:12:35 2017 +0530
@@ -71,6 +71,21 @@
   unresolved conflicts (see hg resolve, then hg rebase --continue)
   [1]
 
+  $ hg status --config commands.status.verbose=1
+  M common
+  ? common.orig
+  # The repository is in an unfinished *rebase* state.
+  
+  # Unresolved merge conflicts:
+  # 
+  #     common
+  # 
+  # To mark files as resolved:  hg resolve --mark FILE
+  
+  # To continue:                hg rebase --continue
+  # To abort:                   hg rebase --abort
+  
+
 Try to continue without solving the conflict:
 
   $ hg rebase --continue
--- a/tests/test-shelve.t	Wed Aug 09 17:01:21 2017 +0200
+++ b/tests/test-shelve.t	Thu Aug 03 05:12:35 2017 +0530
@@ -342,6 +342,23 @@
   warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
   unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
   [1]
+  $ hg status -v
+  M a/a
+  M b.rename/b
+  M c.copy
+  R b/b
+  ? a/a.orig
+  # The repository is in an unfinished *unshelve* state.
+  
+  # Unresolved merge conflicts:
+  # 
+  #     a/a
+  # 
+  # To mark files as resolved:  hg resolve --mark FILE
+  
+  # To continue:                hg unshelve --continue
+  # To abort:                   hg unshelve --abort
+  
 
 ensure that we have a merge with unresolved conflicts