From: Toon Claes <toon@iotcl.com>
To: Karthik Nayak <karthik.188@gmail.com>, git@vger.kernel.org
Cc: ps@pks.im, kristofferhaugsbakk@fastmail.com, gitster@pobox.com,
Karthik Nayak <karthik.188@gmail.com>
Subject: Re: [PATCH v3 2/2] reflog: implement subcommand to drop reflogs
Date: Tue, 18 Mar 2025 16:56:49 +0100 [thread overview]
Message-ID: <8734fagwn2.fsf@iotcl.com> (raw)
In-Reply-To: <20250314-493-add-command-to-purge-reflog-entries-v3-2-c24e23a6146d@gmail.com>
Karthik Nayak <karthik.188@gmail.com> writes:
> 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 an '--all' flag to
> enable dropping all reflogs from all worktrees and an addon flag
> '--single-worktree', to only drop all reflogs from the current worktree.
>
> While here, remove an extraneous newline in the file.
>
> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
> ---
> Documentation/git-reflog.adoc | 23 ++++++--
> builtin/reflog.c | 66 ++++++++++++++++++++++-
> t/t1410-reflog.sh | 122 ++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 206 insertions(+), 5 deletions(-)
>
> diff --git a/Documentation/git-reflog.adoc b/Documentation/git-reflog.adoc
> index a929c52982..b55c060569 100644
> --- a/Documentation/git-reflog.adoc
> +++ b/Documentation/git-reflog.adoc
> @@ -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 [--single-worktree] | <refs>...]
> 'git reflog exists' <ref>
>
> DESCRIPTION
> @@ -48,10 +49,14 @@ 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 "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.
>
> 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
> @@ -132,6 +137,16 @@ 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.
> +
> +--single-worktree::
> + By default when `--all` is specified, reflogs from all working
> + trees are dropped. This option limits the processing to reflogs
> + from the current working tree only.
>
> GIT
> ---
> diff --git a/builtin/reflog.c b/builtin/reflog.c
> index 762719315e..a3652e69f1 100644
> --- a/builtin/reflog.c
> +++ b/builtin/reflog.c
> @@ -29,6 +29,9 @@
> #define BUILTIN_REFLOG_EXISTS_USAGE \
> N_("git reflog exists <ref>")
>
> +#define BUILTIN_REFLOG_DROP_USAGE \
> + N_("git reflog drop [--all [--single-worktree] | <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,64 @@ 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, single_worktree = 0;
> + const struct option options[] = {
> + OPT_BOOL(0, "all", &do_all, N_("drop the reflogs of all references")),
> + OPT_BOOL(0, "single-worktree", &single_worktree,
> + N_("drop reflogs from the current worktree only")),
> + OPT_END()
> + };
> +
> + argc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);
> +
> + if (argc && do_all)
> + usage(_("references specified along with --all"));
What is the intended behavior when both `--all` and `<refs>` are
omitted? It seems nothing happens at the moment. And no error nor
warning is printed, that feels a bit odd to me.
Now, when you do `git reflog expire --expire=all` it also seems to be
doing nothing at all. I also think this is weird. And I don't see any
test coverage for `git reflog expire` without `--all`.
But what is the expected behavior when you omit `--all` and `<refs>`?
Should it give an error or warning? Should it use HEAD, just like `git
reflog show` does?
> +
> + 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++) {
> + if (single_worktree && !(*p)->is_current)
> + continue;
> + 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);
> +
> + return ret;
> + }
> +
> + for (int i = 0; i < argc; i++) {
> + char *ref;
> + if (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {
> + ret |= error(_("reflog could not be found: '%s'"), 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 +528,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()
> };
>
> diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
> index 1f7249be76..42b501f163 100755
> --- a/t/t1410-reflog.sh
> +++ b/t/t1410-reflog.sh
> @@ -551,4 +551,126 @@ 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: reflog could not be found: ${SQ}refs/heads/non-existent${SQ}" 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 multiple references some non-existent' '
> + 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 &&
> + test_must_fail git reflog exists refs/heads/non-existent &&
> + test_must_fail git reflog drop refs/heads/main refs/heads/non-existent refs/heads/branch 2>stderr &&
> + test_must_fail git reflog exists refs/heads/main &&
> + test_must_fail git reflog exists refs/heads/branch &&
> + test_must_fail git reflog exists refs/heads/non-existent &&
> + test_grep "error: reflog could not be found: ${SQ}refs/heads/non-existent${SQ}" stderr
> + )
> +'
> +
> +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
Should we test output of `git reflog list`?
> + )
> +'
> +
> +test_expect_success 'reflog drop --all multiple worktrees' '
> + test_when_finished "rm -rf repo" &&
> + test_when_finished "rm -rf wt" &&
> + git init repo &&
> + (
> + cd repo &&
> + test_commit A &&
> + git worktree add ../wt &&
> + test_commit_bulk -C ../wt --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
Shall we test HEAD in both worktrees does not exists?
> + )
> +'
> +
> +test_expect_success 'reflog drop --all --single-worktree' '
> + test_when_finished "rm -rf repo" &&
> + test_when_finished "rm -rf wt" &&
> + git init repo &&
> + (
> + cd repo &&
> + test_commit A &&
> + git worktree add ../wt &&
> + test_commit -C ../wt foobar &&
> + git reflog exists refs/heads/main &&
> + git reflog exists refs/heads/wt &&
> + test-tool ref-store worktree:wt reflog-exists HEAD &&
> + git reflog drop --all --single-worktree &&
> + test_must_fail git reflog exists refs/heads/main &&
> + test_must_fail git reflog exists refs/heads/wt &&
> + test_must_fail test-tool ref-store worktree:main reflog-exists HEAD &&
> + test-tool ref-store worktree:wt reflog-exists HEAD
Naive question: why is `test-tool ref-store` used and not
`git -C ../wt reflog exist`?
> + )
> +'
> +
> +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 "usage: references specified along with --all" stderr
> + )
> +'
> +
> test_done
>
> --
> 2.48.1
next prev parent reply other threads:[~2025-03-18 15:57 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-07 11:17 [PATCH 0/2] EDITME: cover title for 493-add-command-to-purge-reflog-entries Karthik Nayak
2025-03-07 11:17 ` [PATCH 1/2] reflog: drop usage of global variables Karthik Nayak
2025-03-07 21:19 ` Junio C Hamano
2025-03-10 11:41 ` Karthik Nayak
2025-03-10 15:24 ` Junio C Hamano
2025-03-13 13:30 ` Karthik Nayak
2025-03-07 11:17 ` [PATCH 2/2] reflog: implement subcommand to drop reflogs Karthik Nayak
2025-03-07 11:50 ` Patrick Steinhardt
2025-03-07 12:53 ` Karthik Nayak
2025-03-07 12:59 ` Patrick Steinhardt
2025-03-07 13:28 ` Karthik Nayak
2025-03-07 13:28 ` Karthik Nayak
2025-03-07 21:28 ` Junio C Hamano
2025-03-10 11:28 ` Karthik Nayak
2025-03-10 7:39 ` [PATCH 0/2] EDITME: cover title for 493-add-command-to-purge-reflog-entries Kristoffer Haugsbakk
2025-03-10 12:34 ` Karthik Nayak
2025-03-10 15:28 ` Junio C Hamano
2025-03-10 12:36 ` [PATCH v2] reflog: implement subcommand to drop reflogs Karthik Nayak
2025-03-12 7:15 ` Patrick Steinhardt
2025-03-13 14:24 ` Karthik Nayak
2025-03-13 14:45 ` Patrick Steinhardt
2025-03-14 8:40 ` [PATCH v3 0/2] " Karthik Nayak
2025-03-14 8:40 ` [PATCH v3 1/2] reflog: improve error for when reflog is not found Karthik Nayak
2025-03-14 8:40 ` [PATCH v3 2/2] reflog: implement subcommand to drop reflogs Karthik Nayak
2025-03-18 14:01 ` Christian Couder
2025-03-18 17:44 ` Junio C Hamano
2025-03-19 8:17 ` Christian Couder
2025-03-19 9:06 ` Karthik Nayak
2025-03-18 15:56 ` Toon Claes [this message]
2025-03-19 9:16 ` Karthik Nayak
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=8734fagwn2.fsf@iotcl.com \
--to=toon@iotcl.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=karthik.188@gmail.com \
--cc=kristofferhaugsbakk@fastmail.com \
--cc=ps@pks.im \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.