about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authornfnty <git@nfnty.se>2015-10-09 00:40:34 +0200
committernfnty <git@nfnty.se>2016-02-08 04:43:04 +0100
commit1ca99394fccbfe1db8678530b1b7ab1c017d2310 (patch)
tree1954ed69d8a64cd4dfecff9df819cf95739872d8
parent79cad207f3843576655b2e07e573937fd611e358 (diff)
downloadranger-1ca99394fccbfe1db8678530b1b7ab1c017d2310.tar.gz
VCS: Implement cheap subpath repo updating
-rw-r--r--ranger/colorschemes/default.py2
-rw-r--r--ranger/container/directory.py9
-rw-r--r--ranger/ext/vcs/git.py19
-rw-r--r--ranger/ext/vcs/vcs.py50
-rw-r--r--ranger/gui/context.py2
-rw-r--r--ranger/gui/widgets/__init__.py2
6 files changed, 49 insertions, 35 deletions
diff --git a/ranger/colorschemes/default.py b/ranger/colorschemes/default.py
index 80fa9e39..7a248f2e 100644
--- a/ranger/colorschemes/default.py
+++ b/ranger/colorschemes/default.py
@@ -137,7 +137,7 @@ class Default(ColorScheme):
 
         elif context.vcsremote and not context.selected:
             attr &= ~bold
-            if context.vcssync:
+            if context.vcssync or context.vcslocal:
                 fg = green
             elif context.vcsbehind:
                 fg = red
diff --git a/ranger/container/directory.py b/ranger/container/directory.py
index 0cf2b528..379e0830 100644
--- a/ranger/container/directory.py
+++ b/ranger/container/directory.py
@@ -307,7 +307,6 @@ class Directory(FileSystemObject, Accumulator, Loadable):
                 disk_usage = 0
 
                 if self.settings.vcs_aware and self.vcs.root:
-                    self.has_vcschild = True
                     self.vcs.update(self)
 
                 for name in filenames:
@@ -334,19 +333,17 @@ class Directory(FileSystemObject, Accumulator, Loadable):
                             except:
                                 item = Directory(name, preload=stats, path_is_abs=True)
                                 item.load()
-                        if item.settings.vcs_aware:
+                        if item.settings.vcs_aware and item.vcs.root:
+                            item.vcs.update(item, child=True)
                             if item.vcs.is_root:
                                 self.has_vcschild = True
-                                item.vcs.update(item)
-                            elif item.vcs.root:
-                                item.vcs.update_child(item)
                     else:
                         item = File(name, preload=stats, path_is_abs=True,
                                     basename_is_rel_to=basename_is_rel_to)
                         item.load()
                         disk_usage += item.size
                         if self.settings.vcs_aware and self.vcs.root:
-                            item.vcspathstatus = self.vcs.get_path_status(item.path)
+                            item.vcspathstatus = self.vcs.get_status_subpath(item.path)
 
                     files.append(item)
                     self.percent = 100 * len(files) // len(filenames)
diff --git a/ranger/ext/vcs/git.py b/ranger/ext/vcs/git.py
index 54250026..d10c5428 100644
--- a/ranger/ext/vcs/git.py
+++ b/ranger/ext/vcs/git.py
@@ -156,6 +156,25 @@ class Git(Vcs):
     # Data Interface
     #---------------------------
 
+    def get_status_root_child(self):
+        """Returns the status of a child root, very cheap"""
+        statuses = set()
+        # Paths with status
+        skip = False
+        for line in self._git(['status', '--porcelain', '-z'],
+                              catchout=True, bytes=True).decode('utf-8').split('\x00')[:-1]:
+            if skip:
+                skip = False
+                continue
+            statuses.add(self._git_status_translate(line[:2]))
+            if line.startswith('R'):
+                skip = True
+
+        for status in self.DIR_STATUS:
+            if status in statuses:
+                return status
+        return 'sync'
+
     def get_status_subpaths(self):
         """Returns a dict (path: status) for paths not in sync. Strips trailing '/' from dirs"""
         statuses = {}
diff --git a/ranger/ext/vcs/vcs.py b/ranger/ext/vcs/vcs.py
index 7dc171e8..ebd18385 100644
--- a/ranger/ext/vcs/vcs.py
+++ b/ranger/ext/vcs/vcs.py
@@ -81,8 +81,7 @@ class Vcs(object):
             if setting in ('enabled', 'local')
         ]
 
-        self.status = {}
-        self.ignored = set()
+        self.status_subpaths = {}
         self.head = None
         self.remotestatus = None
         self.branch = None
@@ -142,32 +141,27 @@ class Vcs(object):
             path = os.path.dirname(path)
         return (None, None)
 
