@@ -57,6 +57,7 @@ impl std::fmt::Debug for Filter {
5757 }
5858}
5959
60+ #[ derive( Debug ) ]
6061pub struct Apply < ' a > {
6162 tree : git2:: Tree < ' a > ,
6263 pub author : Option < ( String , String ) > ,
@@ -297,6 +298,7 @@ enum Op {
297298 Chain ( Filter , Filter ) ,
298299 Subtract ( Filter , Filter ) ,
299300 Exclude ( Filter ) ,
301+ Pin ( Filter ) ,
300302}
301303
302304/// Pretty print the filter on multiple lines with initial indentation level.
@@ -342,6 +344,10 @@ fn pretty2(op: &Op, indent: usize, compose: bool) -> String {
342344 Op :: Compose ( filters) => ff ( & filters, "exclude" , indent) ,
343345 b => format ! ( ":exclude[{}]" , pretty2( & b, indent, false ) ) ,
344346 } ,
347+ Op :: Pin ( filter) => match to_op ( * filter) {
348+ Op :: Compose ( filters) => ff ( & filters, "pin" , indent) ,
349+ b => format ! ( ":pin[{}]" , pretty2( & b, indent, false ) ) ,
350+ } ,
345351 Op :: Chain ( a, b) => match ( to_op ( * a) , to_op ( * b) ) {
346352 ( Op :: Subdir ( p1) , Op :: Prefix ( p2) ) if p1 == p2 => {
347353 format ! ( "::{}/" , parse:: quote_if( & p1. to_string_lossy( ) ) )
@@ -392,7 +398,7 @@ pub fn nesting(filter: Filter) -> usize {
392398fn nesting2 ( op : & Op ) -> usize {
393399 match op {
394400 Op :: Compose ( filters) => 1 + filters. iter ( ) . map ( |f| nesting ( * f) ) . fold ( 0 , |a, b| a. max ( b) ) ,
395- Op :: Exclude ( filter) => 1 + nesting ( * filter) ,
401+ Op :: Exclude ( filter) | Op :: Pin ( filter ) => 1 + nesting ( * filter) ,
396402 Op :: Workspace ( _) => usize:: MAX / 2 , // divide by 2 to make sure there is enough headroom to avoid overflows
397403 Op :: Hook ( _) => usize:: MAX / 2 , // divide by 2 to make sure there is enough headroom to avoid overflows
398404 Op :: Chain ( a, b) => 1 + nesting ( * a) . max ( nesting ( * b) ) ,
@@ -430,7 +436,7 @@ fn lazy_refs2(op: &Op) -> Vec<String> {
430436 acc
431437 } )
432438 }
433- Op :: Exclude ( filter) => lazy_refs ( * filter) ,
439+ Op :: Exclude ( filter) | Op :: Pin ( filter ) => lazy_refs ( * filter) ,
434440 Op :: Chain ( a, b) => {
435441 let mut av = lazy_refs ( * a) ;
436442 av. append ( & mut lazy_refs ( * b) ) ;
@@ -481,6 +487,7 @@ fn resolve_refs2(refs: &std::collections::HashMap<String, git2::Oid>, op: &Op) -
481487 Op :: Compose ( filters. iter ( ) . map ( |f| resolve_refs ( refs, * f) ) . collect ( ) )
482488 }
483489 Op :: Exclude ( filter) => Op :: Exclude ( resolve_refs ( refs, * filter) ) ,
490+ Op :: Pin ( filter) => Op :: Pin ( resolve_refs ( refs, * filter) ) ,
484491 Op :: Chain ( a, b) => Op :: Chain ( resolve_refs ( refs, * a) , resolve_refs ( refs, * b) ) ,
485492 Op :: Subtract ( a, b) => Op :: Subtract ( resolve_refs ( refs, * a) , resolve_refs ( refs, * b) ) ,
486493 Op :: Rev ( filters) => {
@@ -565,6 +572,9 @@ fn spec2(op: &Op) -> String {
565572 Op :: Exclude ( b) => {
566573 format ! ( ":exclude[{}]" , spec( * b) )
567574 }
575+ Op :: Pin ( filter) => {
576+ format ! ( ":pin[{}]" , spec( * filter) )
577+ }
568578 Op :: Rev ( filters) => {
569579 let mut v = filters
570580 . iter ( )
@@ -708,6 +718,9 @@ fn as_tree2(repo: &git2::Repository, op: &Op) -> JoshResult<git2::Oid> {
708718 Op :: Exclude ( b) => {
709719 builder. insert ( "exclude" , as_tree ( repo, * b) ?, git2:: FileMode :: Tree . into ( ) ) ?;
710720 }
721+ Op :: Pin ( b) => {
722+ builder. insert ( "pin" , as_tree ( repo, * b) ?, git2:: FileMode :: Tree . into ( ) ) ?;
723+ }
711724 Op :: Subdir ( path) => {
712725 builder. insert (
713726 "subdir" ,
@@ -1084,6 +1097,11 @@ fn from_tree2(repo: &git2::Repository, tree_oid: git2::Oid) -> JoshResult<Op> {
10841097 let filter = from_tree2 ( repo, exclude_tree. id ( ) ) ?;
10851098 Ok ( Op :: Exclude ( to_filter ( filter) ) )
10861099 }
1100+ "pin" => {
1101+ let pin_tree = repo. find_tree ( entry. id ( ) ) ?;
1102+ let filter = from_tree2 ( repo, pin_tree. id ( ) ) ?;
1103+ Ok ( Op :: Pin ( to_filter ( filter) ) )
1104+ }
10871105 "rev" => {
10881106 let rev_tree = repo. find_tree ( entry. id ( ) ) ?;
10891107 let mut filters = std:: collections:: BTreeMap :: new ( ) ;
@@ -1810,6 +1828,8 @@ fn apply2<'a>(transaction: &'a cache::Transaction, op: &Op, x: Apply<'a>) -> Jos
18101828 return apply ( transaction, * b, apply ( transaction, * a, x. clone ( ) ) ?) ;
18111829 }
18121830 Op :: Hook ( _) => Err ( josh_error ( "not applicable to tree" ) ) ,
1831+
1832+ Op :: Pin ( _) => Ok ( x) ,
18131833 }
18141834}
18151835
@@ -2055,6 +2075,23 @@ pub fn is_linear(filter: Filter) -> bool {
20552075 }
20562076}
20572077
2078+ fn legalize_pin < F > ( f : Filter , c : & F ) -> Filter
2079+ where
2080+ F : Fn ( Filter ) -> Filter ,
2081+ {
2082+ match to_op ( f) {
2083+ Op :: Compose ( f) => {
2084+ let f = f. into_iter ( ) . map ( |f| legalize_pin ( f, c) ) . collect ( ) ;
2085+ to_filter ( Op :: Compose ( f) )
2086+ }
2087+ Op :: Chain ( a, b) => to_filter ( Op :: Chain ( legalize_pin ( a, c) , legalize_pin ( b, c) ) ) ,
2088+ Op :: Subtract ( a, b) => to_filter ( Op :: Subtract ( legalize_pin ( a, c) , legalize_pin ( b, c) ) ) ,
2089+ Op :: Exclude ( f) => to_filter ( Op :: Exclude ( legalize_pin ( f, c) ) ) ,
2090+ Op :: Pin ( f) => c ( f) ,
2091+ _ => f,
2092+ }
2093+ }
2094+
20582095fn per_rev_filter (
20592096 transaction : & cache:: Transaction ,
20602097 commit : & git2:: Commit ,
@@ -2085,13 +2122,48 @@ fn per_rev_filter(
20852122 . map ( |parent| transaction. get ( filter, parent) )
20862123 . collect :: < Option < Vec < git2:: Oid > > > ( ) ;
20872124 let normal_parents = some_or ! ( normal_parents, { return Ok ( None ) } ) ;
2125+
2126+ // Special case: `:pin` filter needs to be aware of filtered history
2127+ let pin_details = if let Some ( & parent) = normal_parents. first ( ) {
2128+ let legalized_a = legalize_pin ( cw, & |f| f) ;
2129+ let legalized_b = legalize_pin ( cw, & |f| to_filter ( Op :: Exclude ( f) ) ) ;
2130+
2131+ if legalized_a != legalized_b {
2132+ let pin_subtract = apply (
2133+ transaction,
2134+ opt:: optimize ( to_filter ( Op :: Subtract ( legalized_a, legalized_b) ) ) ,
2135+ Apply :: from_commit ( commit) ?,
2136+ ) ?;
2137+
2138+ let parent = transaction. repo ( ) . find_commit ( parent) ?;
2139+ let parent = parent. tree ( ) ?;
2140+
2141+ let pin_subtract = pin_subtract. tree ( ) ;
2142+ let pin_overlay = tree:: transpose ( transaction, pin_subtract, & parent) ?;
2143+
2144+ Some ( ( pin_subtract. id ( ) , pin_overlay. id ( ) ) )
2145+ } else {
2146+ None
2147+ }
2148+ } else {
2149+ None
2150+ } ;
2151+
20882152 let filtered_parent_ids: Vec < _ > = normal_parents. into_iter ( ) . chain ( extra_parents) . collect ( ) ;
20892153
2090- let tree_data = apply (
2154+ let mut tree_data = apply (
20912155 transaction,
20922156 commit_filter,
20932157 Apply :: from_commit ( commit) ?. with_parents ( filtered_parent_ids. clone ( ) ) ,
20942158 ) ?;
2159+
2160+ if let Some ( ( pin_subtract, pin_overlay) ) = pin_details {
2161+ let with_exclude = tree:: subtract ( transaction, tree_data. tree ( ) . id ( ) , pin_subtract) ?;
2162+ let with_overlay = tree:: overlay ( transaction, pin_overlay, with_exclude) ?;
2163+
2164+ tree_data = tree_data. with_tree ( transaction. repo ( ) . find_tree ( with_overlay) ?) ;
2165+ }
2166+
20952167 return Some ( history:: create_filtered_commit (
20962168 commit,
20972169 filtered_parent_ids,
0 commit comments