Improve oid_from_reference tag id fetching and pass full Repository Object to every function
parent: tbd commit: 6d7e35e
1 | use Error; |
2 | use async_trait; |
3 | |
4 | use BranchType; |
5 | use ; |
6 | |
7 | use Object; |
8 | use |
9 | AccessList, BranchStaleAfter, Commit, CommitSignature, DefaultBranch, Description, IssueLabel, |
10 | Repository, RepositoryBranch, RepositoryBranchFilter, RepositoryBranchRequest, |
11 | RepositoryBranchesRequest, RepositoryChunkLine, RepositoryCommitBeforeRequest, |
12 | RepositoryCommitFromIdRequest, RepositoryDiff, RepositoryDiffFile, RepositoryDiffFileChunk, |
13 | RepositoryDiffFileInfo, RepositoryDiffFileStatus, RepositoryDiffPatchRequest, |
14 | RepositoryDiffRequest, RepositoryFile, RepositoryFileFromIdRequest, |
15 | RepositoryFileFromPathRequest, RepositoryFileInspectRequest, RepositoryIssue, |
16 | RepositoryIssueLabelsRequest, RepositoryIssuesCountRequest, RepositoryIssuesRequest, |
17 | RepositoryLastCommitOfFileRequest, RepositoryObjectType, RepositoryStatistics, |
18 | RepositoryStatisticsRequest, RepositoryTag, RepositoryTagsRequest, RepositoryTreeEntry, |
19 | RepositoryVisibility, Visibility, |
20 | ; |
21 | |
22 | use User; |
23 | |
24 | use ; |
25 | |
26 | use PgPool; |
27 | use Deref; |
28 | use |
29 | , |
30 | , | Arc
31 | ; |
32 | use Error; |
33 | use OnceCell; |
34 | |
35 | use ; |
36 | |
37 | // TODO: Handle this |
38 | //region database structures |
39 | |
40 | /// Repository in the database |
41 | |
42 | |
43 | #[sqlx(try_from = "String")] |
44 | pub owner_user: User, |
45 | pub name: String, |
46 | pub description: , |
47 | pub visibility: RepositoryVisibility, |
48 | pub default_branch: String, |
49 | |
50 | |
51 | |
52 | // Separate function because "Private" will be expanded later |
53 | /// Checks if the user is allowed to view this repository |
54 | pub async |
55 | &self, |
56 | our_instance: &Instance, |
57 | user: & , |
58 | stack: &GiteratedStack, |
59 | |
60 | if matches! |
61 | return true; |
62 | |
63 | |
64 | // User must exist for any further checks to pass |
65 | let user = match user |
66 | Some => user, |
67 | None => return false, |
68 | ; |
69 | |
70 | if *user.deref == self.owner_user |
71 | // owner can always view |
72 | return true; |
73 | |
74 | |
75 | if matches! |
76 | // Check if the user can view\ |
77 | let access_list = stack |
78 | . |
79 | owner: self.owner_user.clone, |
80 | name: self.name.clone, |
81 | instance: our_instance.clone, |
82 | |
83 | .await |
84 | .unwrap; |
85 | |
86 | access_list |
87 | .0 |
88 | .iter |
89 | .any |
90 | else |
91 | false |
92 | |
93 | |
94 | |
95 | // This is in it's own function because I assume I'll have to add logic to this later |
96 | |
97 | &self, |
98 | repository_directory: &str, |
99 | |
100 | match open |
101 | "{}/{}/{}/{}" |
102 | repository_directory, self.owner_user.instance, self.owner_user.username, self.name |
103 | ) |
104 | Ok => Ok, |
105 | Err => |
106 | let err = FailedOpeningFromDisk; |
107 | error!; |
108 | |
109 | Err |
110 | |
111 | |
112 | |
113 | |
114 | |
115 | //endregion |
116 | |
117 | |
118 | |
119 | |
120 | FailedCreatingRepository, |
121 | |
122 | FailedInsertingIntoDatabase, |
123 | |
124 | RepositoryNotFound , |
125 | |
126 | RepositoryAlreadyExists , |
127 | |
128 | CouldNotDeleteFromDisk, |
129 | |
130 | FailedDeletingFromDatabase, |
131 | |
132 | FailedOpeningFromDisk, |
133 | |
134 | RefNotFound, |
135 | |
136 | HeadNotFound, |
137 | |
138 | DefaultNotFound, |
139 | |
140 | PathNotFound, |
141 | |
142 | BranchNotFound, |
143 | |
144 | LastCommitNotFound, |
145 | |
146 | InvalidObjectId, |
147 | |
148 | BlobNotFound, |
149 | |
150 | TreeNotFound, |
151 | |
152 | CommitNotFound, |
153 | |
154 | CommitParentNotFound, |
155 | |
156 | FailedDiffing, |
157 | |
158 | |
159 | |
160 | pg_pool: PgPool, |
161 | repository_folder: String, |
162 | instance: Instance, |
163 | stack: , |
164 | |
165 | |
166 | |
167 | |
168 | pg_pool: &PgPool, |
169 | repository_folder: &str, |
170 | instance: impl , |
171 | stack: , |
172 | |
173 | let instance = instance.to_owned; |
174 | |
175 | Self |
176 | pg_pool: pg_pool.clone, |
177 | // We make sure there's no end slash |
178 | repository_folder: repository_folder.trim_end_matches .to_string, |
179 | instance, |
180 | stack, |
181 | |
182 | |
183 | |
184 | pub async |
185 | &self, |
186 | user: &User, |
187 | repository_name: &str, |
188 | |
189 | // TODO: Patch for use with new GetValue system |
190 | if let Ok = query_as! |
191 | r#"SELECT owner_user, name, description, visibility as "visibility: _", default_branch FROM repositories WHERE owner_user = $1 AND name = $2"#, |
192 | user.to_string, repository_name |
193 | .fetch_one |
194 | .await |
195 | Ok |
196 | else |
197 | Err |
198 | owner_user: user.to_string, |
199 | name: repository_name.to_string, |
200 | |
201 | |
202 | |
203 | |
204 | pub async |
205 | &self, |
206 | user: &User, |
207 | repository_name: &str, |
208 | |
209 | if let Err = remove_dir_all |
210 | "{}/{}/{}/{}" |
211 | self.repository_folder, user.instance, user.username, repository_name |
212 | ) |
213 | let err = CouldNotDeleteFromDisk; |
214 | error! |
215 | "Couldn't delete repository from disk, this is bad! {:?}", |
216 | err |
217 | ; |
218 | |
219 | return Err; |
220 | |
221 | |
222 | // Delete the repository from the database |
223 | self.delete_from_database .await |
224 | |
225 | |
226 | /// Deletes the repository from the database |
227 | pub async |
228 | &self, |
229 | user: &User, |
230 | repository_name: &str, |
231 | |
232 | match query! |
233 | "DELETE FROM repositories WHERE owner_user = $1 AND name = $2", |
234 | user.to_string, |
235 | repository_name |
236 | |
237 | .execute |
238 | .await |
239 | |
240 | Ok => Ok, |
241 | Err => Err, |
242 | |
243 | |
244 | |
245 | pub async |
246 | &self, |
247 | owner: &User, |
248 | name: &str, |
249 | requester: & , |
250 | |
251 | let repository = match self |
252 | .find_by_owner_user_name |
253 | // &request.owner.instance.url, |
254 | owner, name, |
255 | |
256 | .await |
257 | |
258 | Ok => repository, |
259 | Err => return Err, |
260 | ; |
261 | |
262 | if let Some = requester |
263 | if !repository |
264 | .can_user_view_repository |
265 | &self.instance, |
266 | &Some, |
267 | self.stack.get .unwrap, |
268 | |
269 | .await |
270 | |
271 | return Err |
272 | owner_user: repository.owner_user.to_string, |
273 | name: repository.name.clone, |
274 | ; |
275 | |
276 | else if matches! |
277 | // Unauthenticated users can never view private repositories |
278 | |
279 | return Err |
280 | owner_user: repository.owner_user.to_string, |
281 | name: repository.name.clone, |
282 | ; |
283 | |
284 | |
285 | match repository.open_git2_repository |
286 | Ok => Ok, |
287 | Err => Err, |
288 | |
289 | |
290 | |
291 | // TODO: Find where this fits |
292 | // TODO: Cache this and general repository tree and invalidate select files on push |
293 | // TODO: Find better and faster technique for this |
294 | |
295 | path: &str, |
296 | git: & Repository, |
297 | start_commit: & Commit, |
298 | |
299 | trace!; |
300 | |
301 | let mut revwalk = git.revwalk?; |
302 | revwalk.set_sorting?; |
303 | revwalk.push?; |
304 | |
305 | for oid in revwalk |
306 | let oid = oid?; |
307 | let commit = git.find_commit?; |
308 | |
309 | // Merge commits have 2 or more parents |
310 | // Commits with 0 parents are handled different because we can't diff against them |
311 | if commit.parent_count == 0 |
312 | return Ok; |
313 | else if commit.parent_count == 1 |
314 | let tree = commit.tree?; |
315 | let last_tree = commit.parent?.tree?; |
316 | |
317 | // Get the diff between the current tree and the last one |
318 | let diff = git.diff_tree_to_tree?; |
319 | |
320 | for dd in diff.deltas |
321 | // Get the path of the current file we're diffing against |
322 | let current_path = dd.new_file .path .unwrap; |
323 | |
324 | // Path or directory |
325 | if current_path.eq || current_path.starts_with |
326 | return Ok; |
327 | |
328 | |
329 | |
330 | |
331 | |
332 | Err? |
333 | |
334 | |
335 | /// Gets the total amount of commits using revwalk |
336 | |
337 | git: & Repository, |
338 | start_commit: & Commit, |
339 | |
340 | // TODO: There must be a better way |
341 | let mut revwalk = git.revwalk?; |
342 | revwalk.set_sorting?; |
343 | revwalk.push?; |
344 | |
345 | Ok |
346 | |
347 | |
348 | /// Attempts to get the oid in this order: |
349 | /// 1. Full refname (refname_to_id) |
350 | /// 2. Short branch name (find_branch) |
351 | /// 3. Other (revparse_single) |
352 | |
353 | git: & Repository, |
354 | rev: , |
355 | default_branch: &DefaultBranch, |
356 | |
357 | // If the rev is None try and get the default branch instead |
358 | let rev = rev.unwrap_or; |
359 | |
360 | // TODO: This is far from ideal or speedy and would love for a better way to check this in the same order, but I can't find proper methods to do any of this. |
361 | trace!; |
362 | |
363 | // Try getting it as a refname (refs/heads/name) |
364 | if let Ok = git.refname_to_id |
365 | Ok |
366 | // Try finding it as a short branch name |
367 | else if let Ok = git.find_branch |
368 | // SHOULD be safe to unwrap |
369 | Ok |
370 | // As last resort, try revparsing (will catch short oid and tags) |
371 | else if let Ok = git.revparse_single |
372 | Ok |
373 | Some => |
374 | if let Ok = object.peel_to_commit |
375 | commit.id |
376 | else |
377 | object.id |
378 | |
379 | |
380 | _ => object.id, |
381 | |
382 | else |
383 | Err |
384 | |
385 | |
386 | |
387 | /// Gets the last commit in a rev |
388 | |
389 | git: & Repository, |
390 | rev: &str, |
391 | default_branch: &DefaultBranch, |
392 | |
393 | let oid = Self get_oid_from_reference?; |
394 | |
395 | // Walk through the repository commit graph starting at our rev |
396 | let mut revwalk = git.revwalk?; |
397 | revwalk.set_sorting?; |
398 | revwalk.push?; |
399 | |
400 | if let Some = revwalk.next |
401 | if let Ok = git |
402 | .find_commit |
403 | .map_err |
404 | |
405 | return Ok; |
406 | |
407 | |
408 | |
409 | Err |
410 | |
411 | |
412 | |
413 | |
414 | |
415 | async |
416 | &mut self, |
417 | requester: & , |
418 | repository: &Repository, |
419 | |
420 | if let Ok = self |
421 | .find_by_owner_user_name |
422 | .await |
423 | |
424 | Ok |
425 | .can_user_view_repository |
426 | .await |
427 | else |
428 | Ok |
429 | |
430 | |
431 | |
432 | async |
433 | &mut self, |
434 | _user: &AuthenticatedUser, |
435 | request: &RepositoryCreateRequest, |
436 | |
437 | // Check if repository already exists in the database |
438 | if let Ok = self |
439 | .find_by_owner_user_name |
440 | .await |
441 | |
442 | let err = RepositoryAlreadyExists |
443 | owner_user: repository.owner_user.to_string, |
444 | name: repository.name, |
445 | ; |
446 | error!; |
447 | |
448 | return Err; |
449 | |
450 | |
451 | // Insert the repository into the database |
452 | let _ = match query_as! |
453 | r#"INSERT INTO repositories VALUES ($1, $2, $3, $4, $5) RETURNING owner_user, name, description, visibility as "visibility: _", default_branch"#, |
454 | request.owner.to_string, request.name, request.description, request.visibility as _, "master" |
455 | .fetch_one |
456 | .await |
457 | Ok => repository, |
458 | Err => |
459 | let err = FailedInsertingIntoDatabase; |
460 | error!; |
461 | |
462 | return Err; |
463 | |
464 | ; |
465 | |
466 | // Create bare (server side) repository on disk |
467 | match init_bare |
468 | "{}/{}/{}/{}" |
469 | self.repository_folder, request.owner.instance, request.owner.username, request.name |
470 | ) |
471 | Ok => |
472 | debug! |
473 | "Created new repository with the name {}/{}/{}", |
474 | request.owner.instance, request.owner.username, request.name |
475 | ; |
476 | |
477 | let stack = self.stack.get .unwrap; |
478 | |
479 | let repository = Repository |
480 | owner: request.owner.clone, |
481 | name: request.name.clone, |
482 | instance: request.instance.as_ref .unwrap_or .clone, |
483 | ; |
484 | |
485 | stack |
486 | .write_setting |
487 | &repository, |
488 | Description, |
489 | |
490 | .await |
491 | .unwrap; |
492 | |
493 | stack |
494 | .write_setting |
495 | .await |
496 | .unwrap; |
497 | |
498 | stack |
499 | .write_setting |
500 | .await |
501 | .unwrap; |
502 | |
503 | Ok |
504 | |
505 | Err => |
506 | let err = FailedCreatingRepository; |
507 | error!; |
508 | |
509 | // Delete repository from database |
510 | self.delete_from_database |
511 | .await?; |
512 | |
513 | // ??? |
514 | Err |
515 | |
516 | |
517 | |
518 | |
519 | /// If the OID can't be found because there's no repository head, this will return an empty `Vec`. |
520 | async |
521 | &mut self, |
522 | requester: & , |
523 | repository_object: &mut , |
524 | OperationState | : ,
525 | request: &RepositoryFileInspectRequest, |
526 | |
527 | let repository = repository_object.object; |
528 | let git = self |
529 | .open_repository_and_check_permissions |
530 | .await?; |
531 | |
532 | let default_branch = repository_object |
533 | . |
534 | .await?; |
535 | // Try and find the tree_id/branch |
536 | let tree_id = |
537 | match Self get_oid_from_reference |
538 | Ok => oid, |
539 | Err => return Ok, |
540 | Err => return Err, |
541 | ; |
542 | |
543 | // Get the commit from the oid |
544 | let commit = match git.find_commit |
545 | Ok => commit, |
546 | // If the commit isn't found, it's generally safe to assume the tree is empty. |
547 | Err => return Ok, |
548 | ; |
549 | |
550 | // this is stupid |
551 | let rev = request.rev.clone .unwrap_or_else; |
552 | let mut current_path = rev.clone; |
553 | |
554 | // Get the commit tree |
555 | let git_tree = if let Some = &request.path |
556 | // Add it to our full path string |
557 | current_path.push_str; |
558 | // Get the specified path, return an error if it wasn't found. |
559 | let entry = match commit |
560 | .tree |
561 | .unwrap |
562 | .get_path |
563 | .map_err |
564 | |
565 | Ok => entry, |
566 | Err => return Err, |
567 | ; |
568 | // Turn the entry into a git tree |
569 | entry.to_object .unwrap .as_tree .unwrap .clone |
570 | else |
571 | commit.tree .unwrap |
572 | ; |
573 | |
574 | // Iterate over the git tree and collect it into our own tree types |
575 | let mut tree = git_tree |
576 | .iter |
577 | .map |
578 | let object_type = match entry.kind .unwrap |
579 | => Tree, | Tree
580 | => Blob, | Blob
581 | _ => unreachable!, |
582 | ; |
583 | let mut tree_entry = new |
584 | entry.id .to_string .as_str, |
585 | entry.name .unwrap, |
586 | object_type, |
587 | entry.filemode, |
588 | ; |
589 | |
590 | if request.extra_metadata |
591 | // Get the file size if It's a blob |
592 | let object = entry.to_object .unwrap; |
593 | if let Some = object.as_blob |
594 | tree_entry.size = Some; |
595 | |
596 | |
597 | // Get the path to the folder the file is in by removing the rev from current_path |
598 | let mut path = current_path.replace; |
599 | if path.starts_with |
600 | path.remove; |
601 | |
602 | |
603 | // Format it as the path + file name |
604 | let full_path = if path.is_empty |
605 | entry.name .unwrap .to_string |
606 | else |
607 | format! |
608 | ; |
609 | |
610 | // Get the last commit made to the entry |
611 | if let Ok = |
612 | get_last_commit_of_file |
613 | |
614 | tree_entry.last_commit = Some; |
615 | |
616 | |
617 | |
618 | tree_entry |
619 | |
620 | .; |
621 | |
622 | // Sort the tree alphabetically and with tree first |
623 | tree.sort_unstable_by_key; |
624 | tree.sort_unstable_by_key |
625 | Reverse |
626 | ; |
627 | |
628 | Ok |
629 | |
630 | |
631 | async |
632 | &mut self, |
633 | requester: & , |
634 | repository_object: &mut , |
635 | OperationState | : ,
636 | request: &RepositoryFileFromIdRequest, |
637 | |
638 | let repository = repository_object.object; |
639 | let git = self |
640 | .open_repository_and_check_permissions |
641 | .await?; |
642 | |
643 | // Parse the passed object id |
644 | let oid = match from_str |
645 | Ok => oid, |
646 | Err => |
647 | return Err |
648 | |
649 | ; |
650 | |
651 | // Find the file and turn it into our own struct |
652 | let file = match git.find_blob |
653 | Ok => RepositoryFile |
654 | id: blob.id .to_string, |
655 | content: blob.content .to_vec, |
656 | binary: blob.is_binary, |
657 | size: blob.size, |
658 | , |
659 | Err => return Err, |
660 | ; |
661 | |
662 | Ok |
663 | |
664 | |
665 | async |
666 | &mut self, |
667 | requester: & , |
668 | repository_object: &mut , |
669 | OperationState | : ,
670 | request: &RepositoryFileFromPathRequest, |
671 | |
672 | let repository = repository_object.object; |
673 | let git = self |
674 | .open_repository_and_check_permissions |
675 | .await?; |
676 | |
677 | let default_branch = repository_object |
678 | . |
679 | .await?; |
680 | let tree_id = Self get_oid_from_reference?; |
681 | |
682 | // unwrap might be dangerous? |
683 | // Get the commit from the oid |
684 | let commit = git.find_commit .unwrap; |
685 | |
686 | // this is stupid |
687 | let mut current_path = request.rev.clone .unwrap_or_else; |
688 | |
689 | // Add it to our full path string |
690 | current_path.push_str; |
691 | // Get the specified path, return an error if it wasn't found. |
692 | let entry = match commit |
693 | .tree |
694 | .unwrap |
695 | .get_path |
696 | .map_err |
697 | |
698 | Ok => entry, |
699 | Err => return Err, |
700 | ; |
701 | |
702 | // Find the file and turn it into our own struct |
703 | let file = match git.find_blob |
704 | Ok => RepositoryFile |
705 | id: blob.id .to_string, |
706 | content: blob.content .to_vec, |
707 | binary: blob.is_binary, |
708 | size: blob.size, |
709 | , |
710 | Err => |
711 | return Err |
712 | |
713 | ; |
714 | |
715 | Ok |
716 | |
717 | |
718 | async |
719 | &mut self, |
720 | requester: & , |
721 | repository_object: &mut , |
722 | OperationState | : ,
723 | request: &RepositoryCommitFromIdRequest, |
724 | |
725 | let repository = repository_object.object; |
726 | let git = self |
727 | .open_repository_and_check_permissions |
728 | .await?; |
729 | |
730 | // Parse the passed object ids |
731 | let oid = from_str |
732 | .map_err?; |
733 | |
734 | // Get the commit from the oid |
735 | let commit = git |
736 | .find_commit |
737 | .map_err?; |
738 | |
739 | Ok |
740 | |
741 | |
742 | async |
743 | &mut self, |
744 | requester: & , |
745 | repository_object: &mut , |
746 | OperationState | : ,
747 | request: &RepositoryLastCommitOfFileRequest, |
748 | |
749 | let repository = repository_object.object; |
750 | let git = self |
751 | .open_repository_and_check_permissions |
752 | .await?; |
753 | |
754 | // Parse the passed object ids |
755 | let oid = from_str |
756 | .map_err?; |
757 | |
758 | // Get the commit from the oid |
759 | let commit = git |
760 | .find_commit |
761 | .map_err?; |
762 | |
763 | // Find the last commit of the file |
764 | let commit = get_last_commit_of_file?; |
765 | |
766 | Ok |
767 | |
768 | |
769 | /// Returns zero for all statistics if an OID wasn't found |
770 | async |
771 | &mut self, |
772 | requester: & , |
773 | repository_object: &mut , |
774 | OperationState | : ,
775 | request: &RepositoryStatisticsRequest, |
776 | |
777 | let repository = repository_object.object; |
778 | let git = self |
779 | .open_repository_and_check_permissions |
780 | .await?; |
781 | |
782 | let default_branch = repository_object |
783 | . |
784 | .await?; |
785 | let tree_id = |
786 | match Self get_oid_from_reference |
787 | Ok => oid, |
788 | Err => return Ok, |
789 | ; |
790 | |
791 | // Count the amount of branches and tags |
792 | let mut branches = 0; |
793 | let mut tags = 0; |
794 | if let Ok = git.references |
795 | for reference in references.flatten |
796 | if reference.is_branch |
797 | branches += 1; |
798 | else if reference.is_tag |
799 | tags += 1; |
800 | |
801 | |
802 | |
803 | |
804 | // Attempt to get the commit from the oid |
805 | let commits = if let Ok = git.find_commit |
806 | // Get the total commit count if we found the tree oid commit |
807 | ? | get_total_commit_count
808 | else |
809 | 0 |
810 | ; |
811 | |
812 | Ok |
813 | commits, |
814 | branches, |
815 | tags, |
816 | |
817 | |
818 | |
819 | /// .0: List of branches filtering by passed requirements. |
820 | /// .1: Total amount of branches after being filtered |
821 | async |
822 | &mut self, |
823 | requester: & , |
824 | repository_object: &mut , |
825 | OperationState | : ,
826 | request: &RepositoryBranchesRequest, |
827 | |
828 | let repository = repository_object.object; |
829 | let git = self |
830 | .open_repository_and_check_permissions |
831 | .await?; |
832 | |
833 | let default_branch_name = repository_object |
834 | . |
835 | .await?; |
836 | let default_branch = git |
837 | .find_branch |
838 | .map_err?; |
839 | |
840 | // Get the stale(after X seconds) setting |
841 | let stale_after = repository_object |
842 | . |
843 | .await |
844 | .unwrap_or_default |
845 | .0; |
846 | |
847 | // Could be done better with the RepositoryBranchFilter::None check done beforehand. |
848 | let mut filtered_branches = git |
849 | .branches? |
850 | .filter_map |
851 | let branch = branch.ok?.0; |
852 | |
853 | let Some = branch.name .ok .flatten else |
854 | return None; |
855 | ; |
856 | |
857 | // TODO: Non UTF-8? |
858 | let Some = get_last_commit_in_rev |
859 | &git, |
860 | branch.get .name .unwrap, |
861 | &default_branch_name, |
862 | |
863 | .ok else |
864 | return None; |
865 | ; |
866 | |
867 | let stale = now |
868 | .naive_utc |
869 | .signed_duration_since |
870 | .num_seconds |
871 | > stale_after.into; |
872 | |
873 | // Filter based on if the branch is stale or not |
874 | if request.filter != None |
875 | |
876 | if stale && request.filter == Active |
877 | return None; |
878 | else if !stale && request.filter == Stale |
879 | return None; |
880 | |
881 | |
882 | |
883 | Some |
884 | |
885 | .; |
886 | |
887 | // Get the total amount of filtered branches |
888 | let branch_count = filtered_branches.len; |
889 | |
890 | if let Some = &request.search |
891 | // TODO: Caching |
892 | // Search by sorting using a simple fuzzy search algorithm |
893 | filtered_branches.sort_by |
894 | damerau_levenshtein |
895 | .cmp |
896 | ; |
897 | else |
898 | // Sort the branches by commit date |
899 | filtered_branches.sort_by; |
900 | |
901 | |
902 | // Go to the requested position |
903 | let mut filtered_branches = filtered_branches.iter .skip; |
904 | |
905 | let mut branches = vec!; |
906 | |
907 | // Iterate through the filtered branches using the passed range |
908 | for _ in request.range.0..request.range.1 |
909 | let Some = filtered_branches.next else |
910 | break; |
911 | ; |
912 | |
913 | // Get how many commits are ahead of and behind of the head |
914 | let ahead_behind_default = |
915 | if default_branch.get .target .is_some && branch.get .target .is_some |
916 | git.graph_ahead_behind |
917 | branch.get .target .unwrap, |
918 | default_branch.get .target .unwrap, |
919 | |
920 | .ok |
921 | else |
922 | None |
923 | ; |
924 | |
925 | branches.push |
926 | name: name.to_string, |
927 | stale: *stale, |
928 | last_commit: Some, |
929 | ahead_behind_default, |
930 | |
931 | |
932 | |
933 | Ok |
934 | |
935 | |
936 | async |
937 | &mut self, |
938 | requester: & , |
939 | repository_object: &mut , |
940 | OperationState | : ,
941 | request: &RepositoryBranchRequest, |
942 | |
943 | let repository = repository_object.object; |
944 | let git = self |
945 | .open_repository_and_check_permissions |
946 | .await?; |
947 | |
948 | // TODO: Don't duplicate search when the default branch and the requested one are the same |
949 | // Get the default branch to compare against |
950 | let default_branch_name = repository_object |
951 | . |
952 | .await?; |
953 | let default_branch = git |
954 | .find_branch |
955 | .map_err?; |
956 | |
957 | // Find the requested branch |
958 | let branch = git |
959 | .find_branch |
960 | .map_err?; |
961 | |
962 | // Get the stale(after X seconds) setting |
963 | let stale_after = repository_object |
964 | . |
965 | .await |
966 | .unwrap_or_default |
967 | .0; |
968 | |
969 | // TODO: Non UTF-8? |
970 | let last_commit = get_last_commit_in_rev |
971 | &git, |
972 | branch.get .name .unwrap, |
973 | &default_branch_name, |
974 | |
975 | .ok; |
976 | |
977 | let stale = if let Some = last_commit |
978 | now |
979 | .naive_utc |
980 | .signed_duration_since |
981 | .num_seconds |
982 | > stale_after.into |
983 | else |
984 | // TODO: Make sure it's acceptable to return false here |
985 | false |
986 | ; |
987 | |
988 | // Get how many commits are ahead of and behind of the head |
989 | let ahead_behind_default = |
990 | if default_branch.get .target .is_some && branch.get .target .is_some |
991 | git.graph_ahead_behind |
992 | branch.get .target .unwrap, |
993 | default_branch.get .target .unwrap, |
994 | |
995 | .ok |
996 | else |
997 | None |
998 | ; |
999 | |
1000 | Ok |
1001 | name: request.name.clone, |
1002 | stale, |
1003 | last_commit, |
1004 | ahead_behind_default, |
1005 | |
1006 | |
1007 | |
1008 | /// .0: List of tags in passed range |
1009 | /// .1: Total amount of tags |
1010 | async |
1011 | &mut self, |
1012 | requester: & , |
1013 | repository_object: &mut , |
1014 | OperationState | : ,
1015 | request: &RepositoryTagsRequest, |
1016 | |
1017 | let repository = repository_object.object; |
1018 | let git = self |
1019 | .open_repository_and_check_permissions |
1020 | .await?; |
1021 | |
1022 | let mut tags = vec!; |
1023 | |
1024 | // Iterate over each tag |
1025 | let _ = git.tag_foreach |
1026 | // Get the name in utf8 |
1027 | let name = String from_utf8_lossy .replacen; |
1028 | |
1029 | // Find the tag so we can get the messages attached if any |
1030 | if let Ok = git.find_tag |
1031 | // Get the tag message and split it into a summary and body |
1032 | let = if let Some = tag.message |
1033 | // Iterate over the lines |
1034 | let mut lines = message |
1035 | .lines |
1036 | .map |
1037 | // Trim the whitespace for every line |
1038 | let mut whitespace_removed = String with_capacity; |
1039 | |
1040 | line.split_whitespace .for_each |
1041 | if !whitespace_removed.is_empty |
1042 | whitespace_removed.push; |
1043 | |
1044 | |
1045 | whitespace_removed.push_str; |
1046 | ; |
1047 | |
1048 | whitespace_removed |
1049 | |
1050 | .; |
1051 | |
1052 | let summary = Some; |
1053 | let body = if lines.is_empty |
1054 | None |
1055 | else |
1056 | Some |
1057 | ; |
1058 | |
1059 | |
1060 | else |
1061 | |
1062 | ; |
1063 | |
1064 | // Get the commit the tag is (possibly) pointing to |
1065 | let commit = tag |
1066 | .peel |
1067 | .map |
1068 | .ok |
1069 | .flatten |
1070 | .map; |
1071 | // Get the author of the tag |
1072 | let author: = tag.tagger .map; |
1073 | // Get the time the tag or pointed commit was created |
1074 | let time = if let Some = author |
1075 | Some |
1076 | else |
1077 | // Get possible commit time if the tag has no author time |
1078 | commit.as_ref .map |
1079 | ; |
1080 | |
1081 | tags.push |
1082 | id: id.to_string, |
1083 | name: name.to_string, |
1084 | summary, |
1085 | body, |
1086 | author, |
1087 | time, |
1088 | commit, |
1089 | ; |
1090 | else |
1091 | // Lightweight commit, we try and find the commit it's pointing to |
1092 | let commit = git.find_commit .ok .map; |
1093 | |
1094 | tags.push |
1095 | id: id.to_string, |
1096 | name: name.to_string, |
1097 | summary: None, |
1098 | body: None, |
1099 | author: None, |
1100 | time: commit.as_ref .map, |
1101 | commit, |
1102 | ; |
1103 | ; |
1104 | |
1105 | true |
1106 | ; |
1107 | |
1108 | // Get the total amount of tags |
1109 | let tag_count = tags.len; |
1110 | |
1111 | if let Some = &request.search |
1112 | // TODO: Caching |
1113 | // Search by sorting using a simple fuzzy search algorithm |
1114 | tags.sort_by |
1115 | damerau_levenshtein |
1116 | .cmp |
1117 | ; |
1118 | else |
1119 | // Sort the tags using their creation or pointer date |
1120 | tags.sort_by; |
1121 | |
1122 | |
1123 | // Get the requested range of tags |
1124 | let tags = tags |
1125 | .into_iter |
1126 | .skip |
1127 | .take |
1128 | .; |
1129 | |
1130 | Ok |
1131 | |
1132 | |
1133 | async |
1134 | &mut self, |
1135 | requester: & , |
1136 | repository_object: &mut , |
1137 | OperationState | : ,
1138 | request: &RepositoryDiffRequest, |
1139 | |
1140 | let repository = repository_object.object; |
1141 | let git = self |
1142 | .open_repository_and_check_permissions |
1143 | .await?; |
1144 | |
1145 | // Parse the passed object ids |
1146 | let oid_old = from_str |
1147 | .map_err?; |
1148 | let oid_new = from_str |
1149 | .map_err?; |
1150 | |
1151 | // Get the ids associates commits |
1152 | let commit_old = git |
1153 | .find_commit |
1154 | .map_err?; |
1155 | let commit_new = git |
1156 | .find_commit |
1157 | .map_err?; |
1158 | |
1159 | // Get the commit trees |
1160 | let tree_old = commit_old |
1161 | .tree |
1162 | .map_err?; |
1163 | let tree_new = commit_new |
1164 | .tree |
1165 | .map_err?; |
1166 | |
1167 | // Diff the two trees against each other |
1168 | let diff = git |
1169 | .diff_tree_to_tree |
1170 | .map_err |
1171 | FailedDiffing |
1172 | ?; |
1173 | |
1174 | // Should be safe to unwrap? |
1175 | let stats = diff.stats .unwrap; |
1176 | let mut files: = vec!; |
1177 | |
1178 | diff.deltas .enumerate .for_each |
1179 | // Parse the old file info from the delta |
1180 | let old_file_info = match delta.old_file .exists |
1181 | true => Some |
1182 | id: delta.old_file .id .to_string, |
1183 | path: delta |
1184 | .old_file |
1185 | .path |
1186 | .unwrap |
1187 | .to_str |
1188 | .unwrap |
1189 | .to_string, |
1190 | size: delta.old_file .size, |
1191 | binary: delta.old_file .is_binary, |
1192 | , |
1193 | false => None, |
1194 | ; |
1195 | // Parse the new file info from the delta |
1196 | let new_file_info = match delta.new_file .exists |
1197 | true => Some |
1198 | id: delta.new_file .id .to_string, |
1199 | path: delta |
1200 | .new_file |
1201 | .path |
1202 | .unwrap |
1203 | .to_str |
1204 | .unwrap |
1205 | .to_string, |
1206 | size: delta.new_file .size, |
1207 | binary: delta.new_file .is_binary, |
1208 | , |
1209 | false => None, |
1210 | ; |
1211 | |
1212 | let mut chunks: = vec!; |
1213 | if let Some = from_diff .ok .flatten |
1214 | for chunk_num in 0..patch.num_hunks |
1215 | if let Ok = patch.hunk |
1216 | let mut lines: = vec!; |
1217 | |
1218 | for line_num in 0..chunk_num_lines |
1219 | if let Ok = patch.line_in_hunk |
1220 | if let Ok = String from_utf8 |
1221 | lines.push |
1222 | change_type: line.origin_value .into, |
1223 | content: line_utf8, |
1224 | old_line_num: line.old_lineno, |
1225 | new_line_num: line.new_lineno, |
1226 | ; |
1227 | |
1228 | |
1229 | continue; |
1230 | |
1231 | |
1232 | |
1233 | chunks.push |
1234 | header: String from_utf8 .ok, |
1235 | old_start: chunk.old_start, |
1236 | old_lines: chunk.old_lines, |
1237 | new_start: chunk.new_start, |
1238 | new_lines: chunk.new_lines, |
1239 | lines, |
1240 | ; |
1241 | |
1242 | |
1243 | ; |
1244 | |
1245 | let file = RepositoryDiffFile |
1246 | status: from, |
1247 | old_file_info, |
1248 | new_file_info, |
1249 | chunks, |
1250 | ; |
1251 | |
1252 | files.push; |
1253 | ; |
1254 | |
1255 | Ok |
1256 | new_commit: from, |
1257 | files_changed: stats.files_changed, |
1258 | insertions: stats.insertions, |
1259 | deletions: stats.deletions, |
1260 | files, |
1261 | |
1262 | |
1263 | |
1264 | async |
1265 | &mut self, |
1266 | requester: & , |
1267 | repository_object: &mut , |
1268 | OperationState | : ,
1269 | request: &RepositoryDiffPatchRequest, |
1270 | |
1271 | let repository = repository_object.object; |
1272 | let git = self |
1273 | .open_repository_and_check_permissions |
1274 | .await?; |
1275 | |
1276 | // Parse the passed object ids |
1277 | let oid_old = from_str |
1278 | .map_err?; |
1279 | let oid_new = from_str |
1280 | .map_err?; |
1281 | |
1282 | // Get the ids associates commits |
1283 | let commit_old = git |
1284 | .find_commit |
1285 | .map_err?; |
1286 | let commit_new = git |
1287 | .find_commit |
1288 | .map_err?; |
1289 | |
1290 | // Get the commit trees |
1291 | let tree_old = commit_old |
1292 | .tree |
1293 | .map_err?; |
1294 | let tree_new = commit_new |
1295 | .tree |
1296 | .map_err?; |
1297 | |
1298 | // Diff the two trees against each other |
1299 | let diff = git |
1300 | .diff_tree_to_tree |
1301 | .map_err |
1302 | FailedDiffing |
1303 | ?; |
1304 | |
1305 | // Print the entire patch |
1306 | let mut patch = String new; |
1307 | |
1308 | diff.print |
1309 | match line.origin |
1310 | '+' | '-' | ' ' => patch.push, |
1311 | _ => |
1312 | |
1313 | patch.push_str; |
1314 | true |
1315 | |
1316 | .unwrap; |
1317 | |
1318 | Ok |
1319 | |
1320 | |
1321 | async |
1322 | &mut self, |
1323 | requester: & , |
1324 | repository_object: &mut , |
1325 | OperationState | : ,
1326 | request: &RepositoryCommitBeforeRequest, |
1327 | |
1328 | let repository = repository_object.object; |
1329 | let git = self |
1330 | .open_repository_and_check_permissions |
1331 | .await?; |
1332 | |
1333 | // Parse the passed object id |
1334 | let oid = match from_str |
1335 | Ok => oid, |
1336 | Err => |
1337 | return Err |
1338 | |
1339 | ; |
1340 | |
1341 | // Find the commit using the parsed oid |
1342 | let commit = match git.find_commit |
1343 | Ok => commit, |
1344 | Err => return Err, |
1345 | ; |
1346 | |
1347 | // Get the first parent it has |
1348 | let parent = commit.parent; |
1349 | if let Ok = parent |
1350 | return Ok; |
1351 | else |
1352 | // TODO: See if can be done better |
1353 | // Walk through the repository commit graph starting at our current commit |
1354 | let mut revwalk = git.revwalk?; |
1355 | revwalk.set_sorting?; |
1356 | revwalk.push?; |
1357 | |
1358 | if let Some = revwalk.next |
1359 | // Find the commit using the parsed oid |
1360 | if let Ok = git.find_commit |
1361 | return Ok; |
1362 | |
1363 | |
1364 | |
1365 | Err |
1366 | |
1367 | |
1368 | |
1369 | |
1370 | |
1371 | |
1372 | &mut self, |
1373 | _requester: & , |
1374 | _request: &RepositoryIssuesCountRequest, |
1375 | |
1376 | todo! |
1377 | |
1378 | |
1379 | |
1380 | &mut self, |
1381 | _requester: & , |
1382 | _request: &RepositoryIssueLabelsRequest, |
1383 | |
1384 | todo! |
1385 | |
1386 | |
1387 | |
1388 | &mut self, |
1389 | _requester: & , |
1390 | _request: &RepositoryIssuesRequest, |
1391 | |
1392 | todo! |
1393 | |
1394 | |
1395 | |
1396 | |
1397 | |
1398 | |
1399 | pub repository: String, |
1400 | pub name: String, |
1401 | pub value: String, |
1402 | |
1403 |