@@ -1195,6 +1195,101 @@ static struct ref **tail_ref(struct ref **head)
1195
1195
return tail ;
1196
1196
}
1197
1197
1198
+ struct tips {
1199
+ struct commit * * tip ;
1200
+ int nr , alloc ;
1201
+ };
1202
+
1203
+ static void add_to_tips (struct tips * tips , const unsigned char * sha1 )
1204
+ {
1205
+ struct commit * commit ;
1206
+
1207
+ if (is_null_sha1 (sha1 ))
1208
+ return ;
1209
+ commit = lookup_commit_reference_gently (sha1 , 1 );
1210
+ if (!commit || (commit -> object .flags & TMP_MARK ))
1211
+ return ;
1212
+ commit -> object .flags |= TMP_MARK ;
1213
+ ALLOC_GROW (tips -> tip , tips -> nr + 1 , tips -> alloc );
1214
+ tips -> tip [tips -> nr ++ ] = commit ;
1215
+ }
1216
+
1217
+ static void add_missing_tags (struct ref * src , struct ref * * dst , struct ref * * * dst_tail )
1218
+ {
1219
+ struct string_list dst_tag = STRING_LIST_INIT_NODUP ;
1220
+ struct string_list src_tag = STRING_LIST_INIT_NODUP ;
1221
+ struct string_list_item * item ;
1222
+ struct ref * ref ;
1223
+ struct tips sent_tips ;
1224
+
1225
+ /*
1226
+ * Collect everything we know they would have at the end of
1227
+ * this push, and collect all tags they have.
1228
+ */
1229
+ memset (& sent_tips , 0 , sizeof (sent_tips ));
1230
+ for (ref = * dst ; ref ; ref = ref -> next ) {
1231
+ if (ref -> peer_ref &&
1232
+ !is_null_sha1 (ref -> peer_ref -> new_sha1 ))
1233
+ add_to_tips (& sent_tips , ref -> peer_ref -> new_sha1 );
1234
+ else
1235
+ add_to_tips (& sent_tips , ref -> old_sha1 );
1236
+ if (!prefixcmp (ref -> name , "refs/tags/" ))
1237
+ string_list_append (& dst_tag , ref -> name );
1238
+ }
1239
+ clear_commit_marks_many (sent_tips .nr , sent_tips .tip , TMP_MARK );
1240
+
1241
+ sort_string_list (& dst_tag );
1242
+
1243
+ /* Collect tags they do not have. */
1244
+ for (ref = src ; ref ; ref = ref -> next ) {
1245
+ if (prefixcmp (ref -> name , "refs/tags/" ))
1246
+ continue ; /* not a tag */
1247
+ if (string_list_has_string (& dst_tag , ref -> name ))
1248
+ continue ; /* they already have it */
1249
+ if (sha1_object_info (ref -> new_sha1 , NULL ) != OBJ_TAG )
1250
+ continue ; /* be conservative */
1251
+ item = string_list_append (& src_tag , ref -> name );
1252
+ item -> util = ref ;
1253
+ }
1254
+ string_list_clear (& dst_tag , 0 );
1255
+
1256
+ /*
1257
+ * At this point, src_tag lists tags that are missing from
1258
+ * dst, and sent_tips lists the tips we are pushing or those
1259
+ * that we know they already have. An element in the src_tag
1260
+ * that is an ancestor of any of the sent_tips needs to be
1261
+ * sent to the other side.
1262
+ */
1263
+ if (sent_tips .nr ) {
1264
+ for_each_string_list_item (item , & src_tag ) {
1265
+ struct ref * ref = item -> util ;
1266
+ struct ref * dst_ref ;
1267
+ struct commit * commit ;
1268
+
1269
+ if (is_null_sha1 (ref -> new_sha1 ))
1270
+ continue ;
1271
+ commit = lookup_commit_reference_gently (ref -> new_sha1 , 1 );
1272
+ if (!commit )
1273
+ /* not pushing a commit, which is not an error */
1274
+ continue ;
1275
+
1276
+ /*
1277
+ * Is this tag, which they do not have, reachable from
1278
+ * any of the commits we are sending?
1279
+ */
1280
+ if (!in_merge_bases_many (commit , sent_tips .nr , sent_tips .tip ))
1281
+ continue ;
1282
+
1283
+ /* Add it in */
1284
+ dst_ref = make_linked_ref (ref -> name , dst_tail );
1285
+ hashcpy (dst_ref -> new_sha1 , ref -> new_sha1 );
1286
+ dst_ref -> peer_ref = copy_ref (ref );
1287
+ }
1288
+ }
1289
+ string_list_clear (& src_tag , 0 );
1290
+ free (sent_tips .tip );
1291
+ }
1292
+
1198
1293
/*
1199
1294
* Given the set of refs the local repository has, the set of refs the
1200
1295
* remote repository has, and the refspec used for push, determine
@@ -1257,6 +1352,10 @@ int match_push_refs(struct ref *src, struct ref **dst,
1257
1352
free_name :
1258
1353
free (dst_name );
1259
1354
}
1355
+
1356
+ if (flags & MATCH_REFS_FOLLOW_TAGS )
1357
+ add_missing_tags (src , dst , & dst_tail );
1358
+
1260
1359
if (send_prune ) {
1261
1360
/* check for missing refs on the remote */
1262
1361
for (ref = * dst ; ref ; ref = ref -> next ) {
0 commit comments