@@ -16,12 +16,16 @@ pub use parse::get_comments;
1616pub use parse:: parse;
1717
1818static FILTERS : LazyLock < std:: sync:: Mutex < std:: collections:: HashMap < Filter , Op > > > =
19- LazyLock :: new ( || std :: sync :: Mutex :: new ( std :: collections :: HashMap :: new ( ) ) ) ;
19+ LazyLock :: new ( || Default :: default ( ) ) ;
2020static WORKSPACES : LazyLock < std:: sync:: Mutex < std:: collections:: HashMap < git2:: Oid , Filter > > > =
21- LazyLock :: new ( || std :: sync :: Mutex :: new ( std :: collections :: HashMap :: new ( ) ) ) ;
21+ LazyLock :: new ( || Default :: default ( ) ) ;
2222static ANCESTORS : LazyLock <
2323 std:: sync:: Mutex < std:: collections:: HashMap < git2:: Oid , std:: collections:: HashSet < git2:: Oid > > > ,
24- > = LazyLock :: new ( || std:: sync:: Mutex :: new ( std:: collections:: HashMap :: new ( ) ) ) ;
24+ > = LazyLock :: new ( || Default :: default ( ) ) ;
25+
26+ static LEGALIZED : LazyLock <
27+ std:: sync:: Mutex < std:: collections:: HashMap < ( Filter , git2:: Oid ) , Filter > > ,
28+ > = LazyLock :: new ( || Default :: default ( ) ) ;
2529
2630/// Match-all regex pattern used as the default for Op::Message when no regex is specified.
2731/// The pattern `(?s)^.*$` matches any string (including newlines) from start to end.
@@ -442,7 +446,19 @@ fn lazy_refs2(op: &Op) -> Vec<String> {
442446 av. append ( & mut lazy_refs ( * b) ) ;
443447 av
444448 }
445- Op :: Rev ( filters) => lazy_refs2 ( & Op :: Join ( filters. clone ( ) ) ) ,
449+ Op :: Rev ( filters) => {
450+ let mut lr = lazy_refs2 ( & Op :: Compose ( filters. values ( ) . copied ( ) . collect ( ) ) ) ;
451+ lr. extend ( filters. keys ( ) . filter_map ( |x| {
452+ if let LazyRef :: Lazy ( s) = x {
453+ Some ( s. to_owned ( ) )
454+ } else {
455+ None
456+ }
457+ } ) ) ;
458+ lr. sort ( ) ;
459+ lr. dedup ( ) ;
460+ lr
461+ }
446462 Op :: HistoryConcat ( r, f) => {
447463 let mut lr = Vec :: new ( ) ;
448464 if let LazyRef :: Lazy ( s) = r {
@@ -757,53 +773,50 @@ fn resolve_workspace_redirect<'a>(
757773 }
758774}
759775
760- fn get_workspace < ' a > ( repo : & ' a git2:: Repository , tree : & ' a git2:: Tree < ' a > , path : & Path ) -> Filter {
776+ fn get_workspace < ' a > (
777+ transaction : & cache:: Transaction ,
778+ tree : & ' a git2:: Tree < ' a > ,
779+ path : & Path ,
780+ ) -> Filter {
761781 let wsj_file = file ( "workspace.josh" ) ;
762782 let base = to_filter ( Op :: Subdir ( path. to_owned ( ) ) ) ;
763783 let wsj_file = chain ( base, wsj_file) ;
764784 compose (
765785 wsj_file,
766- compose ( get_filter ( repo, tree, & path. join ( "workspace.josh" ) ) , base) ,
786+ compose (
787+ get_filter ( transaction, tree, & path. join ( "workspace.josh" ) ) ,
788+ base,
789+ ) ,
767790 )
768791}
769792
770- // Handle stored.josh files that contain ":stored=..." as their only filter as
771- // a "redirect" to that other stored. We chain an exclude of the redirecting stored
772- // in front to prevent infinite recursion.
773- fn resolve_stored_redirect < ' a > (
774- repo : & ' a git2:: Repository ,
793+ fn get_stored < ' a > (
794+ transaction : & cache:: Transaction ,
775795 tree : & ' a git2:: Tree < ' a > ,
776796 path : & Path ,
777- ) -> Option < ( Filter , std:: path:: PathBuf ) > {
778- let stored_path = path. with_extension ( "josh" ) ;
779- let f = parse:: parse ( & tree:: get_blob ( repo, tree, & stored_path) )
780- . unwrap_or_else ( |_| to_filter ( Op :: Empty ) ) ;
781-
782- if let Op :: Stored ( p) = to_op ( f) {
783- Some ( ( chain ( to_filter ( Op :: Exclude ( file ( stored_path) ) ) , f) , p) )
784- } else {
785- None
786- }
787- }
788-
789- fn get_stored < ' a > ( repo : & ' a git2:: Repository , tree : & ' a git2:: Tree < ' a > , path : & Path ) -> Filter {
797+ ) -> Filter {
790798 let stored_path = path. with_extension ( "josh" ) ;
791799 let sj_file = file ( stored_path. clone ( ) ) ;
792- compose ( sj_file, get_filter ( repo , tree, & stored_path) )
800+ compose ( sj_file, get_filter ( transaction , tree, & stored_path) )
793801}
794802
795- fn get_filter < ' a > ( repo : & ' a git2:: Repository , tree : & ' a git2:: Tree < ' a > , path : & Path ) -> Filter {
803+ fn get_filter < ' a > (
804+ transaction : & cache:: Transaction ,
805+ tree : & ' a git2:: Tree < ' a > ,
806+ path : & Path ,
807+ ) -> Filter {
796808 let ws_path = normalize_path ( path) ;
797809 let ws_id = ok_or ! ( tree. get_path( & ws_path) , {
798810 return to_filter( Op :: Empty ) ;
799811 } )
800812 . id ( ) ;
801- let ws_blob = tree:: get_blob ( repo, tree, & ws_path) ;
813+ let ws_blob = tree:: get_blob ( transaction . repo ( ) , tree, & ws_path) ;
802814
803815 if let Some ( f) = WORKSPACES . lock ( ) . unwrap ( ) . get ( & ws_id) {
804816 * f
805817 } else {
806818 let f = parse:: parse ( & ws_blob) . unwrap_or_else ( |_| to_filter ( Op :: Empty ) ) ;
819+ let f = legalize_stored ( transaction, f, tree) ;
807820
808821 let f = if invert ( f) . is_ok ( ) {
809822 f
@@ -1060,14 +1073,14 @@ fn apply_to_commit2(
10601073 }
10611074 }
10621075
1063- let commit_filter = get_workspace ( repo , & commit. tree ( ) ?, & ws_path) ;
1076+ let commit_filter = get_workspace ( transaction , & commit. tree ( ) ?, & ws_path) ;
10641077
10651078 let parent_filters = commit
10661079 . parents ( )
10671080 . map ( |parent| {
10681081 rs_tracing:: trace_scoped!( "parent" , "id" : parent. id( ) . to_string( ) ) ;
10691082 let pcw = get_workspace (
1070- repo ,
1083+ transaction ,
10711084 & parent. tree ( ) . unwrap_or_else ( |_| tree:: empty ( repo) ) ,
10721085 & ws_path,
10731086 ) ;
@@ -1078,23 +1091,14 @@ fn apply_to_commit2(
10781091 return per_rev_filter ( transaction, commit, filter, commit_filter, parent_filters) ;
10791092 }
10801093 Op :: Stored ( s_path) => {
1081- if let Some ( ( redirect, _) ) = resolve_stored_redirect ( repo, & commit. tree ( ) ?, s_path) {
1082- if let Some ( r) = apply_to_commit2 ( & to_op ( redirect) , commit, transaction) ? {
1083- transaction. insert ( filter, commit. id ( ) , r, true ) ;
1084- return Ok ( Some ( r) ) ;
1085- } else {
1086- return Ok ( None ) ;
1087- }
1088- }
1089-
1090- let commit_filter = get_stored ( repo, & commit. tree ( ) ?, & s_path) ;
1094+ let commit_filter = get_stored ( transaction, & commit. tree ( ) ?, & s_path) ;
10911095
10921096 let parent_filters = commit
10931097 . parents ( )
10941098 . map ( |parent| {
10951099 rs_tracing:: trace_scoped!( "parent" , "id" : parent. id( ) . to_string( ) ) ;
10961100 let pcs = get_stored (
1097- repo ,
1101+ transaction ,
10981102 & parent. tree ( ) . unwrap_or_else ( |_| tree:: empty ( repo) ) ,
10991103 & s_path,
11001104 ) ;
@@ -1302,8 +1306,8 @@ fn apply2<'a>(transaction: &'a cache::Transaction, op: &Op, x: Apply<'a>) -> Jos
13021306 . with_tree ( tree:: invert_paths ( transaction, "" , x. tree ( ) . clone ( ) ) ?) )
13031307 }
13041308
1305- Op :: Workspace ( path) => apply ( transaction, get_workspace ( repo , & x. tree ( ) , & path) , x) ,
1306- Op :: Stored ( path) => apply ( transaction, get_stored ( repo , & x. tree ( ) , & path) , x) ,
1309+ Op :: Workspace ( path) => apply ( transaction, get_workspace ( transaction , & x. tree ( ) , & path) , x) ,
1310+ Op :: Stored ( path) => apply ( transaction, get_stored ( transaction , & x. tree ( ) , & path) , x) ,
13071311
13081312 Op :: Compose ( filters) => {
13091313 let filtered: Vec < _ > = filters
@@ -1394,12 +1398,9 @@ fn unapply_workspace<'a>(
13941398 match op {
13951399 Op :: Workspace ( path) => {
13961400 let tree = pre_process_tree ( transaction. repo ( ) , tree) ?;
1397- let workspace = get_filter ( transaction. repo ( ) , & tree, Path :: new ( "workspace.josh" ) ) ;
1398- let original_workspace = get_filter (
1399- transaction. repo ( ) ,
1400- & parent_tree,
1401- & path. join ( "workspace.josh" ) ,
1402- ) ;
1401+ let workspace = get_filter ( transaction, & tree, Path :: new ( "workspace.josh" ) ) ;
1402+ let original_workspace =
1403+ get_filter ( transaction, & parent_tree, & path. join ( "workspace.josh" ) ) ;
14031404
14041405 let root = to_filter ( Op :: Subdir ( path. to_owned ( ) ) ) ;
14051406 let wsj_file = to_filter ( Op :: File (
@@ -1428,8 +1429,8 @@ fn unapply_workspace<'a>(
14281429 }
14291430 Op :: Stored ( path) => {
14301431 let stored_path = path. with_extension ( "josh" ) ;
1431- let stored = get_filter ( transaction. repo ( ) , & tree, & stored_path) ;
1432- let original_stored = get_filter ( transaction. repo ( ) , & parent_tree, & stored_path) ;
1432+ let stored = get_filter ( transaction, & tree, & stored_path) ;
1433+ let original_stored = get_filter ( transaction, & parent_tree, & stored_path) ;
14331434
14341435 let sj_file = file ( stored_path. clone ( ) ) ;
14351436 let filter = compose ( sj_file, stored) ;
@@ -1628,6 +1629,58 @@ where
16281629 }
16291630}
16301631
1632+ fn legalize_stored ( t : & cache:: Transaction , f : Filter , tree : & git2:: Tree ) -> Filter {
1633+ legalize_stored2 ( t, f, tree, & mut * LEGALIZED . lock ( ) . unwrap ( ) ) . unwrap_or_else ( |_| empty ( ) )
1634+ }
1635+
1636+ fn legalize_stored2 (
1637+ t : & cache:: Transaction ,
1638+ f : Filter ,
1639+ tree : & git2:: Tree ,
1640+ hm : & mut std:: collections:: HashMap < ( Filter , git2:: Oid ) , Filter > ,
1641+ ) -> JoshResult < Filter > {
1642+ if let Some ( f) = hm. get ( & ( f, tree. id ( ) ) ) {
1643+ return Ok ( * f) ;
1644+ }
1645+
1646+ // Put an entry into the hashtable to prevent infinite recursion.
1647+ // If we get called with the same arguments again before we return,
1648+ // Above check breaks the recursion.
1649+ hm. insert ( ( f, tree. id ( ) ) , empty ( ) ) ;
1650+
1651+ let r = match to_op ( f) {
1652+ Op :: Compose ( f) => {
1653+ let f = f
1654+ . into_iter ( )
1655+ . map ( |f| legalize_stored2 ( t, f, tree, hm) )
1656+ . collect :: < JoshResult < Vec < _ > > > ( ) ?;
1657+ to_filter ( Op :: Compose ( f) )
1658+ }
1659+ Op :: Chain ( a, b) => {
1660+ let first = legalize_stored2 ( t, a, tree, hm) ?;
1661+ let second = legalize_stored2 (
1662+ t,
1663+ b,
1664+ & apply ( t, first, Apply :: from_tree ( tree. clone ( ) ) ) ?. tree ,
1665+ hm,
1666+ ) ?;
1667+ to_filter ( Op :: Chain ( first, second) )
1668+ }
1669+ Op :: Subtract ( a, b) => to_filter ( Op :: Subtract (
1670+ legalize_stored2 ( t, a, tree, hm) ?,
1671+ legalize_stored2 ( t, b, tree, hm) ?,
1672+ ) ) ,
1673+ Op :: Exclude ( f) => to_filter ( Op :: Exclude ( legalize_stored2 ( t, f, tree, hm) ?) ) ,
1674+ Op :: Pin ( f) => to_filter ( Op :: Pin ( legalize_stored2 ( t, f, tree, hm) ?) ) ,
1675+ Op :: Stored ( path) => get_stored ( t, tree, & path) ,
1676+ _ => f,
1677+ } ;
1678+
1679+ hm. insert ( ( f, tree. id ( ) ) , r) ;
1680+
1681+ Ok ( r)
1682+ }
1683+
16311684fn per_rev_filter (
16321685 transaction : & cache:: Transaction ,
16331686 commit : & git2:: Commit ,
0 commit comments