Skip to content

Commit c2daa4d

Browse files
committed
Use gitoxide in get_diff
1 parent 6685f9a commit c2daa4d

File tree

2 files changed

+213
-8
lines changed

2 files changed

+213
-8
lines changed

asyncgit/src/error.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,32 @@ pub enum Error {
9595
#[error("sign error: {0}")]
9696
Sign(#[from] crate::sync::sign::SignError),
9797

98+
///
99+
#[error("gix::config::diff::algorithm::Error error: {0}")]
100+
GixConfigDiffAlgorithm(
101+
#[from] gix::config::diff::algorithm::Error,
102+
),
103+
98104
///
99105
#[error("gix::discover error: {0}")]
100106
GixDiscover(#[from] Box<gix::discover::Error>),
101107

108+
///
109+
#[error(
110+
"gix::diff::blob::platform::prepare_diff::Error error: {0}"
111+
)]
112+
GixDiffBlobPlatformPrepareDiff(
113+
#[from] gix::diff::blob::platform::prepare_diff::Error,
114+
),
115+
116+
///
117+
#[error(
118+
"gix::diff::blob::platform::set_resource::Error error: {0}"
119+
)]
120+
GixDiffBlobPlatformSetResource(
121+
#[from] gix::diff::blob::platform::set_resource::Error,
122+
),
123+
102124
///
103125
#[error("gix::reference::find::existing error: {0}")]
104126
GixReferenceFindExisting(
@@ -119,10 +141,14 @@ pub enum Error {
119141

120142
///
121143
#[error("gix::object::find::existing::with_conversion::Error error: {0}")]
122-
GixObjectFindExistingWithConversionError(
144+
GixObjectFindExistingWithConversion(
123145
#[from] gix::object::find::existing::with_conversion::Error,
124146
),
125147

148+
///
149+
#[error("gix::object::find::existing::Error error: {0}")]
150+
GixObjectFindExisting(#[from] gix::object::find::existing::Error),
151+
126152
///
127153
#[error("gix::pathspec::init::Error error: {0}")]
128154
GixPathspecInit(#[from] Box<gix::pathspec::init::Error>),
@@ -133,6 +159,14 @@ pub enum Error {
133159
#[from] gix::reference::head_tree_id::Error,
134160
),
135161

162+
///
163+
#[error(
164+
"gix::repository::diff_resource_cache::Error error: {0}"
165+
)]
166+
GixRepositoryDiffResourceCache(
167+
#[from] gix::repository::diff_resource_cache::Error,
168+
),
169+
136170
///
137171
#[error("gix::status::Error error: {0}")]
138172
GixStatus(#[from] Box<gix::status::Error>),

asyncgit/src/sync/diff.rs

Lines changed: 178 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,22 @@ use super::{
88
CommitId, RepoPath,
99
};
1010
use crate::{
11-
error::Error,
12-
error::Result,
11+
error::{Error, Result},
1312
hash,
14-
sync::{get_stashes, repository::repo},
13+
sync::{get_stashes, gix_repo, repository::repo},
1514
};
1615
use easy_cast::Conv;
1716
use git2::{
1817
Delta, Diff, DiffDelta, DiffFormat, DiffHunk, Patch, Repository,
1918
};
19+
use gix::{
20+
bstr::ByteSlice,
21+
diff::blob::{
22+
unified_diff::{ConsumeHunk, ContextSize, NewlineSeparator},
23+
UnifiedDiff,
24+
},
25+
ObjectId,
26+
};
2027
use scopetime::scope_time;
2128
use serde::{Deserialize, Serialize};
2229
use std::{cell::RefCell, fs, path::Path, rc::Rc};
@@ -200,20 +207,184 @@ pub(crate) fn get_diff_raw<'a>(
200207
Ok(diff)
201208
}
202209

210+
pub(crate) fn tokens_for_diffing(
211+
data: &[u8],
212+
) -> impl gix::diff::blob::intern::TokenSource<Token = &[u8]> {
213+
gix::diff::blob::sources::byte_lines(data)
214+
}
215+
216+
impl ConsumeHunk for FileDiff {
217+
type Out = Self;
218+
219+
fn consume_hunk(
220+
&mut self,
221+
before_hunk_start: u32,
222+
_before_hunk_len: u32,
223+
after_hunk_start: u32,
224+
_after_hunk_len: u32,
225+
header: &str,
226+
hunk: &[u8],
227+
) -> std::io::Result<()> {
228+
let lines = hunk
229+
.lines()
230+
.enumerate()
231+
.map(|(index, line)| DiffLine {
232+
position: DiffLinePosition {
233+
old_lineno: Some(
234+
before_hunk_start + index as u32,
235+
),
236+
new_lineno: Some(after_hunk_start + index as u32),
237+
},
238+
content: String::from_utf8_lossy(line)
239+
.trim_matches(is_newline)
240+
.into(),
241+
// TODO:
242+
// Get correct `line_type`. We could potentially do this by looking at the line's first
243+
// character. We probably want to split it anyway as `gitui` takes care of that character
244+
// itself later in the UI.
245+
line_type: DiffLineType::default(),
246+
})
247+
.collect();
248+
249+
self.hunks.push(Hunk {
250+
// TODO:
251+
// Get correct `header_hash`.
252+
header_hash: 0,
253+
lines,
254+
});
255+
256+
self.lines += hunk.lines().count();
257+
258+
Ok(())
259+
}
260+
261+
fn finish(self) -> Self::Out {
262+
self
263+
}
264+
}
265+
203266
/// returns diff of a specific file either in `stage` or workdir
204267
pub fn get_diff(
205268
repo_path: &RepoPath,
206269
p: &str,
207270
stage: bool,
208271
options: Option<DiffOptions>,
209272
) -> Result<FileDiff> {
273+
// TODO:
274+
// Maybe move this `use` statement` closer to where it is being used by extracting the relevant
275+
// code into a function.
276+
use gix::diff::blob::platform::prepare_diff::Operation;
277+
210278
scope_time!("get_diff");
211279

212-
let repo = repo(repo_path)?;
213-
let work_dir = work_dir(&repo)?;
214-
let diff = get_diff_raw(&repo, p, stage, false, options)?;
280+
let mut gix_repo = gix_repo(repo_path)?;
281+
gix_repo.object_cache_size_if_unset(
282+
gix_repo.compute_object_cache_size_for_tree_diffs(
283+
&**gix_repo.index_or_empty()?,
284+
),
285+
);
286+
287+
let old_revspec = format!("HEAD:{p}");
288+
289+
// TODO:
290+
// This is identical to the corresponding block that gets `(new_blob_id, new_root)`.
291+
let (old_blob_id, old_root) =
292+
gix_repo.rev_parse(&*old_revspec).map_or_else(
293+
|_| {
294+
(
295+
ObjectId::null(gix::hash::Kind::Sha1),
296+
gix_repo.workdir().map(ToOwned::to_owned),
297+
)
298+
},
299+
|resolved_revspec| {
300+
resolved_revspec.single().map_or_else(
301+
|| (ObjectId::null(gix::hash::Kind::Sha1), None),
302+
|id| (id.into(), None),
303+
)
304+
},
305+
);
306+
307+
// TODO:
308+
// Make sure that the revspec logic is correct, i. e. uses the correct syntax for all the
309+
// relevant cases.
310+
let new_revspec = if stage {
311+
format!(":{p}")
312+
} else {
313+
p.to_string()
314+
};
215315

216-
raw_diff_to_file_diff(&diff, work_dir)
316+
let (new_blob_id, new_root) =
317+
gix_repo.rev_parse(&*new_revspec).map_or_else(
318+
|_| {
319+
(
320+
ObjectId::null(gix::hash::Kind::Sha1),
321+
gix_repo.workdir().map(ToOwned::to_owned),
322+
)
323+
},
324+
|resolved_revspec| {
325+
resolved_revspec.single().map_or_else(
326+
|| (ObjectId::null(gix::hash::Kind::Sha1), None),
327+
|id| (id.into(), None),
328+
)
329+
},
330+
);
331+
332+
let worktree_roots = gix::diff::blob::pipeline::WorktreeRoots {
333+
old_root,
334+
new_root,
335+
};
336+
337+
let mut resource_cache = gix_repo.diff_resource_cache(gix::diff::blob::pipeline::Mode::ToGitUnlessBinaryToTextIsPresent, worktree_roots)?;
338+
339+
resource_cache.set_resource(
340+
old_blob_id,
341+
gix::object::tree::EntryKind::Blob,
342+
p.as_ref(),
343+
gix::diff::blob::ResourceKind::OldOrSource,
344+
&gix_repo.objects,
345+
)?;
346+
resource_cache.set_resource(
347+
new_blob_id,
348+
gix::object::tree::EntryKind::Blob,
349+
p.as_ref(),
350+
gix::diff::blob::ResourceKind::NewOrDestination,
351+
&gix_repo.objects,
352+
)?;
353+
354+
let outcome = resource_cache.prepare_diff()?;
355+
356+
let diff_algorithm = match outcome.operation {
357+
Operation::InternalDiff { algorithm } => algorithm,
358+
Operation::ExternalCommand { .. } => {
359+
unreachable!("We disabled that")
360+
}
361+
Operation::SourceOrDestinationIsBinary => {
362+
todo!();
363+
}
364+
};
365+
366+
let input = gix::diff::blob::intern::InternedInput::new(
367+
tokens_for_diffing(
368+
outcome.old.data.as_slice().unwrap_or_default(),
369+
),
370+
tokens_for_diffing(
371+
outcome.new.data.as_slice().unwrap_or_default(),
372+
),
373+
);
374+
375+
let unified_diff = UnifiedDiff::new(
376+
&input,
377+
FileDiff::default(),
378+
// TODO:
379+
// Get values from `options` where necessary and possible.
380+
NewlineSeparator::AfterHeaderAndLine("\n"),
381+
ContextSize::symmetrical(3),
382+
);
383+
384+
let file_diff =
385+
gix::diff::blob::diff(diff_algorithm, &input, unified_diff)?;
386+
387+
Ok(file_diff)
217388
}
218389

219390
/// returns diff of a specific file inside a commit

0 commit comments

Comments
 (0)