@@ -3,7 +3,7 @@ use std::collections::BTreeSet;
3
3
use crate :: { parse:: Operation , types:: Mode , MatchGroup , RefSpecRef } ;
4
4
5
5
pub ( crate ) mod types;
6
- pub use types:: { Item , Mapping , Outcome , Source , SourceRef } ;
6
+ pub use types:: { match_lhs , match_rhs , Item , Mapping , Source , SourceRef } ;
7
7
8
8
///
9
9
pub mod validate;
@@ -26,13 +26,20 @@ impl<'a> MatchGroup<'a> {
26
26
}
27
27
28
28
/// Matching
29
- impl < ' a > MatchGroup < ' a > {
29
+ impl < ' spec > MatchGroup < ' spec > {
30
30
/// 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.
32
36
///
33
37
/// Note that negative matches are not part of the return value, so they are not observable but will be used to remove mappings.
34
38
// 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 > {
36
43
let mut out = Vec :: new ( ) ;
37
44
let mut seen = BTreeSet :: default ( ) ;
38
45
let mut push_unique = |mapping| {
@@ -67,16 +74,15 @@ impl<'a> MatchGroup<'a> {
67
74
continue ;
68
75
}
69
76
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
+ } ) ;
80
86
}
81
87
}
82
88
}
@@ -88,12 +94,78 @@ impl<'a> MatchGroup<'a> {
88
94
. zip ( self . specs . iter ( ) )
89
95
. filter_map ( |( m, spec) | m. and_then ( |m| ( spec. mode == Mode :: Negative ) . then_some ( m) ) )
90
96
{
91
- out. retain ( |m| match m. lhs {
97
+ out. retain ( |m| match & m. lhs {
92
98
SourceRef :: ObjectId ( _) => true ,
93
99
SourceRef :: FullName ( name) => {
94
100
!matcher
95
101
. 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 ( ) ,
97
169
target : & null_id,
98
170
object : None ,
99
171
} )
@@ -102,7 +174,7 @@ impl<'a> MatchGroup<'a> {
102
174
} ) ;
103
175
}
104
176
}
105
- Outcome {
177
+ match_rhs :: Outcome {
106
178
group : self ,
107
179
mappings : out,
108
180
}
0 commit comments