@@ -8,15 +8,22 @@ use super::{
88 CommitId , RepoPath ,
99} ;
1010use 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} ;
1615use easy_cast:: Conv ;
1716use 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+ } ;
2027use scopetime:: scope_time;
2128use serde:: { Deserialize , Serialize } ;
2229use 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
204267pub 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