@@ -72,6 +72,7 @@ enum Op {
72
72
Prefix ( std:: path:: PathBuf ) ,
73
73
Subdir ( std:: path:: PathBuf ) ,
74
74
Workspace ( std:: path:: PathBuf ) ,
75
+ Include ( std:: path:: PathBuf ) ,
75
76
76
77
Glob ( String ) ,
77
78
@@ -169,6 +170,9 @@ fn spec2(op: &Op) -> String {
169
170
Op :: Workspace ( path) => {
170
171
format ! ( ":workspace={}" , path. to_string_lossy( ) )
171
172
}
173
+ Op :: Include ( path) => {
174
+ format ! ( ":include={}" , path. to_string_lossy( ) )
175
+ }
172
176
173
177
Op :: Chain ( a, b) => match ( to_op ( * a) , to_op ( * b) ) {
174
178
( Op :: Subdir ( p1) , Op :: Prefix ( p2) ) if p1 == p2 => {
@@ -354,6 +358,54 @@ fn apply_to_commit2(
354
358
) )
355
359
. transpose ( ) ;
356
360
}
361
+ Op :: Include ( include_path) => {
362
+ let normal_parents = commit
363
+ . parent_ids ( )
364
+ . map ( |parent| transaction. get ( filter, parent) )
365
+ . collect :: < Option < Vec < git2:: Oid > > > ( ) ;
366
+
367
+ let normal_parents = some_or ! ( normal_parents, { return Ok ( None ) } ) ;
368
+
369
+ let cw = parse:: parse ( & tree:: get_blob ( repo, & commit. tree ( ) ?, & include_path) )
370
+ . unwrap_or ( to_filter ( Op :: Empty ) ) ;
371
+
372
+ let extra_parents = commit
373
+ . parents ( )
374
+ . map ( |parent| {
375
+ rs_tracing:: trace_scoped!( "parent" , "id" : parent. id( ) . to_string( ) ) ;
376
+ let pcw = parse:: parse ( & tree:: get_blob (
377
+ repo,
378
+ & parent. tree ( ) . unwrap_or ( tree:: empty ( repo) ) ,
379
+ & include_path,
380
+ ) )
381
+ . unwrap_or ( to_filter ( Op :: Empty ) ) ;
382
+
383
+ apply_to_commit2 (
384
+ & to_op ( opt:: optimize ( to_filter ( Op :: Subtract ( cw, pcw) ) ) ) ,
385
+ & parent,
386
+ transaction,
387
+ )
388
+ } )
389
+ . collect :: < JoshResult < Option < Vec < _ > > > > ( ) ?;
390
+
391
+ let extra_parents = some_or ! ( extra_parents, { return Ok ( None ) } ) ;
392
+
393
+ let filtered_parent_ids = normal_parents
394
+ . into_iter ( )
395
+ . chain ( extra_parents. into_iter ( ) )
396
+ . collect ( ) ;
397
+
398
+ let filtered_tree = apply ( transaction, filter, commit. tree ( ) ?) ?;
399
+
400
+ return Some ( history:: create_filtered_commit (
401
+ commit,
402
+ filtered_parent_ids,
403
+ filtered_tree,
404
+ transaction,
405
+ filter,
406
+ ) )
407
+ . transpose ( ) ;
408
+ }
357
409
Op :: Fold => {
358
410
let filtered_parent_ids = commit
359
411
. parents ( )
@@ -521,6 +573,15 @@ fn apply2<'a>(
521
573
}
522
574
}
523
575
576
+ Op :: Include ( path) => {
577
+ let file = to_filter ( Op :: File ( path. to_owned ( ) ) ) ;
578
+ if let Ok ( cw) = parse:: parse ( & tree:: get_blob ( repo, & tree, & path) ) {
579
+ apply ( transaction, compose ( file, cw) , tree)
580
+ } else {
581
+ apply ( transaction, file, tree)
582
+ }
583
+ }
584
+
524
585
Op :: Compose ( filters) => {
525
586
let filtered: Vec < _ > = filters
526
587
. iter ( )
@@ -626,6 +687,71 @@ fn unapply2<'a>(
626
687
627
688
return Ok ( r) ;
628
689
}
690
+ Op :: Include ( path) => {
691
+ let root = to_filter ( Op :: File ( path. to_owned ( ) ) ) ;
692
+ let mapped = & tree:: get_blob ( transaction. repo ( ) , & tree, path) ;
693
+ let parsed = parse ( mapped) ?;
694
+
695
+ let mut blob = String :: new ( ) ;
696
+ if let Ok ( c) = get_comments ( mapped) {
697
+ if !c. is_empty ( ) {
698
+ blob = c;
699
+ }
700
+ }
701
+ let blob = & format ! ( "{}{}\n " , & blob, pretty( parsed, 0 ) ) ;
702
+
703
+ // TODO: is this still necessary?
704
+ // Remove filters file from the tree to prevent it from being parsed again
705
+ // further down the callstack leading to endless recursion.
706
+ let tree = tree:: insert (
707
+ transaction. repo ( ) ,
708
+ & tree,
709
+ path,
710
+ git2:: Oid :: zero ( ) ,
711
+ 0o0100644 ,
712
+ ) ?;
713
+
714
+ // Insert a dummy file to prevent the directory from dissappearing through becoming
715
+ // empty.
716
+ let tree = tree:: insert (
717
+ transaction. repo ( ) ,
718
+ & tree,
719
+ Path :: new ( "DUMMY-df97a89d-b11f-4e1c-8400-345f895f0d40" ) ,
720
+ transaction. repo ( ) . blob ( "" . as_bytes ( ) ) ?,
721
+ 0o0100644 ,
722
+ ) ?;
723
+
724
+ let r = unapply (
725
+ transaction,
726
+ compose ( root, parsed) ,
727
+ tree. clone ( ) ,
728
+ parent_tree,
729
+ ) ?;
730
+
731
+ // Remove the dummy file inserted above
732
+ let r = tree:: insert (
733
+ transaction. repo ( ) ,
734
+ & r,
735
+ & path. join ( "DUMMY-df97a89d-b11f-4e1c-8400-345f895f0d40" ) ,
736
+ git2:: Oid :: zero ( ) ,
737
+ 0o0100644 ,
738
+ ) ?;
739
+
740
+ // Put the filters file back to it's target location.
741
+ let r = if !mapped. is_empty ( ) {
742
+ tree:: insert (
743
+ transaction. repo ( ) ,
744
+ & r,
745
+ & path,
746
+ transaction. repo ( ) . blob ( blob. as_bytes ( ) ) ?,
747
+ 0o0100644 , // Should this handle filemode?
748
+ ) ?
749
+ } else {
750
+ r
751
+ } ;
752
+
753
+ return Ok ( r) ;
754
+ }
629
755
Op :: Compose ( filters) => {
630
756
let mut remaining = tree. clone ( ) ;
631
757
let mut result = parent_tree. clone ( ) ;
@@ -748,6 +874,16 @@ pub fn compute_warnings<'a>(
748
874
}
749
875
}
750
876
877
+ if let Op :: Include ( path) = to_op ( filter) {
878
+ let full_filter = & tree:: get_blob ( transaction. repo ( ) , & tree, & path) ;
879
+ if let Ok ( res) = parse ( full_filter) {
880
+ filter = res;
881
+ } else {
882
+ warnings. push ( "couldn't parse include file\n " . to_string ( ) ) ;
883
+ return warnings;
884
+ }
885
+ }
886
+
751
887
let filter = opt:: flatten ( filter) ;
752
888
if let Op :: Compose ( filters) = to_op ( filter) {
753
889
for f in filters {
0 commit comments