Merge branch 'jc/maint-reflog-expire-unreachable'

* jc/maint-reflog-expire-unreachable:
  reflog --expire-unreachable: avoid merge-base computation
This commit is contained in:
Junio C Hamano 2010-05-08 22:36:16 -07:00
commit a9eb304129

View file

@ -36,6 +36,8 @@ struct expire_reflog_cb {
FILE *newlog;
const char *ref;
struct commit *ref_commit;
struct commit_list *mark_list;
unsigned long mark_limit;
struct cmd_reflog_expire_cb *cmd;
unsigned char last_kept_sha1[20];
};
@ -210,6 +212,51 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
return 1;
}
/*
* Starting from commits in the cb->mark_list, mark commits that are
* reachable from them. Stop the traversal at commits older than
* the expire_limit and queue them back, so that the caller can call
* us again to restart the traversal with longer expire_limit.
*/
static void mark_reachable(struct expire_reflog_cb *cb)
{
struct commit *commit;
struct commit_list *pending;
unsigned long expire_limit = cb->mark_limit;
struct commit_list *leftover = NULL;
for (pending = cb->mark_list; pending; pending = pending->next)
pending->item->object.flags &= ~REACHABLE;
pending = cb->mark_list;
while (pending) {
struct commit_list *entry = pending;
struct commit_list *parent;
pending = entry->next;
commit = entry->item;
free(entry);
if (commit->object.flags & REACHABLE)
continue;
if (parse_commit(commit))
continue;
commit->object.flags |= REACHABLE;
if (commit->date < expire_limit) {
commit_list_insert(commit, &leftover);
continue;
}
commit->object.flags |= REACHABLE;
parent = commit->parents;
while (parent) {
commit = parent->item;
parent = parent->next;
if (commit->object.flags & REACHABLE)
continue;
commit_list_insert(commit, &pending);
}
}
cb->mark_list = leftover;
}
static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
{
/*
@ -230,48 +277,13 @@ static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsig
/* Reachable from the current ref? Don't prune. */
if (commit->object.flags & REACHABLE)
return 0;
if (in_merge_bases(commit, &cb->ref_commit, 1))
return 0;
/* We can't reach it - prune it. */
return 1;
}
static void mark_reachable(struct commit *commit, unsigned long expire_limit)
{
/*
* We need to compute whether the commit on either side of a reflog
* entry is reachable from the tip of the ref for all entries.
* Mark commits that are reachable from the tip down to the
* time threshold first; we know a commit marked thusly is
* reachable from the tip without running in_merge_bases()
* at all.
*/
struct commit_list *pending = NULL;
commit_list_insert(commit, &pending);
while (pending) {
struct commit_list *entry = pending;
struct commit_list *parent;
pending = entry->next;
commit = entry->item;
free(entry);
if (commit->object.flags & REACHABLE)
continue;
if (parse_commit(commit))
continue;
commit->object.flags |= REACHABLE;
if (commit->date < expire_limit)
continue;
parent = commit->parents;
while (parent) {
commit = parent->item;
parent = parent->next;
if (commit->object.flags & REACHABLE)
continue;
commit_list_insert(commit, &pending);
}
if (cb->mark_list && cb->mark_limit) {
cb->mark_limit = 0; /* dig down to the root */
mark_reachable(cb);
}
return !(commit->object.flags & REACHABLE);
}
static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
@ -348,8 +360,12 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
cb.ref = ref;
cb.cmd = cmd;
if (cb.ref_commit)
mark_reachable(cb.ref_commit, cmd->expire_total);
if (cb.ref_commit) {
cb.mark_list = NULL;
commit_list_insert(cb.ref_commit, &cb.mark_list);
cb.mark_limit = cmd->expire_total;
mark_reachable(&cb);
}
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
if (cb.ref_commit)
clear_commit_marks(cb.ref_commit, REACHABLE);