@@ -297,6 +297,7 @@ enum Op {
297297 Chain ( Filter , Filter ) ,
298298 Subtract ( Filter , Filter ) ,
299299 Exclude ( Filter ) ,
300+ Pin ( Filter ) ,
300301}
301302
302303/// Pretty print the filter on multiple lines with initial indentation level.
@@ -342,6 +343,10 @@ fn pretty2(op: &Op, indent: usize, compose: bool) -> String {
342343 Op :: Compose ( filters) => ff ( & filters, "exclude" , indent) ,
343344 b => format ! ( ":exclude[{}]" , pretty2( & b, indent, false ) ) ,
344345 } ,
346+ Op :: Pin ( filter) => match to_op ( * filter) {
347+ Op :: Compose ( filters) => ff ( & filters, "pin" , indent) ,
348+ b => format ! ( ":pin[{}]" , pretty2( & b, indent, false ) ) ,
349+ } ,
345350 Op :: Chain ( a, b) => match ( to_op ( * a) , to_op ( * b) ) {
346351 ( Op :: Subdir ( p1) , Op :: Prefix ( p2) ) if p1 == p2 => {
347352 format ! ( "::{}/" , parse:: quote_if( & p1. to_string_lossy( ) ) )
@@ -392,7 +397,7 @@ pub fn nesting(filter: Filter) -> usize {
392397fn nesting2 ( op : & Op ) -> usize {
393398 match op {
394399 Op :: Compose ( filters) => 1 + filters. iter ( ) . map ( |f| nesting ( * f) ) . fold ( 0 , |a, b| a. max ( b) ) ,
395- Op :: Exclude ( filter) => 1 + nesting ( * filter) ,
400+ Op :: Exclude ( filter) | Op :: Pin ( filter ) => 1 + nesting ( * filter) ,
396401 Op :: Workspace ( _) => usize:: MAX / 2 , // divide by 2 to make sure there is enough headroom to avoid overflows
397402 Op :: Hook ( _) => usize:: MAX / 2 , // divide by 2 to make sure there is enough headroom to avoid overflows
398403 Op :: Chain ( a, b) => 1 + nesting ( * a) . max ( nesting ( * b) ) ,
@@ -430,7 +435,7 @@ fn lazy_refs2(op: &Op) -> Vec<String> {
430435 acc
431436 } )
432437 }
433- Op :: Exclude ( filter) => lazy_refs ( * filter) ,
438+ Op :: Exclude ( filter) | Op :: Pin ( filter ) => lazy_refs ( * filter) ,
434439 Op :: Chain ( a, b) => {
435440 let mut av = lazy_refs ( * a) ;
436441 av. append ( & mut lazy_refs ( * b) ) ;
@@ -481,6 +486,7 @@ fn resolve_refs2(refs: &std::collections::HashMap<String, git2::Oid>, op: &Op) -
481486 Op :: Compose ( filters. iter ( ) . map ( |f| resolve_refs ( refs, * f) ) . collect ( ) )
482487 }
483488 Op :: Exclude ( filter) => Op :: Exclude ( resolve_refs ( refs, * filter) ) ,
489+ Op :: Pin ( filter) => Op :: Pin ( resolve_refs ( refs, * filter) ) ,
484490 Op :: Chain ( a, b) => Op :: Chain ( resolve_refs ( refs, * a) , resolve_refs ( refs, * b) ) ,
485491 Op :: Subtract ( a, b) => Op :: Subtract ( resolve_refs ( refs, * a) , resolve_refs ( refs, * b) ) ,
486492 Op :: Rev ( filters) => {
@@ -565,6 +571,9 @@ fn spec2(op: &Op) -> String {
565571 Op :: Exclude ( b) => {
566572 format ! ( ":exclude[{}]" , spec( * b) )
567573 }
574+ Op :: Pin ( filter) => {
575+ format ! ( ":pin[{}]" , spec( * filter) )
576+ }
568577 Op :: Rev ( filters) => {
569578 let mut v = filters
570579 . iter ( )
@@ -708,6 +717,9 @@ fn as_tree2(repo: &git2::Repository, op: &Op) -> JoshResult<git2::Oid> {
708717 Op :: Exclude ( b) => {
709718 builder. insert ( "exclude" , as_tree ( repo, * b) ?, git2:: FileMode :: Tree . into ( ) ) ?;
710719 }
720+ Op :: Pin ( b) => {
721+ builder. insert ( "pin" , as_tree ( repo, * b) ?, git2:: FileMode :: Tree . into ( ) ) ?;
722+ }
711723 Op :: Subdir ( path) => {
712724 builder. insert (
713725 "subdir" ,
@@ -1084,6 +1096,11 @@ fn from_tree2(repo: &git2::Repository, tree_oid: git2::Oid) -> JoshResult<Op> {
10841096 let filter = from_tree2 ( repo, exclude_tree. id ( ) ) ?;
10851097 Ok ( Op :: Exclude ( to_filter ( filter) ) )
10861098 }
1099+ "pin" => {
1100+ let pin_tree = repo. find_tree ( entry. id ( ) ) ?;
1101+ let filter = from_tree2 ( repo, pin_tree. id ( ) ) ?;
1102+ Ok ( Op :: Pin ( to_filter ( filter) ) )
1103+ }
10871104 "rev" => {
10881105 let rev_tree = repo. find_tree ( entry. id ( ) ) ?;
10891106 let mut filters = std:: collections:: BTreeMap :: new ( ) ;
@@ -1810,6 +1827,25 @@ fn apply2<'a>(transaction: &'a cache::Transaction, op: &Op, x: Apply<'a>) -> Jos
18101827 return apply ( transaction, * b, apply ( transaction, * a, x. clone ( ) ) ?) ;
18111828 }
18121829 Op :: Hook ( _) => Err ( josh_error ( "not applicable to tree" ) ) ,
1830+
1831+ Op :: Pin ( pin_filter) => {
1832+ let filtered_parent = if let Some ( parent) = x. parents . as_ref ( ) . and_then ( |p| p. first ( ) ) {
1833+ let parent = repo. find_commit ( * parent) ?;
1834+ let filtered = apply ( transaction, * pin_filter, Apply :: from_commit ( & parent) ?) ?;
1835+ filtered. tree . id ( )
1836+ } else {
1837+ tree:: empty_id ( )
1838+ } ;
1839+
1840+ // Mask out all the "pinned" files from current tree
1841+ let exclude = to_filter ( Op :: Exclude ( * pin_filter) ) ;
1842+ let with_mask = apply ( transaction, exclude, x. clone ( ) ) ?;
1843+
1844+ // Overlay filtered parent tree on current one to override versions
1845+ let with_overlay = tree:: overlay ( transaction, with_mask. tree . id ( ) , filtered_parent) ?;
1846+
1847+ Ok ( x. with_tree ( repo. find_tree ( with_overlay) ?) )
1848+ }
18131849 }
18141850}
18151851
0 commit comments