-    def update(self, directoryobject):
+    def update(self, directoryobject, child=False):
         """Update repository"""
         root = self if self.is_root else directoryobject.fm.get_directory(self.root).vcs
-        root.head = root.get_info(root.HEAD)
-        root.branch = root.get_branch()
-        root.remotestatus = root.get_status_remote()
-        root.status = root.get_status_subpaths()
+        if child and self.is_root:
+            directoryobject.vcspathstatus = self.get_status_root_child()
+        elif not child:
+            root.head = root.get_info(root.HEAD)
+            root.branch = root.get_branch()
+            root.status_subpaths = root.get_status_subpaths()
+            if self.is_root:
+                directoryobject.vcspathstatus = self.get_status_root()
 
         if self.is_root:
-            directoryobject.vcspathstatus = self.get_root_status()
+            root.remotestatus = root.get_status_remote()
         else:
             self.head = root.head
             self.branch = root.branch
-            self.status = root.status
-            directoryobject.vcspathstatus = root.get_path_status(
+            self.status_subpaths = root.status_subpaths
+            directoryobject.vcspathstatus = root.get_status_subpath(
                 self.path, is_directory=True)
 
-    def update_child(self, directoryobject):
-        """After update() for subdirectories"""
-        root = directoryobject.fm.get_directory(self.root).vcs
-        self.head = root.head
-        self.branch = root.branch
-        self.status = root.status
-        directoryobject.vcspathstatus = root.get_path_status(
-            self.path, is_directory=True)
-
     # Repo creation
     #---------------------------
 
@@ -241,28 +235,32 @@ class Vcs(object):
         """Checks whether HEAD is tracking a remote repo"""
         return self.get_remote(self.HEAD) is not None
 
-    def get_root_status(self):
+    def get_status_root_child(self):
+        """Returns the status of a child root, very cheap"""
+        raise NotImplementedError
+
+    def get_status_root(self):
         """Returns the status of root"""
-        statuses = set(status for path, status in self.status.items())
+        statuses = set(status for path, status in self.status_subpaths.items())
         for status in self.DIR_STATUS:
             if status in statuses:
                 return status
         return 'sync'
 
-    def get_path_status(self, path, is_directory=False):
+    def get_status_subpath(self, path, is_directory=False):
         """Returns the status of path"""
         relpath = os.path.relpath(path, self.root)
 
         # check if relpath or its parents has a status
         tmppath = relpath
         while tmppath:
-            if tmppath in self.status:
-                return self.status[tmppath]
+            if tmppath in self.status_subpaths:
+                return self.status_subpaths[tmppath]
             tmppath = os.path.dirname(tmppath)
 
         # check if path contains some file in status
         if is_directory:
-            statuses = set(status for subpath, status in self.status.items()
+            statuses = set(status for subpath, status in self.status_subpaths.items()
                            if subpath.startswith(relpath + '/'))
             for status in self.DIR_STATUS:
                 if status in statuses:
@@ -270,7 +268,7 @@ class Vcs(object):
         return 'sync'
 
     def get_status_subpaths(self):
-        """Returns a dict indexed by files not in sync their status as values.
+        """Returns a dict indexed by subpaths not in sync their status as values.
            Paths are given relative to the root.  Strips trailing '/' from dirs."""
         raise NotImplementedError
 
diff --git a/ranger/gui/context.py b/ranger/gui/context.py
index e5aef06c..66d43d1a 100644
--- a/ranger/gui/context.py
+++ b/ranger/gui/context.py
@@ -19,7 +19,7 @@ CONTEXT_KEYS = ['reset', 'error', 'badinfo',
         'infostring',
         'vcsfile', 'vcsremote', 'vcsinfo', 'vcscommit',
         'vcsconflict', 'vcschanged', 'vcsunknown', 'vcsignored',
-        'vcsstaged', 'vcssync', 'vcsbehind', 'vcsahead', 'vcsdiverged']
+        'vcsstaged', 'vcssync', 'vcslocal', 'vcsbehind', 'vcsahead', 'vcsdiverged']
 
 class Context(object):
     def __init__(self, keys):
diff --git a/ranger/gui/widgets/__init__.py b/ranger/gui/widgets/__init__.py
index a1b4c289..4dd28c6d 100644
--- a/ranger/gui/widgets/__init__.py
+++ b/ranger/gui/widgets/__init__.py
@@ -15,7 +15,7 @@ class Widget(Displayable):
                   'none':      (' ', []),
                   'unknown':   ('?', ["vcsunknown"])}
 
-    vcsremotestatus_symb = {'none':     (' ',  []),
+    vcsremotestatus_symb = {'none':     ('⌂',  ["vcslocal"]),
                 'sync':     ('=', ["vcssync"]),
                 'behind':   ('<', ["vcsbehind"]),
                 'ahead':    ('>', ["vcsahead"]),