Skip to content

Commit abb4d92

Browse files
committed
wip
1 parent 6b86a29 commit abb4d92

File tree

9 files changed

+1051
-287
lines changed

9 files changed

+1051
-287
lines changed

api/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
from .info import *
22
from .llm import ask
33
from .graph import *
4+
from .project import *
45
from .entities import *
6+
from .git_utils import *
57
from .code_coverage import *
8+
from .index import create_app
69
from .analyzers.source_analyzer import *
710
from .auto_complete import prefix_search

api/git_utils/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .git_utils import *

api/git_utils/git_graph.py

+177
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import os
2+
import logging
3+
from git import Commit
4+
from falkordb import FalkorDB, Node
5+
from typing import List, Optional
6+
7+
# Configure logging
8+
logging.basicConfig(level=logging.DEBUG, format='%(filename)s - %(asctime)s - %(levelname)s - %(message)s')
9+
10+
class GitGraph():
11+
"""
12+
Represents a git commit graph
13+
nodes are commits where one commit leads to its parents and children
14+
edges contains queries and parameters for transitioning the code-graph
15+
from the current commit to parent / child
16+
"""
17+
18+
def __init__(self, name: str):
19+
20+
self.db = FalkorDB(host=os.getenv('FALKORDB_HOST', 'localhost'),
21+
port=os.getenv('FALKORDB_PORT', 6379),
22+
username=os.getenv('FALKORDB_USERNAME', None),
23+
password=os.getenv('FALKORDB_PASSWORD', None))
24+
25+
self.g = self.db.select_graph(name)
26+
27+
# create indicies
28+
# index commit hash
29+
try:
30+
self.g.create_node_range_index("Commit", "hash")
31+
except Exception:
32+
pass
33+
34+
def _commit_from_node(self, node:Node) -> dict:
35+
"""
36+
Returns a dict representing a commit node
37+
"""
38+
39+
return {'hash': node.properties['hash'],
40+
'date': node.properties['date'],
41+
'author': node.properties['author'],
42+
'message': node.properties['message']}
43+
44+
def add_commit(self, commit: Commit) -> None:
45+
"""
46+
Add a new commit to the graph
47+
"""
48+
date = commit.committed_date
49+
author = commit.author.name
50+
hexsha = commit.hexsha
51+
message = commit.message
52+
logging.info(f"Adding commit {hexsha}: {message}")
53+
54+
q = "MERGE (c:Commit {hash: $hash, author: $author, message: $message, date: $date})"
55+
params = {'hash': hexsha, 'author': author, 'message': message, 'date': date}
56+
self.g.query(q, params)
57+
58+
def list_commits(self) -> List[Node]:
59+
"""
60+
List all commits
61+
"""
62+
63+
q = "MATCH (c:Commit) RETURN c ORDER BY c.date"
64+
result_set = self.g.query(q).result_set
65+
66+
return [self._commit_from_node(row[0]) for row in result_set]
67+
68+
def get_commits(self, hashes: List[str]) -> List[dict]:
69+
logging.info(f"Searching for commits {hashes}")
70+
71+
q = """MATCH (c:Commit)
72+
WHERE c.hash IN $hashes
73+
RETURN c"""
74+
75+
params = {'hashes': hashes}
76+
res = self.g.query(q, params).result_set
77+
78+
commits = []
79+
for row in res:
80+
commit = self._commit_from_node(row[0])
81+
commits.append(commit)
82+
83+
logging.info(f"retrived commits: {commits}")
84+
return commits
85+
86+
def get_child_commit(self, parent) -> Optional[dict]:
87+
q = """MATCH (c:Commit {hash: $parent})-[:CHILD]->(child: Commit)
88+
RETURN child"""
89+
90+
res = self.g.query(q, {'parent': parent}).result_set
91+
92+
if len(res) > 0:
93+
assert(len(res) == 1)
94+
return self._commit_from_node(res[0][0])
95+
96+
return None
97+
98+
def connect_commits(self, child: str, parent: str) -> None:
99+
"""
100+
connect commits via both PARENT and CHILD edges
101+
"""
102+
103+
logging.info(f"Connecting commits {child} -PARENT-> {parent}")
104+
logging.info(f"Connecting commits {parent} -CHILD-> {child}")
105+
106+
q = """MATCH (child :Commit {hash: $child_hash}), (parent :Commit {hash: $parent_hash})
107+
MERGE (child)-[:PARENT]->(parent)
108+
MERGE (parent)-[:CHILD]->(child)"""
109+
110+
params = {'child_hash': child, 'parent_hash': parent}
111+
112+
self.g.query(q, params)
113+
114+
115+
def set_parent_transition(self, child: str, parent: str, queries: [str], params: [str]) -> None:
116+
"""
117+
Sets the queries and parameters needed to transition the code-graph
118+
from the child commit to the parent commit
119+
"""
120+
121+
q = """MATCH (child :Commit {hash: $child})-[e:PARENT]->(parent :Commit {hash: $parent})
122+
SET e.queries = $queries, e.params = $params"""
123+
124+
_params = {'child': child, 'parent': parent, 'queries': queries, 'params': params}
125+
126+
self.g.query(q, _params)
127+
128+
129+
def set_child_transition(self, child: str, parent: str, queries: [str], params: [str]) -> None:
130+
"""
131+
Sets the queries and parameters needed to transition the code-graph
132+
from the parent commit to the child commit
133+
"""
134+
135+
q = """MATCH (parent :Commit {hash: $parent})-[e:CHILD]->(child :Commit {hash: $child})
136+
SET e.queries = $queries, e.params = $params"""
137+
138+
_params = {'child': child, 'parent': parent, 'queries': queries, 'params': params}
139+
140+
self.g.query(q, _params)
141+
142+
143+
def get_parent_transitions(self, child: str, parent: str) -> List[tuple[str: dict]]:
144+
"""
145+
Get queries and parameters transitioning from child commit to parent commit
146+
"""
147+
q = """MATCH path = (:Commit {hash: $child_hash})-[:PARENT*]->(:Commit {hash: $parent_hash})
148+
WITH path
149+
LIMIT 1
150+
UNWIND relationships(path) AS e
151+
WITH e
152+
WHERE e.queries is not NULL
153+
RETURN collect(e.queries), collect(e.params)
154+
"""
155+
156+
res = self.g.query(q, {'child_hash': child, 'parent_hash': parent}).result_set
157+
158+
return (res[0][0], res[0][1])
159+
160+
161+
def get_child_transitions(self, child: str, parent: str) -> List[tuple[str: dict]]:
162+
"""
163+
Get queries and parameters transitioning from parent commit to child commit
164+
"""
165+
q = """MATCH path = (:Commit {hash: $parent_hash})-[:CHILD*]->(:Commit {hash: $child_hash})
166+
WITH path
167+
LIMIT 1
168+
UNWIND relationships(path) AS e
169+
WITH e
170+
WHERE e.queries is not NULL
171+
RETURN collect(e.queries), collect(e.params)
172+
"""
173+
174+
res = self.g.query(q, {'child_hash': child, 'parent_hash': parent}).result_set
175+
176+
return (res[0][0], res[0][1])
177+

0 commit comments

Comments
 (0)