JavaScript is disabled, refresh for a better experience. ambee/giterated

ambee/giterated

Git repository hosting, collaboration, and discovery for the Fediverse.

Move towards having GitBackend split into files

Emilia - ⁨1⁩ year ago

parent: tbd commit: ⁨e55da0e

⁨giterated-daemon/src/backend/git/diff.rs⁩ - ⁨8156⁩ bytes
Raw
1 use anyhow::Error;
2 use giterated_models::{
3 object::Object,
4 repository::{
5 Commit, Repository, RepositoryChunkLine, RepositoryDiff, RepositoryDiffFile,
6 RepositoryDiffFileChunk, RepositoryDiffFileInfo, RepositoryDiffFileStatus,
7 RepositoryDiffPatchRequest, RepositoryDiffRequest,
8 },
9 };
10 use giterated_stack::{AuthenticatedUser, GiteratedStack, OperationState, StackOperationState};
11
12 use super::{GitBackend, GitBackendError};
13
14 impl GitBackend {
15 pub async fn handle_repository_diff(
16 &mut self,
17 requester: &Option<AuthenticatedUser>,
18 repository_object: &mut Object<'_, StackOperationState, Repository, GiteratedStack>,
19 OperationState(_operation_state): OperationState<StackOperationState>,
20 request: &RepositoryDiffRequest,
21 ) -> Result<RepositoryDiff, Error> {
22 let repository = repository_object.object();
23 let git = self
24 .open_repository_and_check_permissions(&repository.owner, &repository.name, requester)
25 .await?;
26
27 // Parse the passed object ids
28 let oid_old = git2::Oid::from_str(request.old_id.as_str())
29 .map_err(|_| GitBackendError::InvalidObjectId(request.old_id.clone()))?;
30 let oid_new = git2::Oid::from_str(request.new_id.as_str())
31 .map_err(|_| GitBackendError::InvalidObjectId(request.new_id.clone()))?;
32
33 // Get the ids associates commits
34 let commit_old = git
35 .find_commit(oid_old)
36 .map_err(|_| GitBackendError::CommitNotFound(oid_old.to_string()))?;
37 let commit_new = git
38 .find_commit(oid_new)
39 .map_err(|_| GitBackendError::CommitNotFound(oid_new.to_string()))?;
40
41 // Get the commit trees
42 let tree_old = commit_old
43 .tree()
44 .map_err(|_| GitBackendError::TreeNotFound(oid_old.to_string()))?;
45 let tree_new = commit_new
46 .tree()
47 .map_err(|_| GitBackendError::TreeNotFound(oid_new.to_string()))?;
48
49 // Diff the two trees against each other
50 let diff = git
51 .diff_tree_to_tree(Some(&tree_old), Some(&tree_new), None)
52 .map_err(|_| {
53 GitBackendError::FailedDiffing(oid_old.to_string(), oid_new.to_string())
54 })?;
55
56 // Should be safe to unwrap?
57 let stats = diff.stats().unwrap();
58 let mut files: Vec<RepositoryDiffFile> = vec![];
59
60 diff.deltas().enumerate().for_each(|(i, delta)| {
61 // Parse the old file info from the delta
62 let old_file_info = match delta.old_file().exists() {
63 true => Some(RepositoryDiffFileInfo {
64 id: delta.old_file().id().to_string(),
65 path: delta
66 .old_file()
67 .path()
68 .unwrap()
69 .to_str()
70 .unwrap()
71 .to_string(),
72 size: delta.old_file().size(),
73 binary: delta.old_file().is_binary(),
74 }),
75 false => None,
76 };
77 // Parse the new file info from the delta
78 let new_file_info = match delta.new_file().exists() {
79 true => Some(RepositoryDiffFileInfo {
80 id: delta.new_file().id().to_string(),
81 path: delta
82 .new_file()
83 .path()
84 .unwrap()
85 .to_str()
86 .unwrap()
87 .to_string(),
88 size: delta.new_file().size(),
89 binary: delta.new_file().is_binary(),
90 }),
91 false => None,
92 };
93
94 let mut chunks: Vec<RepositoryDiffFileChunk> = vec![];
95 if let Some(patch) = git2::Patch::from_diff(&diff, i).ok().flatten() {
96 for chunk_num in 0..patch.num_hunks() {
97 if let Ok((chunk, chunk_num_lines)) = patch.hunk(chunk_num) {
98 let mut lines: Vec<RepositoryChunkLine> = vec![];
99
100 for line_num in 0..chunk_num_lines {
101 if let Ok(line) = patch.line_in_hunk(chunk_num, line_num) {
102 if let Ok(line_utf8) = String::from_utf8(line.content().to_vec()) {
103 lines.push(RepositoryChunkLine {
104 change_type: line.origin_value().into(),
105 content: line_utf8,
106 old_line_num: line.old_lineno(),
107 new_line_num: line.new_lineno(),
108 });
109 }
110
111 continue;
112 }
113 }
114
115 chunks.push(RepositoryDiffFileChunk {
116 header: String::from_utf8(chunk.header().to_vec()).ok(),
117 old_start: chunk.old_start(),
118 old_lines: chunk.old_lines(),
119 new_start: chunk.new_start(),
120 new_lines: chunk.new_lines(),
121 lines,
122 });
123 }
124 }
125 };
126
127 let file = RepositoryDiffFile {
128 status: RepositoryDiffFileStatus::from(delta.status()),
129 old_file_info,
130 new_file_info,
131 chunks,
132 };
133
134 files.push(file);
135 });
136
137 Ok(RepositoryDiff {
138 new_commit: Commit::from(commit_new),
139 files_changed: stats.files_changed(),
140 insertions: stats.insertions(),
141 deletions: stats.deletions(),
142 files,
143 })
144 }
145
146 pub async fn handle_repository_diff_patch(
147 &mut self,
148 requester: &Option<AuthenticatedUser>,
149 repository_object: &mut Object<'_, StackOperationState, Repository, GiteratedStack>,
150 OperationState(_operation_state): OperationState<StackOperationState>,
151 request: &RepositoryDiffPatchRequest,
152 ) -> Result<String, Error> {
153 let repository = repository_object.object();
154 let git = self
155 .open_repository_and_check_permissions(&repository.owner, &repository.name, requester)
156 .await?;
157
158 // Parse the passed object ids
159 let oid_old = git2::Oid::from_str(request.old_id.as_str())
160 .map_err(|_| GitBackendError::InvalidObjectId(request.old_id.clone()))?;
161 let oid_new = git2::Oid::from_str(request.new_id.as_str())
162 .map_err(|_| GitBackendError::InvalidObjectId(request.new_id.clone()))?;
163
164 // Get the ids associates commits
165 let commit_old = git
166 .find_commit(oid_old)
167 .map_err(|_| GitBackendError::CommitNotFound(oid_old.to_string()))?;
168 let commit_new = git
169 .find_commit(oid_new)
170 .map_err(|_| GitBackendError::CommitNotFound(oid_new.to_string()))?;
171
172 // Get the commit trees
173 let tree_old = commit_old
174 .tree()
175 .map_err(|_| GitBackendError::TreeNotFound(oid_old.to_string()))?;
176 let tree_new = commit_new
177 .tree()
178 .map_err(|_| GitBackendError::TreeNotFound(oid_new.to_string()))?;
179
180 // Diff the two trees against each other
181 let diff = git
182 .diff_tree_to_tree(Some(&tree_old), Some(&tree_new), None)
183 .map_err(|_| {
184 GitBackendError::FailedDiffing(oid_old.to_string(), oid_new.to_string())
185 })?;
186
187 // Print the entire patch
188 let mut patch = String::new();
189
190 diff.print(git2::DiffFormat::Patch, |_, _, line| {
191 match line.origin() {
192 '+' | '-' | ' ' => patch.push(line.origin()),
193 _ => {}
194 }
195 patch.push_str(std::str::from_utf8(line.content()).unwrap());
196 true
197 })
198 .unwrap();
199
200 Ok(patch)
201 }
202 }
203