Skip to content

Commit 03633a2

Browse files
committed
Merge branch 'kn/reflog-drop'
"git reflog" learns "drop" subcommand, that discards the entire reflog data for a ref. * kn/reflog-drop: reflog: implement subcommand to drop reflogs reflog: improve error for when reflog is not found
2 parents ee847e0 + d127068 commit 03633a2

File tree

3 files changed

+209
-8
lines changed

3 files changed

+209
-8
lines changed

Documentation/git-reflog.adoc

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ SYNOPSIS
1616
[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
1717
'git reflog delete' [--rewrite] [--updateref]
1818
[--dry-run | -n] [--verbose] <ref>@{<specifier>}...
19+
'git reflog drop' [--all [--single-worktree] | <refs>...]
1920
'git reflog exists' <ref>
2021

2122
DESCRIPTION
@@ -48,10 +49,14 @@ and not reachable from the current tip, are removed from the reflog.
4849
This is typically not used directly by end users -- instead, see
4950
linkgit:git-gc[1].
5051

51-
The "delete" subcommand deletes single entries from the reflog. Its
52-
argument must be an _exact_ entry (e.g. "`git reflog delete
53-
master@{2}`"). This subcommand is also typically not used directly by
54-
end users.
52+
The "delete" subcommand deletes single entries from the reflog, but
53+
not the reflog itself. Its argument must be an _exact_ entry (e.g. "`git
54+
reflog delete master@{2}`"). This subcommand is also typically not used
55+
directly by end users.
56+
57+
The "drop" subcommand completely removes the reflog for the specified
58+
references. This is in contrast to "expire" and "delete", both of which
59+
can be used to delete reflog entries, but not the reflog itself.
5560

5661
The "exists" subcommand checks whether a ref has a reflog. It exits
5762
with zero status if the reflog exists, and non-zero status if it does
@@ -132,6 +137,16 @@ Options for `delete`
132137
`--dry-run`, and `--verbose`, with the same meanings as when they are
133138
used with `expire`.
134139

140+
Options for `drop`
141+
~~~~~~~~~~~~~~~~~~~~
142+
143+
--all::
144+
Drop the reflogs of all references from all worktrees.
145+
146+
--single-worktree::
147+
By default when `--all` is specified, reflogs from all working
148+
trees are dropped. This option limits the processing to reflogs
149+
from the current working tree only.
135150

136151
GIT
137152
---

builtin/reflog.c

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
#define BUILTIN_REFLOG_EXISTS_USAGE \
3030
N_("git reflog exists <ref>")
3131

32+
#define BUILTIN_REFLOG_DROP_USAGE \
33+
N_("git reflog drop [--all [--single-worktree] | <refs>...]")
34+
3235
static const char *const reflog_show_usage[] = {
3336
BUILTIN_REFLOG_SHOW_USAGE,
3437
NULL,
@@ -54,11 +57,17 @@ static const char *const reflog_exists_usage[] = {
5457
NULL,
5558
};
5659

60+
static const char *const reflog_drop_usage[] = {
61+
BUILTIN_REFLOG_DROP_USAGE,
62+
NULL,
63+
};
64+
5765
static const char *const reflog_usage[] = {
5866
BUILTIN_REFLOG_SHOW_USAGE,
5967
BUILTIN_REFLOG_LIST_USAGE,
6068
BUILTIN_REFLOG_EXPIRE_USAGE,
6169
BUILTIN_REFLOG_DELETE_USAGE,
70+
BUILTIN_REFLOG_DROP_USAGE,
6271
BUILTIN_REFLOG_EXISTS_USAGE,
6372
NULL
6473
};
@@ -383,7 +392,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
383392
struct expire_reflog_policy_cb cb = { .cmd = cmd };
384393

385394
if (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) {
386-
status |= error(_("%s points nowhere!"), argv[i]);
395+
status |= error(_("reflog could not be found: '%s'"), argv[i]);
387396
continue;
388397
}
389398
set_reflog_expiry_param(&cb.cmd, ref);
@@ -449,10 +458,64 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,
449458
refname);
450459
}
451460

461+
static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,
462+
struct repository *repo)
463+
{
464+
int ret = 0, do_all = 0, single_worktree = 0;
465+
const struct option options[] = {
466+
OPT_BOOL(0, "all", &do_all, N_("drop the reflogs of all references")),
467+
OPT_BOOL(0, "single-worktree", &single_worktree,
468+
N_("drop reflogs from the current worktree only")),
469+
OPT_END()
470+
};
471+
472+
argc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);
473+
474+
if (argc && do_all)
475+
usage(_("references specified along with --all"));
476+
477+
if (do_all) {
478+
struct worktree_reflogs collected = {
479+
.reflogs = STRING_LIST_INIT_DUP,
480+
};
481+
struct string_list_item *item;
482+
struct worktree **worktrees, **p;
483+
484+
worktrees = get_worktrees();
485+
for (p = worktrees; *p; p++) {
486+
if (single_worktree && !(*p)->is_current)
487+
continue;
488+
collected.worktree = *p;
489+
refs_for_each_reflog(get_worktree_ref_store(*p),
490+
collect_reflog, &collected);
491+
}
492+
free_worktrees(worktrees);
493+
494+
for_each_string_list_item(item, &collected.reflogs)
495+
ret |= refs_delete_reflog(get_main_ref_store(repo),
496+
item->string);
497+
string_list_clear(&collected.reflogs, 0);
498+
499+
return ret;
500+
}
501+
502+
for (int i = 0; i < argc; i++) {
503+
char *ref;
504+
if (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {
505+
ret |= error(_("reflog could not be found: '%s'"), argv[i]);
506+
continue;
507+
}
508+
509+
ret |= refs_delete_reflog(get_main_ref_store(repo), ref);
510+
free(ref);
511+
}
512+
513+
return ret;
514+
}
515+
452516
/*
453517
* main "reflog"
454518
*/
455-
456519
int cmd_reflog(int argc,
457520
const char **argv,
458521
const char *prefix,
@@ -465,6 +528,7 @@ int cmd_reflog(int argc,
465528
OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
466529
OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
467530
OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
531+
OPT_SUBCOMMAND("drop", &fn, cmd_reflog_drop),
468532
OPT_END()
469533
};
470534

t/t1410-reflog.sh

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,9 +315,9 @@ test_expect_success 'git reflog expire unknown reference' '
315315
test_config gc.reflogexpireunreachable never &&
316316
317317
test_must_fail git reflog expire main@{123} 2>stderr &&
318-
test_grep "points nowhere" stderr &&
318+
test_grep "error: reflog could not be found: ${SQ}main@{123}${SQ}" stderr &&
319319
test_must_fail git reflog expire does-not-exist 2>stderr &&
320-
test_grep "points nowhere" stderr
320+
test_grep "error: reflog could not be found: ${SQ}does-not-exist${SQ}" stderr
321321
'
322322

323323
test_expect_success 'checkout should not delete log for packed ref' '
@@ -551,4 +551,126 @@ test_expect_success 'reflog with invalid object ID can be listed' '
551551
)
552552
'
553553

554+
test_expect_success 'reflog drop non-existent ref' '
555+
test_when_finished "rm -rf repo" &&
556+
git init repo &&
557+
(
558+
cd repo &&
559+
test_must_fail git reflog exists refs/heads/non-existent &&
560+
test_must_fail git reflog drop refs/heads/non-existent 2>stderr &&
561+
test_grep "error: reflog could not be found: ${SQ}refs/heads/non-existent${SQ}" stderr
562+
)
563+
'
564+
565+
test_expect_success 'reflog drop' '
566+
test_when_finished "rm -rf repo" &&
567+
git init repo &&
568+
(
569+
cd repo &&
570+
test_commit A &&
571+
test_commit_bulk --ref=refs/heads/branch 1 &&
572+
git reflog exists refs/heads/main &&
573+
git reflog exists refs/heads/branch &&
574+
git reflog drop refs/heads/main &&
575+
test_must_fail git reflog exists refs/heads/main &&
576+
git reflog exists refs/heads/branch
577+
)
578+
'
579+
580+
test_expect_success 'reflog drop multiple references' '
581+
test_when_finished "rm -rf repo" &&
582+
git init repo &&
583+
(
584+
cd repo &&
585+
test_commit A &&
586+
test_commit_bulk --ref=refs/heads/branch 1 &&
587+
git reflog exists refs/heads/main &&
588+
git reflog exists refs/heads/branch &&
589+
git reflog drop refs/heads/main refs/heads/branch &&
590+
test_must_fail git reflog exists refs/heads/main &&
591+
test_must_fail git reflog exists refs/heads/branch
592+
)
593+
'
594+
595+
test_expect_success 'reflog drop multiple references some non-existent' '
596+
test_when_finished "rm -rf repo" &&
597+
git init repo &&
598+
(
599+
cd repo &&
600+
test_commit A &&
601+
test_commit_bulk --ref=refs/heads/branch 1 &&
602+
git reflog exists refs/heads/main &&
603+
git reflog exists refs/heads/branch &&
604+
test_must_fail git reflog exists refs/heads/non-existent &&
605+
test_must_fail git reflog drop refs/heads/main refs/heads/non-existent refs/heads/branch 2>stderr &&
606+
test_must_fail git reflog exists refs/heads/main &&
607+
test_must_fail git reflog exists refs/heads/branch &&
608+
test_must_fail git reflog exists refs/heads/non-existent &&
609+
test_grep "error: reflog could not be found: ${SQ}refs/heads/non-existent${SQ}" stderr
610+
)
611+
'
612+
613+
test_expect_success 'reflog drop --all' '
614+
test_when_finished "rm -rf repo" &&
615+
git init repo &&
616+
(
617+
cd repo &&
618+
test_commit A &&
619+
test_commit_bulk --ref=refs/heads/branch 1 &&
620+
git reflog exists refs/heads/main &&
621+
git reflog exists refs/heads/branch &&
622+
git reflog drop --all &&
623+
test_must_fail git reflog exists refs/heads/main &&
624+
test_must_fail git reflog exists refs/heads/branch
625+
)
626+
'
627+
628+
test_expect_success 'reflog drop --all multiple worktrees' '
629+
test_when_finished "rm -rf repo" &&
630+
test_when_finished "rm -rf wt" &&
631+
git init repo &&
632+
(
633+
cd repo &&
634+
test_commit A &&
635+
git worktree add ../wt &&
636+
test_commit_bulk -C ../wt --ref=refs/heads/branch 1 &&
637+
git reflog exists refs/heads/main &&
638+
git reflog exists refs/heads/branch &&
639+
git reflog drop --all &&
640+
test_must_fail git reflog exists refs/heads/main &&
641+
test_must_fail git reflog exists refs/heads/branch
642+
)
643+
'
644+
645+
test_expect_success 'reflog drop --all --single-worktree' '
646+
test_when_finished "rm -rf repo" &&
647+
test_when_finished "rm -rf wt" &&
648+
git init repo &&
649+
(
650+
cd repo &&
651+
test_commit A &&
652+
git worktree add ../wt &&
653+
test_commit -C ../wt foobar &&
654+
git reflog exists refs/heads/main &&
655+
git reflog exists refs/heads/wt &&
656+
test-tool ref-store worktree:wt reflog-exists HEAD &&
657+
git reflog drop --all --single-worktree &&
658+
test_must_fail git reflog exists refs/heads/main &&
659+
test_must_fail git reflog exists refs/heads/wt &&
660+
test_must_fail test-tool ref-store worktree:main reflog-exists HEAD &&
661+
test-tool ref-store worktree:wt reflog-exists HEAD
662+
)
663+
'
664+
665+
test_expect_success 'reflog drop --all with reference' '
666+
test_when_finished "rm -rf repo" &&
667+
git init repo &&
668+
(
669+
cd repo &&
670+
test_commit A &&
671+
test_must_fail git reflog drop --all refs/heads/main 2>stderr &&
672+
test_grep "usage: references specified along with --all" stderr
673+
)
674+
'
675+
554676
test_done

0 commit comments

Comments
 (0)