5
5
use std:: convert:: TryInto ;
6
6
use std:: ffi:: { c_void, CStr , CString , OsStr } ;
7
7
use std:: io:: { self , Write } ;
8
- use std:: os:: raw:: { c_char, c_int, c_long, c_uint, c_ulong} ;
8
+ use std:: os:: raw:: { c_char, c_int, c_long, c_uint, c_ulong, c_ushort } ;
9
9
#[ cfg( unix) ]
10
10
use std:: os:: unix:: ffi:: OsStrExt ;
11
11
@@ -15,7 +15,7 @@ use derive_more::{Deref, Display};
15
15
use getset:: Getters ;
16
16
17
17
use crate :: oid:: { GitObjectId , ObjectId } ;
18
- use crate :: util:: { FromBytes , SliceExt } ;
18
+ use crate :: util:: { BorrowKey , FromBytes , SliceExt } ;
19
19
20
20
const GIT_MAX_RAWSZ : usize = 32 ;
21
21
@@ -456,3 +456,158 @@ impl Iterator for RevList {
456
456
}
457
457
}
458
458
}
459
+
460
+ const DIFF_STATUS_ADDED : c_char = b'A' as c_char ;
461
+ const DIFF_STATUS_COPIED : c_char = b'C' as c_char ;
462
+ const DIFF_STATUS_DELETED : c_char = b'D' as c_char ;
463
+ const DIFF_STATUS_MODIFIED : c_char = b'M' as c_char ;
464
+ const DIFF_STATUS_TYPE_CHANGED : c_char = b'T' as c_char ;
465
+ const DIFF_STATUS_RENAMED : c_char = b'R' as c_char ;
466
+
467
+ #[ allow( non_camel_case_types) ]
468
+ #[ repr( C ) ]
469
+ struct diff_tree_file {
470
+ oid : * const object_id ,
471
+ path : * const c_char ,
472
+ mode : c_ushort ,
473
+ }
474
+
475
+ #[ allow( non_camel_case_types) ]
476
+ #[ repr( C ) ]
477
+ struct diff_tree_item {
478
+ a : diff_tree_file ,
479
+ b : diff_tree_file ,
480
+ score : c_ushort ,
481
+ status : c_char ,
482
+ }
483
+
484
+ extern "C" {
485
+ fn diff_tree_ (
486
+ argc : c_int ,
487
+ argv : * const * const c_char ,
488
+ cb : unsafe extern "C" fn ( * mut c_void , * const diff_tree_item ) ,
489
+ context : * const c_void ,
490
+ ) ;
491
+ }
492
+
493
+ pub struct FileMode ( u16 ) ;
494
+
495
+ pub enum DiffTreeItem {
496
+ Added {
497
+ path : Box < [ u8 ] > ,
498
+ mode : FileMode ,
499
+ oid : BlobId ,
500
+ } ,
501
+ Deleted {
502
+ path : Box < [ u8 ] > ,
503
+ mode : FileMode ,
504
+ oid : BlobId ,
505
+ } ,
506
+ Modified {
507
+ path : Box < [ u8 ] > ,
508
+ from_mode : FileMode ,
509
+ from_oid : BlobId ,
510
+ to_mode : FileMode ,
511
+ to_oid : BlobId ,
512
+ } ,
513
+ Renamed {
514
+ to_path : Box < [ u8 ] > ,
515
+ to_mode : FileMode ,
516
+ to_oid : BlobId ,
517
+ from_path : Box < [ u8 ] > ,
518
+ from_mode : FileMode ,
519
+ from_oid : BlobId ,
520
+ } ,
521
+ Copied {
522
+ to_path : Box < [ u8 ] > ,
523
+ to_mode : FileMode ,
524
+ to_oid : BlobId ,
525
+ from_path : Box < [ u8 ] > ,
526
+ from_mode : FileMode ,
527
+ from_oid : BlobId ,
528
+ } ,
529
+ }
530
+
531
+ impl BorrowKey for DiffTreeItem {
532
+ type Key = Box < [ u8 ] > ;
533
+ fn borrow_key ( & self ) -> & Self :: Key {
534
+ match self {
535
+ DiffTreeItem :: Added { path, .. } => path,
536
+ DiffTreeItem :: Deleted { path, .. } => path,
537
+ DiffTreeItem :: Modified { path, .. } => path,
538
+ DiffTreeItem :: Renamed { to_path, .. } => to_path,
539
+ DiffTreeItem :: Copied { to_path, .. } => to_path,
540
+ }
541
+ }
542
+ }
543
+
544
+ unsafe extern "C" fn diff_tree_fill ( diff_tree : * mut c_void , item : * const diff_tree_item ) {
545
+ let diff_tree = ( diff_tree as * mut Vec < DiffTreeItem > ) . as_mut ( ) . unwrap ( ) ;
546
+ let item = item. as_ref ( ) . unwrap ( ) ;
547
+ let item = match item. status {
548
+ DIFF_STATUS_MODIFIED | DIFF_STATUS_TYPE_CHANGED => DiffTreeItem :: Modified {
549
+ path : {
550
+ let a_path: Box < [ u8 ] > = CStr :: from_ptr ( item. a . path ) . to_bytes ( ) . into ( ) ;
551
+ let b_path = CStr :: from_ptr ( item. b . path ) . to_bytes ( ) ;
552
+ assert_eq ! ( a_path. as_bstr( ) , b_path. as_bstr( ) ) ;
553
+ a_path
554
+ } ,
555
+ from_oid : BlobId :: from ( GitObjectId :: from ( item. a . oid . as_ref ( ) . unwrap ( ) . clone ( ) ) ) ,
556
+ from_mode : FileMode ( item. a . mode ) ,
557
+ to_oid : BlobId :: from ( GitObjectId :: from ( item. b . oid . as_ref ( ) . unwrap ( ) . clone ( ) ) ) ,
558
+ to_mode : FileMode ( item. b . mode ) ,
559
+ } ,
560
+ DIFF_STATUS_ADDED => DiffTreeItem :: Added {
561
+ path : CStr :: from_ptr ( item. b . path ) . to_bytes ( ) . into ( ) ,
562
+ oid : BlobId :: from ( GitObjectId :: from ( item. b . oid . as_ref ( ) . unwrap ( ) . clone ( ) ) ) ,
563
+ mode : FileMode ( item. b . mode ) ,
564
+ } ,
565
+ DIFF_STATUS_DELETED => DiffTreeItem :: Deleted {
566
+ path : CStr :: from_ptr ( item. a . path ) . to_bytes ( ) . into ( ) ,
567
+ oid : BlobId :: from ( GitObjectId :: from ( item. a . oid . as_ref ( ) . unwrap ( ) . clone ( ) ) ) ,
568
+ mode : FileMode ( item. a . mode ) ,
569
+ } ,
570
+ DIFF_STATUS_RENAMED => DiffTreeItem :: Renamed {
571
+ to_path : CStr :: from_ptr ( item. b . path ) . to_bytes ( ) . into ( ) ,
572
+ to_oid : BlobId :: from ( GitObjectId :: from ( item. b . oid . as_ref ( ) . unwrap ( ) . clone ( ) ) ) ,
573
+ to_mode : FileMode ( item. b . mode ) ,
574
+ from_path : CStr :: from_ptr ( item. a . path ) . to_bytes ( ) . into ( ) ,
575
+ from_oid : BlobId :: from ( GitObjectId :: from ( item. a . oid . as_ref ( ) . unwrap ( ) . clone ( ) ) ) ,
576
+ from_mode : FileMode ( item. a . mode ) ,
577
+ } ,
578
+ DIFF_STATUS_COPIED => DiffTreeItem :: Copied {
579
+ to_path : CStr :: from_ptr ( item. b . path ) . to_bytes ( ) . into ( ) ,
580
+ to_oid : BlobId :: from ( GitObjectId :: from ( item. b . oid . as_ref ( ) . unwrap ( ) . clone ( ) ) ) ,
581
+ to_mode : FileMode ( item. b . mode ) ,
582
+ from_path : CStr :: from_ptr ( item. a . path ) . to_bytes ( ) . into ( ) ,
583
+ from_oid : BlobId :: from ( GitObjectId :: from ( item. a . oid . as_ref ( ) . unwrap ( ) . clone ( ) ) ) ,
584
+ from_mode : FileMode ( item. a . mode ) ,
585
+ } ,
586
+ c => panic ! ( "Unknown diff state: {}" , c) ,
587
+ } ;
588
+ diff_tree. push ( item) ;
589
+ }
590
+
591
+ pub fn diff_tree ( a : & CommitId , b : & CommitId ) -> impl Iterator < Item = DiffTreeItem > {
592
+ let a = CString :: new ( format ! ( "{}" , a) ) . unwrap ( ) ;
593
+ let b = CString :: new ( format ! ( "{}" , b) ) . unwrap ( ) ;
594
+ let args = [
595
+ cstr ! ( "" ) ,
596
+ & a,
597
+ & b,
598
+ cstr ! ( "--ignore-submodules=dirty" ) ,
599
+ cstr ! ( "--" ) ,
600
+ ] ;
601
+ let mut argv: Vec < _ > = args. iter ( ) . map ( |a| a. as_ptr ( ) ) . collect ( ) ;
602
+ argv. push ( std:: ptr:: null ( ) ) ;
603
+ let mut result = Vec :: < DiffTreeItem > :: new ( ) ;
604
+ unsafe {
605
+ diff_tree_ (
606
+ args. len ( ) . try_into ( ) . unwrap ( ) ,
607
+ & argv[ 0 ] ,
608
+ diff_tree_fill,
609
+ & mut result as * mut _ as * mut c_void ,
610
+ ) ;
611
+ }
612
+ result. into_iter ( )
613
+ }
0 commit comments