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