about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAbdo Roig-Maranges <abdo.roig@gmail.com>2013-03-03 16:30:17 +0100
committerAbdo Roig-Maranges <abdo.roig@gmail.com>2013-03-03 18:01:33 +0100
commit51e2da298186c450449fc5278d2f13e0d1532313 (patch)
tree60aa91efb6d8ec881b186c1921068629fef25a42
parentb3660b9a2ad8fefe5969a6c4afb195b973ac1f04 (diff)
downloadranger-51e2da298186c450449fc5278d2f13e0d1532313.tar.gz
vcs extension: fix vcs file status detection
This fixes the following:
  - correct ignore state for directories containing only ignored
    files. (only git and bzr backends)
  - correctly mark as new the contents of untracked directories.
  - auxiliar function _path_contains.
  - small bug in bzr backend status detection.
-rw-r--r--ranger/ext/vcs/bzr.py10
-rw-r--r--ranger/ext/vcs/git.py15
-rw-r--r--ranger/ext/vcs/hg.py4
-rw-r--r--ranger/ext/vcs/vcs.py43
4 files changed, 43 insertions, 29 deletions
diff --git a/ranger/ext/vcs/bzr.py b/ranger/ext/vcs/bzr.py
index a63eea54..ad82ce4d 100644
--- a/ranger/ext/vcs/bzr.py
+++ b/ranger/ext/vcs/bzr.py
@@ -80,7 +80,7 @@ class Bzr(Vcs):
         return log
 
 
-    def _hg_file_status(self, st):
+    def _bzr_file_status(self, st):
         st = st.strip()
         if   st in "AM":     return 'staged'
         elif st in "D":      return 'deleted'
@@ -162,20 +162,20 @@ class Bzr(Vcs):
 
     def get_status_allfiles(self):
         """Returns a dict indexed by files not in sync their status as values.
-           Paths are given relative to the root."""
+           Paths are given relative to the root. Strips trailing '/' from dirs."""
         raw = self._bzr(self.path, ['status', '--short', '--no-classify'], catchout=True, bytes=True)
         L = re.findall('^(..)\s*(.*?)\s*$', raw.decode('utf-8'), re.MULTILINE)
         ret = {}
         for st, p in L:
             sta = self._bzr_file_status(st)
-            ret[p.strip()] = sta
+            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"""
+        """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(raw.split('\n'))
+        return set(os.path.normpath(p) for p in raw.split('\n'))
 
 
     # TODO: slow due to net access
diff --git a/ranger/ext/vcs/git.py b/ranger/ext/vcs/git.py
index b2e79a06..e462b003 100644
--- a/ranger/ext/vcs/git.py
+++ b/ranger/ext/vcs/git.py
@@ -180,7 +180,7 @@ class Git(Vcs):
 
     def get_status_allfiles(self):
         """Returns a dict indexed by files not in sync their status as values.
-           Paths are given relative to the root."""
+           Paths are given relative to the root. Strips trailing '/' from dirs."""
         raw = self._git(self.path, ['status', '--porcelain'], catchout=True, bytes=True)
         L = re.findall('^(..)\s*(.*?)\s*$', raw.decode('utf-8'), re.MULTILINE)
         ret = {}
@@ -188,17 +188,16 @@ class Git(Vcs):
             sta = self._git_file_status(st)
             if 'R' in st:
                 m = re.match('^(.*)\->(.*)$', p)
-                if m: ret[m.group(2).strip()] = sta
-                else: ret[p.strip()] = sta
-            else:
-                ret[p.strip()] = sta
+                if m: p = m.group(2).strip()
+            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._git(self.path, ['ls-files', '--others', '-i', '--exclude-standard'], catchout=True)
-        return set(raw.split('\n'))
+        """Returns a set of all the ignored files in the repo. Strips trailing '/' from dirs."""
+        raw = self._git(self.path, ['ls-files', '--others', '--directory', '-i', '--exclude-standard'],
+                        catchout=True)
+        return set(os.path.normpath(p) for p in raw.split('\n'))
 
 
     def get_remote_status(self):
