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>
Nguyễn Thái Ngọc Duy committed
Oct 21, 2018 at 10:08 UTC
c9ef0d95ebf3ca154ef1d8345e9713eddecd9c90
3 files changed
+63
-5
Documentation/git-reflog.txt
+6
-1
@@ -20,7 +20,7 @@ depending on the subcommand:
20
'git reflog' ['show'] [log-options] [<ref>]
21
'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>]
22
[--rewrite] [--updateref] [--stale-fix]
23
- [--dry-run | -n] [--verbose] [--all | <refs>...]
23
+ [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
24
'git reflog delete' [--rewrite] [--updateref]
25
[--dry-run | -n] [--verbose] ref@\{specifier\}...
26
'git reflog exists' <ref>
@@ -72,6 +72,11 @@ Options for `expire`
72
--all::
73
Process the reflogs of all references.
74
75
+--single-worktree::
76
+ By default when `--all` is specified, reflogs from all working
77
+ trees are processed. This option limits the processing to reflogs
78
+ from the current working tree only.
79
+
80
--expire=<time>::
81
Prune entries older than the specified time. If this option is
82
not specified, the expiration time is taken from the
builtin/reflog.c
+42
-4
@@ -10,6 +10,7 @@
10
#include "diff.h"
11
#include "revision.h"
12
#include "reachable.h"
13
+#include "worktree.h"
14
15
/* NEEDSWORK: switch to using parse_options */
16
static const char reflog_expire_usage[] =
@@ -52,6 +53,7 @@ struct collect_reflog_cb {
53
struct collected_reflog **e;
54
int alloc;
55
int nr;
56
+ struct worktree *wt;
57
};
58
59
/* 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,
332
return 0;
333
}
334
335
+static int is_head(const char *refname)
336
+{
337
+ switch (ref_type(refname)) {
338
+ case REF_TYPE_OTHER_PSEUDOREF:
339
+ case REF_TYPE_MAIN_PSEUDOREF:
340
+ if (parse_worktree_ref(refname, NULL, NULL, &refname))
341
+ BUG("not a worktree ref: %s", refname);
342
+ break;
343
+ default:
344
+ break;
345
+ }
346
+ return !strcmp(refname, "HEAD");
347
+}
348
+
349
static void reflog_expiry_prepare(const char *refname,
350
const struct object_id *oid,
351
void *cb_data)
352
{
353
struct expire_reflog_policy_cb *cb = cb_data;
354
339
- if (!cb->cmd.expire_unreachable || !strcmp(refname, "HEAD")) {
355
+ if (!cb->cmd.expire_unreachable || is_head(refname)) {
356
cb->tip_commit = NULL;
357
cb->unreachable_expire_kind = UE_HEAD;
358
} else {
@@ -388,8 +404,19 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus
404
{
405
struct collected_reflog *e;
406
struct collect_reflog_cb *cb = cb_data;
407
+ struct strbuf newref = STRBUF_INIT;
408
+
409
+ /*
410
+ * Avoid collecting the same shared ref multiple times because
411
+ * they are available via all worktrees.
412
+ */
413
+ if (!cb->wt->is_current && ref_type(ref) == REF_TYPE_NORMAL)
414
+ return 0;
415
+
416
+ strbuf_worktree_ref(cb->wt, &newref, ref);
417
+ FLEX_ALLOC_STR(e, reflog, newref.buf);
418
+ strbuf_release(&newref);
419
392
- FLEX_ALLOC_STR(e, reflog, ref);
420
oidcpy(&e->oid, oid);
421
ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
422
cb->e[cb->nr++] = e;
@@ -512,7 +539,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
539
{
540
struct expire_reflog_policy_cb cb;
541
timestamp_t now = time(NULL);
515
- int i, status, do_all;
542
+ int i, status, do_all, all_worktrees = 1;
543
int explicit_expiry = 0;
544
unsigned int flags = 0;
545
@@ -549,6 +576,8 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
576
flags |= EXPIRE_REFLOGS_UPDATE_REF;
577
else if (!strcmp(arg, "--all"))
578
do_all = 1;
579
+ else if (!strcmp(arg, "--single-worktree"))
580
+ all_worktrees = 0;
581
else if (!strcmp(arg, "--verbose"))
582
flags |= EXPIRE_REFLOGS_VERBOSE;
583
else if (!strcmp(arg, "--")) {
@@ -577,10 +606,19 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
606
607
if (do_all) {
608
struct collect_reflog_cb collected;
609
+ struct worktree **worktrees, **p;
610
int i;
611
612
memset(&collected, 0, sizeof(collected));
583
- for_each_reflog(collect_reflog, &collected);
613
+ worktrees = get_worktrees(0);
614
+ for (p = worktrees; *p; p++) {
615
+ if (!all_worktrees && !(*p)->is_current)
616
+ continue;
617
+ collected.wt = *p;
618
+ refs_for_each_reflog(get_worktree_ref_store(*p),
619
+ collect_reflog, &collected);
620
+ }
621
+ free_worktrees(worktrees);
622
for (i = 0; i < collected.nr; i++) {
623
struct collected_reflog *e = collected.e[i];
624
set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);
t/t1410-reflog.sh
+15
@@ -368,4 +368,19 @@ test_expect_success 'continue walking past root commits' '
368
)
369
'
370
371
+test_expect_success 'expire with multiple worktrees' '
372
+ git init main-wt &&
373
+ (
374
+ cd main-wt &&
375
+ test_tick &&
376
+ test_commit foo &&
377
+ git worktree add link-wt &&
378
+ test_tick &&
379
+ test_commit -C link-wt foobar &&
380
+ test_tick &&
381
+ git reflog expire --verbose --all --expire=$test_tick &&
382
+ test_must_be_empty .git/worktrees/link-wt/logs/HEAD
383
+ )
384
+'
385
+
386
test_done