# This file is part of ranger, the console file manager. # License: GNU GPL version 3, see the file "AUTHORS" for details. """Mercurial module""" from __future__ import (absolute_import, division, print_function) from datetime import datetime import json import os from .vcs import Vcs, VcsError class Hg(Vcs): """VCS implementation for Mercurial""" HEAD = 'tip' _status_translations = ( ('AR', 'staged'), ('M', 'changed'), ('!', 'deleted'), ('?', 'untracked'), ('I', 'ignored'), ) # Generic def _log(self, refspec=None, maxres=None, filelist=None): args = ['log', '--template', 'json'] if refspec: args += ['--limit', '1', '--rev', refspec] elif maxres: args += ['--limit', str(maxres)] if filelist: args += ['--'] + filelist try: output = self._run(args) except VcsError: return None if not output: return None log = [] for entry in json.loads(output): new = {} new['short'] = entry['rev'] new['revid'] = entry['node'] new['author'] = entry['user'] new['date'] = datetime.fromtimestamp(entry['date'][0]) new['summary'] = entry['desc'] log.append(new) return log def _remote_url(self): """Remote url""" try: return self._run(['showconfig', 'paths.default']) or None except VcsError: return None def _status_translate(self, code): """Translate status code""" for code_x, status in self._status_translations: if code in code_x: return status return 'unknown' # Action interface def action_add(self, filelist=None): args = ['add'] if filelist: args += ['--'] + filelist self._run(args, catchout=False) def action_reset(self, filelist=None): args = ['forget', '--'] if filelist: args += filelist else: args += self.rootvcs.status_subpaths.keys() # pylint: disable=no-member self._run(args, catchout=False) # Data interface def data_status_root(self): statuses = set() # Paths with status for entry in json.loads(self._run(['status', '--all', '--template', 'json'])): if entry['status'] == 'C': continue statuses.add(self._status_translate(entry['status'])) if statuses: for status in self.DIRSTATUSES: if status in statuses: return status return 'sync' def data_status_subpaths(self): statuses = {} # Paths with status for entry in json.loads(self._run(['status', '--all', '--template', 'json'])): if entry['status'] == 'C': continue statuses[os.path.normpath(entry['path'])] = self._status_translate(entry['status']) return statuses def data_status_remote(self): if self._remote_url() is None: return 'none' return 'unknown' def data_branch(self): return self._run(['branch']) or None def data_info(self, rev=None): if rev is None: rev = self.HEAD log = self._log(refspec=rev) if not log: if rev == self.HEAD: return None else: raise VcsError('Revision {0:s} does not exist'.format(rev)) elif len(log) == 1: return log[0] else: raise VcsError('More than one instance of revision {0:s}'.format(rev))