@@ -68,6 +68,7 @@ pub(crate) fn mark_complete_and_common_ref(
68
68
graph : & mut gix_negotiate:: Graph < ' _ > ,
69
69
ref_map : & fetch:: RefMap ,
70
70
shallow : & fetch:: Shallow ,
71
+ mapping_is_ignored : impl Fn ( & fetch:: Mapping ) -> bool ,
71
72
) -> Result < Action , Error > {
72
73
if let fetch:: Shallow :: Deepen ( 0 ) = shallow {
73
74
// Avoid deepening (relative) with zero as it seems to upset the server. Git also doesn't actually
@@ -89,6 +90,7 @@ pub(crate) fn mark_complete_and_common_ref(
89
90
let mut cutoff_date = None :: < SecondsSinceUnixEpoch > ;
90
91
let mut num_mappings_with_change = 0 ;
91
92
let mut remote_ref_target_known: Vec < bool > = std:: iter:: repeat ( false ) . take ( ref_map. mappings . len ( ) ) . collect ( ) ;
93
+ let mut remote_ref_included: Vec < bool > = std:: iter:: repeat ( false ) . take ( ref_map. mappings . len ( ) ) . collect ( ) ;
92
94
93
95
for ( mapping_idx, mapping) in ref_map. mappings . iter ( ) . enumerate ( ) {
94
96
let want_id = mapping. remote . as_id ( ) ;
@@ -98,9 +100,13 @@ pub(crate) fn mark_complete_and_common_ref(
98
100
r. target ( ) . try_id ( ) . map ( ToOwned :: to_owned)
99
101
} ) ;
100
102
101
- // Like git, we don't let known unchanged mappings participate in the tree traversal
102
- if want_id. zip ( have_id) . map_or ( true , |( want, have) | want != have) {
103
- num_mappings_with_change += 1 ;
103
+ // Even for ignored mappings we want to know if the `want` is already present locally, so skip nothing else.
104
+ if !mapping_is_ignored ( mapping) {
105
+ remote_ref_included[ mapping_idx] = true ;
106
+ // Like git, we don't let known unchanged mappings participate in the tree traversal
107
+ if want_id. zip ( have_id) . map_or ( true , |( want, have) | want != have) {
108
+ num_mappings_with_change += 1 ;
109
+ }
104
110
}
105
111
106
112
if let Some ( commit) = want_id
@@ -114,11 +120,15 @@ pub(crate) fn mark_complete_and_common_ref(
114
120
}
115
121
}
116
122
117
- // If any kind of shallowing operation is desired, the server may still create a pack for us.
118
123
if matches ! ( shallow, Shallow :: NoChange ) {
119
124
if num_mappings_with_change == 0 {
120
125
return Ok ( Action :: NoChange ) ;
121
- } else if remote_ref_target_known. iter ( ) . all ( |known| * known) {
126
+ } else if remote_ref_target_known
127
+ . iter ( )
128
+ . zip ( remote_ref_included)
129
+ . filter_map ( |( known, included) | included. then_some ( known) )
130
+ . all ( |known| * known)
131
+ {
122
132
return Ok ( Action :: SkipToRefUpdate ) ;
123
133
}
124
134
}
@@ -167,40 +177,50 @@ pub(crate) fn mark_complete_and_common_ref(
167
177
} )
168
178
}
169
179
170
- /// Add all `wants` to `arguments`, which is the unpeeled direct target that the advertised remote ref points to.
171
- pub ( crate ) fn add_wants (
172
- repo : & crate :: Repository ,
173
- arguments : & mut gix_protocol:: fetch:: Arguments ,
174
- ref_map : & fetch:: RefMap ,
175
- mapping_known : & [ bool ] ,
176
- shallow : & fetch:: Shallow ,
180
+ /// Create a predicate that checks if a refspec mapping should be ignored.
181
+ ///
182
+ /// We want to ignore mappings during negotiation if they would be handled implicitly by the server, which is the case
183
+ /// when tags would be sent implicitly due to `Tags::Included`.
184
+ pub ( crate ) fn make_refmapping_ignore_predicate (
177
185
fetch_tags : fetch:: Tags ,
178
- ) {
186
+ ref_map : & fetch:: RefMap ,
187
+ ) -> impl Fn ( & fetch:: Mapping ) -> bool + ' _ {
179
188
// With included tags, we have to keep mappings of tags to handle them later when updating refs, but we don't want to
180
189
// explicitly `want` them as the server will determine by itself which tags are pointing to a commit it wants to send.
181
190
// If we would not exclude implicit tag mappings like this, we would get too much of the graph.
182
191
let tag_refspec_to_ignore = matches ! ( fetch_tags, crate :: remote:: fetch:: Tags :: Included )
183
192
. then ( || fetch_tags. to_refspec ( ) )
184
193
. flatten ( ) ;
194
+ move |mapping| {
195
+ tag_refspec_to_ignore. map_or ( false , |tag_spec| {
196
+ mapping
197
+ . spec_index
198
+ . implicit_index ( )
199
+ . and_then ( |idx| ref_map. extra_refspecs . get ( idx) )
200
+ . map_or ( false , |spec| spec. to_ref ( ) == tag_spec)
201
+ } )
202
+ }
203
+ }
185
204
205
+ /// Add all `wants` to `arguments`, which is the unpeeled direct target that the advertised remote ref points to.
206
+ pub ( crate ) fn add_wants (
207
+ repo : & crate :: Repository ,
208
+ arguments : & mut gix_protocol:: fetch:: Arguments ,
209
+ ref_map : & fetch:: RefMap ,
210
+ mapping_known : & [ bool ] ,
211
+ shallow : & fetch:: Shallow ,
212
+ mapping_is_ignored : impl Fn ( & fetch:: Mapping ) -> bool ,
213
+ ) {
186
214
// When using shallow, we can't exclude `wants` as the remote won't send anything then. Thus we have to resend everything
187
215
// we have as want instead to get exactly the same graph, but possibly deepened.
188
216
let is_shallow = !matches ! ( shallow, fetch:: Shallow :: NoChange ) ;
189
217
let wants = ref_map
190
218
. mappings
191
219
. iter ( )
192
220
. zip ( mapping_known)
193
- . filter_map ( |( m, known) | ( is_shallow || !* known) . then_some ( m) ) ;
221
+ . filter_map ( |( m, known) | ( is_shallow || !* known) . then_some ( m) )
222
+ . filter ( |m| !mapping_is_ignored ( m) ) ;
194
223
for want in wants {
195
- // Here we ignore implicit tag mappings if needed.
196
- if tag_refspec_to_ignore. map_or ( false , |tag_spec| {
197
- want. spec_index
198
- . implicit_index ( )
199
- . and_then ( |idx| ref_map. extra_refspecs . get ( idx) )
200
- . map_or ( false , |spec| spec. to_ref ( ) == tag_spec)
201
- } ) {
202
- continue ;
203
- }
204
224
let id_on_remote = want. remote . as_id ( ) ;
205
225
if !arguments. can_use_ref_in_want ( ) || matches ! ( want. remote, fetch:: Source :: ObjectId ( _) ) {
206
226
if let Some ( id) = id_on_remote {
0 commit comments