D219: morestatus: move fb extension to core as '--repo-state' option to status

pulkit (Pulkit Goyal) phabricator at mercurial-scm.org
Thu Aug 3 04:08:34 UTC 2017


pulkit created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  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 with '--repo-state' flag to
  status command. The extension 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
  
  [morestatus]
  show = true
  
  You can also skip considering some states like bisect as follows:
  
  [morestatus]
  skipstates=bisect
  
  This patch also adds test for the feature.

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  mercurial/cmdutil.py
  mercurial/commands.py
  tests/test-bisect.t
  tests/test-completion.t
  tests/test-conflict.t
  tests/test-graft.t
  tests/test-help.t
  tests/test-histedit-fold.t
  tests/test-rebase-conflicts.t
  tests/test-shelve.t

CHANGE DETAILS

diff --git a/tests/test-shelve.t b/tests/test-shelve.t
--- a/tests/test-shelve.t
+++ b/tests/test-shelve.t
@@ -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 --repo-state
+  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
 
diff --git a/tests/test-rebase-conflicts.t b/tests/test-rebase-conflicts.t
--- a/tests/test-rebase-conflicts.t
+++ b/tests/test-rebase-conflicts.t
@@ -71,6 +71,21 @@
   unresolved conflicts (see hg resolve, then hg rebase --continue)
   [1]
 
+  $ hg status --repo-state
+  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
diff --git a/tests/test-histedit-fold.t b/tests/test-histedit-fold.t
--- a/tests/test-histedit-fold.t
+++ b/tests/test-histedit-fold.t
@@ -294,9 +294,20 @@
   [1]
 There were conflicts, we keep P1 content. This
 should effectively drop the changes from +6.
-  $ hg status
+  $ hg status --repo-state
   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
diff --git a/tests/test-help.t b/tests/test-help.t
--- a/tests/test-help.t
+++ b/tests/test-help.t
@@ -617,6 +617,7 @@
    -0 --print0              end filenames with NUL, for use with xargs
       --rev REV [+]         show difference from revision
       --change REV          list the changed files of a revision
+      --repo-state          show the state of the repo
    -I --include PATTERN [+] include names matching the given patterns
    -X --exclude PATTERN [+] exclude names matching the given patterns
    -S --subrepos            recurse into subrepositories
diff --git a/tests/test-graft.t b/tests/test-graft.t
--- a/tests/test-graft.t
+++ b/tests/test-graft.t
@@ -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 --repo-state
+  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'
diff --git a/tests/test-conflict.t b/tests/test-conflict.t
--- a/tests/test-conflict.t
+++ b/tests/test-conflict.t
@@ -44,6 +44,21 @@
   $ hg id
   618808747361+c0c68e4fe667+ tip
 
+  $ hg status --repo-state
+  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
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -231,7 +231,7 @@
   push: force, rev, bookmark, branch, new-branch, pushvars, ssh, remotecmd, insecure
   remove: after, force, subrepos, include, exclude
   serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, subrepos
-  status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
+  status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, repo-state, include, exclude, subrepos, template
   summary: remote
   update: clean, check, merge, date, rev, tool
   addremove: similarity, subrepos, include, exclude, dry-run
diff --git a/tests/test-bisect.t b/tests/test-bisect.t
--- a/tests/test-bisect.t
+++ b/tests/test-bisect.t
@@ -184,6 +184,15 @@
 
   $ hg bisect -r
   $ hg bisect -b
+  $ hg status --repo-state
+  # 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 --repo-state --config morestatus.skipstates=bisect
   $ hg summary
   parent: 31:58c80a7c8a40 tip
    msg 31
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -4659,6 +4659,7 @@
     ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
     ('', 'rev', [], _('show difference from revision'), _('REV')),
     ('', 'change', '', _('list the changed files of a revision'), _('REV')),
+    ('', 'repo-state', None, _('show the state of the repo')),
     ] + walkopts + subrepoopts + formatteropts,
     _('[OPTION]... [FILE]...'),
     inferrepo=True)
@@ -4710,6 +4711,17 @@
       files are not considered while tersing until 'i' is there in --terse value
       or the --ignored option is used.
 
+      The --repo-state option shows more context about the state of the repo
+      like the repository is in unfinised merge, shelve, rebase state etc.
+
+      You can switch the --repo-state option on permanently as follows:
+
+      [morestatus]
+      show = true
+
+      You can also skip some states like bisect by setting
+      ``morestatus.skipstates = bisect`` in configuration file.
+
       Examples:
 
       - show changes in the working directory relative to a
@@ -4799,6 +4811,10 @@
                 if f in copy:
                     fm.write("copy", '  %s' + end, repo.pathto(copy[f], cwd),
                              label='status.copied')
+
+    if (opts.get('repo_state') or
+            (ui.configbool('morestatus', 'show', False) and not ui.plain())):
+        cmdutil.morestatus(repo, fm)
     fm.end()
 
 @command('^summary|sum',
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -572,6 +572,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: morestatus.skipstates
+    skip = set(repo.ui.configlist('morestatus', '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)



To: pulkit, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list