@@ -52,6 +52,7 @@ struct collect_reflog_cb {
52
52
53
53
#define INCOMPLETE (1u<<10)
54
54
#define STUDYING (1u<<11)
55
+ #define REACHABLE (1u<<12)
55
56
56
57
static int tree_is_complete (const unsigned char * sha1 )
57
58
{
@@ -209,6 +210,70 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
209
210
return 1 ;
210
211
}
211
212
213
+ static int unreachable (struct expire_reflog_cb * cb , struct commit * commit , unsigned char * sha1 )
214
+ {
215
+ /*
216
+ * We may or may not have the commit yet - if not, look it
217
+ * up using the supplied sha1.
218
+ */
219
+ if (!commit ) {
220
+ if (is_null_sha1 (sha1 ))
221
+ return 0 ;
222
+
223
+ commit = lookup_commit_reference_gently (sha1 , 1 );
224
+
225
+ /* Not a commit -- keep it */
226
+ if (!commit )
227
+ return 0 ;
228
+ }
229
+
230
+ /* Reachable from the current ref? Don't prune. */
231
+ if (commit -> object .flags & REACHABLE )
232
+ return 0 ;
233
+ if (in_merge_bases (commit , & cb -> ref_commit , 1 ))
234
+ return 0 ;
235
+
236
+ /* We can't reach it - prune it. */
237
+ return 1 ;
238
+ }
239
+
240
+ static void mark_reachable (struct commit * commit , unsigned long expire_limit )
241
+ {
242
+ /*
243
+ * We need to compute if commit on either side of an reflog
244
+ * entry is reachable from the tip of the ref for all entries.
245
+ * Mark commits that are reachable from the tip down to the
246
+ * time threashold first; we know a commit marked thusly is
247
+ * reachable from the tip without running in_merge_bases()
248
+ * at all.
249
+ */
250
+ struct commit_list * pending = NULL ;
251
+
252
+ commit_list_insert (commit , & pending );
253
+ while (pending ) {
254
+ struct commit_list * entry = pending ;
255
+ struct commit_list * parent ;
256
+ pending = entry -> next ;
257
+ commit = entry -> item ;
258
+ free (entry );
259
+ if (commit -> object .flags & REACHABLE )
260
+ continue ;
261
+ if (parse_commit (commit ))
262
+ continue ;
263
+ commit -> object .flags |= REACHABLE ;
264
+ if (commit -> date < expire_limit )
265
+ continue ;
266
+ parent = commit -> parents ;
267
+ while (parent ) {
268
+ commit = parent -> item ;
269
+ parent = parent -> next ;
270
+ if (commit -> object .flags & REACHABLE )
271
+ continue ;
272
+ commit_list_insert (commit , & pending );
273
+ }
274
+ }
275
+ }
276
+
212
277
static int expire_reflog_ent (unsigned char * osha1 , unsigned char * nsha1 ,
213
278
const char * email , unsigned long timestamp , int tz ,
214
279
const char * message , void * cb_data )
@@ -230,12 +295,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
230
295
if (timestamp < cb -> cmd -> expire_unreachable ) {
231
296
if (!cb -> ref_commit )
232
297
goto prune ;
233
- if (!old && !is_null_sha1 (osha1 ))
234
- old = lookup_commit_reference_gently (osha1 , 1 );
235
- if (!new && !is_null_sha1 (nsha1 ))
236
- new = lookup_commit_reference_gently (nsha1 , 1 );
237
- if ((old && !in_merge_bases (old , & cb -> ref_commit , 1 )) ||
238
- (new && !in_merge_bases (new , & cb -> ref_commit , 1 )))
298
+ if (unreachable (cb , old , osha1 ) || unreachable (cb , new , nsha1 ))
239
299
goto prune ;
240
300
}
241
301
@@ -288,7 +348,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
288
348
cb .ref_commit = lookup_commit_reference_gently (sha1 , 1 );
289
349
cb .ref = ref ;
290
350
cb .cmd = cmd ;
351
+ if (cb .ref_commit )
352
+ mark_reachable (cb .ref_commit , cmd -> expire_total );
291
353
for_each_reflog_ent (ref , expire_reflog_ent , & cb );
354
+ if (cb .ref_commit )
355
+ clear_commit_marks (cb .ref_commit , REACHABLE );
292
356
finish :
293
357
if (cb .newlog ) {
294
358
if (fclose (cb .newlog )) {
0 commit comments