@@ -300,10 +300,13 @@ enum Op {
300300 Pattern ( String ) ,
301301 Message ( String ) ,
302302
303+ HistoryConcat ( LazyRef , Filter ) ,
304+
303305 Compose ( Vec < Filter > ) ,
304306 Chain ( Filter , Filter ) ,
305307 Subtract ( Filter , Filter ) ,
306308 Exclude ( Filter ) ,
309+ Pin ( Filter ) ,
307310}
308311
309312/// Pretty print the filter on multiple lines with initial indentation level.
@@ -349,6 +352,10 @@ fn pretty2(op: &Op, indent: usize, compose: bool) -> String {
349352 Op :: Compose ( filters) => ff ( & filters, "exclude" , indent) ,
350353 b => format ! ( ":exclude[{}]" , pretty2( & b, indent, false ) ) ,
351354 } ,
355+ Op :: Pin ( filter) => match to_op ( * filter) {
356+ Op :: Compose ( filters) => ff ( & filters, "pin" , indent) ,
357+ b => format ! ( ":pin[{}]" , pretty2( & b, indent, false ) ) ,
358+ } ,
352359 Op :: Chain ( a, b) => match ( to_op ( * a) , to_op ( * b) ) {
353360 ( Op :: Subdir ( p1) , Op :: Prefix ( p2) ) if p1 == p2 => {
354361 format ! ( "::{}/" , parse:: quote_if( & p1. to_string_lossy( ) ) )
@@ -407,7 +414,7 @@ fn lazy_refs2(op: &Op) -> Vec<String> {
407414 acc
408415 } )
409416 }
410- Op :: Exclude ( filter) => lazy_refs ( * filter) ,
417+ Op :: Exclude ( filter) | Op :: Pin ( filter ) => lazy_refs ( * filter) ,
411418 Op :: Chain ( a, b) => {
412419 let mut av = lazy_refs ( * a) ;
413420 av. append ( & mut lazy_refs ( * b) ) ;
@@ -419,6 +426,13 @@ fn lazy_refs2(op: &Op) -> Vec<String> {
419426 av
420427 }
421428 Op :: Rev ( filters) => lazy_refs2 ( & Op :: Join ( filters. clone ( ) ) ) ,
429+ Op :: HistoryConcat ( r, _) => {
430+ let mut lr = Vec :: new ( ) ;
431+ if let LazyRef :: Lazy ( s) = r {
432+ lr. push ( s. to_owned ( ) ) ;
433+ }
434+ lr
435+ }
422436 Op :: Join ( filters) => {
423437 let mut lr = lazy_refs2 ( & Op :: Compose ( filters. values ( ) . copied ( ) . collect ( ) ) ) ;
424438 lr. extend ( filters. keys ( ) . filter_map ( |x| {
@@ -458,6 +472,7 @@ fn resolve_refs2(refs: &std::collections::HashMap<String, git2::Oid>, op: &Op) -
458472 Op :: Compose ( filters. iter ( ) . map ( |f| resolve_refs ( refs, * f) ) . collect ( ) )
459473 }
460474 Op :: Exclude ( filter) => Op :: Exclude ( resolve_refs ( refs, * filter) ) ,
475+ Op :: Pin ( filter) => Op :: Pin ( resolve_refs ( refs, * filter) ) ,
461476 Op :: Chain ( a, b) => Op :: Chain ( resolve_refs ( refs, * a) , resolve_refs ( refs, * b) ) ,
462477 Op :: Subtract ( a, b) => Op :: Subtract ( resolve_refs ( refs, * a) , resolve_refs ( refs, * b) ) ,
463478 Op :: Rev ( filters) => {
@@ -478,6 +493,19 @@ fn resolve_refs2(refs: &std::collections::HashMap<String, git2::Oid>, op: &Op) -
478493 . collect ( ) ;
479494 Op :: Rev ( lr)
480495 }
496+ Op :: HistoryConcat ( r, filter) => {
497+ let f = resolve_refs ( refs, * filter) ;
498+ let resolved_ref = if let LazyRef :: Lazy ( s) = r {
499+ if let Some ( res) = refs. get ( s) {
500+ LazyRef :: Resolved ( * res)
501+ } else {
502+ r. clone ( )
503+ }
504+ } else {
505+ r. clone ( )
506+ } ;
507+ Op :: HistoryConcat ( resolved_ref, f)
508+ }
481509 Op :: Join ( filters) => {
482510 let lr = filters
483511 . iter ( )
@@ -545,6 +573,9 @@ fn spec2(op: &Op) -> String {
545573 Op :: Exclude ( b) => {
546574 format ! ( ":exclude[{}]" , spec( * b) )
547575 }
576+ Op :: Pin ( filter) => {
577+ format ! ( ":pin[{}]" , spec( * filter) )
578+ }
548579 Op :: Rev ( filters) => {
549580 let mut v = filters
550581 . iter ( )
@@ -616,6 +647,9 @@ fn spec2(op: &Op) -> String {
616647 Op :: Message ( m) => {
617648 format ! ( ":{}" , parse:: quote( m) )
618649 }
650+ Op :: HistoryConcat ( r, filter) => {
651+ format ! ( ":concat({}{})" , r. to_string( ) , spec( * filter) )
652+ }
619653 Op :: Hook ( hook) => {
620654 format ! ( ":hook={}" , parse:: quote( hook) )
621655 }
@@ -688,6 +722,9 @@ fn as_tree2(repo: &git2::Repository, op: &Op) -> JoshResult<git2::Oid> {
688722 Op :: Exclude ( b) => {
689723 builder. insert ( "exclude" , as_tree ( repo, * b) ?, git2:: FileMode :: Tree . into ( ) ) ?;
690724 }
725+ Op :: Pin ( b) => {
726+ builder. insert ( "pin" , as_tree ( repo, * b) ?, git2:: FileMode :: Tree . into ( ) ) ?;
727+ }
691728 Op :: Subdir ( path) => {
692729 builder. insert (
693730 "subdir" ,
@@ -808,6 +845,13 @@ fn as_tree2(repo: &git2::Repository, op: &Op) -> JoshResult<git2::Oid> {
808845 v. sort ( ) ;
809846 builder. insert ( "rev" , rev_params ( repo, & v) ?, git2:: FileMode :: Tree . into ( ) ) ?;
810847 }
848+ Op :: HistoryConcat ( r, f) => {
849+ builder. insert (
850+ "historyconcat" ,
851+ rev_params ( repo, & vec ! [ ( r. to_string( ) , * f) ] ) ?,
852+ git2:: FileMode :: Tree . into ( ) ,
853+ ) ?;
854+ }
811855 Op :: Join ( filters) => {
812856 let mut v = filters
813857 . iter ( )
@@ -1064,6 +1108,11 @@ fn from_tree2(repo: &git2::Repository, tree_oid: git2::Oid) -> JoshResult<Op> {
10641108 let filter = from_tree2 ( repo, exclude_tree. id ( ) ) ?;
10651109 Ok ( Op :: Exclude ( to_filter ( filter) ) )
10661110 }
1111+ "pin" => {
1112+ let pin_tree = repo. find_tree ( entry. id ( ) ) ?;
1113+ let filter = from_tree2 ( repo, pin_tree. id ( ) ) ?;
1114+ Ok ( Op :: Pin ( to_filter ( filter) ) )
1115+ }
10671116 "rev" => {
10681117 let rev_tree = repo. find_tree ( entry. id ( ) ) ?;
10691118 let mut filters = std:: collections:: BTreeMap :: new ( ) ;
@@ -1608,6 +1657,19 @@ fn apply_to_commit2(
16081657 parent_filters,
16091658 ) ;
16101659 }
1660+ Op :: HistoryConcat ( r, f) => {
1661+ if let LazyRef :: Resolved ( c) = r {
1662+ let a = apply_to_commit2 ( & to_op ( * f) , & repo. find_commit ( * c) ?, transaction) ?;
1663+ let a = some_or ! ( a, { return Ok ( None ) } ) ;
1664+ if commit. id ( ) == a {
1665+ transaction. insert ( filter, commit. id ( ) , * c, true ) ;
1666+ return Ok ( Some ( * c) ) ;
1667+ }
1668+ } else {
1669+ return Err ( josh_error ( "unresolved lazy ref" ) ) ;
1670+ }
1671+ Apply :: from_commit ( commit) ?
1672+ }
16111673 _ => {
16121674 let filtered_parent_ids = commit
16131675 . parent_ids ( )
@@ -1659,7 +1721,7 @@ fn apply2<'a>(transaction: &'a cache::Transaction, op: &Op, x: Apply<'a>) -> Jos
16591721 Op :: Nop => Ok ( x) ,
16601722 Op :: Empty => Ok ( x. with_tree ( tree:: empty ( repo) ) ) ,
16611723 Op :: Fold => Ok ( x) ,
1662- Op :: Squash ( None ) => Ok ( x) ,
1724+ Op :: Squash ( .. ) => Ok ( x) ,
16631725 Op :: Author ( author, email) => Ok ( x. with_author ( ( author. clone ( ) , email. clone ( ) ) ) ) ,
16641726 Op :: Committer ( author, email) => Ok ( x. with_committer ( ( author. clone ( ) , email. clone ( ) ) ) ) ,
16651727 Op :: Message ( m) => Ok ( x. with_message (
@@ -1669,7 +1731,7 @@ fn apply2<'a>(transaction: &'a cache::Transaction, op: &Op, x: Apply<'a>) -> Jos
16691731 & std:: collections:: HashMap :: < String , & dyn strfmt:: DisplayStr > :: new ( ) ,
16701732 ) ?,
16711733 ) ) ,
1672- Op :: Squash ( Some ( _ ) ) => Err ( josh_error ( "not applicable to tree" ) ) ,
1734+ Op :: HistoryConcat ( .. ) => Ok ( x ) ,
16731735 Op :: Linear => Ok ( x) ,
16741736 Op :: Prune => Ok ( x) ,
16751737 Op :: Unsign => Ok ( x) ,
@@ -1784,6 +1846,25 @@ fn apply2<'a>(transaction: &'a cache::Transaction, op: &Op, x: Apply<'a>) -> Jos
17841846 return apply ( transaction, * b, apply ( transaction, * a, x. clone ( ) ) ?) ;
17851847 }
17861848 Op :: Hook ( _) => Err ( josh_error ( "not applicable to tree" ) ) ,
1849+
1850+ Op :: Pin ( pin_filter) => {
1851+ let filtered_parent = if let Some ( parent) = x. parents . as_ref ( ) . and_then ( |p| p. first ( ) ) {
1852+ let parent = repo. find_commit ( * parent) ?;
1853+ let filtered = apply ( transaction, * pin_filter, Apply :: from_commit ( & parent) ?) ?;
1854+ filtered. tree . id ( )
1855+ } else {
1856+ tree:: empty_id ( )
1857+ } ;
1858+
1859+ // Mask out all the "pinned" files from current tree
1860+ let exclude = to_filter ( Op :: Exclude ( * pin_filter) ) ;
1861+ let with_mask = apply ( transaction, exclude, x. clone ( ) ) ?;
1862+
1863+ // Overlay filtered parent tree on current one to override versions
1864+ let with_overlay = tree:: overlay ( transaction, with_mask. tree . id ( ) , filtered_parent) ?;
1865+
1866+ Ok ( x. with_tree ( repo. find_tree ( with_overlay) ?) )
1867+ }
17871868 }
17881869}
17891870
0 commit comments