@@ -52,6 +52,7 @@ struct collect_reflog_cb {
5252
5353#define INCOMPLETE (1u<<10)
5454#define STUDYING (1u<<11)
55+ #define REACHABLE (1u<<12)
5556
5657static int tree_is_complete (const unsigned char * sha1 )
5758{
@@ -209,6 +210,70 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
209210 return 1 ;
210211}
211212
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+
212277static int expire_reflog_ent (unsigned char * osha1 , unsigned char * nsha1 ,
213278 const char * email , unsigned long timestamp , int tz ,
214279 const char * message , void * cb_data )
@@ -230,12 +295,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
230295 if (timestamp < cb -> cmd -> expire_unreachable ) {
231296 if (!cb -> ref_commit )
232297 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 ))
239299 goto prune ;
240300 }
241301
@@ -288,7 +348,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
288348 cb .ref_commit = lookup_commit_reference_gently (sha1 , 1 );
289349 cb .ref = ref ;
290350 cb .cmd = cmd ;
351+ if (cb .ref_commit )
352+ mark_reachable (cb .ref_commit , cmd -> expire_total );
291353 for_each_reflog_ent (ref , expire_reflog_ent , & cb );
354+ if (cb .ref_commit )
355+ clear_commit_marks (cb .ref_commit , REACHABLE );
292356 finish :
293357 if (cb .newlog ) {
294358 if (fclose (cb .newlog )) {
0 commit comments