55mod parse;
66pub mod tree;
77
8+ pub use opt:: invert;
89pub use parse:: get_comments;
910pub use parse:: parse;
1011
@@ -241,6 +242,17 @@ pub fn apply_to_commit(
241242 }
242243}
243244
245+ fn get_workspace < ' a > ( repo : & ' a git2:: Repository , tree : & ' a git2:: Tree < ' a > , path : & Path ) -> Filter {
246+ let f = parse:: parse ( & tree:: get_blob ( repo, & tree, & path. join ( "workspace.josh" ) ) )
247+ . unwrap_or ( to_filter ( Op :: Empty ) ) ;
248+
249+ if invert ( f) . is_ok ( ) {
250+ f
251+ } else {
252+ to_filter ( Op :: Empty )
253+ }
254+ }
255+
244256pub fn apply_to_commit3 (
245257 filter : Filter ,
246258 commit : & git2:: Commit ,
@@ -328,23 +340,15 @@ fn apply_to_commit2(
328340
329341 let normal_parents = some_or ! ( normal_parents, { return Ok ( None ) } ) ;
330342
331- let cw = parse:: parse ( & tree:: get_blob (
332- repo,
333- & commit. tree ( ) ?,
334- & ws_path. join ( "workspace.josh" ) ,
335- ) )
336- . unwrap_or ( to_filter ( Op :: Empty ) ) ;
343+ let cw = get_workspace ( repo, & commit. tree ( ) ?, & ws_path) ;
337344
338345 let extra_parents = commit
339346 . parents ( )
340347 . map ( |parent| {
341348 rs_tracing:: trace_scoped!( "parent" , "id" : parent. id( ) . to_string( ) ) ;
342- let pcw = parse:: parse ( & tree:: get_blob (
343- repo,
344- & parent. tree ( ) . unwrap_or ( tree:: empty ( repo) ) ,
345- & ws_path. join ( "workspace.josh" ) ,
346- ) )
347- . unwrap_or ( to_filter ( Op :: Empty ) ) ;
349+
350+ let pcw =
351+ get_workspace ( repo, & parent. tree ( ) . unwrap_or ( tree:: empty ( repo) ) , & ws_path) ;
348352
349353 apply_to_commit2 (
350354 & to_op ( opt:: optimize ( to_filter ( Op :: Subtract ( cw, pcw) ) ) ) ,
@@ -415,7 +419,7 @@ fn apply_to_commit2(
415419 . unwrap_or ( tree:: empty_id ( ) )
416420 } ;
417421 let bf = repo. find_tree ( bf) ?;
418- let bu = unapply ( transaction, * b , bf, tree :: empty ( repo ) ) ?;
422+ let bu = apply ( transaction, opt :: invert ( * b ) ? , bf) ?;
419423 let ba = apply ( transaction, * a, bu) ?. id ( ) ;
420424 repo. find_tree ( tree:: subtract ( transaction, af, ba) ?) ?
421425 }
@@ -515,7 +519,7 @@ fn apply2<'a>(
515519 Op :: Subtract ( a, b) => {
516520 let af = apply ( transaction, * a, tree. clone ( ) ) ?;
517521 let bf = apply ( transaction, * b, tree. clone ( ) ) ?;
518- let bu = unapply ( transaction, * b , bf, tree :: empty ( repo ) ) ?;
522+ let bu = apply ( transaction, opt :: invert ( * b ) ? , bf) ?;
519523 let ba = apply ( transaction, * a, bu) ?. id ( ) ;
520524 Ok ( repo. find_tree ( tree:: subtract ( transaction, af. id ( ) , ba) ?) ?)
521525 }
@@ -532,12 +536,11 @@ fn apply2<'a>(
532536
533537 Op :: Workspace ( path) => {
534538 let base = to_filter ( Op :: Subdir ( path. to_owned ( ) ) ) ;
535- if let Ok ( cw) = parse:: parse ( & tree:: get_blob ( repo, & tree, & path. join ( "workspace.josh" ) ) )
536- {
537- apply ( transaction, compose ( base, cw) , tree)
538- } else {
539- apply ( transaction, base, tree)
540- }
539+ apply (
540+ transaction,
541+ compose ( get_workspace ( repo, & tree, & path) , base) ,
542+ tree,
543+ )
541544 }
542545
543546 Op :: Compose ( filters) => {
@@ -563,176 +566,104 @@ pub fn unapply<'a>(
563566 tree : git2:: Tree < ' a > ,
564567 parent_tree : git2:: Tree < ' a > ,
565568) -> JoshResult < git2:: Tree < ' a > > {
566- unapply2 ( transaction, & to_op ( filter) , tree, parent_tree)
569+ if let Ok ( inverted) = opt:: invert ( filter) {
570+ let matching = apply ( transaction, chain ( filter, inverted) , parent_tree. clone ( ) ) ?;
571+ let stripped = tree:: subtract ( transaction, parent_tree. id ( ) , matching. id ( ) ) ?;
572+ let new_tree = apply ( transaction, inverted, tree) ?;
573+
574+ return Ok ( transaction. repo ( ) . find_tree ( tree:: overlay (
575+ transaction. repo ( ) ,
576+ new_tree. id ( ) ,
577+ stripped,
578+ ) ?) ?) ;
579+ }
580+
581+ if let Some ( ws) = unapply_workspace (
582+ transaction,
583+ & to_op ( filter) ,
584+ tree. clone ( ) ,
585+ parent_tree. clone ( ) ,
586+ ) ? {
587+ return Ok ( ws) ;
588+ }
589+
590+ if let Op :: Chain ( a, b) = to_op ( filter) {
591+ let p = apply ( transaction, a, parent_tree. clone ( ) ) ?;
592+ return unapply (
593+ transaction,
594+ a,
595+ unapply ( transaction, b, tree, p) ?,
596+ parent_tree,
597+ ) ;
598+ }
599+
600+ return Err ( josh_error ( "filter cannot be unapplied" ) ) ;
567601}
568602
569- fn unapply2 < ' a > (
603+ fn unapply_workspace < ' a > (
570604 transaction : & ' a cache:: Transaction ,
571605 op : & Op ,
572606 tree : git2:: Tree < ' a > ,
573607 parent_tree : git2:: Tree < ' a > ,
574- ) -> JoshResult < git2:: Tree < ' a > > {
608+ ) -> JoshResult < Option < git2:: Tree < ' a > > > {
575609 return match op {
576- Op :: Nop => Ok ( tree) ,
577- Op :: Linear => Ok ( tree) ,
578- Op :: Empty => Ok ( parent_tree) ,
579-
580- Op :: Chain ( a, b) => {
581- let p = apply ( transaction, * a, parent_tree. clone ( ) ) ?;
582- let x = unapply ( transaction, * b, tree, p) ?;
583- unapply ( transaction, * a, x, parent_tree)
584- }
585610 Op :: Workspace ( path) => {
586- let root = to_filter ( Op :: Subdir ( path. to_owned ( ) ) ) ;
587- let mapped = & tree:: get_blob ( transaction. repo ( ) , & tree, Path :: new ( "workspace.josh" ) ) ;
588- let parsed = parse ( mapped) ?;
589-
590- let mut blob = String :: new ( ) ;
591- if let Ok ( c) = get_comments ( mapped) {
592- if !c. is_empty ( ) {
593- blob = c;
594- }
595- }
596- let blob = & format ! ( "{}{}\n " , & blob, pretty( parsed, 0 ) ) ;
611+ let tree = pre_process_tree ( transaction. repo ( ) , tree) ?;
612+ let workspace = get_workspace ( transaction. repo ( ) , & tree, & Path :: new ( "" ) ) ;
613+ let original_workspace = get_workspace ( transaction. repo ( ) , & parent_tree, & path) ;
597614
598- // Remove workspace.josh from the tree to prevent it from being parsed again
599- // further down the callstack leading to endless recursion.
600- let tree = tree:: insert (
601- transaction. repo ( ) ,
602- & tree,
603- Path :: new ( "workspace.josh" ) ,
604- git2:: Oid :: zero ( ) ,
605- 0o0100644 ,
606- ) ?;
607-
608- // Insert a dummy file to prevent the directory from dissappearing through becoming
609- // empty.
610- let tree = tree:: insert (
611- transaction. repo ( ) ,
612- & tree,
613- Path :: new ( "DUMMY-df97a89d-b11f-4e1c-8400-345f895f0d40" ) ,
614- transaction. repo ( ) . blob ( "" . as_bytes ( ) ) ?,
615- 0o0100644 ,
616- ) ?;
617-
618- let r = unapply (
615+ let root = to_filter ( Op :: Subdir ( path. to_owned ( ) ) ) ;
616+ let filter = compose ( workspace, root) ;
617+ let original_filter = compose ( original_workspace, root) ;
618+ let matching = apply (
619619 transaction,
620- compose ( root, parsed) ,
621- tree. clone ( ) ,
622- parent_tree,
620+ chain ( original_filter, opt:: invert ( original_filter) ?) ,
621+ parent_tree. clone ( ) ,
623622 ) ?;
623+ let stripped = tree:: subtract ( transaction, parent_tree. id ( ) , matching. id ( ) ) ?;
624+ let new_tree = apply ( transaction, opt:: invert ( filter) ?, tree) ?;
624625
625- // Remove the dummy file inserted above
626- let r = tree:: insert (
626+ let result = transaction. repo ( ) . find_tree ( tree:: overlay (
627627 transaction. repo ( ) ,
628- & r,
629- & path. join ( "DUMMY-df97a89d-b11f-4e1c-8400-345f895f0d40" ) ,
630- git2:: Oid :: zero ( ) ,
631- 0o0100644 ,
632- ) ?;
633-
634- // Put the workspace.josh file back to it's target location.
635- let r = if !mapped. is_empty ( ) {
636- tree:: insert (
637- transaction. repo ( ) ,
638- & r,
639- & path. join ( "workspace.josh" ) ,
640- transaction. repo ( ) . blob ( blob. as_bytes ( ) ) ?,
641- 0o0100644 , // Should this handle filemode?
642- ) ?
643- } else {
644- r
645- } ;
628+ new_tree. id ( ) ,
629+ stripped,
630+ ) ?) ?;
646631
647- return Ok ( r ) ;
632+ return Ok ( Some ( result ) ) ;
648633 }
649- Op :: Compose ( filters) => {
650- let mut remaining = tree. clone ( ) ;
651- let mut result = parent_tree. clone ( ) ;
652-
653- for other in filters. iter ( ) . rev ( ) {
654- let from_empty = unapply (
655- transaction,
656- * other,
657- remaining. clone ( ) ,
658- tree:: empty ( transaction. repo ( ) ) ,
659- ) ?;
660- if tree:: empty_id ( ) == from_empty. id ( ) {
661- continue ;
662- }
663- result = unapply ( transaction, * other, remaining. clone ( ) , result) ?;
664- let reapply = apply ( transaction, * other, from_empty. clone ( ) ) ?;
665-
666- remaining = transaction. repo ( ) . find_tree ( tree:: subtract (
667- transaction,
668- remaining. id ( ) ,
669- reapply. id ( ) ,
670- ) ?) ?;
671- }
634+ _ => Ok ( None ) ,
635+ } ;
636+ }
672637
673- return Ok ( result) ;
674- }
638+ fn pre_process_tree < ' a > (
639+ repo : & ' a git2:: Repository ,
640+ tree : git2:: Tree < ' a > ,
641+ ) -> JoshResult < git2:: Tree < ' a > > {
642+ let path = std:: path:: Path :: new ( "workspace.josh" ) ;
643+ let ws_file = filter:: tree:: get_blob ( repo, & tree, & path) ;
644+ let parsed = filter:: parse ( & ws_file) ?;
675645
676- Op :: File ( path) => {
677- let ( file, mode) = tree
678- . get_path ( path)
679- . map ( |x| ( x. id ( ) , x. filemode ( ) ) )
680- . unwrap_or ( ( git2:: Oid :: zero ( ) , 0o0100644 ) ) ;
681- if let Ok ( _) = transaction. repo ( ) . find_blob ( file) {
682- tree:: insert ( transaction. repo ( ) , & parent_tree, path, file, mode)
683- } else {
684- tree:: insert (
685- transaction. repo ( ) ,
686- & parent_tree,
687- path,
688- git2:: Oid :: zero ( ) ,
689- 0o0100644 ,
690- )
691- }
692- }
646+ if !invert ( parsed) . is_ok ( ) {
647+ return Err ( josh_error ( "Invalid workspace: not reversible" ) ) ;
648+ }
693649
694- Op :: Subtract ( _, _) => return Err ( josh_error ( "filter not reversible" ) ) ,
695- Op :: Exclude ( b) => {
696- let subtracted = tree:: subtract (
697- transaction,
698- tree. id ( ) ,
699- unapply ( transaction, * b, tree, tree:: empty ( transaction. repo ( ) ) ) ?. id ( ) ,
700- ) ?;
701- Ok ( transaction. repo ( ) . find_tree ( tree:: overlay (
702- transaction. repo ( ) ,
703- parent_tree. id ( ) ,
704- subtracted,
705- ) ?) ?)
650+ let mut blob = String :: new ( ) ;
651+ if let Ok ( c) = filter:: get_comments ( & ws_file) {
652+ if !c. is_empty ( ) {
653+ blob = c;
706654 }
707- Op :: Glob ( pattern) => {
708- let pattern = glob:: Pattern :: new ( pattern) ?;
709- let options = glob:: MatchOptions {
710- case_sensitive : true ,
711- require_literal_separator : true ,
712- require_literal_leading_dot : true ,
713- } ;
714- let subtracted = tree:: remove_pred (
715- transaction,
716- "" ,
717- tree. id ( ) ,
718- & |path, isblob| isblob && ( pattern. matches_path_with ( path, options) ) ,
719- to_filter ( op. clone ( ) ) . id ( ) ,
720- ) ?;
721- Ok ( transaction. repo ( ) . find_tree ( tree:: overlay (
722- transaction. repo ( ) ,
723- parent_tree. id ( ) ,
724- subtracted. id ( ) ,
725- ) ?) ?)
726- }
727- Op :: Prefix ( path) => Ok ( tree
728- . get_path ( path)
729- . and_then ( |x| transaction. repo ( ) . find_tree ( x. id ( ) ) )
730- . unwrap_or ( tree:: empty ( transaction. repo ( ) ) ) ) ,
731- Op :: Subdir ( path) => {
732- tree:: insert ( transaction. repo ( ) , & parent_tree, path, tree. id ( ) , 0o0040000 )
733- }
734- _ => return Err ( josh_error ( "filter not reversible" ) ) ,
735- } ;
655+ }
656+ let blob = & format ! ( "{}{}\n " , & blob, filter:: pretty( parsed, 0 ) ) ;
657+
658+ let tree = filter:: tree:: insert (
659+ repo,
660+ & tree,
661+ & path,
662+ repo. blob ( blob. as_bytes ( ) ) ?,
663+ 0o0100644 , // Should this handle filemode?
664+ ) ?;
665+
666+ Ok ( tree)
736667}
737668
738669/// Create a filter that is the result of feeding the output of `first` into `second`
0 commit comments