Skip to content

Commit 2d967ef

Browse files
committed
Use gitoxide in get_diff
1 parent fd46b9a commit 2d967ef

File tree

2 files changed

+229
-7
lines changed

2 files changed

+229
-7
lines changed

asyncgit/src/error.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,25 @@ use thiserror::Error;
77
///
88
#[derive(Error, Debug)]
99
pub enum GixError {
10+
#[error("gix::config::diff::algorithm::Error error: {0}")]
11+
ConfigDiffAlgorithm(#[from] gix::config::diff::algorithm::Error),
12+
13+
///
14+
#[error(
15+
"gix::diff::blob::platform::prepare_diff::Error error: {0}"
16+
)]
17+
DiffBlobPlatformPrepareDiff(
18+
#[from] gix::diff::blob::platform::prepare_diff::Error,
19+
),
20+
21+
///
22+
#[error(
23+
"gix::diff::blob::platform::set_resource::Error error: {0}"
24+
)]
25+
DiffBlobPlatformSetResource(
26+
#[from] gix::diff::blob::platform::set_resource::Error,
27+
),
28+
1029
///
1130
#[error("gix::discover error: {0}")]
1231
Discover(#[from] Box<gix::discover::Error>),
@@ -47,6 +66,14 @@ pub enum GixError {
4766
#[error("gix::reference::iter::init::Error error: {0}")]
4867
ReferenceIterInit(#[from] gix::reference::iter::init::Error),
4968

69+
///
70+
#[error(
71+
"gix::repository::diff_resource_cache::Error error: {0}"
72+
)]
73+
RepositoryDiffResourceCache(
74+
#[from] gix::repository::diff_resource_cache::Error,
75+
),
76+
5077
///
5178
#[error("gix::revision::walk error: {0}")]
5279
RevisionWalk(#[from] gix::revision::walk::Error),
@@ -207,6 +234,22 @@ impl From<gix::discover::Error> for GixError {
207234
}
208235
}
209236

237+
impl From<gix::diff::blob::platform::prepare_diff::Error> for Error {
238+
fn from(
239+
error: gix::diff::blob::platform::prepare_diff::Error,
240+
) -> Self {
241+
Self::Gix(GixError::from(error))
242+
}
243+
}
244+
245+
impl From<gix::diff::blob::platform::set_resource::Error> for Error {
246+
fn from(
247+
error: gix::diff::blob::platform::set_resource::Error,
248+
) -> Self {
249+
Self::Gix(GixError::from(error))
250+
}
251+
}
252+
210253
impl From<gix::discover::Error> for Error {
211254
fn from(error: gix::discover::Error) -> Self {
212255
Self::Gix(GixError::from(error))
@@ -271,6 +314,14 @@ impl From<gix::reference::iter::init::Error> for Error {
271314
}
272315
}
273316

317+
impl From<gix::repository::diff_resource_cache::Error> for Error {
318+
fn from(
319+
error: gix::repository::diff_resource_cache::Error,
320+
) -> Self {
321+
Self::Gix(GixError::from(error))
322+
}
323+
}
324+
274325
impl From<gix::revision::walk::Error> for Error {
275326
fn from(error: gix::revision::walk::Error) -> Self {
276327
Self::Gix(GixError::from(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)