Skip to content

Commit 9a085e9

Browse files
KarthikNayakgitster
authored andcommitted
reflog: implement subcommand to drop reflogs
While 'git-reflog(1)' currently allows users to expire reflogs and delete individual entries, it lacks functionality to completely remove reflogs for specific references. This becomes problematic in repositories where reflogs are not needed but continue to accumulate entries despite setting 'core.logAllRefUpdates=false'. Add a new 'drop' subcommand to git-reflog that allows users to delete the entire reflog for a specified reference. Include a '--all' flag to enable dropping all reflogs in a repository. While here, remove an extraneous newline in the file. Signed-off-by: Karthik Nayak <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a36e024 commit 9a085e9

File tree

3 files changed

+140
-5
lines changed

3 files changed

+140
-5
lines changed

Documentation/git-reflog.adoc

Lines changed: 14 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 | <refs>...]
1920
'git reflog exists' <ref>
2021

2122
DESCRIPTION
@@ -48,15 +49,19 @@ 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.
5556

5657
The "exists" subcommand checks whether a ref has a reflog. It exits
5758
with zero status if the reflog exists, and non-zero status if it does
5859
not.
5960

61+
The "drop" subcommand completely removes the reflog for the specified
62+
references. This is in contrast to "expire" and "delete", both of which
63+
can be used to delete reflog entries, but not the reflog itself.
64+
6065
OPTIONS
6166
-------
6267

@@ -132,6 +137,11 @@ 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.
135145

136146
GIT
137147
---

builtin/reflog.c

Lines changed: 59 additions & 1 deletion
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 | <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
};
@@ -449,10 +458,58 @@ 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;
465+
const struct option options[] = {
466+
OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")),
467+
OPT_END()
468+
};
469+
470+
argc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);
471+
472+
if (argc && do_all)
473+
die(_("references specified along with --all"));
474+
475+
if (do_all) {
476+
struct worktree_reflogs collected = {
477+
.reflogs = STRING_LIST_INIT_DUP,
478+
};
479+
struct string_list_item *item;
480+
struct worktree **worktrees, **p;
481+
482+
worktrees = get_worktrees();
483+
for (p = worktrees; *p; p++) {
484+
collected.worktree = *p;
485+
refs_for_each_reflog(get_worktree_ref_store(*p),
486+
collect_reflog, &collected);
487+
}
488+
free_worktrees(worktrees);
489+
490+
for_each_string_list_item(item, &collected.reflogs)
491+
ret |= refs_delete_reflog(get_main_ref_store(repo),
492+
item->string);
493+
string_list_clear(&collected.reflogs, 0);
494+
}
495+
496+
for (int i = 0; i < argc; i++) {
497+
char *ref;
498+
if (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {
499+
ret |= error(_("%s points nowhere!"), argv[i]);
500+
continue;
501+
}
502+
503+
ret |= refs_delete_reflog(get_main_ref_store(repo), ref);
504+
free(ref);
505+
}
506+
507+
return ret;
508+
}
509+
452510
/*
453511
* main "reflog"
454512
*/
455-
456513
int cmd_reflog(int argc,
457514
const char **argv,
458515
const char *prefix,
@@ -465,6 +522,7 @@ int cmd_reflog(int argc,
465522
OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
466523
OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
467524
OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
525+
OPT_SUBCOMMAND("drop", &fn, cmd_reflog_drop),
468526
OPT_END()
469527
};
470528

t/t1410-reflog.sh

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,4 +551,71 @@ 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: refs/heads/non-existent points nowhere!" 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 --all' '
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+
git reflog drop --all &&
605+
test_must_fail git reflog exists refs/heads/main &&
606+
test_must_fail git reflog exists refs/heads/branch
607+
)
608+
'
609+
610+
test_expect_success 'reflog drop --all with reference' '
611+
test_when_finished "rm -rf repo" &&
612+
git init repo &&
613+
(
614+
cd repo &&
615+
test_commit A &&
616+
test_must_fail git reflog drop --all refs/heads/main 2>stderr &&
617+
test_grep "fatal: references specified along with --all" stderr
618+
)
619+
'
620+
554621
test_done

0 commit comments

Comments
 (0)