reflog expire: cover reflog from all worktrees

Reported-by: Jeff King <peff@peff.net>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Nguyễn Thái Ngọc Duy
2018-10-21 10:08:59 +02:00
committed by Junio C Hamano
parent b29759d89a
commit c9ef0d95eb
3 changed files with 63 additions and 5 deletions

View File

@ -20,7 +20,7 @@ depending on the subcommand:
'git reflog' ['show'] [log-options] [<ref>] 'git reflog' ['show'] [log-options] [<ref>]
'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>] 'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]
[--rewrite] [--updateref] [--stale-fix] [--rewrite] [--updateref] [--stale-fix]
[--dry-run | -n] [--verbose] [--all | <refs>...] [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
'git reflog delete' [--rewrite] [--updateref] 'git reflog delete' [--rewrite] [--updateref]
[--dry-run | -n] [--verbose] ref@\{specifier\}... [--dry-run | -n] [--verbose] ref@\{specifier\}...
'git reflog exists' <ref> 'git reflog exists' <ref>
@ -72,6 +72,11 @@ Options for `expire`
--all:: --all::
Process the reflogs of all references. Process the reflogs of all references.
--single-worktree::
By default when `--all` is specified, reflogs from all working
trees are processed. This option limits the processing to reflogs
from the current working tree only.
--expire=<time>:: --expire=<time>::
Prune entries older than the specified time. If this option is Prune entries older than the specified time. If this option is
not specified, the expiration time is taken from the not specified, the expiration time is taken from the

View File

@ -10,6 +10,7 @@
#include "diff.h" #include "diff.h"
#include "revision.h" #include "revision.h"
#include "reachable.h" #include "reachable.h"
#include "worktree.h"
/* NEEDSWORK: switch to using parse_options */ /* NEEDSWORK: switch to using parse_options */
static const char reflog_expire_usage[] = static const char reflog_expire_usage[] =
@ -52,6 +53,7 @@ struct collect_reflog_cb {
struct collected_reflog **e; struct collected_reflog **e;
int alloc; int alloc;
int nr; int nr;
struct worktree *wt;
}; };
/* Remember to update object flag allocation in object.h */ /* Remember to update object flag allocation in object.h */
@ -330,13 +332,27 @@ static int push_tip_to_list(const char *refname, const struct object_id *oid,
return 0; return 0;
} }
static int is_head(const char *refname)
{
switch (ref_type(refname)) {
case REF_TYPE_OTHER_PSEUDOREF:
case REF_TYPE_MAIN_PSEUDOREF:
if (parse_worktree_ref(refname, NULL, NULL, &refname))
BUG("not a worktree ref: %s", refname);
break;
default:
break;
}
return !strcmp(refname, "HEAD");
}
static void reflog_expiry_prepare(const char *refname, static void reflog_expiry_prepare(const char *refname,
const struct object_id *oid, const struct object_id *oid,
void *cb_data) void *cb_data)
{ {
struct expire_reflog_policy_cb *cb = cb_data; struct expire_reflog_policy_cb *cb = cb_data;
if (!cb->cmd.expire_unreachable || !strcmp(refname, "HEAD")) { if (!cb->cmd.expire_unreachable || is_head(refname)) {
cb->tip_commit = NULL; cb->tip_commit = NULL;
cb->unreachable_expire_kind = UE_HEAD; cb->unreachable_expire_kind = UE_HEAD;
} else { } else {
@ -388,8 +404,19 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus
{ {
struct collected_reflog *e; struct collected_reflog *e;
struct collect_reflog_cb *cb = cb_data; struct collect_reflog_cb *cb = cb_data;
struct strbuf newref = STRBUF_INIT;
/*
* Avoid collecting the same shared ref multiple times because
* they are available via all worktrees.
*/
if (!cb->wt->is_current && ref_type(ref) == REF_TYPE_NORMAL)
return 0;
strbuf_worktree_ref(cb->wt, &newref, ref);
FLEX_ALLOC_STR(e, reflog, newref.buf);
strbuf_release(&newref);
FLEX_ALLOC_STR(e, reflog, ref);
oidcpy(&e->oid, oid); oidcpy(&e->oid, oid);
ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc); ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
cb->e[cb->nr++] = e; cb->e[cb->nr++] = e;
@ -512,7 +539,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
{ {
struct expire_reflog_policy_cb cb; struct expire_reflog_policy_cb cb;
timestamp_t now = time(NULL); timestamp_t now = time(NULL);
int i, status, do_all; int i, status, do_all, all_worktrees = 1;
int explicit_expiry = 0; int explicit_expiry = 0;
unsigned int flags = 0; unsigned int flags = 0;
@ -549,6 +576,8 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
flags |= EXPIRE_REFLOGS_UPDATE_REF; flags |= EXPIRE_REFLOGS_UPDATE_REF;
else if (!strcmp(arg, "--all")) else if (!strcmp(arg, "--all"))
do_all = 1; do_all = 1;
else if (!strcmp(arg, "--single-worktree"))
all_worktrees = 0;
else if (!strcmp(arg, "--verbose")) else if (!strcmp(arg, "--verbose"))
flags |= EXPIRE_REFLOGS_VERBOSE; flags |= EXPIRE_REFLOGS_VERBOSE;
else if (!strcmp(arg, "--")) { else if (!strcmp(arg, "--")) {
@ -577,10 +606,19 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
if (do_all) { if (do_all) {
struct collect_reflog_cb collected; struct collect_reflog_cb collected;
struct worktree **worktrees, **p;
int i; int i;
memset(&collected, 0, sizeof(collected)); memset(&collected, 0, sizeof(collected));
for_each_reflog(collect_reflog, &collected); worktrees = get_worktrees(0);
for (p = worktrees; *p; p++) {
if (!all_worktrees && !(*p)->is_current)
continue;
collected.wt = *p;
refs_for_each_reflog(get_worktree_ref_store(*p),
collect_reflog, &collected);
}
free_worktrees(worktrees);
for (i = 0; i < collected.nr; i++) { for (i = 0; i < collected.nr; i++) {
struct collected_reflog *e = collected.e[i]; struct collected_reflog *e = collected.e[i];
set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog); set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);

View File

@ -368,4 +368,19 @@ test_expect_success 'continue walking past root commits' '
) )
' '
test_expect_success 'expire with multiple worktrees' '
git init main-wt &&
(
cd main-wt &&
test_tick &&
test_commit foo &&
git worktree add link-wt &&
test_tick &&
test_commit -C link-wt foobar &&
test_tick &&
git reflog expire --verbose --all --expire=$test_tick &&
test_must_be_empty .git/worktrees/link-wt/logs/HEAD
)
'
test_done test_done