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) 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/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/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-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-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-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-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-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-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