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