kodit 0.5.3__py3-none-any.whl → 0.5.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kodit might be problematic. Click here for more details.
- kodit/_version.py +2 -2
- kodit/application/factories/server_factory.py +54 -32
- kodit/application/services/code_search_application_service.py +89 -12
- kodit/application/services/commit_indexing_application_service.py +314 -195
- kodit/application/services/enrichment_query_service.py +274 -43
- kodit/application/services/indexing_worker_service.py +1 -1
- kodit/application/services/queue_service.py +15 -10
- kodit/application/services/sync_scheduler.py +2 -1
- kodit/domain/enrichments/architecture/architecture.py +1 -1
- kodit/domain/enrichments/architecture/physical/physical.py +1 -1
- kodit/domain/enrichments/development/development.py +1 -1
- kodit/domain/enrichments/development/snippet/snippet.py +12 -5
- kodit/domain/enrichments/enrichment.py +31 -4
- kodit/domain/enrichments/usage/api_docs.py +1 -1
- kodit/domain/enrichments/usage/usage.py +1 -1
- kodit/domain/entities/git.py +30 -25
- kodit/domain/factories/git_repo_factory.py +20 -5
- kodit/domain/protocols.py +56 -125
- kodit/domain/services/embedding_service.py +14 -16
- kodit/domain/services/git_repository_service.py +60 -38
- kodit/domain/services/git_service.py +18 -11
- kodit/domain/tracking/resolution_service.py +6 -16
- kodit/domain/value_objects.py +2 -9
- kodit/infrastructure/api/v1/dependencies.py +12 -3
- kodit/infrastructure/api/v1/query_params.py +27 -0
- kodit/infrastructure/api/v1/routers/commits.py +91 -85
- kodit/infrastructure/api/v1/routers/repositories.py +53 -37
- kodit/infrastructure/api/v1/routers/search.py +1 -1
- kodit/infrastructure/api/v1/schemas/enrichment.py +14 -0
- kodit/infrastructure/api/v1/schemas/repository.py +1 -1
- kodit/infrastructure/providers/litellm_provider.py +23 -1
- kodit/infrastructure/slicing/api_doc_extractor.py +0 -2
- kodit/infrastructure/sqlalchemy/embedding_repository.py +44 -34
- kodit/infrastructure/sqlalchemy/enrichment_association_repository.py +73 -0
- kodit/infrastructure/sqlalchemy/enrichment_v2_repository.py +116 -97
- kodit/infrastructure/sqlalchemy/entities.py +12 -116
- kodit/infrastructure/sqlalchemy/git_branch_repository.py +52 -244
- kodit/infrastructure/sqlalchemy/git_commit_repository.py +35 -324
- kodit/infrastructure/sqlalchemy/git_file_repository.py +70 -0
- kodit/infrastructure/sqlalchemy/git_repository.py +60 -230
- kodit/infrastructure/sqlalchemy/git_tag_repository.py +53 -240
- kodit/infrastructure/sqlalchemy/query.py +331 -0
- kodit/infrastructure/sqlalchemy/repository.py +203 -0
- kodit/infrastructure/sqlalchemy/task_repository.py +79 -58
- kodit/infrastructure/sqlalchemy/task_status_repository.py +45 -52
- kodit/migrations/versions/4b1a3b2c8fa5_refactor_git_tracking.py +190 -0
- {kodit-0.5.3.dist-info → kodit-0.5.5.dist-info}/METADATA +1 -1
- {kodit-0.5.3.dist-info → kodit-0.5.5.dist-info}/RECORD +51 -49
- kodit/infrastructure/mappers/enrichment_mapper.py +0 -83
- kodit/infrastructure/mappers/git_mapper.py +0 -193
- kodit/infrastructure/mappers/snippet_mapper.py +0 -104
- kodit/infrastructure/sqlalchemy/snippet_v2_repository.py +0 -479
- {kodit-0.5.3.dist-info → kodit-0.5.5.dist-info}/WHEEL +0 -0
- {kodit-0.5.3.dist-info → kodit-0.5.5.dist-info}/entry_points.txt +0 -0
- {kodit-0.5.3.dist-info → kodit-0.5.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -44,7 +44,9 @@ class GitRepositoryScanner:
|
|
|
44
44
|
self._log = structlog.getLogger(__name__)
|
|
45
45
|
self.git_adapter = git_adapter
|
|
46
46
|
|
|
47
|
-
async def scan_repository(
|
|
47
|
+
async def scan_repository(
|
|
48
|
+
self, cloned_path: Path, repo_id: int
|
|
49
|
+
) -> RepositoryScanResult:
|
|
48
50
|
"""Scan repository and return immutable result data."""
|
|
49
51
|
self._log.info(f"Starting repository scan at: {cloned_path}")
|
|
50
52
|
|
|
@@ -58,13 +60,16 @@ class GitRepositoryScanner:
|
|
|
58
60
|
|
|
59
61
|
# Process branches efficiently using bulk commit data
|
|
60
62
|
branches, commit_cache = await self._process_branches_bulk(
|
|
61
|
-
cloned_path, branch_data, all_commits_data
|
|
63
|
+
cloned_path, branch_data, all_commits_data, repo_id
|
|
62
64
|
)
|
|
63
65
|
self._log.info(f"Found {len(branches)} branches")
|
|
64
|
-
tags = await self._process_tags(cloned_path, commit_cache)
|
|
66
|
+
tags = await self._process_tags(cloned_path, commit_cache, repo_id)
|
|
65
67
|
self._log.info(f"Found {len(tags)} tags")
|
|
66
68
|
|
|
67
|
-
|
|
69
|
+
all_files = await self._process_files(cloned_path, commit_cache)
|
|
70
|
+
self._log.info(f"Found {len(all_files)} files")
|
|
71
|
+
|
|
72
|
+
return self._create_scan_result(branches, commit_cache, tags, all_files)
|
|
68
73
|
|
|
69
74
|
async def _process_commits_concurrently(
|
|
70
75
|
self,
|
|
@@ -86,7 +91,7 @@ class GitRepositoryScanner:
|
|
|
86
91
|
semaphore = asyncio.Semaphore(50) # Limit concurrent operations
|
|
87
92
|
|
|
88
93
|
async def bounded_process(
|
|
89
|
-
item: tuple[str, dict[str, Any]]
|
|
94
|
+
item: tuple[str, dict[str, Any]],
|
|
90
95
|
) -> tuple[str, GitCommit | None]:
|
|
91
96
|
async with semaphore:
|
|
92
97
|
return await process_single_commit(item[0], item[1])
|
|
@@ -112,6 +117,7 @@ class GitRepositoryScanner:
|
|
|
112
117
|
cloned_path: Path,
|
|
113
118
|
branch_data: list[dict],
|
|
114
119
|
all_commits_data: dict[str, dict[str, Any]],
|
|
120
|
+
repo_id: int,
|
|
115
121
|
) -> tuple[list[GitBranch], dict[str, GitCommit]]:
|
|
116
122
|
"""Process branches efficiently using bulk commit data."""
|
|
117
123
|
branches = []
|
|
@@ -124,7 +130,9 @@ class GitRepositoryScanner:
|
|
|
124
130
|
self._log.info(f"Processing {len(all_commits_data)} commits (metadata only)")
|
|
125
131
|
|
|
126
132
|
for commit_sha, commit_data in all_commits_data.items():
|
|
127
|
-
git_commit = self._create_lightweight_git_commit(
|
|
133
|
+
git_commit = self._create_lightweight_git_commit(
|
|
134
|
+
commit_data, current_time, repo_id
|
|
135
|
+
)
|
|
128
136
|
if git_commit:
|
|
129
137
|
commit_cache[commit_sha] = git_commit
|
|
130
138
|
|
|
@@ -139,9 +147,10 @@ class GitRepositoryScanner:
|
|
|
139
147
|
if commit_shas and commit_shas[0] in commit_cache:
|
|
140
148
|
head_commit = commit_cache[commit_shas[0]]
|
|
141
149
|
branch = GitBranch(
|
|
150
|
+
repo_id=repo_id,
|
|
142
151
|
created_at=current_time,
|
|
143
152
|
name=branch_info["name"],
|
|
144
|
-
|
|
153
|
+
head_commit_sha=head_commit.commit_sha,
|
|
145
154
|
)
|
|
146
155
|
branches.append(branch)
|
|
147
156
|
self._log.debug(f"Processed branch: {branch_info['name']}")
|
|
@@ -159,14 +168,14 @@ class GitRepositoryScanner:
|
|
|
159
168
|
return branches, commit_cache
|
|
160
169
|
|
|
161
170
|
async def _create_git_commit_from_data(
|
|
162
|
-
self, cloned_path: Path, commit_data: dict[str, Any]
|
|
171
|
+
self, cloned_path: Path, commit_data: dict[str, Any], repo_id: int | None = None
|
|
163
172
|
) -> GitCommit | None:
|
|
164
173
|
"""Create GitCommit from pre-fetched commit data."""
|
|
165
174
|
commit_sha = commit_data["sha"]
|
|
166
175
|
|
|
167
176
|
# Get files for this commit
|
|
168
177
|
files_data = await self.git_adapter.get_commit_files(cloned_path, commit_sha)
|
|
169
|
-
|
|
178
|
+
self._create_git_files(cloned_path, files_data, commit_sha)
|
|
170
179
|
author = self._format_author_from_data(commit_data)
|
|
171
180
|
|
|
172
181
|
# Cache datetime creation
|
|
@@ -175,10 +184,10 @@ class GitRepositoryScanner:
|
|
|
175
184
|
return GitCommit(
|
|
176
185
|
created_at=created_at,
|
|
177
186
|
commit_sha=commit_sha,
|
|
187
|
+
repo_id=repo_id or 0, # Use 0 as default if not provided
|
|
178
188
|
date=commit_data["date"],
|
|
179
189
|
message=commit_data["message"],
|
|
180
190
|
parent_commit_sha=commit_data["parent_sha"],
|
|
181
|
-
files=files,
|
|
182
191
|
author=author,
|
|
183
192
|
)
|
|
184
193
|
|
|
@@ -191,7 +200,7 @@ class GitRepositoryScanner:
|
|
|
191
200
|
return author_name or "Unknown"
|
|
192
201
|
|
|
193
202
|
def _create_lightweight_git_commit(
|
|
194
|
-
self, commit_data: dict[str, Any], created_at: datetime
|
|
203
|
+
self, commit_data: dict[str, Any], created_at: datetime, repo_id: int | None
|
|
195
204
|
) -> GitCommit | None:
|
|
196
205
|
"""Create a GitCommit without expensive file data fetching."""
|
|
197
206
|
try:
|
|
@@ -203,10 +212,10 @@ class GitRepositoryScanner:
|
|
|
203
212
|
return GitCommit(
|
|
204
213
|
created_at=created_at,
|
|
205
214
|
commit_sha=commit_sha,
|
|
215
|
+
repo_id=repo_id or 0, # Use 0 as default if not provided
|
|
206
216
|
date=commit_data["date"],
|
|
207
217
|
message=commit_data["message"],
|
|
208
218
|
parent_commit_sha=commit_data["parent_sha"],
|
|
209
|
-
files=[], # Empty for performance - load on demand
|
|
210
219
|
author=author,
|
|
211
220
|
)
|
|
212
221
|
except Exception as e: # noqa: BLE001
|
|
@@ -214,7 +223,7 @@ class GitRepositoryScanner:
|
|
|
214
223
|
return None
|
|
215
224
|
|
|
216
225
|
async def _process_branches(
|
|
217
|
-
self, cloned_path: Path, branch_data: list[dict]
|
|
226
|
+
self, cloned_path: Path, branch_data: list[dict], repo_id: int
|
|
218
227
|
) -> tuple[list[GitBranch], dict[str, GitCommit]]:
|
|
219
228
|
"""Process branches and return branches with commit cache."""
|
|
220
229
|
branches = []
|
|
@@ -222,7 +231,7 @@ class GitRepositoryScanner:
|
|
|
222
231
|
|
|
223
232
|
for branch_info in branch_data:
|
|
224
233
|
branch = await self._process_single_branch(
|
|
225
|
-
cloned_path, branch_info, commit_cache
|
|
234
|
+
cloned_path, branch_info, commit_cache, repo_id
|
|
226
235
|
)
|
|
227
236
|
if branch:
|
|
228
237
|
branches.append(branch)
|
|
@@ -234,6 +243,7 @@ class GitRepositoryScanner:
|
|
|
234
243
|
cloned_path: Path,
|
|
235
244
|
branch_info: dict,
|
|
236
245
|
commit_cache: dict[str, GitCommit],
|
|
246
|
+
repo_id: int,
|
|
237
247
|
) -> GitBranch | None:
|
|
238
248
|
"""Process a single branch and return GitBranch or None."""
|
|
239
249
|
self._log.info(f"Processing branch: {branch_info['name']}")
|
|
@@ -246,21 +256,19 @@ class GitRepositoryScanner:
|
|
|
246
256
|
self._log.warning(f"No commits found for branch {branch_info['name']}")
|
|
247
257
|
return None
|
|
248
258
|
|
|
249
|
-
head_commit = await self._process_branch_commits(
|
|
250
|
-
cloned_path, commits_data, commit_cache
|
|
251
|
-
)
|
|
259
|
+
head_commit = await self._process_branch_commits(commits_data, commit_cache)
|
|
252
260
|
|
|
253
261
|
if head_commit:
|
|
254
262
|
return GitBranch(
|
|
263
|
+
repo_id=repo_id,
|
|
255
264
|
created_at=datetime.now(UTC),
|
|
256
265
|
name=branch_info["name"],
|
|
257
|
-
|
|
266
|
+
head_commit_sha=head_commit.commit_sha,
|
|
258
267
|
)
|
|
259
268
|
return None
|
|
260
269
|
|
|
261
270
|
async def _process_branch_commits(
|
|
262
271
|
self,
|
|
263
|
-
cloned_path: Path,
|
|
264
272
|
commits_data: list[dict],
|
|
265
273
|
commit_cache: dict[str, GitCommit],
|
|
266
274
|
) -> GitCommit | None:
|
|
@@ -276,7 +284,7 @@ class GitRepositoryScanner:
|
|
|
276
284
|
head_commit = commit_cache[commit_sha]
|
|
277
285
|
continue
|
|
278
286
|
|
|
279
|
-
git_commit = await self._create_git_commit(
|
|
287
|
+
git_commit = await self._create_git_commit(commit_data)
|
|
280
288
|
if git_commit:
|
|
281
289
|
commit_cache[commit_sha] = git_commit
|
|
282
290
|
if head_commit is None:
|
|
@@ -285,27 +293,25 @@ class GitRepositoryScanner:
|
|
|
285
293
|
return head_commit
|
|
286
294
|
|
|
287
295
|
async def _create_git_commit(
|
|
288
|
-
self,
|
|
296
|
+
self, commit_data: dict, repo_id: int | None = None
|
|
289
297
|
) -> GitCommit | None:
|
|
290
298
|
"""Create GitCommit from commit data."""
|
|
291
299
|
commit_sha = commit_data["sha"]
|
|
292
300
|
|
|
293
|
-
files_data = await self.git_adapter.get_commit_files(cloned_path, commit_sha)
|
|
294
|
-
files = self._create_git_files(cloned_path, files_data)
|
|
295
301
|
author = self._format_author(commit_data)
|
|
296
302
|
|
|
297
303
|
return GitCommit(
|
|
298
304
|
created_at=datetime.now(UTC),
|
|
299
305
|
commit_sha=commit_sha,
|
|
306
|
+
repo_id=repo_id or 0, # Use 0 as default if not provided
|
|
300
307
|
date=commit_data["date"],
|
|
301
308
|
message=commit_data["message"],
|
|
302
309
|
parent_commit_sha=commit_data["parent_sha"],
|
|
303
|
-
files=files,
|
|
304
310
|
author=author,
|
|
305
311
|
)
|
|
306
312
|
|
|
307
313
|
def _create_git_files(
|
|
308
|
-
self, cloned_path: Path, files_data: list[dict]
|
|
314
|
+
self, cloned_path: Path, files_data: list[dict], commit_sha: str
|
|
309
315
|
) -> list[GitFile]:
|
|
310
316
|
"""Create GitFile entities from files data."""
|
|
311
317
|
# Cache expensive path operations
|
|
@@ -318,14 +324,17 @@ class GitRepositoryScanner:
|
|
|
318
324
|
file_path = f["path"]
|
|
319
325
|
full_path = f"{cloned_path_str}/{file_path}"
|
|
320
326
|
|
|
321
|
-
result.append(
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
327
|
+
result.append(
|
|
328
|
+
GitFile(
|
|
329
|
+
blob_sha=f["blob_sha"],
|
|
330
|
+
commit_sha=commit_sha,
|
|
331
|
+
path=full_path,
|
|
332
|
+
mime_type=f.get("mime_type", "application/octet-stream"),
|
|
333
|
+
size=f["size"],
|
|
334
|
+
extension=GitFile.extension_from_path(file_path),
|
|
335
|
+
created_at=f.get("created_at", current_time),
|
|
336
|
+
)
|
|
337
|
+
)
|
|
329
338
|
return result
|
|
330
339
|
|
|
331
340
|
def _format_author(self, commit_data: dict) -> str:
|
|
@@ -337,7 +346,7 @@ class GitRepositoryScanner:
|
|
|
337
346
|
return author_name or "Unknown"
|
|
338
347
|
|
|
339
348
|
async def _process_tags(
|
|
340
|
-
self, cloned_path: Path, commit_cache: dict[str, GitCommit]
|
|
349
|
+
self, cloned_path: Path, commit_cache: dict[str, GitCommit], repo_id: int
|
|
341
350
|
) -> list[GitTag]:
|
|
342
351
|
"""Process repository tags."""
|
|
343
352
|
tag_data = await self.git_adapter.get_all_tags(cloned_path)
|
|
@@ -346,8 +355,9 @@ class GitRepositoryScanner:
|
|
|
346
355
|
try:
|
|
347
356
|
target_commit = commit_cache[tag_info["target_commit_sha"]]
|
|
348
357
|
git_tag = GitTag(
|
|
358
|
+
repo_id=repo_id,
|
|
349
359
|
name=tag_info["name"],
|
|
350
|
-
|
|
360
|
+
target_commit_sha=target_commit.commit_sha,
|
|
351
361
|
created_at=target_commit.created_at or datetime.now(UTC),
|
|
352
362
|
updated_at=target_commit.updated_at or datetime.now(UTC),
|
|
353
363
|
)
|
|
@@ -366,17 +376,17 @@ class GitRepositoryScanner:
|
|
|
366
376
|
branches: list[GitBranch],
|
|
367
377
|
commit_cache: dict[str, GitCommit],
|
|
368
378
|
tags: list[GitTag],
|
|
379
|
+
all_files: list[GitFile],
|
|
369
380
|
) -> RepositoryScanResult:
|
|
370
381
|
"""Create final scan result."""
|
|
371
382
|
# Files are loaded on-demand for performance, so total_files is 0 during scan
|
|
372
|
-
total_files = 0
|
|
373
|
-
|
|
374
383
|
scan_result = RepositoryScanResult(
|
|
375
384
|
branches=branches,
|
|
376
385
|
all_commits=list(commit_cache.values()),
|
|
377
386
|
scan_timestamp=datetime.now(UTC),
|
|
378
|
-
total_files_across_commits=
|
|
387
|
+
total_files_across_commits=len(all_files),
|
|
379
388
|
all_tags=tags,
|
|
389
|
+
all_files=all_files,
|
|
380
390
|
)
|
|
381
391
|
|
|
382
392
|
self._log.info(
|
|
@@ -385,6 +395,18 @@ class GitRepositoryScanner:
|
|
|
385
395
|
)
|
|
386
396
|
return scan_result
|
|
387
397
|
|
|
398
|
+
async def _process_files(
|
|
399
|
+
self, cloned_path: Path, commit_cache: dict[str, GitCommit]
|
|
400
|
+
) -> list[GitFile]:
|
|
401
|
+
"""Process files for a commit."""
|
|
402
|
+
files = []
|
|
403
|
+
for commit_sha in commit_cache:
|
|
404
|
+
files_data = await self.git_adapter.get_commit_files(
|
|
405
|
+
cloned_path, commit_sha
|
|
406
|
+
)
|
|
407
|
+
files.extend(self._create_git_files(cloned_path, files_data, commit_sha))
|
|
408
|
+
return files
|
|
409
|
+
|
|
388
410
|
|
|
389
411
|
class RepositoryCloner:
|
|
390
412
|
"""Pure service for cloning repositories."""
|
|
@@ -120,22 +120,22 @@ class GitService:
|
|
|
120
120
|
# Get current branch as tracking branch
|
|
121
121
|
try:
|
|
122
122
|
current_branch = repo.active_branch
|
|
123
|
-
|
|
124
|
-
(b for b in branches if b.name == current_branch.name),
|
|
125
|
-
branches[0] if branches else None,
|
|
123
|
+
tracking_branch_name = next(
|
|
124
|
+
(b.name for b in branches if b.name == current_branch.name),
|
|
125
|
+
branches[0].name if branches else None,
|
|
126
126
|
)
|
|
127
127
|
except (AttributeError, TypeError):
|
|
128
128
|
# Handle detached HEAD state or other branch access issues
|
|
129
|
-
|
|
129
|
+
tracking_branch_name = branches[0].name if branches else None
|
|
130
130
|
|
|
131
|
-
if
|
|
131
|
+
if tracking_branch_name is None:
|
|
132
132
|
raise ValueError("No branches found in repository")
|
|
133
133
|
|
|
134
134
|
return GitRepoFactory.create_from_path_scan(
|
|
135
135
|
remote_uri=remote_uri,
|
|
136
136
|
sanitized_remote_uri=sanitized_remote_uri,
|
|
137
137
|
repo_path=repo_path,
|
|
138
|
-
|
|
138
|
+
tracking_branch_name=tracking_branch_name,
|
|
139
139
|
last_scanned_at=datetime.now(UTC),
|
|
140
140
|
num_commits=num_commits,
|
|
141
141
|
num_branches=len(branches),
|
|
@@ -182,7 +182,13 @@ class GitService:
|
|
|
182
182
|
try:
|
|
183
183
|
# Get head commit for this branch
|
|
184
184
|
head_commit = self._convert_commit(repo, branch.commit)
|
|
185
|
-
branches.append(
|
|
185
|
+
branches.append(
|
|
186
|
+
GitBranch(
|
|
187
|
+
name=branch.name,
|
|
188
|
+
head_commit_sha=head_commit.commit_sha,
|
|
189
|
+
repo_id=0, # No repo context yet, use placeholder
|
|
190
|
+
)
|
|
191
|
+
)
|
|
186
192
|
except Exception: # noqa: BLE001, S112
|
|
187
193
|
# Skip branches that can't be accessed
|
|
188
194
|
continue
|
|
@@ -210,7 +216,7 @@ class GitService:
|
|
|
210
216
|
def _get_all_tags(self, repo: Repo) -> list[GitTag]:
|
|
211
217
|
"""Get all tags in the repository."""
|
|
212
218
|
all_commits = self._get_all_commits(repo)
|
|
213
|
-
|
|
219
|
+
{commit.commit_sha: commit for commit in all_commits}
|
|
214
220
|
tags = []
|
|
215
221
|
try:
|
|
216
222
|
for tag_ref in repo.tags:
|
|
@@ -221,7 +227,7 @@ class GitService:
|
|
|
221
227
|
tag = GitTag(
|
|
222
228
|
created_at=datetime.now(UTC),
|
|
223
229
|
name=tag_ref.name,
|
|
224
|
-
|
|
230
|
+
target_commit_sha=target_commit.hexsha,
|
|
225
231
|
)
|
|
226
232
|
tags.append(tag)
|
|
227
233
|
except Exception: # noqa: BLE001, S112
|
|
@@ -242,7 +248,7 @@ class GitService:
|
|
|
242
248
|
parent_sha = commit.parents[0].hexsha if commit.parents else ""
|
|
243
249
|
|
|
244
250
|
# Get files changed in this commit
|
|
245
|
-
|
|
251
|
+
self._get_commit_files(repo, commit)
|
|
246
252
|
|
|
247
253
|
# Format author string from name and email
|
|
248
254
|
author_name = str(commit.author.name) if commit.author.name else ""
|
|
@@ -254,10 +260,10 @@ class GitService:
|
|
|
254
260
|
|
|
255
261
|
return GitCommit(
|
|
256
262
|
commit_sha=commit.hexsha,
|
|
263
|
+
repo_id=0, # GitService doesn't have repo context, use placeholder
|
|
257
264
|
date=commit_date,
|
|
258
265
|
message=str(commit.message).strip(),
|
|
259
266
|
parent_commit_sha=parent_sha,
|
|
260
|
-
files=files,
|
|
261
267
|
author=author,
|
|
262
268
|
)
|
|
263
269
|
|
|
@@ -283,6 +289,7 @@ class GitService:
|
|
|
283
289
|
file_entity = GitFile(
|
|
284
290
|
created_at=datetime.now(UTC),
|
|
285
291
|
blob_sha=blob.hexsha,
|
|
292
|
+
commit_sha=commit.hexsha,
|
|
286
293
|
path=str(Path(repo.working_dir) / file_path),
|
|
287
294
|
mime_type="application/octet-stream", # Default
|
|
288
295
|
size=blob.size,
|
|
@@ -43,30 +43,20 @@ class TrackableResolutionService:
|
|
|
43
43
|
# COMMIT_SHA
|
|
44
44
|
return [trackable.identifier]
|
|
45
45
|
|
|
46
|
-
async def _resolve_branch(
|
|
47
|
-
self, trackable: Trackable, limit: int
|
|
48
|
-
) -> list[str]:
|
|
46
|
+
async def _resolve_branch(self, trackable: Trackable, limit: int) -> list[str]:
|
|
49
47
|
"""Get commits from branch HEAD backwards through history."""
|
|
50
48
|
branch = await self.branch_repo.get_by_name(
|
|
51
49
|
trackable.identifier, trackable.repo_id
|
|
52
50
|
)
|
|
53
51
|
# Walk commit history from head_commit backwards
|
|
54
|
-
return await self._walk_commit_history(
|
|
55
|
-
branch.head_commit.commit_sha, limit
|
|
56
|
-
)
|
|
52
|
+
return await self._walk_commit_history(branch.head_commit_sha, limit)
|
|
57
53
|
|
|
58
54
|
async def _resolve_tag(self, trackable: Trackable, limit: int) -> list[str]:
|
|
59
55
|
"""Get commits from tag target backwards through history."""
|
|
60
|
-
tag = await self.tag_repo.get_by_name(
|
|
61
|
-
|
|
62
|
-
)
|
|
63
|
-
return await self._walk_commit_history(
|
|
64
|
-
tag.target_commit.commit_sha, limit
|
|
65
|
-
)
|
|
56
|
+
tag = await self.tag_repo.get_by_name(trackable.identifier, trackable.repo_id)
|
|
57
|
+
return await self._walk_commit_history(tag.target_commit_sha, limit)
|
|
66
58
|
|
|
67
|
-
async def _walk_commit_history(
|
|
68
|
-
self, start_sha: str, limit: int
|
|
69
|
-
) -> list[str]:
|
|
59
|
+
async def _walk_commit_history(self, start_sha: str, limit: int) -> list[str]:
|
|
70
60
|
"""Walk commit history backwards from start_sha."""
|
|
71
61
|
result = []
|
|
72
62
|
current_sha: str | None = start_sha
|
|
@@ -75,7 +65,7 @@ class TrackableResolutionService:
|
|
|
75
65
|
if not current_sha:
|
|
76
66
|
break
|
|
77
67
|
result.append(current_sha)
|
|
78
|
-
commit = await self.commit_repo.
|
|
68
|
+
commit = await self.commit_repo.get(current_sha)
|
|
79
69
|
current_sha = commit.parent_commit_sha or None
|
|
80
70
|
|
|
81
71
|
return result
|
kodit/domain/value_objects.py
CHANGED
|
@@ -25,18 +25,11 @@ class SnippetContentType(StrEnum):
|
|
|
25
25
|
SUMMARY = "summary"
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
class EnrichmentType(StrEnum):
|
|
29
|
-
"""Type of enrichment."""
|
|
30
|
-
|
|
31
|
-
UNKNOWN = "unknown"
|
|
32
|
-
SUMMARIZATION = "summarization"
|
|
33
|
-
|
|
34
|
-
|
|
35
28
|
@dataclass(frozen=True)
|
|
36
29
|
class Enrichment:
|
|
37
30
|
"""Enrichment domain value object."""
|
|
38
31
|
|
|
39
|
-
type:
|
|
32
|
+
type: str
|
|
40
33
|
content: str
|
|
41
34
|
|
|
42
35
|
|
|
@@ -163,7 +156,7 @@ class DocumentSearchResult:
|
|
|
163
156
|
score: float
|
|
164
157
|
|
|
165
158
|
|
|
166
|
-
@dataclass
|
|
159
|
+
@dataclass(frozen=True)
|
|
167
160
|
class SearchResult:
|
|
168
161
|
"""Generic search result model."""
|
|
169
162
|
|
|
@@ -21,6 +21,7 @@ from kodit.config import AppContext
|
|
|
21
21
|
from kodit.domain.protocols import (
|
|
22
22
|
GitBranchRepository,
|
|
23
23
|
GitCommitRepository,
|
|
24
|
+
GitFileRepository,
|
|
24
25
|
GitRepoRepository,
|
|
25
26
|
GitTagRepository,
|
|
26
27
|
)
|
|
@@ -131,9 +132,7 @@ async def get_git_tag_repository(
|
|
|
131
132
|
return server_factory.git_tag_repository()
|
|
132
133
|
|
|
133
134
|
|
|
134
|
-
GitTagRepositoryDep = Annotated[
|
|
135
|
-
GitTagRepository, Depends(get_git_tag_repository)
|
|
136
|
-
]
|
|
135
|
+
GitTagRepositoryDep = Annotated[GitTagRepository, Depends(get_git_tag_repository)]
|
|
137
136
|
|
|
138
137
|
|
|
139
138
|
async def get_commit_indexing_app_service(
|
|
@@ -170,3 +169,13 @@ async def get_enrichment_query_service(
|
|
|
170
169
|
EnrichmentQueryServiceDep = Annotated[
|
|
171
170
|
EnrichmentQueryService, Depends(get_enrichment_query_service)
|
|
172
171
|
]
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
async def get_git_file_repository(
|
|
175
|
+
server_factory: ServerFactoryDep,
|
|
176
|
+
) -> GitFileRepository:
|
|
177
|
+
"""Get git file repository dependency."""
|
|
178
|
+
return server_factory.git_file_repository()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
GitFileRepositoryDep = Annotated[GitFileRepository, Depends(get_git_file_repository)]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Query parameters for the API."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
from fastapi import Depends, Query
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PaginationParams:
|
|
9
|
+
"""Pagination parameters for the API."""
|
|
10
|
+
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
page: Annotated[
|
|
14
|
+
int, Query(ge=1, description="Page number, starting from 1")
|
|
15
|
+
] = 1,
|
|
16
|
+
page_size: Annotated[
|
|
17
|
+
int, Query(ge=1, le=100, description="Items per page")
|
|
18
|
+
] = 20,
|
|
19
|
+
) -> None:
|
|
20
|
+
"""Initialize pagination parameters."""
|
|
21
|
+
self.page = page
|
|
22
|
+
self.page_size = page_size
|
|
23
|
+
self.offset = (page - 1) * page_size
|
|
24
|
+
self.limit = page_size
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
PaginationParamsDep = Annotated[PaginationParams, Depends()]
|