@matheusbsbs / matheus-bagliano / commits / 03cb91b18c
Fork 0

reflog --expire-unreachable: special case entries in "HEAD" reflog

"git reflog expire" (and "git gc") examines the reflog entries and discards old/stale ones using two criteria. The entries that are older than "reflogexpire" (defaults to 90 days) are unconditionally removed, and the entries that are older than "reflogexpireunreachable" (defaults to 30 days) are removed if the entry point at commits that are not reachable from the value of the ref. This is reasonable for local branches, remote tracking branches and tags. You (or other people) may have failed experiments that have been made and then later discarded by resetting the tip of the branch back, and setting the value of "reflogexpireunreachable" shorter than that of "reflogexpire" will prune the entries that describe these failed experiments earlier than the entries that describe the steps that led to the current history. It however doesn't make much sense for "HEAD" reflog. When you switch between branches, it is normal that the tip of the branch you were on is not an ancestor of the branch you have switched to. The moral equivalent of expiring failed experiments in per-branch reflog for "HEAD" reflog is to expire entries that talk about commits that cannot be reached from any ref. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Apr 9, 2010 at 13:20 UTC 03cb91b18cc7f9c19c053c97711f592cebddb5f4
1 file changed +59 -10
builtin-reflog.c
+59 -10
@@ -34,8 +34,11 @@ struct cmd_reflog_expire_cb {
34
35 struct expire_reflog_cb {
36 FILE *newlog;
37 - const char *ref;
38 - struct commit *ref_commit;
37 + enum {
38 + UE_NORMAL,
39 + UE_ALWAYS,
40 + UE_HEAD
41 + } unreachable_expire_kind;
42 struct commit_list *mark_list;
43 unsigned long mark_limit;
44 struct cmd_reflog_expire_cb *cmd;
@@ -305,7 +308,7 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
308 goto prune;
309
310 if (timestamp < cb->cmd->expire_unreachable) {
308 - if (!cb->ref_commit)
311 + if (cb->unreachable_expire_kind == UE_ALWAYS)
312 goto prune;
313 if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
314 goto prune;
@@ -332,12 +335,27 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
335 return 0;
336 }
337
338 +static int push_tip_to_list(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
339 +{
340 + struct commit_list **list = cb_data;
341 + struct commit *tip_commit;
342 + if (flags & REF_ISSYMREF)
343 + return 0;
344 + tip_commit = lookup_commit_reference_gently(sha1, 1);
345 + if (!tip_commit)
346 + return 0;
347 + commit_list_insert(tip_commit, list);
348 + return 0;
349 +}
350 +
351 static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
352 {
353 struct cmd_reflog_expire_cb *cmd = cb_data;
354 struct expire_reflog_cb cb;
355 struct ref_lock *lock;
356 char *log_file, *newlog_path = NULL;
357 + struct commit *tip_commit;
358 + struct commit_list *tips;
359 int status = 0;
360
361 memset(&cb, 0, sizeof(cb));
@@ -357,18 +375,49 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
375 cb.newlog = fopen(newlog_path, "w");
376 }
377
360 - cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
361 - cb.ref = ref;
378 cb.cmd = cmd;
363 - if (cb.ref_commit) {
364 - cb.mark_list = NULL;
365 - commit_list_insert(cb.ref_commit, &cb.mark_list);
379 +
380 + if (!cmd->expire_unreachable || !strcmp(ref, "HEAD")) {
381 + tip_commit = NULL;
382 + cb.unreachable_expire_kind = UE_HEAD;
383 + } else {
384 + tip_commit = lookup_commit_reference_gently(sha1, 1);
385 + if (!tip_commit)
386 + cb.unreachable_expire_kind = UE_ALWAYS;
387 + else
388 + cb.unreachable_expire_kind = UE_NORMAL;
389 + }
390 +
391 + if (cmd->expire_unreachable <= cmd->expire_total)
392 + cb.unreachable_expire_kind = UE_ALWAYS;
393 +
394 + cb.mark_list = NULL;
395 + tips = NULL;
396 + if (cb.unreachable_expire_kind != UE_ALWAYS) {
397 + if (cb.unreachable_expire_kind == UE_HEAD) {
398 + struct commit_list *elem;
399 + for_each_ref(push_tip_to_list, &tips);
400 + for (elem = tips; elem; elem = elem->next)
401 + commit_list_insert(elem->item, &cb.mark_list);
402 + } else {
403 + commit_list_insert(tip_commit, &cb.mark_list);
404 + }
405 cb.mark_limit = cmd->expire_total;
406 mark_reachable(&cb);
407 }
408 +
409 for_each_reflog_ent(ref, expire_reflog_ent, &cb);
370 - if (cb.ref_commit)
371 - clear_commit_marks(cb.ref_commit, REACHABLE);
410 +
411 + if (cb.unreachable_expire_kind != UE_ALWAYS) {
412 + if (cb.unreachable_expire_kind == UE_HEAD) {
413 + struct commit_list *elem;
414 + for (elem = tips; elem; elem = elem->next)
415 + clear_commit_marks(tip_commit, REACHABLE);
416 + free_commit_list(tips);
417 + } else {
418 + clear_commit_marks(tip_commit, REACHABLE);
419 + }
420 + }
421 finish:
422 if (cb.newlog) {
423 if (fclose(cb.newlog)) {