| 
          #!/usr/bin/env python | 
        
        
           | 
          
 | 
        
        
           | 
          from __future__ import print_function | 
        
        
           | 
          
 | 
        
        
           | 
          from collections import namedtuple | 
        
        
           | 
          from itertools import tee | 
        
        
           | 
          from operator import itemgetter | 
        
        
           | 
          from subprocess import Popen, PIPE | 
        
        
           | 
          
 | 
        
        
           | 
          
 | 
        
        
           | 
          Commit = namedtuple('Commit', ['sha1', 'parents', 'author_email', 'refs', 'subject', 'timestamp', 'date_iso_8601']) | 
        
        
           | 
          GIT_LOG_FORMAT = '%x1E'.join(['%H', '%P', '%ae', '%d', '%s', '%at', '%ai']) | 
        
        
           | 
          
 | 
        
        
           | 
          seen_sha1s = set() | 
        
        
           | 
          
 | 
        
        
           | 
          def as_list(elements): | 
        
        
           | 
            items = [e for e in elements if e] | 
        
        
           | 
            return items or None | 
        
        
           | 
          
 | 
        
        
           | 
          def fix_parents(parents_string): | 
        
        
           | 
            return as_list(parents_string.split()) | 
        
        
           | 
          
 | 
        
        
           | 
          def fix_refs(refs_string): | 
        
        
           | 
            return as_list(refs_string.strip()[1:-1].split(', ')) | 
        
        
           | 
          
 | 
        
        
           | 
          def mk_commit(commit_line): | 
        
        
           | 
            commit = Commit(*commit_line.split('\x1E')) | 
        
        
           | 
            parents = fix_parents(commit.parents) | 
        
        
           | 
            refs = fix_refs(commit.refs) | 
        
        
           | 
            return commit._replace(parents=parents, refs=refs) | 
        
        
           | 
          
 | 
        
        
           | 
          def create_node(neo4j_op, sha1_ident, property_pairs): | 
        
        
           | 
            if property_pairs: | 
        
        
           | 
              properties = ','.join('{0}:{1!r}'.format(k, v) for k, v in property_pairs) | 
        
        
           | 
              return neo4j_op + ' (c_{0}:Commit {{{1}}})'.format(sha1_ident, properties) | 
        
        
           | 
            else: | 
        
        
           | 
              return neo4j_op + ' (c_{0}:Commit)'.format(sha1_ident) | 
        
        
           | 
          
 | 
        
        
           | 
          def create_rel(sha1, parent): | 
        
        
           | 
            return '(c_{0})<-[:PARENT]-(c_{1})'.format(sha1, parent) | 
        
        
           | 
          
 | 
        
        
           | 
          def mk_node_stmts(neo4j_op, sha1_ident, **properties): | 
        
        
           | 
            if sha1_ident not in seen_sha1s: | 
        
        
           | 
              seen_sha1s.add(sha1_ident) | 
        
        
           | 
              props = {k:v for k, v in properties.iteritems() if v is not None} | 
        
        
           | 
              yield create_node(neo4j_op, sha1_ident, sorted(props.items(), key=itemgetter(0))) | 
        
        
           | 
          
 | 
        
        
           | 
          def node_stmts(neo4j_op, commit_or_sha1): | 
        
        
           | 
            if (hasattr(commit_or_sha1, 'sha1')): | 
        
        
           | 
              sha1, properties = (commit_or_sha1.sha1, commit_or_sha1._asdict()) | 
        
        
           | 
            else: | 
        
        
           | 
              sha1, properties = (commit_or_sha1, {}) | 
        
        
           | 
            return mk_node_stmts(neo4j_op, sha1, **properties) | 
        
        
           | 
          
 | 
        
        
           | 
          def nodes(neo4j_op, cs): | 
        
        
           | 
            for c in cs: | 
        
        
           | 
              for s in node_stmts(neo4j_op, c): | 
        
        
           | 
                yield s | 
        
        
           | 
          
 | 
        
        
           | 
          def missing_parents(neo4j_op, parents): | 
        
        
           | 
            for parent in parents or []: | 
        
        
           | 
              for n in node_stmts(neo4j_op, parent): | 
        
        
           | 
                yield n | 
        
        
           | 
          
 | 
        
        
           | 
          def rel_stmts(neo4j_op, sha1, parents): | 
        
        
           | 
            for parent in parents or []: | 
        
        
           | 
              yield create_rel(sha1, parent) | 
        
        
           | 
          
 | 
        
        
           | 
          def join_rel_stmts(neo4j_op, cs): | 
        
        
           | 
            it = (s for c in cs for s in rel_stmts(neo4j_op, c.sha1, c.parents)) | 
        
        
           | 
            a, b = tee(it) | 
        
        
           | 
            next(b, None) | 
        
        
           | 
            for _ in b: | 
        
        
           | 
              yield next(a) + ',' | 
        
        
           | 
            yield next(a) | 
        
        
           | 
          
 | 
        
        
           | 
          def rels(neo4j_op, cs): | 
        
        
           | 
            for c in cs: | 
        
        
           | 
              for s in missing_parents(neo4j_op, c.parents): | 
        
        
           | 
                yield s | 
        
        
           | 
            yield neo4j_op | 
        
        
           | 
            for s in join_rel_stmts(neo4j_op, cs): | 
        
        
           | 
              yield '  ' + s | 
        
        
           | 
          
 | 
        
        
           | 
          def mk_cmd(limit, branch): | 
        
        
           | 
            git_log = ['git', 'log', '--format=format:{0}'.format(GIT_LOG_FORMAT)] | 
        
        
           | 
            if limit is not None: | 
        
        
           | 
              git_log.append('-n') | 
        
        
           | 
              git_log.append(str(limit)) | 
        
        
           | 
            if branch is not None: | 
        
        
           | 
              git_log.append(str(branch)) | 
        
        
           | 
            return git_log | 
        
        
           | 
          
 | 
        
        
           | 
          def commits(limit, branch): | 
        
        
           | 
            proc = Popen(mk_cmd(limit, branch), stdout=PIPE) | 
        
        
           | 
            for line in proc.stdout: | 
        
        
           | 
              yield line.strip() | 
        
        
           | 
          
 | 
        
        
           | 
          def main(neo4j_op, limit=None, branch=None): | 
        
        
           | 
            cs = map(mk_commit, commits(limit, branch)) | 
        
        
           | 
            statements = [] | 
        
        
           | 
            if cs: | 
        
        
           | 
              first_commit = cs[0].sha1 | 
        
        
           | 
              statements = list(nodes(neo4j_op, cs)) | 
        
        
           | 
              if len(cs) > 1: | 
        
        
           | 
                statements.extend(rels(neo4j_op, cs)) | 
        
        
           | 
              statements.append('RETURN c_' + first_commit + ';') | 
        
        
           | 
            return statements | 
        
        
           | 
          
 | 
        
        
           | 
          if __name__ == "__main__": | 
        
        
           | 
            import argparse | 
        
        
           | 
            parser = argparse.ArgumentParser() | 
        
        
           | 
            parser.add_argument('-n', '--limit', type=int, metavar='N', help='only N latest commits', default=None) | 
        
        
           | 
            parser.add_argument('--merge', dest='neo4j_op', const='MERGE', default='CREATE', action='store_const', help='use MERGE instead of CREATE') | 
        
        
           | 
            parser.add_argument('--json', action='store_true', help='print JSON output for the Cypher REST API.') | 
        
        
           | 
            parser.add_argument('branch', help='which branch to examine - defaults to HEAD', nargs='?', default=None) | 
        
        
           | 
            arguments = parser.parse_args() | 
        
        
           | 
            res = main(arguments.neo4j_op, arguments.limit, arguments.branch) | 
        
        
           | 
            if arguments.json: | 
        
        
           | 
              import json, sys | 
        
        
           | 
              json.dump({"query": ' '.join(res)}, sys.stdout) | 
        
        
           | 
            else: | 
        
        
           | 
              print('\n'.join(res)) |