diff --git a/ranger/ext/vcs/hg.py b/ranger/ext/vcs/hg.py
index 08e5753b..92aae349 100644
--- a/ranger/ext/vcs/hg.py
+++ b/ranger/ext/vcs/hg.py
@@ -168,7 +168,7 @@ class Hg(Vcs):
 
     def get_status_allfiles(self):
         """Returns a dict indexed by files not in sync their status as values.
-           Paths are given relative to the root."""
+           Paths are given relative to the root. Strips trailing '/' from dirs."""
         raw = self._hg(self.path, ['status'], catchout=True, bytes=True)
         L = re.findall('^(.)\s*(.*?)\s*$', raw.decode('utf-8'), re.MULTILINE)
         ret = {}
@@ -176,7 +176,7 @@ class Hg(Vcs):
             # Detect conflict by the existence of .orig files
             if st == '?' and re.match('^.*\.orig\s*$', p):  st = 'X'
             sta = self._hg_file_status(st)
-            ret[p.strip()] = sta
+            ret[os.path.normpath(p.strip())] = sta
         return ret
 
 
diff --git a/ranger/ext/vcs/vcs.py b/ranger/ext/vcs/vcs.py
index e0d434e0..dfd187cb 100644
--- a/ranger/ext/vcs/vcs.py
+++ b/ranger/ext/vcs/vcs.py
@@ -95,6 +95,8 @@ class Vcs(object):
     def _path_contains(self, parent, path):
         """Checks wether path is an object belonging to the subtree in parent"""
         if parent == path: return True
+        parent = os.path.normpath(parent + '/')
+        path = os.path.normpath(path)
         return os.path.commonprefix([parent, path]) == parent
 
 
@@ -229,20 +231,33 @@ class Vcs(object):
         # if path is relative, join it with root. otherwise do nothing
         path = os.path.join(self.root, path)
 
-        if os.path.commonprefix([self.root, path]) == self.root:
-            prel = os.path.relpath(path, self.root)
-            if prel in self.ignored:   return "ignored"
-            if os.path.isdir(path):
-                sts = set([st for p, st in self.status.items()
-                           if self._path_contains(path, os.path.join(self.root, p))]) | set(['sync'])
-                for st in self.FILE_STATUS:
-                    if st in sts: return st
-            else:
-                if prel in self.status:  return self.status[prel]
-                else:                    return "sync"
-        else:
+        # path is not in the repo
+        if not self._path_contains(self.root, path):
             return "none"
 
+        # check if prel or some parent of prel is ignored
+        prel = os.path.relpath(path, self.root)
+        while len(prel) > 0 and prel != '/' and prel != '.':
+            if prel in self.ignored: return "ignored"
+            prel, tail = os.path.split(prel)
+
+        # check if prel or some parent of prel is listed in status
+        prel = os.path.relpath(path, self.root)
+        while len(prel) > 0 and prel != '/' and prel != '.':
+            if prel in self.status: return self.status[prel]
+            prel, tail = os.path.split(prel)
+
+        # check if prel is a directory that contains some file in status
+        prel = os.path.relpath(path, self.root)
+        if os.path.isdir(path):
+            sts = set(st for p, st in self.status.items()
+                      if self._path_contains(path, os.path.join(self.root, p)))
+            for st in self.FILE_STATUS:
+                if st in sts: return st
+
+        # it seems prel is in sync
+        return "sync"
+
 
     def get_status(self, path=None):
         """Returns a dict with changed files under path and their status.
@@ -262,12 +277,12 @@ class Vcs(object):
 
     def get_status_allfiles(self):
         """Returns a dict indexed by files not in sync their status as values.
-           Paths are given relative to the root."""
+           Paths are given relative to the root.  Strips trailing '/' from dirs."""
         raise NotImplementedError
 
 
     def get_ignore_allfiles(self):
-        """Returns a set of all the ignored files in the repo"""
+        """Returns a set of all the ignored files in the repo. Strips trailing '/' from dirs."""
         raise NotImplementedError