@@ -16,6 +16,7 @@ SYNOPSIS
[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]
'git reflog delete' [--rewrite] [--updateref]
[--dry-run | -n] [--verbose] <ref>@{<specifier>}...
+'git reflog drop' [--all | <refs>...]
'git reflog exists' <ref>
DESCRIPTION
@@ -48,15 +49,19 @@ and not reachable from the current tip, are removed from the reflog.
This is typically not used directly by end users -- instead, see
linkgit:git-gc[1].
-The "delete" subcommand deletes single entries from the reflog. Its
-argument must be an _exact_ entry (e.g. "`git reflog delete
-master@{2}`"). This subcommand is also typically not used directly by
-end users.
+The "delete" subcommand deletes single entries from the reflog, but
+not the reflog itself. Its argument must be an _exact_ entry (e.g. "`git
+reflog delete master@{2}`"). This subcommand is also typically not used
+directly by end users.
The "exists" subcommand checks whether a ref has a reflog. It exits
with zero status if the reflog exists, and non-zero status if it does
not.
+The "drop" subcommand completely removes the reflog for the specified
+references. This is in contrast to "expire" and "delete", both of which
+can be used to delete reflog entries, but not the reflog itself.
+
OPTIONS
-------
@@ -132,6 +137,11 @@ Options for `delete`
`--dry-run`, and `--verbose`, with the same meanings as when they are
used with `expire`.
+Options for `drop`
+~~~~~~~~~~~~~~~~~~~~
+
+--all::
+ Drop the reflogs of all references from all worktrees.
GIT
---
@@ -29,6 +29,9 @@
#define BUILTIN_REFLOG_EXISTS_USAGE \
N_("git reflog exists <ref>")
+#define BUILTIN_REFLOG_DROP_USAGE \
+ N_("git reflog drop [--all | <refs>...]")
+
static const char *const reflog_show_usage[] = {
BUILTIN_REFLOG_SHOW_USAGE,
NULL,
@@ -54,11 +57,17 @@ static const char *const reflog_exists_usage[] = {
NULL,
};
+static const char *const reflog_drop_usage[] = {
+ BUILTIN_REFLOG_DROP_USAGE,
+ NULL,
+};
+
static const char *const reflog_usage[] = {
BUILTIN_REFLOG_SHOW_USAGE,
BUILTIN_REFLOG_LIST_USAGE,
BUILTIN_REFLOG_EXPIRE_USAGE,
BUILTIN_REFLOG_DELETE_USAGE,
+ BUILTIN_REFLOG_DROP_USAGE,
BUILTIN_REFLOG_EXISTS_USAGE,
NULL
};
@@ -449,10 +458,58 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,
refname);
}
+static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,
+ struct repository *repo)
+{
+ int ret = 0, do_all = 0;
+ const struct option options[] = {
+ OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);
+
+ if (argc && do_all)
+ die(_("references specified along with --all"));
+
+ if (do_all) {
+ struct worktree_reflogs collected = {
+ .reflogs = STRING_LIST_INIT_DUP,
+ };
+ struct string_list_item *item;
+ struct worktree **worktrees, **p;
+
+ worktrees = get_worktrees();
+ for (p = worktrees; *p; p++) {
+ collected.worktree = *p;
+ refs_for_each_reflog(get_worktree_ref_store(*p),
+ collect_reflog, &collected);
+ }
+ free_worktrees(worktrees);
+
+ for_each_string_list_item(item, &collected.reflogs)
+ ret |= refs_delete_reflog(get_main_ref_store(repo),
+ item->string);
+ string_list_clear(&collected.reflogs, 0);
+ }
+
+ for (int i = 0; i < argc; i++) {
+ char *ref;
+ if (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {
+ ret |= error(_("%s points nowhere!"), argv[i]);
+ continue;
+ }
+
+ ret |= refs_delete_reflog(get_main_ref_store(repo), ref);
+ free(ref);
+ }
+
+ return ret;
+}
+
/*
* main "reflog"
*/
-
int cmd_reflog(int argc,
const char **argv,
const char *prefix,
@@ -465,6 +522,7 @@ int cmd_reflog(int argc,
OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
+ OPT_SUBCOMMAND("drop", &fn, cmd_reflog_drop),
OPT_END()
};
@@ -551,4 +551,71 @@ test_expect_success 'reflog with invalid object ID can be listed' '
)
'
+test_expect_success 'reflog drop non-existent ref' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_must_fail git reflog exists refs/heads/non-existent &&
+ test_must_fail git reflog drop refs/heads/non-existent 2>stderr &&
+ test_grep "error: refs/heads/non-existent points nowhere!" stderr
+ )
+'
+
+test_expect_success 'reflog drop' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_commit_bulk --ref=refs/heads/branch 1 &&
+ git reflog exists refs/heads/main &&
+ git reflog exists refs/heads/branch &&
+ git reflog drop refs/heads/main &&
+ test_must_fail git reflog exists refs/heads/main &&
+ git reflog exists refs/heads/branch
+ )
+'
+
+test_expect_success 'reflog drop multiple references' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_commit_bulk --ref=refs/heads/branch 1 &&
+ git reflog exists refs/heads/main &&
+ git reflog exists refs/heads/branch &&
+ git reflog drop refs/heads/main refs/heads/branch &&
+ test_must_fail git reflog exists refs/heads/main &&
+ test_must_fail git reflog exists refs/heads/branch
+ )
+'
+
+test_expect_success 'reflog drop --all' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_commit_bulk --ref=refs/heads/branch 1 &&
+ git reflog exists refs/heads/main &&
+ git reflog exists refs/heads/branch &&
+ git reflog drop --all &&
+ test_must_fail git reflog exists refs/heads/main &&
+ test_must_fail git reflog exists refs/heads/branch
+ )
+'
+
+test_expect_success 'reflog drop --all with reference' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ test_commit A &&
+ test_must_fail git reflog drop --all refs/heads/main 2>stderr &&
+ test_grep "fatal: references specified along with --all" stderr
+ )
+'
+
test_done