aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xgit-deps111
1 files changed, 69 insertions, 42 deletions
diff --git a/git-deps b/git-deps
index 3359033..935ac78 100755
--- a/git-deps
+++ b/git-deps
@@ -27,6 +27,7 @@ import subprocess
import types
from textwrap import dedent, wrap
+
def abort(msg, exitcode=1):
print(msg, file=sys.stderr)
sys.exit(exitcode)
@@ -39,7 +40,7 @@ except ImportError:
import platform
if platform.system() == 'Linux':
distro, version, d_id = platform.linux_distribution()
- distro = distro.strip() # why are there trailing spaces??
+ distro = distro.strip() # why are there trailing spaces??
if distro == 'openSUSE':
install_guide = \
"You should be able to install it with something like:\n\n" \
@@ -54,6 +55,7 @@ except ImportError:
msg += "\n\n" + install_guide
abort(msg)
+
class DependencyListener(object):
"""Class for listening to result events generated by
DependencyDetector. Add an instance of this class to a
@@ -78,6 +80,7 @@ class DependencyListener(object):
def dependent_done(self, dependent, dependencies):
pass
+
class CLIDependencyListener(DependencyListener):
"""Dependency listener for use when running in CLI mode.
@@ -88,7 +91,7 @@ class CLIDependencyListener(DependencyListener):
"""
def new_dependency(self, dependent, dependency, path, line_num):
- dependent_sha = dependent.hex
+ dependent_sha = dependent.hex
dependency_sha = dependency.hex
if self.options.recurse:
@@ -101,14 +104,22 @@ class CLIDependencyListener(DependencyListener):
print(dependency_sha)
if self.options.log:
- cmd = [ 'git', '--no-pager', '-c', 'color.ui=always', 'log', '-n1', dependency_sha ]
+ cmd = [
+ 'git',
+ '--no-pager',
+ '-c', 'color.ui=always',
+ 'log', '-n1',
+ dependency_sha
+ ]
print(subprocess.check_output(cmd))
# dependency = detector.get_commit(dependency_sha)
# print(dependency.message + "\n")
# for path in self.dependencies[dependency]:
# print(" %s" % path)
- # print(" %s" % ", ".join(sorted(self.dependencies[dependency][path].keys())))
+ # keys = sorted(self.dependencies[dependency][path].keys()
+ # print(" %s" % ", ".join(keys)))
+
class DependencyDetector(object):
"""Class for automatically detecting dependencies between git commits.
@@ -144,12 +155,12 @@ class DependencyDetector(object):
# A TODO list (queue) and dict of dependencies which haven't
# yet been recursively followed. Only useful when recursing.
- self.todo = []
+ self.todo = []
self.todo_d = {}
# An ordered list and dict of commits whose dependencies we
# have already detected.
- self.done = []
+ self.done = []
self.done_d = {}
# A cache mapping SHAs to commit objects
@@ -184,7 +195,7 @@ class DependencyDetector(object):
formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
handler = logging.StreamHandler(stream=sys.stdout)
handler.setFormatter(formatter)
- #logger = logging.getLogger(__name__)
+ # logger = logging.getLogger(__name__)
logger = logging.getLogger(self.__class__.__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)
@@ -213,17 +224,19 @@ class DependencyDetector(object):
self.todo_d[dependent.hex] = True
while self.todo:
- self.logger.debug("TODO list: %s" %
- " ".join([ commit.hex[:8] for commit in self.todo ]))
+ shas = [commit.hex[:8] for commit in self.todo]
+ self.logger.debug("TODO list: %s" % " ".join(shas))
dependent = self.todo.pop(0)
del self.todo_d[dependent.hex]
- self.logger.debug("Processing %s from TODO list" % dependent.hex[:8])
+ self.logger.debug("Processing %s from TODO list" %
+ dependent.hex[:8])
for parent in dependent.parents:
self.find_dependencies_with_parent(dependent, parent)
self.done.append(dependent.hex)
self.done_d[dependent.hex] = True
- self.logger.debug("Found all dependencies for %s" % dependent.hex[:8])
+ self.logger.debug("Found all dependencies for %s" %
+ dependent.hex[:8])
# A commit won't have any dependencies if it only added new files
dependencies = self.dependencies.get(dependent.hex, {})
self.notify_listeners('dependent_done', dependent, dependencies)
@@ -235,7 +248,8 @@ class DependencyDetector(object):
"""
self.logger.debug(" Finding dependencies of %s via parent %s" %
(dependent.hex[:8], parent.hex[:8]))
- diff = self.repo.diff(parent, dependent, context_lines=self.options.context_lines)
+ diff = self.repo.diff(parent, dependent,
+ context_lines=self.options.context_lines)
for patch in diff:
path = patch.old_file_path
self.logger.debug(" Examining hunks in %s" % path)
@@ -278,7 +292,7 @@ class DependencyDetector(object):
line_to_culprit = {}
for line in blame.split('\n'):
- #self.logger.debug(' !' + line.rstrip())
+ # self.logger.debug(' !' + line.rstrip())
m = re.match('^([0-9a-f]{40}) (\d+) (\d+)( \d+)?$', line)
if not m:
continue
@@ -288,43 +302,51 @@ class DependencyDetector(object):
line_to_culprit[line_num] = dependency.hex
if self.is_excluded(dependency):
- self.logger.debug(' Excluding dependency %s from line %s (%s)' %
- (dependency_sha[:8], line_num,
- self.oneline(dependency)))
+ self.logger.debug(
+ ' Excluding dependency %s from line %s (%s)' %
+ (dependency_sha[:8], line_num,
+ self.oneline(dependency)))
continue
if dependency_sha not in self.dependencies[dependent_sha]:
if dependency_sha in self.todo_d:
- self.logger.debug(' Dependency %s via line %s already in TODO' %
- (dependency_sha[:8], line_num,))
+ self.logger.debug(
+ ' Dependency %s via line %s already in TODO' %
+ (dependency_sha[:8], line_num,))
continue
if dependency_sha in self.done_d:
- self.logger.debug(' Dependency %s via line %s already done' %
- (dependency_sha[:8], line_num,))
+ self.logger.debug(
+ ' Dependency %s via line %s already done' %
+ (dependency_sha[:8], line_num,))
continue
- self.logger.debug(' New dependency %s via line %s (%s)' %
- (dependency_sha[:8], line_num,
- self.oneline(dependency)))
+ self.logger.debug(
+ ' New dependency %s via line %s (%s)' %
+ (dependency_sha[:8], line_num, self.oneline(dependency)))
self.dependencies[dependent_sha][dependency_sha] = {}
- self.notify_listeners('new_dependency', dependent, dependency, path, line_num)
+ self.notify_listeners('new_dependency',
+ dependent, dependency, path, line_num)
if dependency_sha not in self.dependencies:
if self.options.recurse:
self.todo.append(dependency)
self.todo_d[dependency.hex] = True
self.logger.debug(' added to TODO')
- if path not in self.dependencies[dependent_sha][dependency_sha]:
- self.dependencies[dependent_sha][dependency_sha][path] = {}
- self.notify_listeners('new_path', dependent, dependency, path, line_num)
+ dep_sources = self.dependencies[dependent_sha][dependency_sha]
+
+ if path not in dep_sources:
+ dep_sources[path] = {}
+ self.notify_listeners('new_path',
+ dependent, dependency, path, line_num)
- if line_num in self.dependencies[dependent_sha][dependency_sha][path]:
+ if line_num in dep_sources[path]:
abort("line %d already found when blaming %s:%s" %
(line_num, parent.hex[:8], path))
- self.dependencies[dependent_sha][dependency_sha][path][line_num] = True
- self.notify_listeners('new_line', dependent, dependency, path, line_num)
+ dep_sources[path][line_num] = True
+ self.notify_listeners('new_line',
+ dependent, dependency, path, line_num)
diff_format = ' |%8.8s %5s %s%s'
hunk_header = '@@ %s %s @@' % (line_range_before, line_range_after)
@@ -350,7 +372,8 @@ class DependencyDetector(object):
return False
def branch_contains(self, commit, branch):
- self.logger.debug(" Does %s contain %s?" % (branch, commit.hex[:8]))
+ self.logger.debug(" Does %s contain %s?" %
+ (branch, commit.hex[:8]))
branch_commit = self.get_commit(branch)
if commit.hex not in self.branch_contains_cache:
@@ -360,10 +383,10 @@ class DependencyDetector(object):
self.logger.debug(" %s (memoized)" % memoized)
return memoized
- cmd = [ 'git', 'merge-base', commit.hex, branch_commit.hex ]
- #self.logger.debug(" ".join(cmd))
+ cmd = ['git', 'merge-base', commit.hex, branch_commit.hex]
+ # self.logger.debug(" ".join(cmd))
out = subprocess.check_output(cmd).strip()
- #self.logger.debug(out)
+ # self.logger.debug(out)
result = out == commit.hex
self.logger.debug(" %s" % result)
self.branch_contains_cache[commit.hex][branch_commit.hex] = result
@@ -383,7 +406,7 @@ class DependencyDetector(object):
if isinstance(tree_or_blob, pygit2.Tree):
if dirent in tree_or_blob:
tree_or_blob = self.repo[tree_or_blob[dirent].oid]
- #self.logger.debug('%s in %s' % (dirent, path))
+ # self.logger.debug('%s in %s' % (dirent, path))
if path:
path += '/'
path += dirent
@@ -401,10 +424,12 @@ class DependencyDetector(object):
def edges(self):
return [
- [ (dependent, dependency) for dependency in self.dependencies[dependent] ]
+ [(dependent, dependency)
+ for dependency in self.dependencies[dependent]]
for dependent in self.dependencies.keys()
]
+
def parse_args():
parser = argparse.ArgumentParser(
description='Auto-detects commits which the given commit(s) depend on.',
@@ -416,11 +441,12 @@ def parse_args():
help='Follow dependencies recursively')
parser.add_argument('-e', '--exclude-commits', dest='exclude_commits',
action='append', metavar='COMMITISH',
- help='Exclude commits which are ancestors of the given COMMITISH'
- ' (can be repeated)')
- parser.add_argument('-c', '--context-lines', dest='context_lines', type=int,
- metavar='NUM', default=1,
- help='Number of lines of diff context to use [%(default)s]')
+ help='Exclude commits which are ancestors of the '
+ 'given COMMITISH (can be repeated)')
+ parser.add_argument('-c', '--context-lines', dest='context_lines',
+ type=int, metavar='NUM', default=1,
+ help='Number of lines of diff context to use '
+ '[%(default)s]')
parser.add_argument('-d', '--debug', dest='debug', action='store_true',
help='Show debugging')
@@ -432,9 +458,10 @@ def parse_args():
return options, args
+
def main():
options, args = parse_args()
- #rev_list = sys.stdin.readlines()
+ # rev_list = sys.stdin.readlines()
listener = CLIDependencyListener(options)
detector = DependencyDetector(options, listener=listener)