diff options
-rwxr-xr-x | ranger/config/commands.py | 106 | ||||
-rw-r--r-- | ranger/ext/vcs/__init__.py | 2 | ||||
-rw-r--r-- | ranger/ext/vcs/bzr.py | 154 | ||||
-rw-r--r-- | ranger/ext/vcs/git.py | 157 | ||||
-rw-r--r-- | ranger/ext/vcs/hg.py | 128 | ||||
-rw-r--r-- | ranger/ext/vcs/svn.py | 128 | ||||
-rw-r--r-- | ranger/ext/vcs/vcs.py | 137 |
7 files changed, 100 insertions, 712 deletions
diff --git a/ranger/config/commands.py b/ranger/config/commands.py index b2c704c7..6012ed4e 100755 --- a/ranger/config/commands.py +++ b/ranger/config/commands.py @@ -1324,9 +1324,31 @@ class grep(Command): action.extend(f.path for f in self.fm.thistab.get_selection()) self.fm.execute_command(action, flags='p') +class flat(Command): + """ + :flat <level> + + Flattens the directory view up to the specified level. + + -1 fully flattened + 0 remove flattened view + """ + + def execute(self): + try: + level = self.rest(1) + level = int(level) + except ValueError: + level = self.quantifier + if level < -1: + self.fm.notify("Need an integer number (-1, 0, 1, ...)", bad=True) + self.fm.thisdir.unload() + self.fm.thisdir.flat = level + self.fm.thisdir.load_content() # Version control commands # -------------------------------- + class stage(Command): """ :stage @@ -1336,14 +1358,13 @@ class stage(Command): def execute(self): from ranger.ext.vcs import VcsError - if self.fm.thisdir.vcs and self.fm.thisdir.vcs.track: filelist = [f.path for f in self.fm.thistab.get_selection()] try: self.fm.thisdir.vcs.add(filelist) except VcsError as error: - self.fm.notify('Unable to unstage files: {0:s}'.format(str(error))) - self.fm.reload_cwd() + self.fm.notify('Unable to stage files: {0:s}'.format(str(error))) + self.fm.ui.vcsthread.wakeup() else: self.fm.notify('Unable to stage files: Not in repository') @@ -1356,93 +1377,16 @@ class unstage(Command): def execute(self): from ranger.ext.vcs import VcsError - if self.fm.thisdir.vcs and self.fm.thisdir.vcs.track: filelist = [f.path for f in self.fm.thistab.get_selection()] try: self.fm.thisdir.vcs.reset(filelist) except VcsError as error: self.fm.notify('Unable to unstage files: {0:s}'.format(str(error))) - self.fm.reload_cwd() + self.fm.ui.vcsthread.wakeup() else: self.fm.notify('Unable to unstage files: Not in repository') - -class diff(Command): - """ - :diff - - Displays a diff of selected files against the last committed version - """ - def execute(self): - from ranger.ext.vcs import VcsError - import tempfile - - L = self.fm.thistab.get_selection() - if len(L) == 0: return - - filelist = [f.path for f in L] - vcs = L[0].vcs - - diff = vcs.get_raw_diff(filelist=filelist) - if len(diff.strip()) > 0: - tmp = tempfile.NamedTemporaryFile() - tmp.write(diff.encode('utf-8')) - tmp.flush() - - pager = os.environ.get('PAGER', ranger.DEFAULT_PAGER) - self.fm.run([pager, tmp.name]) - else: - raise Exception("diff is empty") - - -class log(Command): - """ - :log - - Displays the log of the current repo or files - """ - def execute(self): - from ranger.ext.vcs import VcsError - import tempfile - - L = self.fm.thistab.get_selection() - if len(L) == 0: return - - filelist = [f.path for f in L] - vcs = L[0].vcs - - log = vcs.get_raw_log(filelist=filelist) - tmp = tempfile.NamedTemporaryFile() - tmp.write(log.encode('utf-8')) - tmp.flush() - - pager = os.environ.get('PAGER', ranger.DEFAULT_PAGER) - self.fm.run([pager, tmp.name]) - -class flat(Command): - """ - :flat <level> - - Flattens the directory view up to the specified level. - - -1 fully flattened - 0 remove flattened view - """ - - def execute(self): - try: - level = self.rest(1) - level = int(level) - except ValueError: - level = self.quantifier - if level < -1: - self.fm.notify("Need an integer number (-1, 0, 1, ...)", bad=True) - self.fm.thisdir.unload() - self.fm.thisdir.flat = level - self.fm.thisdir.load_content() - - # Metadata commands # -------------------------------- diff --git a/ranger/ext/vcs/__init__.py b/ranger/ext/vcs/__init__.py index c6c691b8..3e62310c 100644 --- a/ranger/ext/vcs/__init__.py +++ b/ranger/ext/vcs/__init__.py @@ -1,3 +1,3 @@ -"""VCS Extension Package""" +"""VCS Extension""" from .vcs import Vcs, VcsError, VcsThread diff --git a/ranger/ext/vcs/bzr.py b/ranger/ext/vcs/bzr.py index 7b870d7d..358db8ad 100644 --- a/ranger/ext/vcs/bzr.py +++ b/ranger/ext/vcs/bzr.py @@ -2,28 +2,25 @@ import os import re -import shutil from datetime import datetime from .vcs import Vcs, VcsError - class Bzr(Vcs): + """VCS implementiation for Bzr""" HEAD="last:1" - # Auxiliar stuff + # Generic #--------------------------- def _bzr(self, path, args, silent=True, catchout=False, bytes=False): return self._vcs(path, 'bzr', args, silent=silent, catchout=catchout, bytes=bytes) - def _has_head(self): """Checks whether repo has head""" rnum = self._bzr(self.path, ['revno'], catchout=True) return rnum != '0' - def _sanitize_rev(self, rev): if rev == None: return None rev = rev.strip() @@ -31,7 +28,6 @@ class Bzr(Vcs): return rev - def _log(self, refspec=None, filelist=None): """Gets a list of dicts containing revision info, for the revisions matching refspec""" args = ['log', '-n0', '--show-ids'] @@ -61,7 +57,6 @@ class Bzr(Vcs): log.append(dt) return log - def _bzr_file_status(self, st): st = st.strip() if st in "AM": return 'staged' @@ -69,76 +64,19 @@ class Bzr(Vcs): elif st in "?": return 'untracked' else: return 'unknown' - - - # Repo creation - #--------------------------- - - def init(self): - """Initializes a repo in current path""" - self._bzr(self.path, ['init']) - self.update() - - - def clone(self, src): - """Clones a repo from src""" - path = os.path.dirname(self.path) - name = os.path.basename(self.path) - try: - os.rmdir(self.path) - except OSError: - raise VcsError("Can't clone to %s. It is not an empty directory" % self.path) - - self._bzr(path, ['branch', src, name]) - self.update() - - - # Action Interface #--------------------------- - def commit(self, message): - """Commits with a given message""" - self._bzr(self.path, ['commit', '-m', message]) - - def add(self, filelist=None): """Adds files to the index, preparing for commit""" if filelist != None: self._bzr(self.path, ['add'] + filelist) else: self._bzr(self.path, ['add']) - def reset(self, filelist=None): """Removes files from the index""" if filelist != None: self._bzr(self.path, ['remove', '--keep', '--new'] + filelist) else: self._bzr(self.path, ['remove', '--keep', '--new']) - - def pull(self): - """Pulls a git repo""" - self._bzr(self.path, ['pull']) - - - def push(self): - """Pushes a git repo""" - self._bzr(self.path, ['push']) - - - def checkout(self, rev): - """Checks out a branch or revision""" - self._bzr(self.path, ['update', '-r', rev]) - - - def extract_file(self, rev, name, dest): - """Extracts a file from a given revision and stores it in dest dir""" - if rev == self.INDEX: - shutil.copyfile(os.path.join(self.path, name), dest) - else: - out = self._bzr(self.path, ['cat', '--r', rev, name], catchout=True, bytes=True) - with open(dest, 'wb') as fd: fd.write(out) - - - # Data Interface #--------------------------- @@ -153,65 +91,7 @@ class Bzr(Vcs): ret[os.path.normpath(p.strip())] = sta return ret - - def get_ignore_allfiles(self): - """Returns a set of all the ignored files in the repo. Strips trailing '/' from dirs.""" - raw = self._bzr(self.path, ['ls', '--ignored'], catchout=True) - return set(os.path.normpath(p) for p in raw.split('\n')) - - - # TODO: slow due to net access def get_status_remote(self): - """Checks the status of the repo regarding sync state with remote branch""" - if self.get_remote() == None: - return "none" - - ahead = behind = True - try: - self._bzr(self.path, ['missing', '--mine-only'], silent=True) - except: - ahead = False - - try: - self._bzr(self.path, ['missing', '--theirs-only'], silent=True) - except: - behind = False - - if ahead and behind: return "diverged" - elif ahead and not behind: return "ahead" - elif not ahead and behind: return "behind" - elif not ahead and not behind: return "sync" - - - def get_branch(self): - """Returns the current named branch, if this makes sense for the backend. None otherwise""" - branch = self._bzr(self.path, ['nick'], catchout=True) - return branch or None - - - def get_log(self, filelist=None, maxres=None): - """Get the entire log for the current HEAD""" - if not self._has_head(): return [] - return self._log(refspec=None, filelist=filelist) - - - def get_raw_log(self, filelist=None): - """Gets the raw log as a string""" - if not self._has_head(): return [] - args = ['log'] - if filelist: args = args + filelist - return self._bzr(self.path, args, catchout=True) - - - def get_raw_diff(self, refspec=None, filelist=None): - """Gets the raw diff as a string""" - args = ['diff', '--git'] - if refspec: args = args + [refspec] - if filelist: args = args + filelist - return self._bzr(self.path, args, catchout=True) - - - def get_remote(self): """Returns the url for the remote repo attached to head""" try: remote = self._bzr(self.path, ['config', 'parent_location'], catchout=True) @@ -220,19 +100,10 @@ class Bzr(Vcs): return remote.strip() or None - - def get_revision_id(self, rev=None): - """Get a canonical key for the revision rev""" - if rev == None: rev = self.HEAD - elif rev == self.INDEX: return None - rev = self._sanitize_rev(rev) - try: - L = self._log(refspec=rev) - except VcsError: - L = [] - if len(L) == 0: return None - else: return L[0]['revid'] - + def get_branch(self): + """Returns the current named branch, if this makes sense for the backend. None otherwise""" + branch = self._bzr(self.path, ['nick'], catchout=True) + return branch or None def get_info(self, rev=None): """Gets info about the given revision rev""" @@ -247,16 +118,3 @@ class Bzr(Vcs): raise VcsError("More than one instance of revision %s ?!?" % rev) else: return L[0] - - - def get_files(self, rev=None): - """Gets a list of files in revision rev""" - if rev == None: rev = self.HEAD - rev = self._sanitize_rev(rev) - - if rev: - if rev == self.INDEX: raw = self._bzr(self.path, ["ls"], catchout=True) - else: raw = self._bzr(self.path, ['ls', '--R', '-V', '-r', rev], catchout=True) - return raw.split('\n') - else: - return [] diff --git a/ranger/ext/vcs/git.py b/ranger/ext/vcs/git.py index ab971423..ab7f516f 100644 --- a/ranger/ext/vcs/git.py +++ b/ranger/ext/vcs/git.py @@ -2,7 +2,6 @@ import os import re -import shutil from datetime import datetime import json @@ -23,22 +22,14 @@ class Git(Vcs): ('!', '!', 'ignored'), ) - # Auxiliar stuff + # Generic #--------------------------- def _git(self, args, path=None, silent=True, catchout=False, bytes=False): """Call git""" - return self._vcs(path if path else self.path, 'git', args, silent=silent, + return self._vcs(path or self.path, 'git', args, silent=silent, catchout=catchout, bytes=bytes) - def _has_head(self): - """Checks whether repo has head""" - try: - self._git(['rev-parse', 'HEAD'], silent=True) - except VcsError: - return False - return True - def _head_ref(self): """Gets HEAD's ref""" return self._git(['symbolic-ref', self.HEAD], catchout=True, silent=True) or None @@ -51,17 +42,15 @@ class Git(Vcs): catchout=True, silent=True) \ or None - def _sanitize_rev(self, rev): - """Sanitize revision string""" - if rev is None: - return None - return rev.strip() - def _log(self, refspec=None, maxres=None, filelist=None): """Gets a list of dicts containing revision info, for the revisions matching refspec""" args = [ '--no-pager', 'log', - '--pretty={%x00short%x00: %x00%h%x00, %x00revid%x00: %x00%H%x00, %x00author%x00: %x00%an <%ae>%x00, %x00date%x00: %ct, %x00summary%x00: %x00%s%x00}' + '--pretty={' + '%x00short%x00:%x00%h%x00,' + '%x00revid%x00: %x00%H%x00,' + '%x00author%x00: %x00%an <%ae>%x00, %x00date%x00: %ct, %x00summary%x00: %x00%s%x00' + '}' ] if refspec: args += ['-1', refspec] @@ -70,9 +59,14 @@ class Git(Vcs): if filelist: args += ['--'] + filelist + try: + log_raw = self._git(args, catchout=True)\ + .replace('\\', '\\\\').replace('"', '\\"').replace('\x00', '"').splitlines() + except VcsError: + return [] + log = [] - for line in self._git(args, catchout=True)\ - .replace('\\', '\\\\').replace('"', '\\"').replace('\x00', '"').splitlines(): + for line in log_raw: line = json.loads(line) line['date'] = datetime.fromtimestamp(line['date']) log.append(line) @@ -85,31 +79,9 @@ class Git(Vcs): return status return 'unknown' - # Repo creation - #--------------------------- - - def init(self): - """Initializes a repo in current path""" - self._git(['init']) - self.update() - - def clone(self, src): - """Clones a repo from src""" - try: - os.rmdir(self.path) - except OSError: - raise VcsError("Can't clone to {0:s}: Not an empty directory".format(self.path)) - - self._git(['clone', src, os.path.basename(self.path)], path=os.path.dirname(self.path)) - self.update() - # Action interface #--------------------------- - def commit(self, message): - """Commits with a given message""" - self._git(['commit', '--message', message]) - def add(self, filelist=None): """Adds files to the index, preparing for commit""" if filelist: @@ -124,30 +96,6 @@ class Git(Vcs): else: self._git(['reset']) - def pull(self, *args): - """Pulls from remote""" - self._git(['pull'] + list(args)) - - def push(self, *args): - """Pushes to remote""" - self._git(['push'] + list(args)) - - def checkout(self, rev): - """Checks out a branch or revision""" - self._git(['checkout', self._sanitize_rev(rev)]) - - def extract_file(self, rev, name, dest): - """Extracts a file from a given revision and stores it in dest dir""" - if rev == self.INDEX: - shutil.copyfile(os.path.join(self.path, name), dest) - else: - with open(dest, 'wb') as fd: - fd.write( - self._git([ - '--no-pager', 'show', '{0:s}:{1:s}'.format(self._sanitize_rev(rev), name) - ], catchout=True, bytes=True) - ) - # Data Interface #--------------------------- @@ -237,85 +185,18 @@ class Git(Vcs): else: return None - def get_log(self, filelist=None, maxres=None): - """Get the entire log for the current HEAD""" - if not self._has_head(): - return [] - return self._log(refspec=None, maxres=maxres, filelist=filelist) - - def get_raw_log(self, filelist=None): - """Gets the raw log as a string""" - if not self._has_head(): - return [] - args = ['log'] - if filelist: - args += ['--'] + filelist - return self._git(args, catchout=True) - - def get_raw_diff(self, refspec=None, filelist=None): - """Gets the raw diff as a string""" - args = ['diff'] - if refspec: - args += [refspec] - if filelist: - args += ['--'] + filelist - return self._git(args, catchout=True) - - def get_remote(self): - """Returns the url for the remote repo attached to head""" - try: - ref = self._head_ref() - remote = self._remote_ref(ref) - except VcsError: - ref = remote = None - if not remote: - return None - - match = re.match('refs/remotes/([^/]+)/', remote) - if match: - return self._git(['config', '--get', 'remote.{0:s}.url'.format(match.group(1))], - catchout=True).strip() \ - or None - return None - - - def get_revision_id(self, rev=None): - """Get a canonical key for the revision rev""" - if rev is None: - rev = self.HEAD - elif rev == self.INDEX: - return None - rev = self._sanitize_rev(rev) - - return self._sanitize_rev(self._git(['rev-parse', rev], catchout=True)) - def get_info(self, rev=None): """Gets info about the given revision rev""" if rev is None: rev = self.HEAD - rev = self._sanitize_rev(rev) - if rev == self.HEAD and not self._has_head(): - return None log = self._log(refspec=rev) if len(log) == 0: - raise VcsError("Revision {0:s} does not exist".format(rev)) + if rev == self.HEAD: + return None + else: + raise VcsError('Revision {0:s} does not exist'.format(rev)) elif len(log) > 1: - raise VcsError("More than one instance of revision {0:s} ?!?".format(rev)) + raise VcsError('More than one instance of revision {0:s} ?!?'.format(rev)) else: return log[0] - - def get_files(self, rev=None): - """Gets a list of files in revision rev""" - if rev is None: - rev = self.HEAD - rev = self._sanitize_rev(rev) - if rev is None: - return [] - - if rev == self.INDEX: - return self._git(['ls-files', '-z'], - catchout=True, bytes=True).decode('utf-8').split('\x00') - else: - return self._git(['ls-tree', '--name-only', '-r', '-z', rev], - catchout=True, bytes=True).decode('utf-8').split('\x00') diff --git a/ranger/ext/vcs/hg.py b/ranger/ext/vcs/hg.py index 48e991bf..58651795 100644 --- a/ranger/ext/vcs/hg.py +++ b/ranger/ext/vcs/hg.py @@ -2,31 +2,24 @@ import os import re -import shutil from datetime import datetime -try: - from configparser import RawConfigParser -except ImportError: - from ConfigParser import RawConfigParser from .vcs import Vcs, VcsError - class Hg(Vcs): HEAD = 'tip' - # Auxiliar stuff + + # Generic #--------------------------- def _hg(self, path, args, silent=True, catchout=False, bytes=False): return self._vcs(path, 'hg', args, silent=silent, catchout=catchout, bytes=bytes) - def _has_head(self): """Checks whether repo has head""" rnum = self._hg(self.path, ['-q', 'identify', '--num', '-r', self.HEAD], catchout=True) return rnum != '-1' - def _sanitize_rev(self, rev): if rev == None: return None rev = rev.strip() @@ -40,7 +33,6 @@ class Hg(Vcs): return rev - def _log(self, refspec=None, maxres=None, filelist=None): fmt = "changeset: {rev}:{node}\ntag: {tags}\nuser: {author}\ndate: {date}\nsummary: {desc}\n" @@ -66,7 +58,6 @@ class Hg(Vcs): log.append(dt) return log - def _hg_file_status(self, st): if len(st) != 1: raise VcsError("Wrong hg file status string: %s" % st) if st in "ARM": return 'staged' @@ -77,74 +68,19 @@ class Hg(Vcs): elif st in "C": return 'sync' else: return 'unknown' - - - # Repo creation - #--------------------------- - - def init(self): - """Initializes a repo in current path""" - self._hg(self.path, ['init']) - self.update() - - - def clone(self, src): - """Clones a repo from src""" - name = os.path.basename(self.path) - path = os.path.dirname(self.path) - try: - os.rmdir(self.path) - except OSError: - raise VcsError("Can't clone to %s. It is not an empty directory" % self.path) - - self._hg(path, ['clone', src, name]) - self.update() - - - # Action Interface #--------------------------- - def commit(self, message): - """Commits with a given message""" - self._hg(self.path, ['commit', '-m', message]) - - def add(self, filelist=None): """Adds files to the index, preparing for commit""" if filelist != None: self._hg(self.path, ['addremove'] + filelist) else: self._hg(self.path, ['addremove']) - def reset(self, filelist=None): """Removes files from the index""" if filelist == None: filelist = self.get_status_subpaths().keys() self._hg(self.path, ['forget'] + filelist) - - def pull(self): - """Pulls a hg repo""" - self._hg(self.path, ['pull', '-u']) - - - def push(self): - """Pushes a hg repo""" - self._hg(self.path, ['push']) - - - def checkout(self, rev): - """Checks out a branch or revision""" - self._hg(self.path, ['update', rev]) - - - def extract_file(self, rev, name, dest): - """Extracts a file from a given revision and stores it in dest dir""" - if rev == self.INDEX: - shutil.copyfile(os.path.join(self.path, name), dest) - else: - self._hg(self.path, ['cat', '--rev', rev, '--output', dest, name]) - - # Data Interface #--------------------------- @@ -161,14 +97,6 @@ class Hg(Vcs): ret[os.path.normpath(p.strip())] = sta return ret - - def get_ignore_allfiles(self): - """Returns a set of all the ignored files in the repo""" - raw = self._hg(self.path, ['status', '-i'], catchout=True, bytes=True) - L = re.findall('^I\s*(.*?)\s*$', raw.decode('utf-8'), re.MULTILINE) - return set(L) - - def get_status_remote(self): """Checks the status of the repo regarding sync state with remote branch""" if self.get_remote() == None: @@ -190,50 +118,11 @@ class Hg(Vcs): elif not ahead and behind: return "behind" elif not ahead and not behind: return "sync" - def get_branch(self): """Returns the current named branch, if this makes sense for the backend. None otherwise""" branch = self._hg(self.path, ['branch'], catchout=True) return branch or None - - def get_log(self, filelist=None, maxres=None): - """Get the entire log for the current HEAD""" - if not self._has_head(): return [] - return self._log(refspec=None, maxres=maxres, filelist=filelist) - - - def get_raw_log(self, filelist=None): - """Gets the raw log as a string""" - if not self._has_head(): return [] - args = ['log'] - if filelist: args = args + filelist - return self._hg(self.path, args, catchout=True) - - - def get_raw_diff(self, refspec=None, filelist=None): - """Gets the raw diff as a string""" - args = ['diff', '--git'] - if refspec: args = args + [refspec] - if filelist: args = args + filelist - return self._hg(self.path, args, catchout=True) - - - def get_remote(self, rev=None): - """Returns the url for the remote repo attached to head""" - remote = self._hg(self.path, ['showconfig', 'paths.default'], catchout=True) - return remote or None - - - def get_revision_id(self, rev=None): - """Get a canonical key for the revision rev""" - if rev == None: rev = self.HEAD - elif rev == self.INDEX: return None - rev = self._sanitize_rev(rev) - - return self._sanitize_rev(self._hg(self.path, ['-q', 'identify', '--id', '-r', rev], catchout=True)) - - def get_info(self, rev=None): """Gets info about the given revision rev""" if rev == None: rev = self.HEAD @@ -247,16 +136,3 @@ class Hg(Vcs): raise VcsError("More than one instance of revision %s ?!?" % rev) else: return L[0] - - - def get_files(self, rev=None): - """Gets a list of files in revision rev""" - if rev == None: rev = self.HEAD - rev = self._sanitize_rev(rev) - - if rev: - if rev == self.INDEX: raw = self._hg(self.path, ['locate', "*"], catchout=True) - else: raw = self._hg(self.path, ['locate', '--rev', rev, "*"], catchout=True) - return raw.split('\n') - else: - return [] diff --git a/ranger/ext/vcs/svn.py b/ranger/ext/vcs/svn.py index f7e4bbf4..94617b1c 100644 --- a/ranger/ext/vcs/svn.py +++ b/ranger/ext/vcs/svn.py @@ -3,28 +3,23 @@ from __future__ import with_statement import os import re -import shutil import logging from datetime import datetime from .vcs import Vcs, VcsError -#logging.basicConfig(filename='rangersvn.log',level=logging.DEBUG, -# filemode='w') - class SVN(Vcs): HEAD = 'HEAD' - # Auxiliar stuff + + # Generic #--------------------------- def _svn(self, path, args, silent=True, catchout=False, bytes=False): return self._vcs(path, 'svn', args, silent=silent, catchout=catchout, bytes=bytes) - def _has_head(self): """Checks whether repo has head""" return True - def _sanitize_rev(self, rev): if rev == None: return None rev = rev.strip() @@ -38,7 +33,6 @@ class SVN(Vcs): return rev - def _log(self, refspec=None, maxres=None, filelist=None): """ Retrieves log message and parses it. """ @@ -77,7 +71,6 @@ class SVN(Vcs): logging.debug(log) return log - def _svn_file_status(self, st): if len(st) != 1: raise VcsError("Wrong hg file status string: %s" % st) if st in "A": return 'staged' @@ -87,77 +80,19 @@ class SVN(Vcs): elif st in "C": return 'conflict' else: return 'unknown' - - - # Repo creation - #--------------------------- - - def init(self): - """Initializes a repo in current path""" - self._svn(self.path, ['init']) - self.update() - - - def clone(self, src): - """Checks out SVN repo""" - name = os.path.basename(self.path) - path = os.path.dirname(self.path) - try: - os.rmdir(self.path) - except OSError: - raise VcsError("Can't clone to %s. It is not an empty directory" % self.path) - - self._svn(path, ['co', src, name]) - self.update() - - - # Action Interface #--------------------------- - def commit(self, message): - """Commits with a given message""" - self._svn(self.path, ['commit', '-m', message]) - - def add(self, filelist=None): """Adds files to the index, preparing for commit""" if filelist != None: self._svn(self.path, ['add'] + filelist) else: self._svn(self.path, ['add']) - def reset(self, filelist=None): """Equivalent to svn revert""" if filelist == None: filelist = self.get_status_subpaths().keys() self._svn(self.path, ['revert'] + filelist) - - def pull(self): - """Executes SVN Update""" - self._svn(self.path, ['update']) - - - def push(self): - """Push doesn't have an SVN analog.""" - raise NotImplementedError - - - def checkout(self, rev): - """Checks out a branch or revision""" - raise NotImplementedError - self._svn(self.path, ['update', rev]) - - - def extract_file(self, rev, name, dest): - """Extracts a file from a given revision and stores it in dest dir""" - if rev == self.INDEX: - shutil.copyfile(os.path.join(self.path, name), dest) - else: - file_contents = self._svn(self.path, ['cat', '-r', rev, name], catchout=True) - with open(dest, 'w') as f: - f.write(file_contents) - - # Data Interface #--------------------------- @@ -175,15 +110,6 @@ class SVN(Vcs): ret[os.path.normpath(p.strip())] = sta return ret - - def get_ignore_allfiles(self): - """Returns a set of all the ignored files in the repo""" - raw = self._svn(self.path, ['status'], catchout=True, bytes=True) -# logging.debug(raw) - L = re.findall(r'^I\s*(.*?)\s*$', raw.decode('utf-8'), re.MULTILINE) - return set(L) - - def get_status_remote(self): """Checks the status of the repo regarding sync state with remote branch. @@ -196,47 +122,6 @@ class SVN(Vcs): branch = self._svn(self.path, ['branch'], catchout=True) return branch or None - - def get_log(self, filelist=None, maxres=None): - """Get the entire log for the current HEAD""" - if not self._has_head(): return [] - return self._log(refspec=None, maxres=maxres, filelist=filelist) - - - def get_raw_log(self, filelist=None): - """Gets the raw log as a string""" - if not self._has_head(): return [] - args = ['log'] - if filelist: args = args + filelist - return self._svn(self.path, args, catchout=True) - - - def get_raw_diff(self, refspec=None, filelist=None): - """Gets the raw diff as a string""" - args = ['diff', '--git'] - if refspec: args = args + [refspec] - if filelist: args = args + filelist - return self._svn(self.path, args, catchout=True) - - - def get_remote(self, rev=None): - """Returns the url for the remote repo attached to head""" - raw = self.get_info(rev=rev) - L = re.findall('URL: (.*)\n', raw.decode('utf-8')) - - return L[0][0] - - - def get_revision_id(self, rev=None): - """Get a canonical key for the revision rev. - - This is just returning the rev for SVN""" - if rev == None: rev = self.HEAD - elif rev == self.INDEX: return None - rev = self._sanitize_rev(rev) - return rev - - def get_info(self, rev=None): """Gets info about the given revision rev""" if rev == None: rev = self.HEAD @@ -251,12 +136,3 @@ class SVN(Vcs): raise VcsError("More than one instance of revision %s ?!?" % rev) else: return L[0] - - - def get_files(self, rev=None): - """Gets a list of files in revision rev""" - if rev == None: rev = self.HEAD - rev = self._sanitize_rev(rev) - - raw = self._svn(self.path, ['ls', "-r", rev], catchout=True) - return raw.split('\n') diff --git a/ranger/ext/vcs/vcs.py b/ranger/ext/vcs/vcs.py index 241038e0..69bfcf74 100644 --- a/ranger/ext/vcs/vcs.py +++ b/ranger/ext/vcs/vcs.py @@ -66,7 +66,7 @@ class Vcs(object): self.in_repodir = False self.track = False - self.root, self.repodir, self.repotype, self.links = self.find_root(self.path) + self.root, self.repodir, self.repotype, self.links = self._find_root(self.path) self.is_root = True if self.obj.path == self.root else False if self.root: @@ -102,11 +102,11 @@ class Vcs(object): self.track = self.rootvcs.track - # Auxiliar + # Generic #--------------------------- def _vcs(self, path, cmd, args, silent=False, catchout=False, bytes=False): - """Executes a vcs command""" + """Executes a VCS command""" with open(os.devnull, 'w') as devnull: out = devnull if silent else None try: @@ -121,10 +121,7 @@ class Vcs(object): except FileNotFoundError: raise VcsError("{0:s} error on {1:s}: File not found".format(cmd, path)) - # Generic - #--------------------------- - - def get_repotype(self, path): + def _get_repotype(self, path): """Returns the right repo type for path. None if no repo present in path""" for repotype in self.repotypes_settings: repodir = os.path.join(path, '.{0:s}'.format(repotype)) @@ -132,7 +129,7 @@ class Vcs(object): return (repodir, repotype) return (None, None) - def find_root(self, path): + def _find_root(self, path): """Finds the repository root path. Otherwise returns none""" links = set() while True: @@ -141,7 +138,7 @@ class Vcs(object): relpath = os.path.relpath(self.path, path) path = os.path.realpath(path) self.path = os.path.normpath(os.path.join(path, relpath)) - repodir, repotype = self.get_repotype(path) + repodir, repotype = self._get_repotype(path) if repodir: return (path, repodir, repotype, links) if path == '/': @@ -149,29 +146,6 @@ class Vcs(object): path = os.path.dirname(path) return (None, None, None, None) - def check(self): - """Check repository health""" - if not self.in_repodir \ - and (not self.track or (not self.is_root and self.get_repotype(self.path)[0])): - self.__init__(self.obj) - elif self.track and not os.path.exists(self.repodir): - self.update_tree(purge=True) - return False - return True - - def update_root(self): - """Update repository""" - try: - self.rootvcs.head = self.rootvcs.get_info(self.HEAD) - self.rootvcs.branch = self.rootvcs.get_branch() - self.rootvcs.status_subpaths = self.rootvcs.get_status_subpaths() - self.rootvcs.remotestatus = self.rootvcs.get_status_remote() - self.rootvcs.obj.vcspathstatus = self.rootvcs.get_status_root() - except VcsError: - self.update_tree(purge=True) - return False - return True - def _update_walk(self, path, purge): """Update walk""" for wroot, wdirs, _ in os.walk(path): @@ -231,43 +205,28 @@ class Vcs(object): if purge: self.rootvcs.__init__(self.rootvcs.obj) - # Action interface - #--------------------------- - - def commit(self, message): - """Commits with a given message""" - raise NotImplementedError - - def add(self, filelist): - """Adds files to the index, preparing for commit""" - raise NotImplementedError - - def reset(self, filelist): - """Removes files from the index""" - raise NotImplementedError - - def pull(self, **kwargs): - """Pulls from remote""" - raise NotImplementedError - - def push(self, **kwargs): - """Pushes to remote""" - raise NotImplementedError - - def checkout(self, rev): - """Checks out a branch or revision""" - raise NotImplementedError - - def extract_file(self, rev, name, dest): - """Extracts a file from a given revision and stores it in dest dir""" - raise NotImplementedError - - # Data - #--------------------------- + def update_root(self): + """Update repository""" + try: + self.rootvcs.head = self.rootvcs.get_info(self.HEAD) + self.rootvcs.branch = self.rootvcs.get_branch() + self.rootvcs.status_subpaths = self.rootvcs.get_status_subpaths() + self.rootvcs.remotestatus = self.rootvcs.get_status_remote() + self.rootvcs.obj.vcspathstatus = self.rootvcs.get_status_root() + except VcsError: + self.update_tree(purge=True) + return False + return True - def get_status_root_cheap(self): - """Returns the status of self.root, very cheap""" - raise NotImplementedError + def check(self): + """Check repository health""" + if not self.in_repodir \ + and (not self.track or (not self.is_root and self._get_repotype(self.path)[0])): + self.__init__(self.obj) + elif self.track and not os.path.exists(self.repodir): + self.update_tree(purge=True) + return False + return True def get_status_root(self): """Returns the status of root""" @@ -307,47 +266,41 @@ class Vcs(object): return status return 'sync' - def get_status_subpaths(self): - """Returns a dict indexed by subpaths not in sync their status as values. - Paths are given relative to self.root. Strips trailing '/' from dirs.""" - raise NotImplementedError + # Action interface + #--------------------------- - def get_status_remote(self): - """Checks the status of the entire repo""" + def add(self, filelist): + """Adds files to the index, preparing for commit""" raise NotImplementedError - def get_branch(self): - """Returns the current named branch, if this makes sense for the backend. None otherwise""" + def reset(self, filelist): + """Removes files from the index""" raise NotImplementedError - def get_log(self): - """Get the entire log for the current HEAD""" - raise NotImplementedError + # Data interface + #--------------------------- - def get_raw_log(self, filelist=None): - """Gets the raw log as a string""" + def get_status_root_cheap(self): + """Returns the status of self.root cheaply""" raise NotImplementedError - def get_raw_diff(self, refspec=None, filelist=None): - """Gets the raw diff as a string""" + def get_status_subpaths(self): + """Returns a dict indexed by subpaths not in sync with their status as values. + Paths are given relative to self.root. Strips trailing '/' from dirs.""" raise NotImplementedError - def get_remote(self): - """Returns the url for the remote repo attached to head""" + def get_status_remote(self): + """Checks the status of the entire repo""" raise NotImplementedError - def get_revision_id(self, rev=None): - """Get a canonical key for the revision rev""" + def get_branch(self): + """Returns the current named branch, if this makes sense for the backend. None otherwise""" raise NotImplementedError def get_info(self, rev=None): """Gets info about the given revision rev""" raise NotImplementedError - def get_files(self, rev=None): - """Gets a list of files in revision rev""" - raise NotImplementedError - class VcsThread(threading.Thread): """Vcs thread""" def __init__(self, ui, idle_delay): @@ -379,7 +332,7 @@ class VcsThread(threading.Thread): # Redraw if tree is purged if not target.vcs.check(): redraw = True - if target.vcs.track and not target.vcs.root in roots: + if target.vcs.track and target.vcs.root not in roots: roots.add(target.vcs.root) # Do not update repo when repodir is displayed (causes strobing) if tuple(clmn for clmn in self.ui.browser.columns |