@@ -3,7 +3,7 @@ use std::collections::BTreeSet;
33use crate :: { parse:: Operation , types:: Mode , MatchGroup , RefSpecRef } ;
44
55pub ( crate ) mod types;
6- pub use types:: { Item , Mapping , Outcome , Source , SourceRef } ;
6+ pub use types:: { match_lhs , match_rhs , Item , Mapping , Source , SourceRef } ;
77
88///
99pub mod validate;
@@ -26,13 +26,20 @@ impl<'a> MatchGroup<'a> {
2626}
2727
2828/// Matching
29- impl < ' a > MatchGroup < ' a > {
29+ impl < ' spec > MatchGroup < ' spec > {
3030 /// Match all `items` against all *fetch* specs present in this group, returning deduplicated mappings from source to destination.
31- /// *Note that this method is correct only for specs*, even though it also *works for push-specs*.
31+ /// `items` are expected to be references on the remote, which will be matched and mapped to obtain their local counterparts,
32+ /// i.e. *left side of refspecs is mapped to their right side*.
33+ /// *Note that this method is correct only for fetch-specs*, even though it also *works for push-specs*.
34+ ///
35+ /// Object names are never mapped and always returned as match.
3236 ///
3337 /// Note that negative matches are not part of the return value, so they are not observable but will be used to remove mappings.
3438 // TODO: figure out how to deal with push-specs, probably when push is being implemented.
35- pub fn match_remotes < ' item > ( self , mut items : impl Iterator < Item = Item < ' item > > + Clone ) -> Outcome < ' a , ' item > {
39+ pub fn match_lhs < ' item > (
40+ self ,
41+ mut items : impl Iterator < Item = Item < ' item > > + Clone ,
42+ ) -> match_lhs:: Outcome < ' spec , ' item > {
3643 let mut out = Vec :: new ( ) ;
3744 let mut seen = BTreeSet :: default ( ) ;
3845 let mut push_unique = |mapping| {
@@ -67,16 +74,15 @@ impl<'a> MatchGroup<'a> {
6774 continue ;
6875 }
6976 for ( item_index, item) in items. clone ( ) . enumerate ( ) {
70- if let Some ( matcher) = matcher {
71- let ( matched, rhs) = matcher. matches_lhs ( item) ;
72- if matched {
73- push_unique ( Mapping {
74- item_index : Some ( item_index) ,
75- lhs : SourceRef :: FullName ( item. full_ref_name ) ,
76- rhs,
77- spec_index,
78- } ) ;
79- }
77+ let Some ( matcher) = matcher else { continue } ;
78+ let ( matched, rhs) = matcher. matches_lhs ( item) ;
79+ if matched {
80+ push_unique ( Mapping {
81+ item_index : Some ( item_index) ,
82+ lhs : SourceRef :: FullName ( item. full_ref_name . into ( ) ) ,
83+ rhs,
84+ spec_index,
85+ } ) ;
8086 }
8187 }
8288 }
@@ -88,12 +94,78 @@ impl<'a> MatchGroup<'a> {
8894 . zip ( self . specs . iter ( ) )
8995 . filter_map ( |( m, spec) | m. and_then ( |m| ( spec. mode == Mode :: Negative ) . then_some ( m) ) )
9096 {
91- out. retain ( |m| match m. lhs {
97+ out. retain ( |m| match & m. lhs {
9298 SourceRef :: ObjectId ( _) => true ,
9399 SourceRef :: FullName ( name) => {
94100 !matcher
95101 . matches_lhs ( Item {
96- full_ref_name : name,
102+ full_ref_name : name. as_ref ( ) ,
103+ target : & null_id,
104+ object : None ,
105+ } )
106+ . 0
107+ }
108+ } ) ;
109+ }
110+ }
111+ match_lhs:: Outcome {
112+ group : self ,
113+ mappings : out,
114+ }
115+ }
116+
117+ /// Match all `items` against all *fetch* specs present in this group, returning deduplicated mappings from destination to source.
118+ /// `items` are expected to be tracking references in the local clone, which will be matched and reverse-mapped to obtain their remote counterparts,
119+ /// i.e. *right side of refspecs is mapped to their left side*.
120+ /// *Note that this method is correct only for fetch-specs*, even though it also *works for push-specs*.
121+ ///
122+ /// Note that negative matches are not part of the return value, so they are not observable but will be used to remove mappings.
123+ // Reverse-mapping is implemented here: https://github.com/git/git/blob/76cf4f61c87855ebf0784b88aaf737d6b09f504b/branch.c#L252
124+ pub fn match_rhs < ' item > (
125+ self ,
126+ mut items : impl Iterator < Item = Item < ' item > > + Clone ,
127+ ) -> match_rhs:: Outcome < ' spec , ' item > {
128+ let mut out = Vec :: < Mapping < ' spec , ' item > > :: new ( ) ;
129+ let mut seen = BTreeSet :: default ( ) ;
130+ let mut push_unique = |mapping| {
131+ if seen. insert ( calculate_hash ( & mapping) ) {
132+ out. push ( mapping) ;
133+ }
134+ } ;
135+ let mut matchers: Vec < Matcher < ' _ > > = self . specs . iter ( ) . copied ( ) . map ( Matcher :: from) . collect ( ) ;
136+
137+ let mut has_negation = false ;
138+ for ( spec_index, ( spec, matcher) ) in self . specs . iter ( ) . zip ( matchers. iter_mut ( ) ) . enumerate ( ) {
139+ if spec. mode == Mode :: Negative {
140+ has_negation = true ;
141+ continue ;
142+ }
143+ for ( item_index, item) in items. clone ( ) . enumerate ( ) {
144+ let ( matched, lhs) = matcher. matches_rhs ( item) ;
145+ if let Some ( lhs) = lhs. filter ( |_| matched) {
146+ push_unique ( Mapping {
147+ item_index : Some ( item_index) ,
148+ lhs : SourceRef :: FullName ( lhs) ,
149+ rhs : Some ( item. full_ref_name . into ( ) ) ,
150+ spec_index,
151+ } ) ;
152+ }
153+ }
154+ }
155+
156+ if let Some ( hash_kind) = has_negation. then ( || items. next ( ) . map ( |i| i. target . kind ( ) ) ) . flatten ( ) {
157+ let null_id = hash_kind. null ( ) ;
158+ for matcher in matchers
159+ . into_iter ( )
160+ . zip ( self . specs . iter ( ) )
161+ . filter_map ( |( m, spec) | ( spec. mode == Mode :: Negative ) . then_some ( m) )
162+ {
163+ out. retain ( |m| match & m. lhs {
164+ SourceRef :: ObjectId ( _) => true ,
165+ SourceRef :: FullName ( name) => {
166+ !matcher
167+ . matches_rhs ( Item {
168+ full_ref_name : name. as_ref ( ) ,
97169 target : & null_id,
98170 object : None ,
99171 } )
@@ -102,7 +174,7 @@ impl<'a> MatchGroup<'a> {
102174 } ) ;
103175 }
104176 }
105- Outcome {
177+ match_rhs :: Outcome {
106178 group : self ,
107179 mappings : out,
108180 }
0 commit comments