From 51e2da298186c450449fc5278d2f13e0d1532313 Mon Sep 17 00:00:00 2001 From: Abdo Roig-Maranges Date: Sun, 3 Mar 2013 16:30:17 +0100 Subject: 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. --- ranger/ext/vcs/bzr.py | 10 +++++----- ranger/ext/vcs/git.py | 15 +++++++-------- ranger/ext/vcs/hg.py | 4 ++-- ranger/ext/vcs/vcs.py | 43 +++++++++++++++++++++++++++++-------------- 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 -- cgit 1.4.1-2-gfad0