@@ -312,6 +312,7 @@ enum Op {
312312 Prefix ( std:: path:: PathBuf ) ,
313313 Subdir ( std:: path:: PathBuf ) ,
314314 Workspace ( std:: path:: PathBuf ) ,
315+ Stored ( std:: path:: PathBuf ) ,
315316
316317 Pattern ( String ) ,
317318 Message ( String , regex:: Regex ) ,
@@ -589,6 +590,9 @@ fn spec2(op: &Op) -> String {
589590 Op :: Workspace ( path) => {
590591 format ! ( ":workspace={}" , parse:: quote_if( & path. to_string_lossy( ) ) )
591592 }
593+ Op :: Stored ( path) => {
594+ format ! ( ":+{}" , parse:: quote_if( & path. to_string_lossy( ) ) )
595+ }
592596 Op :: RegexReplace ( replacements) => {
593597 let v = replacements
594598 . iter ( )
@@ -737,6 +741,31 @@ fn get_workspace<'a>(repo: &'a git2::Repository, tree: &'a git2::Tree<'a>, path:
737741 )
738742}
739743
744+ // Handle stored.josh files that contain ":stored=..." as their only filter as
745+ // a "redirect" to that other stored. We chain an exclude of the redirecting stored
746+ // in front to prevent infinite recursion.
747+ fn resolve_stored_redirect < ' a > (
748+ repo : & ' a git2:: Repository ,
749+ tree : & ' a git2:: Tree < ' a > ,
750+ path : & Path ,
751+ ) -> Option < ( Filter , std:: path:: PathBuf ) > {
752+ let stored_path = path. with_extension ( "josh" ) ;
753+ let f = parse:: parse ( & tree:: get_blob ( repo, tree, & stored_path) )
754+ . unwrap_or_else ( |_| to_filter ( Op :: Empty ) ) ;
755+
756+ if let Op :: Stored ( p) = to_op ( f) {
757+ Some ( ( chain ( to_filter ( Op :: Exclude ( file ( stored_path) ) ) , f) , p) )
758+ } else {
759+ None
760+ }
761+ }
762+
763+ fn get_stored < ' a > ( repo : & ' a git2:: Repository , tree : & ' a git2:: Tree < ' a > , path : & Path ) -> Filter {
764+ let stored_path = path. with_extension ( "josh" ) ;
765+ let sj_file = file ( stored_path. clone ( ) ) ;
766+ compose ( sj_file, get_filter ( repo, tree, & stored_path) )
767+ }
768+
740769fn get_filter < ' a > ( repo : & ' a git2:: Repository , tree : & ' a git2:: Tree < ' a > , path : & Path ) -> Filter {
741770 let ws_path = normalize_path ( path) ;
742771 let ws_id = ok_or ! ( tree. get_path( & ws_path) , {
@@ -1022,6 +1051,33 @@ fn apply_to_commit2(
10221051
10231052 return per_rev_filter ( transaction, commit, filter, commit_filter, parent_filters) ;
10241053 }
1054+ Op :: Stored ( s_path) => {
1055+ if let Some ( ( redirect, _) ) = resolve_stored_redirect ( repo, & commit. tree ( ) ?, s_path) {
1056+ if let Some ( r) = apply_to_commit2 ( & to_op ( redirect) , commit, transaction) ? {
1057+ transaction. insert ( filter, commit. id ( ) , r, true ) ;
1058+ return Ok ( Some ( r) ) ;
1059+ } else {
1060+ return Ok ( None ) ;
1061+ }
1062+ }
1063+
1064+ let commit_filter = get_stored ( repo, & commit. tree ( ) ?, & s_path) ;
1065+
1066+ let parent_filters = commit
1067+ . parents ( )
1068+ . map ( |parent| {
1069+ rs_tracing:: trace_scoped!( "parent" , "id" : parent. id( ) . to_string( ) ) ;
1070+ let pcs = get_stored (
1071+ repo,
1072+ & parent. tree ( ) . unwrap_or_else ( |_| tree:: empty ( repo) ) ,
1073+ & s_path,
1074+ ) ;
1075+ Ok ( ( parent, pcs) )
1076+ } )
1077+ . collect :: < JoshResult < Vec < _ > > > ( ) ?;
1078+
1079+ return per_rev_filter ( transaction, commit, filter, commit_filter, parent_filters) ;
1080+ }
10251081 Op :: Fold => {
10261082 let filtered_parent_ids = commit
10271083 . parents ( )
@@ -1207,6 +1263,7 @@ fn apply2<'a>(transaction: &'a cache::Transaction, op: &Op, x: Apply<'a>) -> Jos
12071263 }
12081264
12091265 Op :: Workspace ( path) => apply ( transaction, get_workspace ( repo, & x. tree ( ) , & path) , x) ,
1266+ Op :: Stored ( path) => apply ( transaction, get_stored ( repo, & x. tree ( ) , & path) , x) ,
12101267
12111268 Op :: Compose ( filters) => {
12121269 let filtered: Vec < _ > = filters
@@ -1329,6 +1386,31 @@ fn unapply_workspace<'a>(
13291386
13301387 Ok ( Some ( result) )
13311388 }
1389+ Op :: Stored ( path) => {
1390+ let stored_path = path. with_extension ( "josh" ) ;
1391+ let stored = get_filter ( transaction. repo ( ) , & tree, & stored_path) ;
1392+ let original_stored = get_filter ( transaction. repo ( ) , & parent_tree, & stored_path) ;
1393+
1394+ let sj_file = file ( stored_path. clone ( ) ) ;
1395+ let filter = compose ( sj_file, stored) ;
1396+ let original_filter = compose ( sj_file, original_stored) ;
1397+ let filtered = apply (
1398+ transaction,
1399+ original_filter,
1400+ Apply :: from_tree ( parent_tree. clone ( ) ) ,
1401+ ) ?;
1402+ let matching = apply ( transaction, invert ( original_filter) ?, filtered. clone ( ) ) ?;
1403+ let stripped = tree:: subtract ( transaction, parent_tree. id ( ) , matching. tree ( ) . id ( ) ) ?;
1404+ let x = apply ( transaction, invert ( filter) ?, Apply :: from_tree ( tree) ) ?;
1405+
1406+ let result = transaction. repo ( ) . find_tree ( tree:: overlay (
1407+ transaction,
1408+ x. tree ( ) . id ( ) ,
1409+ stripped,
1410+ ) ?) ?;
1411+
1412+ Ok ( Some ( result) )
1413+ }
13321414 _ => Ok ( None ) ,
13331415 }
13341416}
@@ -1397,6 +1479,17 @@ pub fn compute_warnings<'a>(
13971479 }
13981480 }
13991481
1482+ if let Op :: Stored ( path) = to_op ( filter) {
1483+ let stored_path = path. with_extension ( "josh" ) ;
1484+ let stored_filter = & tree:: get_blob ( transaction. repo ( ) , & tree, & stored_path) ;
1485+ if let Ok ( res) = parse ( stored_filter) {
1486+ filter = res;
1487+ } else {
1488+ warnings. push ( "couldn't parse stored\n " . to_string ( ) ) ;
1489+ return warnings;
1490+ }
1491+ }
1492+
14001493 let filter = opt:: flatten ( filter) ;
14011494 if let Op :: Compose ( filters) = to_op ( filter) {
14021495 for f in filters {
0 commit comments