@@ -8,15 +8,22 @@ use super::{
8
8
CommitId , RepoPath ,
9
9
} ;
10
10
use crate :: {
11
- error:: Error ,
12
- error:: Result ,
11
+ error:: { Error , Result } ,
13
12
hash,
14
- sync:: { get_stashes, repository:: repo} ,
13
+ sync:: { get_stashes, gix_repo , repository:: repo} ,
15
14
} ;
16
15
use easy_cast:: Conv ;
17
16
use git2:: {
18
17
Delta , Diff , DiffDelta , DiffFormat , DiffHunk , Patch , Repository ,
19
18
} ;
19
+ use gix:: {
20
+ bstr:: ByteSlice ,
21
+ diff:: blob:: {
22
+ unified_diff:: { ConsumeHunk , ContextSize , NewlineSeparator } ,
23
+ UnifiedDiff ,
24
+ } ,
25
+ ObjectId ,
26
+ } ;
20
27
use scopetime:: scope_time;
21
28
use serde:: { Deserialize , Serialize } ;
22
29
use std:: { cell:: RefCell , fs, path:: Path , rc:: Rc } ;
@@ -200,20 +207,184 @@ pub(crate) fn get_diff_raw<'a>(
200
207
Ok ( diff)
201
208
}
202
209
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
+
203
266
/// returns diff of a specific file either in `stage` or workdir
204
267
pub fn get_diff (
205
268
repo_path : & RepoPath ,
206
269
p : & str ,
207
270
stage : bool ,
208
271
options : Option < DiffOptions > ,
209
272
) -> 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
+
210
278
scope_time ! ( "get_diff" ) ;
211
279
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
+ } ;
215
315
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)
217
388
}
218
389
219
390
/// returns diff of a specific file inside a commit
0 commit comments