summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xranger/config/commands.py106
-rw-r--r--ranger/ext/vcs/__init__.py2
-rw-r--r--ranger/ext/vcs/bzr.py154
-rw-r--r--ranger/ext/vcs/git.py157
-rw-r--r--ranger/ext/vcs/hg.py128
-rw-r--r--ranger/ext/vcs/svn.py128
-rw-r--r--ranger/ext/vcs/vcs.py137
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
title='author Kartik Agaram <vc@akkartik.com> 2020-07-10 23:44:10 -0700 committer Kartik Agaram <vc@akkartik.com> 2020-07-10 23:53:28 -0700 6631' href='/akkartik/mu/commit/html/135next-word-or-string.subx.html?h=main&id=ec73ed1230d75deb0f913a32617c9f1e0a5ca640'>ec73ed12 ^
4a4a392d ^

2104d1a7 ^
4a4a392d ^





ec73ed12 ^
4a4a392d ^
ec73ed12 ^
4a4a392d ^




ec73ed12 ^
4a4a392d ^
ec73ed12 ^
4a4a392d ^




ec73ed12 ^
4a4a392d ^


ec73ed12 ^
4a4a392d ^

ec73ed12 ^
4a4a392d ^




ec73ed12 ^
4a4a392d ^


ec73ed12 ^
4a4a392d ^

ec73ed12 ^
4a4a392d ^

b1635a5c ^
4a4a392d ^








1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479