{"thread":{"id":"63072","subject":"[PATCH 0/2] EDITME: cover title for 493-add-command-to-purge-reflog-entries","startedAt":"2025-03-07T11:17:39Z","lastAt":"2025-03-19T09:16:11Z","messageCount":30,"participants":["Karthik Nayak","Patrick Steinhardt","Junio C Hamano","Kristoffer Haugsbakk","Christian Couder","Toon Claes"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"513746","messageId":"20250307-493-add-command-to-purge-reflog-entries-v1-0-84ab8529cf9e@gmail.com","threadId":"63072","inReplyTo":null,"subject":"[PATCH 0/2] EDITME: cover title for 493-add-command-to-purge-reflog-entries","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-07T11:17:24Z","receivedAt":"2025-03-07T11:17:39Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":" Documentation/git-reflog.adoc |  6 ++++\n builtin/reflog.c              | 84 +++++++++++++++++++++++++++++++++++--------\n t/t1410-reflog.sh             | 55 ++++++++++++++++++++++++++++\n 3 files changed, 130 insertions(+), 15 deletions(-)\n\nKarthik Nayak (2):\n      reflog: drop usage of global variables\n      reflog: implement subcommand to drop reflogs\n\nThis patch series adds a new 'drop' subcommand to git-reflog that allows\nusers to delete the reflog for a specified reference. Additionally, it\nadds an '--all' flag to enable dropping all reflogs in a repository.\nThis is a followup to the discussion we had when I sent in a patch to\nadd '--no-reflog' option to 'git refs migrate' [1].\n\nWhile 'git-reflog(1)' currently allows users to expire reflogs and\ndelete individual entries, it lacks functionality to completely remove\nreflogs for specific references. This becomes problematic in\nrepositories where reflogs are not needed but continue to accumulate\nentries despite setting 'core.logAllRefUpdates=false'.\n\nThe first patch is a small cleanup which ensures that 'builtin/reflog.c' no\nlonger uses global variables. The second patch add the required changes.\n\n[1]: https://lore.kernel.org/all/xmqqa5aqu7g9.fsf@gitster.g/\n\n\n\nbase-commit: e969bc875963a10890d61ba84eab3a460bd9e535\nchange-id: 20250306-493-add-command-to-purge-reflog-entries-bd22547ad34a\n\nThanks\n- Karthik\n\n"},{"id":"513747","messageId":"20250307-493-add-command-to-purge-reflog-entries-v1-1-84ab8529cf9e@gmail.com","threadId":"63072","inReplyTo":"20250307-493-add-command-to-purge-reflog-entries-v1-0-84ab8529cf9e@gmail.com","subject":"[PATCH 1/2] reflog: drop usage of global variables","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-07T11:17:25Z","receivedAt":"2025-03-07T11:17:39Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"The 'builtin/reflog.c' file uses the 'the_repository' global variable\ndirectly and also via 'git_config()'. Since this is a builtin command\nwhich has access to the 'struct repository', drop usage of the global\nvariable and use the available repository struct.\n\nWith this, remove the 'USE_THE_REPOSITORY_VARIABLE' macro from the file.\n\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n builtin/reflog.c | 26 ++++++++++++--------------\n 1 file changed, 12 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 95f264989b..f92258f6b6 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -1,5 +1,3 @@\n-#define USE_THE_REPOSITORY_VARIABLE\n-\n #include \"builtin.h\"\n #include \"config.h\"\n #include \"gettext.h\"\n@@ -236,7 +234,7 @@ static int expire_total_callback(const struct option *opt,\n }\n \n static int cmd_reflog_show(int argc, const char **argv, const char *prefix,\n-\t\t\t   struct repository *repo UNUSED)\n+\t\t\t   struct repository *repo)\n {\n \tstruct option options[] = {\n \t\tOPT_END()\n@@ -246,7 +244,7 @@ static int cmd_reflog_show(int argc, const char **argv, const char *prefix,\n \t\t      PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 |\n \t\t      PARSE_OPT_KEEP_UNKNOWN_OPT);\n \n-\treturn cmd_log_reflog(argc, argv, prefix, the_repository);\n+\treturn cmd_log_reflog(argc, argv, prefix, repo);\n }\n \n static int show_reflog(const char *refname, void *cb_data UNUSED)\n@@ -256,7 +254,7 @@ static int show_reflog(const char *refname, void *cb_data UNUSED)\n }\n \n static int cmd_reflog_list(int argc, const char **argv, const char *prefix,\n-\t\t\t   struct repository *repo UNUSED)\n+\t\t\t   struct repository *repo)\n {\n \tstruct option options[] = {\n \t\tOPT_END()\n@@ -268,13 +266,13 @@ static int cmd_reflog_list(int argc, const char **argv, const char *prefix,\n \t\treturn error(_(\"%s does not accept arguments: '%s'\"),\n \t\t\t     \"list\", argv[0]);\n \n-\tref_store = get_main_ref_store(the_repository);\n+\tref_store = get_main_ref_store(repo);\n \n \treturn refs_for_each_reflog(ref_store, show_reflog, NULL);\n }\n \n static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,\n-\t\t\t     struct repository *repo UNUSED)\n+\t\t\t     struct repository *repo)\n {\n \tstruct cmd_reflog_expire_cb cmd = { 0 };\n \ttimestamp_t now = time(NULL);\n@@ -310,7 +308,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,\n \n \tdefault_reflog_expire_unreachable = now - 30 * 24 * 3600;\n \tdefault_reflog_expire = now - 90 * 24 * 3600;\n-\tgit_config(reflog_expire_config, NULL);\n+\trepo_config(repo, reflog_expire_config, NULL);\n \n \tsave_commit_buffer = 0;\n \tdo_all = status = 0;\n@@ -332,7 +330,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,\n \tif (cmd.stalefix) {\n \t\tstruct rev_info revs;\n \n-\t\trepo_init_revisions(the_repository, &revs, prefix);\n+\t\trepo_init_revisions(repo, &revs, prefix);\n \t\trevs.do_not_die_on_missing_objects = 1;\n \t\trevs.ignore_missing = 1;\n \t\trevs.ignore_missing_links = 1;\n@@ -368,7 +366,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,\n \t\t\t};\n \n \t\t\tset_reflog_expiry_param(&cb.cmd,  item->string);\n-\t\t\tstatus |= refs_reflog_expire(get_main_ref_store(the_repository),\n+\t\t\tstatus |= refs_reflog_expire(get_main_ref_store(repo),\n \t\t\t\t\t\t     item->string, flags,\n \t\t\t\t\t\t     reflog_expiry_prepare,\n \t\t\t\t\t\t     should_prune_fn,\n@@ -382,12 +380,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,\n \t\tchar *ref;\n \t\tstruct expire_reflog_policy_cb cb = { .cmd = cmd };\n \n-\t\tif (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) {\n+\t\tif (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {\n \t\t\tstatus |= error(_(\"%s points nowhere!\"), argv[i]);\n \t\t\tcontinue;\n \t\t}\n \t\tset_reflog_expiry_param(&cb.cmd, ref);\n-\t\tstatus |= refs_reflog_expire(get_main_ref_store(the_repository),\n+\t\tstatus |= refs_reflog_expire(get_main_ref_store(repo),\n \t\t\t\t\t     ref, flags,\n \t\t\t\t\t     reflog_expiry_prepare,\n \t\t\t\t\t     should_prune_fn,\n@@ -430,7 +428,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix,\n }\n \n static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,\n-\t\t\t     struct repository *repo UNUSED)\n+\t\t\t     struct repository *repo)\n {\n \tstruct option options[] = {\n \t\tOPT_END()\n@@ -445,7 +443,7 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,\n \trefname = argv[0];\n \tif (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))\n \t\tdie(_(\"invalid ref format: %s\"), refname);\n-\treturn !refs_reflog_exists(get_main_ref_store(the_repository),\n+\treturn !refs_reflog_exists(get_main_ref_store(repo),\n \t\t\t\t   refname);\n }\n \n\n-- \n2.48.1\n\n"},{"id":"513748","messageId":"20250307-493-add-command-to-purge-reflog-entries-v1-2-84ab8529cf9e@gmail.com","threadId":"63072","inReplyTo":"20250307-493-add-command-to-purge-reflog-entries-v1-0-84ab8529cf9e@gmail.com","subject":"[PATCH 2/2] reflog: implement subcommand to drop reflogs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-07T11:17:26Z","receivedAt":"2025-03-07T11:17:40Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Add a new 'drop' subcommand to git-reflog that allows users to delete\nthe entire reflog for a specified reference. Include a '--all' flag to\nenable dropping all reflogs in a repository.\n\nWhile 'git-reflog(1)' currently allows users to expire reflogs and\ndelete individual entries, it lacks functionality to completely remove\nreflogs for specific references. This becomes problematic in\nrepositories where reflogs are not needed but continue to accumulate\nentries despite setting 'core.logAllRefUpdates=false'.\n\nWhile here, remove an erranous newline in the file.\n\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-reflog.adoc |  6 +++++\n builtin/reflog.c              | 58 ++++++++++++++++++++++++++++++++++++++++++-\n t/t1410-reflog.sh             | 55 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 118 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/git-reflog.adoc b/Documentation/git-reflog.adoc\nindex a929c52982..4ecee297de 100644\n--- a/Documentation/git-reflog.adoc\n+++ b/Documentation/git-reflog.adoc\n@@ -17,6 +17,7 @@ SYNOPSIS\n 'git reflog delete' [--rewrite] [--updateref]\n \t[--dry-run | -n] [--verbose] <ref>@{<specifier>}...\n 'git reflog exists' <ref>\n+'git reflog drop' [--all | <refs>...]\n \n DESCRIPTION\n -----------\n@@ -57,6 +58,11 @@ The \"exists\" subcommand checks whether a ref has a reflog.  It exits\n with zero status if the reflog exists, and non-zero status if it does\n not.\n \n+The \"drop\" subcommand removes the reflog for the specified references.\n+In contrast, \"expire\" can be used to prune all entries from a reflog,\n+but the reflog itself will still exist for that reference. To fully\n+remove the reflog for specific references, use the \"drop\" subcommand.\n+\n OPTIONS\n -------\n \ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex f92258f6b6..232602c1a6 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -27,6 +27,9 @@\n #define BUILTIN_REFLOG_EXISTS_USAGE \\\n \tN_(\"git reflog exists <ref>\")\n \n+#define BUILTIN_REFLOG_DROP_USAGE \\\n+\tN_(\"git reflog drop [--all | <refs>...]\")\n+\n static const char *const reflog_show_usage[] = {\n \tBUILTIN_REFLOG_SHOW_USAGE,\n \tNULL,\n@@ -52,12 +55,18 @@ static const char *const reflog_exists_usage[] = {\n \tNULL,\n };\n \n+static const char *const reflog_drop_usage[] = {\n+\tBUILTIN_REFLOG_DROP_USAGE,\n+\tNULL,\n+};\n+\n static const char *const reflog_usage[] = {\n \tBUILTIN_REFLOG_SHOW_USAGE,\n \tBUILTIN_REFLOG_LIST_USAGE,\n \tBUILTIN_REFLOG_EXPIRE_USAGE,\n \tBUILTIN_REFLOG_DELETE_USAGE,\n \tBUILTIN_REFLOG_EXISTS_USAGE,\n+\tBUILTIN_REFLOG_DROP_USAGE,\n \tNULL\n };\n \n@@ -447,10 +456,56 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,\n \t\t\t\t   refname);\n }\n \n+static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,\n+\t\t\t   struct repository *repo)\n+{\n+\tint i, ret, do_all;\n+\tconst struct option options[] = {\n+\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"process the reflogs of all references\")),\n+\t\tOPT_END()\n+\t};\n+\n+\tdo_all = ret = 0;\n+\targc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);\n+\n+\tif (do_all) {\n+\t\tstruct worktree_reflogs collected = {\n+\t\t\t.reflogs = STRING_LIST_INIT_DUP,\n+\t\t};\n+\t\tstruct string_list_item *item;\n+\t\tstruct worktree **worktrees, **p;\n+\n+\t\tworktrees = get_worktrees();\n+\t\tfor (p = worktrees; *p; p++) {\n+\t\t\tcollected.worktree = *p;\n+\t\t\trefs_for_each_reflog(get_worktree_ref_store(*p),\n+\t\t\t\t\t     collect_reflog, &collected);\n+\t\t}\n+\t\tfree_worktrees(worktrees);\n+\n+\t\tfor_each_string_list_item(item, &collected.reflogs)\n+\t\t\tret |= refs_delete_reflog(get_main_ref_store(repo),\n+\t\t\t\t\t\t     item->string);\n+\t\tstring_list_clear(&collected.reflogs, 0);\n+\t}\n+\n+\tfor (i = 0; i < argc; i++) {\n+\t\tchar *ref;\n+\t\tif (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {\n+\t\t\tret |= error(_(\"%s points nowhere!\"), argv[i]);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tret |= refs_delete_reflog(get_main_ref_store(repo), ref);\n+\t\tfree(ref);\n+\t}\n+\n+\treturn ret;\n+}\n+\n /*\n  * main \"reflog\"\n  */\n-\n int cmd_reflog(int argc,\n \t       const char **argv,\n \t       const char *prefix,\n@@ -463,6 +518,7 @@ int cmd_reflog(int argc,\n \t\tOPT_SUBCOMMAND(\"expire\", &fn, cmd_reflog_expire),\n \t\tOPT_SUBCOMMAND(\"delete\", &fn, cmd_reflog_delete),\n \t\tOPT_SUBCOMMAND(\"exists\", &fn, cmd_reflog_exists),\n+\t\tOPT_SUBCOMMAND(\"drop\", &fn, cmd_reflog_drop),\n \t\tOPT_END()\n \t};\n \ndiff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh\nindex 388fdf9ae5..b6e44ce6b9 100755\n--- a/t/t1410-reflog.sh\n+++ b/t/t1410-reflog.sh\n@@ -551,4 +551,59 @@ test_expect_success 'reflog with invalid object ID can be listed' '\n \t)\n '\n \n+test_expect_success 'reflog drop non-existent ref' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n+\t\ttest_must_fail git reflog drop refs/heads/non-existent\n+\t)\n+'\n+\n+test_expect_success 'reflog drop' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_commit A &&\n+\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n+\t\tgit reflog exists refs/heads/main &&\n+\t\tgit reflog exists refs/heads/branch &&\n+\t\tgit reflog drop refs/heads/main &&\n+\t\ttest_must_fail git reflog exists refs/heads/main &&\n+\t\tgit reflog exists refs/heads/branch\n+\t)\n+'\n+\n+test_expect_success 'reflog drop multiple references' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_commit A &&\n+\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n+\t\tgit reflog exists refs/heads/main &&\n+\t\tgit reflog exists refs/heads/branch &&\n+\t\tgit reflog drop refs/heads/main refs/heads/branch &&\n+\t\ttest_must_fail git reflog exists refs/heads/main &&\n+\t\ttest_must_fail git reflog exists refs/heads/branch\n+\t)\n+'\n+\n+test_expect_success 'reflog drop --all' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_commit A &&\n+\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n+\t\tgit reflog exists refs/heads/main &&\n+\t\tgit reflog exists refs/heads/branch &&\n+\t\tgit reflog drop --all &&\n+\t\ttest_must_fail git reflog exists refs/heads/main &&\n+\t\ttest_must_fail git reflog exists refs/heads/branch\n+\t)\n+'\n+\n test_done\n\n-- \n2.48.1\n\n"},{"id":"513749","messageId":"Z8rdg90kxmKHHbyh@pks.im","threadId":"63072","inReplyTo":"20250307-493-add-command-to-purge-reflog-entries-v1-2-84ab8529cf9e@gmail.com","subject":"Re: [PATCH 2/2] reflog: implement subcommand to drop reflogs","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-03-07T11:50:27Z","receivedAt":"2025-03-07T11:50:36Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Mar 07, 2025 at 12:17:26PM +0100, Karthik Nayak wrote:\n> Add a new 'drop' subcommand to git-reflog that allows users to delete\n> the entire reflog for a specified reference. Include a '--all' flag to\n> enable dropping all reflogs in a repository.\n> \n> While 'git-reflog(1)' currently allows users to expire reflogs and\n> delete individual entries, it lacks functionality to completely remove\n> reflogs for specific references. This becomes problematic in\n> repositories where reflogs are not needed but continue to accumulate\n> entries despite setting 'core.logAllRefUpdates=false'.\n\nI think the order of the two paragraphs should be switched: we tend to\nfirst explain the problem before explaining how to address it.\n\n> While here, remove an erranous newline in the file.\n\nI suspet this should either be \"extraneous\" or \"erroneous\"? I cannot\nquite tell which of both it shuld be :)\n\n> \n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n>  Documentation/git-reflog.adoc |  6 +++++\n>  builtin/reflog.c              | 58 ++++++++++++++++++++++++++++++++++++++++++-\n>  t/t1410-reflog.sh             | 55 ++++++++++++++++++++++++++++++++++++++++\n>  3 files changed, 118 insertions(+), 1 deletion(-)\n> \n> diff --git a/Documentation/git-reflog.adoc b/Documentation/git-reflog.adoc\n> index a929c52982..4ecee297de 100644\n> --- a/Documentation/git-reflog.adoc\n> +++ b/Documentation/git-reflog.adoc\n> @@ -17,6 +17,7 @@ SYNOPSIS\n>  'git reflog delete' [--rewrite] [--updateref]\n>  \t[--dry-run | -n] [--verbose] <ref>@{<specifier>}...\n>  'git reflog exists' <ref>\n> +'git reflog drop' [--all | <refs>...]\n\nShould we put the command next to `delete`?\n\n>  DESCRIPTION\n>  -----------\n> @@ -57,6 +58,11 @@ The \"exists\" subcommand checks whether a ref has a reflog.  It exits\n>  with zero status if the reflog exists, and non-zero status if it does\n>  not.\n>  \n> +The \"drop\" subcommand removes the reflog for the specified references.\n> +In contrast, \"expire\" can be used to prune all entries from a reflog,\n> +but the reflog itself will still exist for that reference. To fully\n> +remove the reflog for specific references, use the \"drop\" subcommand.\n\nThe last sentence feels like pointless duplication to me. We should\nlikely also point out how it is different from \"delete\". How about:\n\n    The \"drop\" subcommand completely removes the reflog for the\n    specified references. This is in contrast to \"expire\" and \"delete\",\n    both of which can be used to delete reflog entries, but not the\n    reflog itself.\n\nIt might also be useful to add a comment to \"delete\" to say that it\ndeletes entries, but not the reflog.\n\n>  OPTIONS\n>  -------\n>  \n> diff --git a/builtin/reflog.c b/builtin/reflog.c\n> index f92258f6b6..232602c1a6 100644\n> --- a/builtin/reflog.c\n> +++ b/builtin/reflog.c\n> @@ -27,6 +27,9 @@\n>  #define BUILTIN_REFLOG_EXISTS_USAGE \\\n>  \tN_(\"git reflog exists <ref>\")\n>  \n> +#define BUILTIN_REFLOG_DROP_USAGE \\\n> +\tN_(\"git reflog drop [--all | <refs>...]\")\n> +\n>  static const char *const reflog_show_usage[] = {\n>  \tBUILTIN_REFLOG_SHOW_USAGE,\n>  \tNULL,\n> @@ -52,12 +55,18 @@ static const char *const reflog_exists_usage[] = {\n>  \tNULL,\n>  };\n>  \n> +static const char *const reflog_drop_usage[] = {\n> +\tBUILTIN_REFLOG_DROP_USAGE,\n> +\tNULL,\n> +};\n> +\n>  static const char *const reflog_usage[] = {\n>  \tBUILTIN_REFLOG_SHOW_USAGE,\n>  \tBUILTIN_REFLOG_LIST_USAGE,\n>  \tBUILTIN_REFLOG_EXPIRE_USAGE,\n>  \tBUILTIN_REFLOG_DELETE_USAGE,\n>  \tBUILTIN_REFLOG_EXISTS_USAGE,\n> +\tBUILTIN_REFLOG_DROP_USAGE,\n>  \tNULL\n>  };\n>  \n> @@ -447,10 +456,56 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,\n>  \t\t\t\t   refname);\n>  }\n>  \n> +static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,\n> +\t\t\t   struct repository *repo)\n> +{\n> +\tint i, ret, do_all;\n> +\tconst struct option options[] = {\n> +\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"process the reflogs of all references\")),\n> +\t\tOPT_END()\n> +\t};\n> +\n> +\tdo_all = ret = 0;\n\nCan't we initiailize the variables directly when declaring them?\n\n> +\targc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);\n> +\n> +\tif (do_all) {\n\n`do_all` and `argc > 0` should be mutually exclusive from my point of\nview, as the combination does not make any sense. We should likely die\nif we see both to be non-zero. Similarly, I think we should abort on\n`!do_all && !argc`.\n\n> +\t\tstruct worktree_reflogs collected = {\n> +\t\t\t.reflogs = STRING_LIST_INIT_DUP,\n> +\t\t};\n> +\t\tstruct string_list_item *item;\n> +\t\tstruct worktree **worktrees, **p;\n\nWould it be useful to point out in the docs that we also prune logs of\nworktrees?\n\n> +\t\tworktrees = get_worktrees();\n> +\t\tfor (p = worktrees; *p; p++) {\n> +\t\t\tcollected.worktree = *p;\n> +\t\t\trefs_for_each_reflog(get_worktree_ref_store(*p),\n> +\t\t\t\t\t     collect_reflog, &collected);\n> +\t\t}\n> +\t\tfree_worktrees(worktrees);\n> +\n> +\t\tfor_each_string_list_item(item, &collected.reflogs)\n> +\t\t\tret |= refs_delete_reflog(get_main_ref_store(repo),\n> +\t\t\t\t\t\t     item->string);\n> +\t\tstring_list_clear(&collected.reflogs, 0);\n> +\t}\n> +\n> +\tfor (i = 0; i < argc; i++) {\n> +\t\tchar *ref;\n> +\t\tif (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {\n> +\t\t\tret |= error(_(\"%s points nowhere!\"), argv[i]);\n> +\t\t\tcontinue;\n> +\t\t}\n\nIs there a particular reason why we have to double check that the reflog\nthat we just enumerated really exists? It feels rather unnecessary to\nme.\n\n> diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh\n> index 388fdf9ae5..b6e44ce6b9 100755\n> --- a/t/t1410-reflog.sh\n> +++ b/t/t1410-reflog.sh\n> @@ -551,4 +551,59 @@ test_expect_success 'reflog with invalid object ID can be listed' '\n>  \t)\n>  '\n>  \n> +test_expect_success 'reflog drop non-existent ref' '\n> +\ttest_when_finished \"rm -rf repo\" &&\n> +\tgit init repo &&\n> +\t(\n> +\t\tcd repo &&\n> +\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n> +\t\ttest_must_fail git reflog drop refs/heads/non-existent\n\nDo we want to check the error message of the latter command?\n\nPatrick\n"},{"id":"513752","messageId":"CAOLa=ZSMLwt53TwziCe71UbKWgRyNgg5VvUwTUUDkN60ymmFPA@mail.gmail.com","threadId":"63072","inReplyTo":"Z8rdg90kxmKHHbyh@pks.im","subject":"Re: [PATCH 2/2] reflog: implement subcommand to drop reflogs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-07T12:53:31Z","receivedAt":"2025-03-07T12:53:34Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> On Fri, Mar 07, 2025 at 12:17:26PM +0100, Karthik Nayak wrote:\n>> Add a new 'drop' subcommand to git-reflog that allows users to delete\n>> the entire reflog for a specified reference. Include a '--all' flag to\n>> enable dropping all reflogs in a repository.\n>>\n>> While 'git-reflog(1)' currently allows users to expire reflogs and\n>> delete individual entries, it lacks functionality to completely remove\n>> reflogs for specific references. This becomes problematic in\n>> repositories where reflogs are not needed but continue to accumulate\n>> entries despite setting 'core.logAllRefUpdates=false'.\n>\n> I think the order of the two paragraphs should be switched: we tend to\n> first explain the problem before explaining how to address it.\n>\n\nThat makes sense, let me do that.\n\n>> While here, remove an erranous newline in the file.\n>\n> I suspet this should either be \"extraneous\" or \"erroneous\"? I cannot\n> quite tell which of both it shuld be :)\n>\n\nI was thinking similar but decided to go with the latter (sans typo).\nWill fix it.\n\n>\n>>\n>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n>> ---\n>>  Documentation/git-reflog.adoc |  6 +++++\n>>  builtin/reflog.c              | 58 ++++++++++++++++++++++++++++++++++++++++++-\n>>  t/t1410-reflog.sh             | 55 ++++++++++++++++++++++++++++++++++++++++\n>>  3 files changed, 118 insertions(+), 1 deletion(-)\n>>\n>> diff --git a/Documentation/git-reflog.adoc b/Documentation/git-reflog.adoc\n>> index a929c52982..4ecee297de 100644\n>> --- a/Documentation/git-reflog.adoc\n>> +++ b/Documentation/git-reflog.adoc\n>> @@ -17,6 +17,7 @@ SYNOPSIS\n>>  'git reflog delete' [--rewrite] [--updateref]\n>>  \t[--dry-run | -n] [--verbose] <ref>@{<specifier>}...\n>>  'git reflog exists' <ref>\n>> +'git reflog drop' [--all | <refs>...]\n>\n> Should we put the command next to `delete`?\n>\n\nI would have like it if they were actually alphabetically sorted, but I\nguess this is good alternative to cluster similar sub-commands.\n\n>>  DESCRIPTION\n>>  -----------\n>> @@ -57,6 +58,11 @@ The \"exists\" subcommand checks whether a ref has a reflog.  It exits\n>>  with zero status if the reflog exists, and non-zero status if it does\n>>  not.\n>>\n>> +The \"drop\" subcommand removes the reflog for the specified references.\n>> +In contrast, \"expire\" can be used to prune all entries from a reflog,\n>> +but the reflog itself will still exist for that reference. To fully\n>> +remove the reflog for specific references, use the \"drop\" subcommand.\n>\n> The last sentence feels like pointless duplication to me. We should\n> likely also point out how it is different from \"delete\". How about:\n>\n>     The \"drop\" subcommand completely removes the reflog for the\n>     specified references. This is in contrast to \"expire\" and \"delete\",\n>     both of which can be used to delete reflog entries, but not the\n>     reflog itself.\n>\n> It might also be useful to add a comment to \"delete\" to say that it\n> deletes entries, but not the reflog.\n>\n\nThanks, this does look better and I agree, we should call out \"delete\"\ntoo.\n\n>>  OPTIONS\n>>  -------\n>>\n>> diff --git a/builtin/reflog.c b/builtin/reflog.c\n>> index f92258f6b6..232602c1a6 100644\n>> --- a/builtin/reflog.c\n>> +++ b/builtin/reflog.c\n>> @@ -27,6 +27,9 @@\n>>  #define BUILTIN_REFLOG_EXISTS_USAGE \\\n>>  \tN_(\"git reflog exists <ref>\")\n>>\n>> +#define BUILTIN_REFLOG_DROP_USAGE \\\n>> +\tN_(\"git reflog drop [--all | <refs>...]\")\n>> +\n>>  static const char *const reflog_show_usage[] = {\n>>  \tBUILTIN_REFLOG_SHOW_USAGE,\n>>  \tNULL,\n>> @@ -52,12 +55,18 @@ static const char *const reflog_exists_usage[] = {\n>>  \tNULL,\n>>  };\n>>\n>> +static const char *const reflog_drop_usage[] = {\n>> +\tBUILTIN_REFLOG_DROP_USAGE,\n>> +\tNULL,\n>> +};\n>> +\n>>  static const char *const reflog_usage[] = {\n>>  \tBUILTIN_REFLOG_SHOW_USAGE,\n>>  \tBUILTIN_REFLOG_LIST_USAGE,\n>>  \tBUILTIN_REFLOG_EXPIRE_USAGE,\n>>  \tBUILTIN_REFLOG_DELETE_USAGE,\n>>  \tBUILTIN_REFLOG_EXISTS_USAGE,\n>> +\tBUILTIN_REFLOG_DROP_USAGE,\n>>  \tNULL\n>>  };\n>>\n>> @@ -447,10 +456,56 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,\n>>  \t\t\t\t   refname);\n>>  }\n>>\n>> +static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,\n>> +\t\t\t   struct repository *repo)\n>> +{\n>> +\tint i, ret, do_all;\n>> +\tconst struct option options[] = {\n>> +\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"process the reflogs of all references\")),\n>> +\t\tOPT_END()\n>> +\t};\n>> +\n>> +\tdo_all = ret = 0;\n>\n> Can't we initiailize the variables directly when declaring them?\n>\n\nWe can, let me fix it! I'll also move the initialization of 'i' down to\nthe loop while we're here.\n\n>> +\targc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);\n>> +\n>> +\tif (do_all) {\n>\n> `do_all` and `argc > 0` should be mutually exclusive from my point of\n> view, as the combination does not make any sense. We should likely die\n> if we see both to be non-zero. Similarly, I think we should abort on\n> `!do_all && !argc`.\n>\n\nMakes sense, let me add a 'die()' there.\n\n>> +\t\tstruct worktree_reflogs collected = {\n>> +\t\t\t.reflogs = STRING_LIST_INIT_DUP,\n>> +\t\t};\n>> +\t\tstruct string_list_item *item;\n>> +\t\tstruct worktree **worktrees, **p;\n>\n> Would it be useful to point out in the docs that we also prune logs of\n> worktrees?\n>\n\nYes it would, will add.\n\n>> +\t\tworktrees = get_worktrees();\n>> +\t\tfor (p = worktrees; *p; p++) {\n>> +\t\t\tcollected.worktree = *p;\n>> +\t\t\trefs_for_each_reflog(get_worktree_ref_store(*p),\n>> +\t\t\t\t\t     collect_reflog, &collected);\n>> +\t\t}\n>> +\t\tfree_worktrees(worktrees);\n>> +\n>> +\t\tfor_each_string_list_item(item, &collected.reflogs)\n>> +\t\t\tret |= refs_delete_reflog(get_main_ref_store(repo),\n>> +\t\t\t\t\t\t     item->string);\n>> +\t\tstring_list_clear(&collected.reflogs, 0);\n>> +\t}\n>> +\n>> +\tfor (i = 0; i < argc; i++) {\n>> +\t\tchar *ref;\n>> +\t\tif (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {\n>> +\t\t\tret |= error(_(\"%s points nowhere!\"), argv[i]);\n>> +\t\t\tcontinue;\n>> +\t\t}\n>\n> Is there a particular reason why we have to double check that the reflog\n> that we just enumerated really exists? It feels rather unnecessary to\n> me.\n>\n\nYou mean we could directly do `(get_main_ref_store(repo), argv[i]);` ?\nThe issue is that this returns '0', even when the reflog doesn't exist.\nSo to notify the user correctly, we do this check.\n\n>> diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh\n>> index 388fdf9ae5..b6e44ce6b9 100755\n>> --- a/t/t1410-reflog.sh\n>> +++ b/t/t1410-reflog.sh\n>> @@ -551,4 +551,59 @@ test_expect_success 'reflog with invalid object ID can be listed' '\n>>  \t)\n>>  '\n>>\n>> +test_expect_success 'reflog drop non-existent ref' '\n>> +\ttest_when_finished \"rm -rf repo\" &&\n>> +\tgit init repo &&\n>> +\t(\n>> +\t\tcd repo &&\n>> +\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n>> +\t\ttest_must_fail git reflog drop refs/heads/non-existent\n>\n> Do we want to check the error message of the latter command?\n>\n\nThat would be nice addittion, will add.\n\n> Patrick\n"},{"id":"513753","messageId":"Z8rtuZysCngh5FQd@pks.im","threadId":"63072","inReplyTo":"CAOLa=ZSMLwt53TwziCe71UbKWgRyNgg5VvUwTUUDkN60ymmFPA@mail.gmail.com","subject":"Re: [PATCH 2/2] reflog: implement subcommand to drop reflogs","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-03-07T12:59:37Z","receivedAt":"2025-03-07T12:59:43Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Mar 07, 2025 at 06:53:31AM -0600, Karthik Nayak wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> > On Fri, Mar 07, 2025 at 12:17:26PM +0100, Karthik Nayak wrote:\n> >> @@ -447,10 +456,56 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,\n> >>  \t\t\t\t   refname);\n> >>  }\n> >>\n> >> +static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,\n> >> +\t\t\t   struct repository *repo)\n> >> +{\n> >> +\tint i, ret, do_all;\n> >> +\tconst struct option options[] = {\n> >> +\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"process the reflogs of all references\")),\n> >> +\t\tOPT_END()\n> >> +\t};\n> >> +\n> >> +\tdo_all = ret = 0;\n> >\n> > Can't we initiailize the variables directly when declaring them?\n> >\n> \n> We can, let me fix it! I'll also move the initialization of 'i' down to\n> the loop while we're here.\n\nYou can also avoid declaring `i` here at all and just declare it inside\nthe loop.\n\nPatrick\n"},{"id":"513754","messageId":"CAOLa=ZQfsJdjzpopWGRdQALOiz87Xi=_1YDwarUwCz7jXgGK4Q@mail.gmail.com","threadId":"63072","inReplyTo":"Z8rtuZysCngh5FQd@pks.im","subject":"Re: [PATCH 2/2] reflog: implement subcommand to drop reflogs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-07T13:28:00Z","receivedAt":"2025-03-07T13:28:01Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> On Fri, Mar 07, 2025 at 06:53:31AM -0600, Karthik Nayak wrote:\n>> Patrick Steinhardt <ps@pks.im> writes:\n>> > On Fri, Mar 07, 2025 at 12:17:26PM +0100, Karthik Nayak wrote:\n>> >> @@ -447,10 +456,56 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,\n>> >>  \t\t\t\t   refname);\n>> >>  }\n>> >>\n>> >> +static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,\n>> >> +\t\t\t   struct repository *repo)\n>> >> +{\n>> >> +\tint i, ret, do_all;\n>> >> +\tconst struct option options[] = {\n>> >> +\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"process the reflogs of all references\")),\n>> >> +\t\tOPT_END()\n>> >> +\t};\n>> >> +\n>> >> +\tdo_all = ret = 0;\n>> >\n>> > Can't we initiailize the variables directly when declaring them?\n>> >\n>>\n>> We can, let me fix it! I'll also move the initialization of 'i' down to\n>> the loop while we're here.\n>\n> You can also avoid declaring `i` here at all and just declare it inside\n> the loop.\n>\n> Patrick\n"},{"id":"513755","messageId":"CAOLa=ZT4+LOrjnKVJ=8DZsxd=ij7UhO32iLotyj3_7ko+A0osw@mail.gmail.com","threadId":"63072","inReplyTo":"Z8rtuZysCngh5FQd@pks.im","subject":"Re: [PATCH 2/2] reflog: implement subcommand to drop reflogs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-07T13:28:35Z","receivedAt":"2025-03-07T13:28:37Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> On Fri, Mar 07, 2025 at 06:53:31AM -0600, Karthik Nayak wrote:\n>> Patrick Steinhardt <ps@pks.im> writes:\n>> > On Fri, Mar 07, 2025 at 12:17:26PM +0100, Karthik Nayak wrote:\n>> >> @@ -447,10 +456,56 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,\n>> >>  \t\t\t\t   refname);\n>> >>  }\n>> >>\n>> >> +static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,\n>> >> +\t\t\t   struct repository *repo)\n>> >> +{\n>> >> +\tint i, ret, do_all;\n>> >> +\tconst struct option options[] = {\n>> >> +\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"process the reflogs of all references\")),\n>> >> +\t\tOPT_END()\n>> >> +\t};\n>> >> +\n>> >> +\tdo_all = ret = 0;\n>> >\n>> > Can't we initiailize the variables directly when declaring them?\n>> >\n>>\n>> We can, let me fix it! I'll also move the initialization of 'i' down to\n>> the loop while we're here.\n>\n> You can also avoid declaring `i` here at all and just declare it inside\n> the loop.\n>\n> Patrick\n\nYup, this is what I was trying to convey too :)\n"},{"id":"513798","messageId":"xmqqo6ycczdr.fsf@gitster.g","threadId":"63072","inReplyTo":"20250307-493-add-command-to-purge-reflog-entries-v1-1-84ab8529cf9e@gmail.com","subject":"Re: [PATCH 1/2] reflog: drop usage of global variables","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-03-07T21:19:28Z","receivedAt":"2025-03-07T21:19:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> The 'builtin/reflog.c' file uses the 'the_repository' global variable\n> directly and also via 'git_config()'. Since this is a builtin command\n> which has access to the 'struct repository', drop usage of the global\n> variable and use the available repository struct.\n>\n> With this, remove the 'USE_THE_REPOSITORY_VARIABLE' macro from the file.\n\nI suspect that this is not quite right.\n\n    $ cd w/git.git/; make\n    $ ./git-reflog list -h\n    usage: git reflog list\n    $ cd .. # not a repository\n    $ git.git/git-reflog list -h\n    fatal: not a git repository (or any of the parent directories): .git\n    $ git.git/git-reflog -h\n    usage: git reflog [show] ...\n\nbut I also suspect that it is mostly due to the original program\nstructure that uses OPT_SUBCOMMAND() that the subcommands fail to\nrespond to \"-h\" unlike the top-level command, so this may not be a\nregression.  I do think however that this change is making it harder\nto fix.\n\nIn any case, when you are adding a new feature, I would strongly\nprefer you did *not* take it hostage to unrelated internal clean-up\nwith a dubious value.  For the library-ish parts of the system\n(e.g. reflog.c at the top-level), not depending on the_repository is\nabsolutely the good thing to do, but the top level cmd_foo() are not\nmeant to be called as helper functions repeatedly with arbirary\nrepository instance, and a churn like this one, only to mollify the\nUSE_THE_REPOSITORY_VARIABLE macro, does not deserve to take a more\ninteresting (in the sense that it improves the life of end users)\nchange hostage by pretending to be its prerequisite clean-up.\n\nThanks.\n"},{"id":"513799","messageId":"xmqqbjuccyz0.fsf@gitster.g","threadId":"63072","inReplyTo":"Z8rdg90kxmKHHbyh@pks.im","subject":"Re: [PATCH 2/2] reflog: implement subcommand to drop reflogs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-03-07T21:28:19Z","receivedAt":"2025-03-07T21:28:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n>> +The \"drop\" subcommand removes the reflog for the specified references.\n>> +In contrast, \"expire\" can be used to prune all entries from a reflog,\n>> +but the reflog itself will still exist for that reference. To fully\n>> +remove the reflog for specific references, use the \"drop\" subcommand.\n>\n> The last sentence feels like pointless duplication to me. We should\n> likely also point out how it is different from \"delete\". How about:\n>\n>     The \"drop\" subcommand completely removes the reflog for the\n>     specified references. This is in contrast to \"expire\" and \"delete\",\n>     both of which can be used to delete reflog entries, but not the\n>     reflog itself.\n>\n> It might also be useful to add a comment to \"delete\" to say that it\n> deletes entries, but not the reflog.\n\nGood.\n\n\n>> +#define BUILTIN_REFLOG_DROP_USAGE \\\n>> +\tN_(\"git reflog drop [--all | <refs>...]\")\n>> +\n\nWe need a matching change to Documentation/git-reflog.adoc file,\ntoo, right?\n"},{"id":"513886","messageId":"ba1fbb96-ae87-40b5-814c-4fcea106f956@app.fastmail.com","threadId":"63072","inReplyTo":"20250307-493-add-command-to-purge-reflog-entries-v1-0-84ab8529cf9e@gmail.com","subject":"Re: [PATCH 0/2] EDITME: cover title for 493-add-command-to-purge-reflog-entries","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-03-10T07:39:31Z","receivedAt":"2025-03-10T07:39:54Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Fri, Mar 7, 2025, at 12:17, Karthik Nayak wrote:\n> This patch series adds a new 'drop' subcommand to git-reflog that allows\n> users to delete the reflog for a specified reference. Additionally, it\n> adds an '--all' flag to enable dropping all reflogs in a repository.\n> This is a followup to the discussion we had when I sent in a patch to\n> add '--no-reflog' option to 'git refs migrate' [1].\n\nI’ve been wanting a command to drop reflogs.  I use `always` and get a\nlot of entries that I don’t care about.  But I don’t want to set it to\n`true` because I care about some of them.\n\nSo this is great.\n\n-- \nKristoffer Haugsbakk\n\n\n"},{"id":"513893","messageId":"CAOLa=ZS=1mJFJrL=0wLYJ6exhHPyU4n_HxnV1PARfWT+WxcQSg@mail.gmail.com","threadId":"63072","inReplyTo":"xmqqbjuccyz0.fsf@gitster.g","subject":"Re: [PATCH 2/2] reflog: implement subcommand to drop reflogs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-10T11:28:55Z","receivedAt":"2025-03-10T11:28:56Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Patrick Steinhardt <ps@pks.im> writes:\n>\n>>> +The \"drop\" subcommand removes the reflog for the specified references.\n>>> +In contrast, \"expire\" can be used to prune all entries from a reflog,\n>>> +but the reflog itself will still exist for that reference. To fully\n>>> +remove the reflog for specific references, use the \"drop\" subcommand.\n>>\n>> The last sentence feels like pointless duplication to me. We should\n>> likely also point out how it is different from \"delete\". How about:\n>>\n>>     The \"drop\" subcommand completely removes the reflog for the\n>>     specified references. This is in contrast to \"expire\" and \"delete\",\n>>     both of which can be used to delete reflog entries, but not the\n>>     reflog itself.\n>>\n>> It might also be useful to add a comment to \"delete\" to say that it\n>> deletes entries, but not the reflog.\n>\n> Good.\n>\n>\n>>> +#define BUILTIN_REFLOG_DROP_USAGE \\\n>>> +\tN_(\"git reflog drop [--all | <refs>...]\")\n>>> +\n>\n> We need a matching change to Documentation/git-reflog.adoc file,\n> too, right?\n\nI'm assuming you mean documentation specifically for the '--all' option,\nI will include that in the next version.\n"},{"id":"513894","messageId":"CAOLa=ZRqMnus=MZV1ASce4pxnMkVRGqhZZ+vZNcGhWdUQE-JRg@mail.gmail.com","threadId":"63072","inReplyTo":"xmqqo6ycczdr.fsf@gitster.g","subject":"Re: [PATCH 1/2] reflog: drop usage of global variables","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-10T11:41:26Z","receivedAt":"2025-03-10T11:41:28Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>> The 'builtin/reflog.c' file uses the 'the_repository' global variable\n>> directly and also via 'git_config()'. Since this is a builtin command\n>> which has access to the 'struct repository', drop usage of the global\n>> variable and use the available repository struct.\n>>\n>> With this, remove the 'USE_THE_REPOSITORY_VARIABLE' macro from the file.\n>\n> I suspect that this is not quite right.\n>\n>     $ cd w/git.git/; make\n>     $ ./git-reflog list -h\n>     usage: git reflog list\n>     $ cd .. # not a repository\n>     $ git.git/git-reflog list -h\n>     fatal: not a git repository (or any of the parent directories): .git\n>     $ git.git/git-reflog -h\n>     usage: git reflog [show] ...\n>\n> but I also suspect that it is mostly due to the original program\n> structure that uses OPT_SUBCOMMAND() that the subcommands fail to\n> respond to \"-h\" unlike the top-level command, so this may not be a\n> regression.  I do think however that this change is making it harder\n> to fix.\n>\n\nHmm. But this is the existing behavior, no?\n\n  # Inside a git directory\n  $ eza .git\n  b4.template  branches  COMMIT_EDITMSG  config  description\nFETCH_HEAD  filter-repo  HEAD  hooks  index  info  objects  refs\nreftable  rr-cache\n\n  $ git reflog -h\n  usage: git reflog [show] [<log-options>] [<ref>]\n     or: git reflog list\n     or: git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n                           [--rewrite] [--updateref] [--stale-fix]\n                           [--dry-run | -n] [--verbose] [--all\n[--single-worktree] | <refs>...]\n     or: git reflog delete [--rewrite] [--updateref]\n                           [--dry-run | -n] [--verbose] <ref>@{<specifier>}...\n     or: git reflog exists <ref>\n\n  $ git reflog list -h\n  usage: git reflog list\n\n  $ ~/code/git/build/bin-wrappers/git reflog -h\n  usage: git reflog [show] [<log-options>] [<ref>]\n     or: git reflog list\n     or: git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n                           [--rewrite] [--updateref] [--stale-fix]\n                           [--dry-run | -n] [--verbose] [--all\n[--single-worktree] | <refs>...]\n     or: git reflog delete [--rewrite] [--updateref]\n                           [--dry-run | -n] [--verbose] <ref>@{<specifier>}...\n     or: git reflog exists <ref>\n     or: git reflog drop [--all | <refs>...]\n\n  $ ~/code/git/build/bin-wrappers/git reflog list -h\n  usage: git reflog list\n\n  # Outside a git repository\n  $ eza .git\n  \".git\": No such file or directory (os error 2)\n\n  $ git reflog -h\n  usage: git reflog [show] [<log-options>] [<ref>]\n     or: git reflog list\n     or: git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n                           [--rewrite] [--updateref] [--stale-fix]\n                           [--dry-run | -n] [--verbose] [--all\n[--single-worktree] | <refs>...]\n     or: git reflog delete [--rewrite] [--updateref]\n                           [--dry-run | -n] [--verbose] <ref>@{<specifier>}...\n     or: git reflog exists <ref>\n\n\n  $ git reflog list -h\n  fatal: not a git repository (or any of the parent directories): .git\n\n  $ ~/code/git/build/bin-wrappers/git reflog -h\n  usage: git reflog [show] [<log-options>] [<ref>]\n     or: git reflog list\n     or: git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n                           [--rewrite] [--updateref] [--stale-fix]\n                           [--dry-run | -n] [--verbose] [--all\n[--single-worktree] | <refs>...]\n     or: git reflog delete [--rewrite] [--updateref]\n                           [--dry-run | -n] [--verbose] <ref>@{<specifier>}...\n     or: git reflog exists <ref>\n     or: git reflog drop [--all | <refs>...]\n\n\n  $ ~/code/git/build/bin-wrappers/git reflog list -h\n  fatal: not a git repository (or any of the parent directories): .git\n\nSeems like the behavior is the same with as with (git 2.48.1).\n\n> In any case, when you are adding a new feature, I would strongly\n> prefer you did *not* take it hostage to unrelated internal clean-up\n> with a dubious value.  For the library-ish parts of the system\n> (e.g. reflog.c at the top-level), not depending on the_repository is\n> absolutely the good thing to do, but the top level cmd_foo() are not\n> meant to be called as helper functions repeatedly with arbirary\n> repository instance, and a churn like this one, only to mollify the\n> USE_THE_REPOSITORY_VARIABLE macro, does not deserve to take a more\n> interesting (in the sense that it improves the life of end users)\n> change hostage by pretending to be its prerequisite clean-up.\n>\n> Thanks.\n\nBut point taken, I'll drop this patch in the next version! Thanks\n"},{"id":"513896","messageId":"CAOLa=ZTL3mUHKRa2cxL2jJ-SKKncB3AjQ4deJkN3k7mgTAwibw@mail.gmail.com","threadId":"63072","inReplyTo":"ba1fbb96-ae87-40b5-814c-4fcea106f956@app.fastmail.com","subject":"Re: [PATCH 0/2] EDITME: cover title for 493-add-command-to-purge-reflog-entries","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-10T12:34:13Z","receivedAt":"2025-03-10T12:34:15Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"\"Kristoffer Haugsbakk\" <kristofferhaugsbakk@fastmail.com> writes:\n\n> On Fri, Mar 7, 2025, at 12:17, Karthik Nayak wrote:\n>> This patch series adds a new 'drop' subcommand to git-reflog that allows\n>> users to delete the reflog for a specified reference. Additionally, it\n>> adds an '--all' flag to enable dropping all reflogs in a repository.\n>> This is a followup to the discussion we had when I sent in a patch to\n>> add '--no-reflog' option to 'git refs migrate' [1].\n>\n> I’ve been wanting a command to drop reflogs.  I use `always` and get a\n> lot of entries that I don’t care about.  But I don’t want to set it to\n> `true` because I care about some of them.\n>\n> So this is great.\n>\n\nThat's good to know! I was surprised that this didn't exist till date.\n\n> --\n> Kristoffer Haugsbakk\n"},{"id":"513897","messageId":"20250310-493-add-command-to-purge-reflog-entries-v2-1-05caa92e0bfa@gmail.com","threadId":"63072","inReplyTo":"20250307-493-add-command-to-purge-reflog-entries-v1-0-84ab8529cf9e@gmail.com","subject":"[PATCH v2] reflog: implement subcommand to drop reflogs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-10T12:36:25Z","receivedAt":"2025-03-10T12:36:41Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"While 'git-reflog(1)' currently allows users to expire reflogs and\ndelete individual entries, it lacks functionality to completely remove\nreflogs for specific references. This becomes problematic in\nrepositories where reflogs are not needed but continue to accumulate\nentries despite setting 'core.logAllRefUpdates=false'.\n\nAdd a new 'drop' subcommand to git-reflog that allows users to delete\nthe entire reflog for a specified reference. Include a '--all' flag to\nenable dropping all reflogs in a repository.\n\nWhile here, remove an extraneous newline in the file.\n\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\nChanges in v2:\n- Rephrase the commit message to be clearer and fix typo.\n- Move the documentation to be next to 'git reflog delete' and also\n  add missing documentation for the '--all' flag.\n- Ensure '--all' is not used with references and add a test.\n- Cleanup variable assignment.\n- Check for error message in the test.\n- Drop the cleanup commit.\n- Rebased on top of master a36e024e98 (Merge branch 'js/win-2.49-build-fixes',\n  2025-03-06), this was to include the adoc changes which were breaking\n  tests on the CI. \n- Link to v1: https://lore.kernel.org/r/20250307-493-add-command-to-purge-reflog-entries-v1-0-84ab8529cf9e@gmail.com\n\n Documentation/git-reflog.adoc | 18 +++++++++---\n builtin/reflog.c              | 60 +++++++++++++++++++++++++++++++++++++-\n t/t1410-reflog.sh             | 67 +++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 140 insertions(+), 5 deletions(-)\n\nKarthik Nayak (1):\n      reflog: implement subcommand to drop reflogs\n\nRange-diff versus v1:\n\n1:  7c37f97eb0 < -:  ---------- reflog: drop usage of global variables\n2:  d321bf14ae ! 1:  cac494d5cc reflog: implement subcommand to drop reflogs\n    @@ Metadata\n      ## Commit message ##\n         reflog: implement subcommand to drop reflogs\n     \n    -    Add a new 'drop' subcommand to git-reflog that allows users to delete\n    -    the entire reflog for a specified reference. Include a '--all' flag to\n    -    enable dropping all reflogs in a repository.\n    -\n         While 'git-reflog(1)' currently allows users to expire reflogs and\n         delete individual entries, it lacks functionality to completely remove\n         reflogs for specific references. This becomes problematic in\n         repositories where reflogs are not needed but continue to accumulate\n         entries despite setting 'core.logAllRefUpdates=false'.\n     \n    -    While here, remove an erranous newline in the file.\n    +    Add a new 'drop' subcommand to git-reflog that allows users to delete\n    +    the entire reflog for a specified reference. Include a '--all' flag to\n    +    enable dropping all reflogs in a repository.\n    +\n    +    While here, remove an extraneous newline in the file.\n     \n         Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n     \n      ## Documentation/git-reflog.adoc ##\n     @@ Documentation/git-reflog.adoc: SYNOPSIS\n    + \t[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]\n      'git reflog delete' [--rewrite] [--updateref]\n      \t[--dry-run | -n] [--verbose] <ref>@{<specifier>}...\n    - 'git reflog exists' <ref>\n     +'git reflog drop' [--all | <refs>...]\n    + 'git reflog exists' <ref>\n      \n      DESCRIPTION\n    - -----------\n    -@@ Documentation/git-reflog.adoc: The \"exists\" subcommand checks whether a ref has a reflog.  It exits\n    +@@ Documentation/git-reflog.adoc: and not reachable from the current tip, are removed from the reflog.\n    + This is typically not used directly by end users -- instead, see\n    + linkgit:git-gc[1].\n    + \n    +-The \"delete\" subcommand deletes single entries from the reflog. Its\n    +-argument must be an _exact_ entry (e.g. \"`git reflog delete\n    +-master@{2}`\"). This subcommand is also typically not used directly by\n    +-end users.\n    ++The \"delete\" subcommand deletes single entries from the reflog, but\n    ++not the reflog itself. Its argument must be an _exact_ entry (e.g. \"`git\n    ++reflog delete master@{2}`\"). This subcommand is also typically not used\n    ++directly by end users.\n    + \n    + The \"exists\" subcommand checks whether a ref has a reflog.  It exits\n      with zero status if the reflog exists, and non-zero status if it does\n      not.\n      \n    -+The \"drop\" subcommand removes the reflog for the specified references.\n    -+In contrast, \"expire\" can be used to prune all entries from a reflog,\n    -+but the reflog itself will still exist for that reference. To fully\n    -+remove the reflog for specific references, use the \"drop\" subcommand.\n    ++The \"drop\" subcommand completely removes the reflog for the specified\n    ++references. This is in contrast to \"expire\" and \"delete\", both of which\n    ++can be used to delete reflog entries, but not the reflog itself.\n     +\n      OPTIONS\n      -------\n      \n    +@@ Documentation/git-reflog.adoc: Options for `delete`\n    + `--dry-run`, and `--verbose`, with the same meanings as when they are\n    + used with `expire`.\n    + \n    ++Options for `drop`\n    ++~~~~~~~~~~~~~~~~~~~~\n    ++\n    ++--all::\n    ++\tDrop the reflogs of all references from all worktrees.\n    + \n    + GIT\n    + ---\n     \n      ## builtin/reflog.c ##\n     @@\n    @@ builtin/reflog.c: static const char *const reflog_exists_usage[] = {\n      \tBUILTIN_REFLOG_LIST_USAGE,\n      \tBUILTIN_REFLOG_EXPIRE_USAGE,\n      \tBUILTIN_REFLOG_DELETE_USAGE,\n    - \tBUILTIN_REFLOG_EXISTS_USAGE,\n     +\tBUILTIN_REFLOG_DROP_USAGE,\n    + \tBUILTIN_REFLOG_EXISTS_USAGE,\n      \tNULL\n      };\n    - \n     @@ builtin/reflog.c: static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,\n      \t\t\t\t   refname);\n      }\n    @@ builtin/reflog.c: static int cmd_reflog_exists(int argc, const char **argv, cons\n     +static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,\n     +\t\t\t   struct repository *repo)\n     +{\n    -+\tint i, ret, do_all;\n    ++\tint ret = 0, do_all = 0;\n     +\tconst struct option options[] = {\n     +\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"process the reflogs of all references\")),\n     +\t\tOPT_END()\n     +\t};\n     +\n    -+\tdo_all = ret = 0;\n     +\targc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);\n     +\n    ++\tif (argc && do_all)\n    ++\t\tdie(_(\"references specified along with --all\"));\n    ++\n     +\tif (do_all) {\n     +\t\tstruct worktree_reflogs collected = {\n     +\t\t\t.reflogs = STRING_LIST_INIT_DUP,\n    @@ builtin/reflog.c: static int cmd_reflog_exists(int argc, const char **argv, cons\n     +\t\tstring_list_clear(&collected.reflogs, 0);\n     +\t}\n     +\n    -+\tfor (i = 0; i < argc; i++) {\n    ++\tfor (int i = 0; i < argc; i++) {\n     +\t\tchar *ref;\n     +\t\tif (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {\n     +\t\t\tret |= error(_(\"%s points nowhere!\"), argv[i]);\n    @@ t/t1410-reflog.sh: test_expect_success 'reflog with invalid object ID can be lis\n     +\t(\n     +\t\tcd repo &&\n     +\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n    -+\t\ttest_must_fail git reflog drop refs/heads/non-existent\n    ++\t\ttest_must_fail git reflog drop refs/heads/non-existent 2>stderr &&\n    ++\t\ttest_grep \"error: refs/heads/non-existent points nowhere!\" stderr\n     +\t)\n     +'\n     +\n    @@ t/t1410-reflog.sh: test_expect_success 'reflog with invalid object ID can be lis\n     +\t\ttest_must_fail git reflog exists refs/heads/branch\n     +\t)\n     +'\n    ++\n    ++test_expect_success 'reflog drop --all with reference' '\n    ++\ttest_when_finished \"rm -rf repo\" &&\n    ++\tgit init repo &&\n    ++\t(\n    ++\t\tcd repo &&\n    ++\t\ttest_commit A &&\n    ++\t\ttest_must_fail git reflog drop --all refs/heads/main 2>stderr &&\n    ++\t\ttest_grep \"fatal: references specified along with --all\" stderr\n    ++\t)\n    ++'\n     +\n      test_done\n\n\nbase-commit: a36e024e989f4d35f35987a60e3af8022cac3420\nchange-id: 20250306-493-add-command-to-purge-reflog-entries-bd22547ad34a\n\nThanks\n- Karthik\n---\n\n---\n Documentation/git-reflog.adoc | 18 +++++++++---\n builtin/reflog.c              | 60 +++++++++++++++++++++++++++++++++++++-\n t/t1410-reflog.sh             | 67 +++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 140 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-reflog.adoc b/Documentation/git-reflog.adoc\nindex a929c52982..6ed98ddaef 100644\n--- a/Documentation/git-reflog.adoc\n+++ b/Documentation/git-reflog.adoc\n@@ -16,6 +16,7 @@ SYNOPSIS\n \t[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]\n 'git reflog delete' [--rewrite] [--updateref]\n \t[--dry-run | -n] [--verbose] <ref>@{<specifier>}...\n+'git reflog drop' [--all | <refs>...]\n 'git reflog exists' <ref>\n \n DESCRIPTION\n@@ -48,15 +49,19 @@ and not reachable from the current tip, are removed from the reflog.\n This is typically not used directly by end users -- instead, see\n linkgit:git-gc[1].\n \n-The \"delete\" subcommand deletes single entries from the reflog. Its\n-argument must be an _exact_ entry (e.g. \"`git reflog delete\n-master@{2}`\"). This subcommand is also typically not used directly by\n-end users.\n+The \"delete\" subcommand deletes single entries from the reflog, but\n+not the reflog itself. Its argument must be an _exact_ entry (e.g. \"`git\n+reflog delete master@{2}`\"). This subcommand is also typically not used\n+directly by end users.\n \n The \"exists\" subcommand checks whether a ref has a reflog.  It exits\n with zero status if the reflog exists, and non-zero status if it does\n not.\n \n+The \"drop\" subcommand completely removes the reflog for the specified\n+references. This is in contrast to \"expire\" and \"delete\", both of which\n+can be used to delete reflog entries, but not the reflog itself.\n+\n OPTIONS\n -------\n \n@@ -132,6 +137,11 @@ Options for `delete`\n `--dry-run`, and `--verbose`, with the same meanings as when they are\n used with `expire`.\n \n+Options for `drop`\n+~~~~~~~~~~~~~~~~~~~~\n+\n+--all::\n+\tDrop the reflogs of all references from all worktrees.\n \n GIT\n ---\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 95f264989b..cd93a0bef9 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -29,6 +29,9 @@\n #define BUILTIN_REFLOG_EXISTS_USAGE \\\n \tN_(\"git reflog exists <ref>\")\n \n+#define BUILTIN_REFLOG_DROP_USAGE \\\n+\tN_(\"git reflog drop [--all | <refs>...]\")\n+\n static const char *const reflog_show_usage[] = {\n \tBUILTIN_REFLOG_SHOW_USAGE,\n \tNULL,\n@@ -54,11 +57,17 @@ static const char *const reflog_exists_usage[] = {\n \tNULL,\n };\n \n+static const char *const reflog_drop_usage[] = {\n+\tBUILTIN_REFLOG_DROP_USAGE,\n+\tNULL,\n+};\n+\n static const char *const reflog_usage[] = {\n \tBUILTIN_REFLOG_SHOW_USAGE,\n \tBUILTIN_REFLOG_LIST_USAGE,\n \tBUILTIN_REFLOG_EXPIRE_USAGE,\n \tBUILTIN_REFLOG_DELETE_USAGE,\n+\tBUILTIN_REFLOG_DROP_USAGE,\n \tBUILTIN_REFLOG_EXISTS_USAGE,\n \tNULL\n };\n@@ -449,10 +458,58 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,\n \t\t\t\t   refname);\n }\n \n+static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,\n+\t\t\t   struct repository *repo)\n+{\n+\tint ret = 0, do_all = 0;\n+\tconst struct option options[] = {\n+\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"process the reflogs of all references\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);\n+\n+\tif (argc && do_all)\n+\t\tdie(_(\"references specified along with --all\"));\n+\n+\tif (do_all) {\n+\t\tstruct worktree_reflogs collected = {\n+\t\t\t.reflogs = STRING_LIST_INIT_DUP,\n+\t\t};\n+\t\tstruct string_list_item *item;\n+\t\tstruct worktree **worktrees, **p;\n+\n+\t\tworktrees = get_worktrees();\n+\t\tfor (p = worktrees; *p; p++) {\n+\t\t\tcollected.worktree = *p;\n+\t\t\trefs_for_each_reflog(get_worktree_ref_store(*p),\n+\t\t\t\t\t     collect_reflog, &collected);\n+\t\t}\n+\t\tfree_worktrees(worktrees);\n+\n+\t\tfor_each_string_list_item(item, &collected.reflogs)\n+\t\t\tret |= refs_delete_reflog(get_main_ref_store(repo),\n+\t\t\t\t\t\t     item->string);\n+\t\tstring_list_clear(&collected.reflogs, 0);\n+\t}\n+\n+\tfor (int i = 0; i < argc; i++) {\n+\t\tchar *ref;\n+\t\tif (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {\n+\t\t\tret |= error(_(\"%s points nowhere!\"), argv[i]);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tret |= refs_delete_reflog(get_main_ref_store(repo), ref);\n+\t\tfree(ref);\n+\t}\n+\n+\treturn ret;\n+}\n+\n /*\n  * main \"reflog\"\n  */\n-\n int cmd_reflog(int argc,\n \t       const char **argv,\n \t       const char *prefix,\n@@ -465,6 +522,7 @@ int cmd_reflog(int argc,\n \t\tOPT_SUBCOMMAND(\"expire\", &fn, cmd_reflog_expire),\n \t\tOPT_SUBCOMMAND(\"delete\", &fn, cmd_reflog_delete),\n \t\tOPT_SUBCOMMAND(\"exists\", &fn, cmd_reflog_exists),\n+\t\tOPT_SUBCOMMAND(\"drop\", &fn, cmd_reflog_drop),\n \t\tOPT_END()\n \t};\n \ndiff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh\nindex 388fdf9ae5..251caaf9a4 100755\n--- a/t/t1410-reflog.sh\n+++ b/t/t1410-reflog.sh\n@@ -551,4 +551,71 @@ test_expect_success 'reflog with invalid object ID can be listed' '\n \t)\n '\n \n+test_expect_success 'reflog drop non-existent ref' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n+\t\ttest_must_fail git reflog drop refs/heads/non-existent 2>stderr &&\n+\t\ttest_grep \"error: refs/heads/non-existent points nowhere!\" stderr\n+\t)\n+'\n+\n+test_expect_success 'reflog drop' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_commit A &&\n+\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n+\t\tgit reflog exists refs/heads/main &&\n+\t\tgit reflog exists refs/heads/branch &&\n+\t\tgit reflog drop refs/heads/main &&\n+\t\ttest_must_fail git reflog exists refs/heads/main &&\n+\t\tgit reflog exists refs/heads/branch\n+\t)\n+'\n+\n+test_expect_success 'reflog drop multiple references' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_commit A &&\n+\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n+\t\tgit reflog exists refs/heads/main &&\n+\t\tgit reflog exists refs/heads/branch &&\n+\t\tgit reflog drop refs/heads/main refs/heads/branch &&\n+\t\ttest_must_fail git reflog exists refs/heads/main &&\n+\t\ttest_must_fail git reflog exists refs/heads/branch\n+\t)\n+'\n+\n+test_expect_success 'reflog drop --all' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_commit A &&\n+\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n+\t\tgit reflog exists refs/heads/main &&\n+\t\tgit reflog exists refs/heads/branch &&\n+\t\tgit reflog drop --all &&\n+\t\ttest_must_fail git reflog exists refs/heads/main &&\n+\t\ttest_must_fail git reflog exists refs/heads/branch\n+\t)\n+'\n+\n+test_expect_success 'reflog drop --all with reference' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_commit A &&\n+\t\ttest_must_fail git reflog drop --all refs/heads/main 2>stderr &&\n+\t\ttest_grep \"fatal: references specified along with --all\" stderr\n+\t)\n+'\n+\n test_done\n\n\n\n"},{"id":"513901","messageId":"xmqqsenkaoy3.fsf@gitster.g","threadId":"63072","inReplyTo":"CAOLa=ZRqMnus=MZV1ASce4pxnMkVRGqhZZ+vZNcGhWdUQE-JRg@mail.gmail.com","subject":"Re: [PATCH 1/2] reflog: drop usage of global variables","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-03-10T15:24:36Z","receivedAt":"2025-03-10T15:24:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n>> but I also suspect that it is mostly due to the original program\n>> structure that uses OPT_SUBCOMMAND() that the subcommands fail to\n>> respond to \"-h\" unlike the top-level command, so this may not be a\n>> regression.  I do think however that this change is making it harder\n>> to fix.\n>>\n>\n> Hmm. But this is the existing behavior, no?\n\nDidn't I said I also suspect?\n\n> But point taken, I'll drop this patch in the next version! Thanks\n\nYup.  Take your time, as it is already deep in prerelease feature\nfreeze.  I'd prefer to see us leaving spare capacity in our minds to\nfix regressions introduced during this period once they are noticed,\nwithout getting distracted by shiny new toys.\n\nThanks.\n\n"},{"id":"513902","messageId":"xmqqo6y8aorb.fsf@gitster.g","threadId":"63072","inReplyTo":"ba1fbb96-ae87-40b5-814c-4fcea106f956@app.fastmail.com","subject":"Re: [PATCH 0/2] EDITME: cover title for 493-add-command-to-purge-reflog-entries","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-03-10T15:28:40Z","receivedAt":"2025-03-10T15:28:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Kristoffer Haugsbakk\" <kristofferhaugsbakk@fastmail.com> writes:\n\n> I’ve been wanting a command to drop reflogs.  I use `always` and get a\n> lot of entries that I don’t care about.  But I don’t want to set it to\n> `true` because I care about some of them.\n\nI am not sure what these references to always and true are about,\nbut nevertheless, instead of having to release entries one by one\nwith \"git reflog delete\", a command that lets you discard all the\nreflog entries for a given ref would be a great addition to the\ntoolset.\n\nThanks for commenting.\n"},{"id":"514025","messageId":"Z9E0oDgGOM1R-4mI@pks.im","threadId":"63072","inReplyTo":"20250310-493-add-command-to-purge-reflog-entries-v2-1-05caa92e0bfa@gmail.com","subject":"Re: [PATCH v2] reflog: implement subcommand to drop reflogs","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-03-12T07:15:44Z","receivedAt":"2025-03-12T07:15:52Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Mon, Mar 10, 2025 at 01:36:25PM +0100, Karthik Nayak wrote:\n> diff --git a/Documentation/git-reflog.adoc b/Documentation/git-reflog.adoc\n> index a929c52982..6ed98ddaef 100644\n> --- a/Documentation/git-reflog.adoc\n> +++ b/Documentation/git-reflog.adoc\n> @@ -16,6 +16,7 @@ SYNOPSIS\n>  \t[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]\n>  'git reflog delete' [--rewrite] [--updateref]\n>  \t[--dry-run | -n] [--verbose] <ref>@{<specifier>}...\n> +'git reflog drop' [--all | <refs>...]\n>  'git reflog exists' <ref>\n>  \n>  DESCRIPTION\n> @@ -48,15 +49,19 @@ and not reachable from the current tip, are removed from the reflog.\n>  This is typically not used directly by end users -- instead, see\n>  linkgit:git-gc[1].\n>  \n> -The \"delete\" subcommand deletes single entries from the reflog. Its\n> -argument must be an _exact_ entry (e.g. \"`git reflog delete\n> -master@{2}`\"). This subcommand is also typically not used directly by\n> -end users.\n> +The \"delete\" subcommand deletes single entries from the reflog, but\n> +not the reflog itself. Its argument must be an _exact_ entry (e.g. \"`git\n> +reflog delete master@{2}`\"). This subcommand is also typically not used\n> +directly by end users.\n>  \n>  The \"exists\" subcommand checks whether a ref has a reflog.  It exits\n>  with zero status if the reflog exists, and non-zero status if it does\n>  not.\n>  \n> +The \"drop\" subcommand completely removes the reflog for the specified\n> +references. This is in contrast to \"expire\" and \"delete\", both of which\n> +can be used to delete reflog entries, but not the reflog itself.\n> +\n\nI guess this paragraph should also moved between \"delete\" and \"exists\"\nnow.\n\n> diff --git a/builtin/reflog.c b/builtin/reflog.c\n> index 95f264989b..cd93a0bef9 100644\n> --- a/builtin/reflog.c\n> +++ b/builtin/reflog.c\n> @@ -449,10 +458,58 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,\n>  \t\t\t\t   refname);\n>  }\n>  \n> +static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,\n> +\t\t\t   struct repository *repo)\n> +{\n> +\tint ret = 0, do_all = 0;\n> +\tconst struct option options[] = {\n> +\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"process the reflogs of all references\")),\n> +\t\tOPT_END()\n> +\t};\n> +\n> +\targc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);\n> +\n> +\tif (argc && do_all)\n> +\t\tdie(_(\"references specified along with --all\"));\n\nWe should probably use `usage()` instead of `die()` here.\n\n> +\tif (do_all) {\n> +\t\tstruct worktree_reflogs collected = {\n> +\t\t\t.reflogs = STRING_LIST_INIT_DUP,\n> +\t\t};\n> +\t\tstruct string_list_item *item;\n> +\t\tstruct worktree **worktrees, **p;\n> +\n> +\t\tworktrees = get_worktrees();\n> +\t\tfor (p = worktrees; *p; p++) {\n> +\t\t\tcollected.worktree = *p;\n> +\t\t\trefs_for_each_reflog(get_worktree_ref_store(*p),\n> +\t\t\t\t\t     collect_reflog, &collected);\n> +\t\t}\n> +\t\tfree_worktrees(worktrees);\n> +\n> +\t\tfor_each_string_list_item(item, &collected.reflogs)\n> +\t\t\tret |= refs_delete_reflog(get_main_ref_store(repo),\n> +\t\t\t\t\t\t     item->string);\n> +\t\tstring_list_clear(&collected.reflogs, 0);\n> +\t}\n\nI noticed that `git reflog expire` has the same arguments to specify\nwhich reflogs to expire:\n\n    [--all [--single-worktree] | <refs>...]\n\nThe only exception is that they also support `--single-worktree` to only\nexpire relfogs from the current worktree. Supporting it should probably\nnot be too much work, so do we want to do so to have feature parity\nregarding the reflog selection?\n\n> +\tfor (int i = 0; i < argc; i++) {\n> +\t\tchar *ref;\n> +\t\tif (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {\n> +\t\t\tret |= error(_(\"%s points nowhere!\"), argv[i]);\n\nAs a user I wouldn't know what this error is trying to tell me. Does the\nreflog exist but it's a symreflog that points to another reflog that\ndoes not exist? Do its entries point nowhere?\n\nHow about: `error(_(\"reflog could not be found: '%s'\"))` instead? And\nseeing that you copied the error message from the \"expire\" subcommand\nwe could also adapt it in a preparatory commit.\n\n> +\t\t\tcontinue;\n> +\t\t}\n> +\n> +\t\tret |= refs_delete_reflog(get_main_ref_store(repo), ref);\n> +\t\tfree(ref);\n> +\t}\n\nThe code is correct, but do we want to maybe wrap this loop in the\n`else` branch to guide the reader and make it blindingly obvious that\nthe loop does nothing `if (do_all)`?\n\n> diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh\n> index 388fdf9ae5..251caaf9a4 100755\n> --- a/t/t1410-reflog.sh\n> +++ b/t/t1410-reflog.sh\n> @@ -551,4 +551,71 @@ test_expect_success 'reflog with invalid object ID can be listed' '\n>  \t)\n>  '\n>  \n> +test_expect_success 'reflog drop non-existent ref' '\n> +\ttest_when_finished \"rm -rf repo\" &&\n> +\tgit init repo &&\n> +\t(\n> +\t\tcd repo &&\n> +\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n> +\t\ttest_must_fail git reflog drop refs/heads/non-existent 2>stderr &&\n> +\t\ttest_grep \"error: refs/heads/non-existent points nowhere!\" stderr\n> +\t)\n> +'\n\nOne edge case that I haven't seen is to try and drop multiple\nreferences, some of which exist and some of which don't. The loops you\nhave seem to explicitly allow for deletion of only a subset, so it would\nbe nice to verify that the logic works as expected.\n\nPatrick\n"},{"id":"514181","messageId":"CAOLa=ZQq6ax6Ba4gLN8P_TTV-KOrfN5yyNJB98oFx4fd9SFkhQ@mail.gmail.com","threadId":"63072","inReplyTo":"xmqqsenkaoy3.fsf@gitster.g","subject":"Re: [PATCH 1/2] reflog: drop usage of global variables","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-13T13:30:43Z","receivedAt":"2025-03-13T13:30:44Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n>>> but I also suspect that it is mostly due to the original program\n>>> structure that uses OPT_SUBCOMMAND() that the subcommands fail to\n>>> respond to \"-h\" unlike the top-level command, so this may not be a\n>>> regression.  I do think however that this change is making it harder\n>>> to fix.\n>>>\n>>\n>> Hmm. But this is the existing behavior, no?\n>\n> Didn't I said I also suspect?\n>\n\nYes, I misunderstood!\n\n>> But point taken, I'll drop this patch in the next version! Thanks\n>\n> Yup.  Take your time, as it is already deep in prerelease feature\n> freeze.  I'd prefer to see us leaving spare capacity in our minds to\n> fix regressions introduced during this period once they are noticed,\n> without getting distracted by shiny new toys.\n>\n> Thanks.\n\nFair enough. I only pushed this new topic, because I had mentioned to\nyou that I would pick it up and I didn't want to forget about it.\n\nEither ways, I will take it slow here! :)\n"},{"id":"514189","messageId":"CAOLa=ZQ3Rgi+yyma0Zo6uK04eu9Lk1dk_uYTU-4hFfh9POTNYA@mail.gmail.com","threadId":"63072","inReplyTo":"Z9E0oDgGOM1R-4mI@pks.im","subject":"Re: [PATCH v2] reflog: implement subcommand to drop reflogs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-13T14:24:16Z","receivedAt":"2025-03-13T14:24:18Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> On Mon, Mar 10, 2025 at 01:36:25PM +0100, Karthik Nayak wrote:\n>> diff --git a/Documentation/git-reflog.adoc b/Documentation/git-reflog.adoc\n>> index a929c52982..6ed98ddaef 100644\n>> --- a/Documentation/git-reflog.adoc\n>> +++ b/Documentation/git-reflog.adoc\n>> @@ -16,6 +16,7 @@ SYNOPSIS\n>>  \t[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]\n>>  'git reflog delete' [--rewrite] [--updateref]\n>>  \t[--dry-run | -n] [--verbose] <ref>@{<specifier>}...\n>> +'git reflog drop' [--all | <refs>...]\n>>  'git reflog exists' <ref>\n>>\n>>  DESCRIPTION\n>> @@ -48,15 +49,19 @@ and not reachable from the current tip, are removed from the reflog.\n>>  This is typically not used directly by end users -- instead, see\n>>  linkgit:git-gc[1].\n>>\n>> -The \"delete\" subcommand deletes single entries from the reflog. Its\n>> -argument must be an _exact_ entry (e.g. \"`git reflog delete\n>> -master@{2}`\"). This subcommand is also typically not used directly by\n>> -end users.\n>> +The \"delete\" subcommand deletes single entries from the reflog, but\n>> +not the reflog itself. Its argument must be an _exact_ entry (e.g. \"`git\n>> +reflog delete master@{2}`\"). This subcommand is also typically not used\n>> +directly by end users.\n>>\n>>  The \"exists\" subcommand checks whether a ref has a reflog.  It exits\n>>  with zero status if the reflog exists, and non-zero status if it does\n>>  not.\n>>\n>> +The \"drop\" subcommand completely removes the reflog for the specified\n>> +references. This is in contrast to \"expire\" and \"delete\", both of which\n>> +can be used to delete reflog entries, but not the reflog itself.\n>> +\n>\n> I guess this paragraph should also moved between \"delete\" and \"exists\"\n> now.\n>\n\nYeah, make sense.\n\n>> diff --git a/builtin/reflog.c b/builtin/reflog.c\n>> index 95f264989b..cd93a0bef9 100644\n>> --- a/builtin/reflog.c\n>> +++ b/builtin/reflog.c\n>> @@ -449,10 +458,58 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,\n>>  \t\t\t\t   refname);\n>>  }\n>>\n>> +static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,\n>> +\t\t\t   struct repository *repo)\n>> +{\n>> +\tint ret = 0, do_all = 0;\n>> +\tconst struct option options[] = {\n>> +\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"process the reflogs of all references\")),\n>> +\t\tOPT_END()\n>> +\t};\n>> +\n>> +\targc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);\n>> +\n>> +\tif (argc && do_all)\n>> +\t\tdie(_(\"references specified along with --all\"));\n>\n> We should probably use `usage()` instead of `die()` here.\n>\n\nGood point.\n\n>> +\tif (do_all) {\n>> +\t\tstruct worktree_reflogs collected = {\n>> +\t\t\t.reflogs = STRING_LIST_INIT_DUP,\n>> +\t\t};\n>> +\t\tstruct string_list_item *item;\n>> +\t\tstruct worktree **worktrees, **p;\n>> +\n>> +\t\tworktrees = get_worktrees();\n>> +\t\tfor (p = worktrees; *p; p++) {\n>> +\t\t\tcollected.worktree = *p;\n>> +\t\t\trefs_for_each_reflog(get_worktree_ref_store(*p),\n>> +\t\t\t\t\t     collect_reflog, &collected);\n>> +\t\t}\n>> +\t\tfree_worktrees(worktrees);\n>> +\n>> +\t\tfor_each_string_list_item(item, &collected.reflogs)\n>> +\t\t\tret |= refs_delete_reflog(get_main_ref_store(repo),\n>> +\t\t\t\t\t\t     item->string);\n>> +\t\tstring_list_clear(&collected.reflogs, 0);\n>> +\t}\n>\n> I noticed that `git reflog expire` has the same arguments to specify\n> which reflogs to expire:\n>\n>     [--all [--single-worktree] | <refs>...]\n>\n> The only exception is that they also support `--single-worktree` to only\n> expire relfogs from the current worktree. Supporting it should probably\n> not be too much work, so do we want to do so to have feature parity\n> regarding the reflog selection?\n>\n\nI think it would make sense to add support for `--single-worktree`. Let\nme add that in, since it mostly a single-line change.\n\n>> +\tfor (int i = 0; i < argc; i++) {\n>> +\t\tchar *ref;\n>> +\t\tif (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {\n>> +\t\t\tret |= error(_(\"%s points nowhere!\"), argv[i]);\n>\n> As a user I wouldn't know what this error is trying to tell me. Does the\n> reflog exist but it's a symreflog that points to another reflog that\n> does not exist? Do its entries point nowhere?\n>\n> How about: `error(_(\"reflog could not be found: '%s'\"))` instead? And\n> seeing that you copied the error message from the \"expire\" subcommand\n> we could also adapt it in a preparatory commit.\n>\n\nThanks! let change it in both places.\n\n>> +\t\t\tcontinue;\n>> +\t\t}\n>> +\n>> +\t\tret |= refs_delete_reflog(get_main_ref_store(repo), ref);\n>> +\t\tfree(ref);\n>> +\t}\n>\n> The code is correct, but do we want to maybe wrap this loop in the\n> `else` branch to guide the reader and make it blindingly obvious that\n> the loop does nothing `if (do_all)`?\n>\n\nWouldn't it be simpler to return at the end of the `if (do_all)`? I've\nadded that, but if feel strongly about this form, happy to change it.\n\n>> diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh\n>> index 388fdf9ae5..251caaf9a4 100755\n>> --- a/t/t1410-reflog.sh\n>> +++ b/t/t1410-reflog.sh\n>> @@ -551,4 +551,71 @@ test_expect_success 'reflog with invalid object ID can be listed' '\n>>  \t)\n>>  '\n>>\n>> +test_expect_success 'reflog drop non-existent ref' '\n>> +\ttest_when_finished \"rm -rf repo\" &&\n>> +\tgit init repo &&\n>> +\t(\n>> +\t\tcd repo &&\n>> +\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n>> +\t\ttest_must_fail git reflog drop refs/heads/non-existent 2>stderr &&\n>> +\t\ttest_grep \"error: refs/heads/non-existent points nowhere!\" stderr\n>> +\t)\n>> +'\n>\n> One edge case that I haven't seen is to try and drop multiple\n> references, some of which exist and some of which don't. The loops you\n> have seem to explicitly allow for deletion of only a subset, so it would\n> be nice to verify that the logic works as expected.\n>\n\nGood catch, I also noticed that I didn't have a test for worktrees. So\nwill add that in too.\n\n> Patrick\n"},{"id":"514193","messageId":"Z9Lvc2YKr5jD5D6h@pks.im","threadId":"63072","inReplyTo":"CAOLa=ZQ3Rgi+yyma0Zo6uK04eu9Lk1dk_uYTU-4hFfh9POTNYA@mail.gmail.com","subject":"Re: [PATCH v2] reflog: implement subcommand to drop reflogs","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-03-13T14:45:07Z","receivedAt":"2025-03-13T14:45:12Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Thu, Mar 13, 2025 at 09:24:16AM -0500, Karthik Nayak wrote:\n> Patrick Steinhardt <ps@pks.im> writes:\n> > On Mon, Mar 10, 2025 at 01:36:25PM +0100, Karthik Nayak wrote:\n> >> +\t\t\tcontinue;\n> >> +\t\t}\n> >> +\n> >> +\t\tret |= refs_delete_reflog(get_main_ref_store(repo), ref);\n> >> +\t\tfree(ref);\n> >> +\t}\n> >\n> > The code is correct, but do we want to maybe wrap this loop in the\n> > `else` branch to guide the reader and make it blindingly obvious that\n> > the loop does nothing `if (do_all)`?\n> >\n> \n> Wouldn't it be simpler to return at the end of the `if (do_all)`? I've\n> added that, but if feel strongly about this form, happy to change it.\n\nI don't feel overly strong about it. I'm not a huge fan of early\nreturns as I think it's easier to reason about functions if they have a\ncommon exit path. But I can live with whatever solution you come up\nwith.\n\nPatrick\n"},{"id":"514255","messageId":"20250314-493-add-command-to-purge-reflog-entries-v3-0-c24e23a6146d@gmail.com","threadId":"63072","inReplyTo":"20250307-493-add-command-to-purge-reflog-entries-v1-0-84ab8529cf9e@gmail.com","subject":"[PATCH v3 0/2] reflog: implement subcommand to drop reflogs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-14T08:40:33Z","receivedAt":"2025-03-14T08:40:50Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"While 'git-reflog(1)' currently allows users to expire reflogs and\ndelete individual entries, it lacks functionality to completely remove\nreflogs for specific references. This becomes problematic in\nrepositories where reflogs are not needed but continue to accumulate\nentries despite setting 'core.logAllRefUpdates=false'.\n\nAdd a new 'drop' subcommand to git-reflog that allows users to delete\nthe entire reflog for a specified reference. Include a '--all' flag to\nenable dropping all reflogs from all worktrees and an addon flag\n'--single-worktree', to drop all reflogs from the current worktree.\n\nThe first patch is a small cleanup in 'git refs expire' which improves\nthe error message used when there is no reflog present for a given\nreference.\n\nChanges in v3:\n- Add a preparatory commit to fix the error message in 'git reflog\n  expire' when a non-existent ref is provided.\n- Add support for '--single-worktree' to provide feature parity with\n  'git reflog expire'.\n- Improved error message and small code fixes.\n- Added some additional tests.\n- Link to v2: https://lore.kernel.org/r/20250310-493-add-command-to-purge-reflog-entries-v2-1-05caa92e0bfa@gmail.com\n\nChanges in v2:\n- Rephrase the commit message to be clearer and fix typo.\n- Move the documentation to be next to 'git reflog delete' and also\n  add missing documentation for the '--all' flag.\n- Ensure '--all' is not used with references and add a test.\n- Cleanup variable assignment.\n- Check for error message in the test.\n- Drop the cleanup commit.\n- Rebased on top of master a36e024e98 (Merge branch 'js/win-2.49-build-fixes',\n  2025-03-06), this was to include the adoc changes which were breaking\n  tests on the CI.\n- Link to v1: https://lore.kernel.org/r/20250307-493-add-command-to-purge-reflog-entries-v1-0-84ab8529cf9e@gmail.com\n\n Documentation/git-reflog.adoc |  23 ++++++--\n builtin/reflog.c              |  68 ++++++++++++++++++++++-\n t/t1410-reflog.sh             | 126 +++++++++++++++++++++++++++++++++++++++++-\n 3 files changed, 209 insertions(+), 8 deletions(-)\n\nKarthik Nayak (2):\n      reflog: improve error for when reflog is not found\n      reflog: implement subcommand to drop reflogs\n\nRange-diff versus v2:\n\n-:  ---------- > 1:  d4c4bf8c99 reflog: improve error for when reflog is not found\n1:  9405eaacb7 ! 2:  a7f88d71da reflog: implement subcommand to drop reflogs\n    @@ Commit message\n         entries despite setting 'core.logAllRefUpdates=false'.\n     \n         Add a new 'drop' subcommand to git-reflog that allows users to delete\n    -    the entire reflog for a specified reference. Include a '--all' flag to\n    -    enable dropping all reflogs in a repository.\n    +    the entire reflog for a specified reference. Include an '--all' flag to\n    +    enable dropping all reflogs from all worktrees and an addon flag\n    +    '--single-worktree', to only drop all reflogs from the current worktree.\n     \n         While here, remove an extraneous newline in the file.\n     \n    @@ Documentation/git-reflog.adoc: SYNOPSIS\n      \t[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]\n      'git reflog delete' [--rewrite] [--updateref]\n      \t[--dry-run | -n] [--verbose] <ref>@{<specifier>}...\n    -+'git reflog drop' [--all | <refs>...]\n    ++'git reflog drop' [--all [--single-worktree] | <refs>...]\n      'git reflog exists' <ref>\n      \n      DESCRIPTION\n    @@ Documentation/git-reflog.adoc: and not reachable from the current tip, are remov\n     +not the reflog itself. Its argument must be an _exact_ entry (e.g. \"`git\n     +reflog delete master@{2}`\"). This subcommand is also typically not used\n     +directly by end users.\n    - \n    - The \"exists\" subcommand checks whether a ref has a reflog.  It exits\n    - with zero status if the reflog exists, and non-zero status if it does\n    - not.\n    - \n    ++\n     +The \"drop\" subcommand completely removes the reflog for the specified\n     +references. This is in contrast to \"expire\" and \"delete\", both of which\n     +can be used to delete reflog entries, but not the reflog itself.\n    -+\n    - OPTIONS\n    - -------\n      \n    + The \"exists\" subcommand checks whether a ref has a reflog.  It exits\n    + with zero status if the reflog exists, and non-zero status if it does\n     @@ Documentation/git-reflog.adoc: Options for `delete`\n      `--dry-run`, and `--verbose`, with the same meanings as when they are\n      used with `expire`.\n    @@ Documentation/git-reflog.adoc: Options for `delete`\n     +\n     +--all::\n     +\tDrop the reflogs of all references from all worktrees.\n    ++\n    ++--single-worktree::\n    ++\tBy default when `--all` is specified, reflogs from all working\n    ++\ttrees are dropped. This option limits the processing to reflogs\n    ++\tfrom the current working tree only.\n      \n      GIT\n      ---\n    @@ builtin/reflog.c\n      \tN_(\"git reflog exists <ref>\")\n      \n     +#define BUILTIN_REFLOG_DROP_USAGE \\\n    -+\tN_(\"git reflog drop [--all | <refs>...]\")\n    ++\tN_(\"git reflog drop [--all [--single-worktree] | <refs>...]\")\n     +\n      static const char *const reflog_show_usage[] = {\n      \tBUILTIN_REFLOG_SHOW_USAGE,\n    @@ builtin/reflog.c: static int cmd_reflog_exists(int argc, const char **argv, cons\n     +static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,\n     +\t\t\t   struct repository *repo)\n     +{\n    -+\tint ret = 0, do_all = 0;\n    ++\tint ret = 0, do_all = 0, single_worktree = 0;\n     +\tconst struct option options[] = {\n    -+\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"process the reflogs of all references\")),\n    ++\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"drop the reflogs of all references\")),\n    ++\t\tOPT_BOOL(0, \"single-worktree\", &single_worktree,\n    ++\t\t\t N_(\"drop reflogs from the current worktree only\")),\n     +\t\tOPT_END()\n     +\t};\n     +\n     +\targc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);\n     +\n     +\tif (argc && do_all)\n    -+\t\tdie(_(\"references specified along with --all\"));\n    ++\t\tusage(_(\"references specified along with --all\"));\n     +\n     +\tif (do_all) {\n     +\t\tstruct worktree_reflogs collected = {\n    @@ builtin/reflog.c: static int cmd_reflog_exists(int argc, const char **argv, cons\n     +\n     +\t\tworktrees = get_worktrees();\n     +\t\tfor (p = worktrees; *p; p++) {\n    ++\t\t\tif (single_worktree && !(*p)->is_current)\n    ++\t\t\t\tcontinue;\n     +\t\t\tcollected.worktree = *p;\n     +\t\t\trefs_for_each_reflog(get_worktree_ref_store(*p),\n     +\t\t\t\t\t     collect_reflog, &collected);\n    @@ builtin/reflog.c: static int cmd_reflog_exists(int argc, const char **argv, cons\n     +\t\t\tret |= refs_delete_reflog(get_main_ref_store(repo),\n     +\t\t\t\t\t\t     item->string);\n     +\t\tstring_list_clear(&collected.reflogs, 0);\n    ++\n    ++\t\treturn ret;\n     +\t}\n     +\n     +\tfor (int i = 0; i < argc; i++) {\n     +\t\tchar *ref;\n     +\t\tif (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {\n    -+\t\t\tret |= error(_(\"%s points nowhere!\"), argv[i]);\n    ++\t\t\tret |= error(_(\"reflog could not be found: '%s'\"), argv[i]);\n     +\t\t\tcontinue;\n     +\t\t}\n     +\n    @@ t/t1410-reflog.sh: test_expect_success 'reflog with invalid object ID can be lis\n     +\t\tcd repo &&\n     +\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n     +\t\ttest_must_fail git reflog drop refs/heads/non-existent 2>stderr &&\n    -+\t\ttest_grep \"error: refs/heads/non-existent points nowhere!\" stderr\n    ++\t\ttest_grep \"error: reflog could not be found: ${SQ}refs/heads/non-existent${SQ}\" stderr\n     +\t)\n     +'\n     +\n    @@ t/t1410-reflog.sh: test_expect_success 'reflog with invalid object ID can be lis\n     +\t)\n     +'\n     +\n    ++test_expect_success 'reflog drop multiple references some non-existent' '\n    ++\ttest_when_finished \"rm -rf repo\" &&\n    ++\tgit init repo &&\n    ++\t(\n    ++\t\tcd repo &&\n    ++\t\ttest_commit A &&\n    ++\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n    ++\t\tgit reflog exists refs/heads/main &&\n    ++\t\tgit reflog exists refs/heads/branch &&\n    ++\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n    ++\t\ttest_must_fail git reflog drop refs/heads/main refs/heads/non-existent refs/heads/branch 2>stderr &&\n    ++\t\ttest_must_fail git reflog exists refs/heads/main &&\n    ++\t\ttest_must_fail git reflog exists refs/heads/branch &&\n    ++\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n    ++\t\ttest_grep \"error: reflog could not be found: ${SQ}refs/heads/non-existent${SQ}\" stderr\n    ++\t)\n    ++'\n    ++\n     +test_expect_success 'reflog drop --all' '\n     +\ttest_when_finished \"rm -rf repo\" &&\n     +\tgit init repo &&\n    @@ t/t1410-reflog.sh: test_expect_success 'reflog with invalid object ID can be lis\n     +\t)\n     +'\n     +\n    ++test_expect_success 'reflog drop --all multiple worktrees' '\n    ++\ttest_when_finished \"rm -rf repo\" &&\n    ++\ttest_when_finished \"rm -rf wt\" &&\n    ++\tgit init repo &&\n    ++\t(\n    ++\t\tcd repo &&\n    ++\t\ttest_commit A &&\n    ++\t\tgit worktree add ../wt &&\n    ++\t\ttest_commit_bulk -C ../wt --ref=refs/heads/branch 1 &&\n    ++\t\tgit reflog exists refs/heads/main &&\n    ++\t\tgit reflog exists refs/heads/branch &&\n    ++\t\tgit reflog drop --all &&\n    ++\t\ttest_must_fail git reflog exists refs/heads/main &&\n    ++\t\ttest_must_fail git reflog exists refs/heads/branch\n    ++\t)\n    ++'\n    ++\n    ++test_expect_success 'reflog drop --all --single-worktree' '\n    ++\ttest_when_finished \"rm -rf repo\" &&\n    ++\ttest_when_finished \"rm -rf wt\" &&\n    ++\tgit init repo &&\n    ++\t(\n    ++\t\tcd repo &&\n    ++\t\ttest_commit A &&\n    ++\t\tgit worktree add ../wt &&\n    ++\t\ttest_commit -C ../wt foobar &&\n    ++\t\tgit reflog exists refs/heads/main &&\n    ++\t\tgit reflog exists refs/heads/wt &&\n    ++\t\ttest-tool ref-store worktree:wt reflog-exists HEAD &&\n    ++\t\tgit reflog drop --all --single-worktree &&\n    ++\t\ttest_must_fail git reflog exists refs/heads/main &&\n    ++\t\ttest_must_fail git reflog exists refs/heads/wt &&\n    ++\t\ttest_must_fail test-tool ref-store worktree:main reflog-exists HEAD &&\n    ++\t\ttest-tool ref-store worktree:wt reflog-exists HEAD\n    ++\t)\n    ++'\n    ++\n     +test_expect_success 'reflog drop --all with reference' '\n     +\ttest_when_finished \"rm -rf repo\" &&\n     +\tgit init repo &&\n    @@ t/t1410-reflog.sh: test_expect_success 'reflog with invalid object ID can be lis\n     +\t\tcd repo &&\n     +\t\ttest_commit A &&\n     +\t\ttest_must_fail git reflog drop --all refs/heads/main 2>stderr &&\n    -+\t\ttest_grep \"fatal: references specified along with --all\" stderr\n    ++\t\ttest_grep \"usage: references specified along with --all\" stderr\n     +\t)\n     +'\n     +\n\n\nbase-commit: a36e024e989f4d35f35987a60e3af8022cac3420\nchange-id: 20250306-493-add-command-to-purge-reflog-entries-bd22547ad34a\n\nThanks\n- Karthik\n\n"},{"id":"514256","messageId":"20250314-493-add-command-to-purge-reflog-entries-v3-1-c24e23a6146d@gmail.com","threadId":"63072","inReplyTo":"20250314-493-add-command-to-purge-reflog-entries-v3-0-c24e23a6146d@gmail.com","subject":"[PATCH v3 1/2] reflog: improve error for when reflog is not found","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-14T08:40:34Z","receivedAt":"2025-03-14T08:40:50Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"The 'git reflog expire' prints the error message '<ref> points nowhere!'\nwhen used with a non-existent ref. This message is a bit confusing and\nvague. Modify the message to be more clear and direct.\n\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n builtin/reflog.c  | 2 +-\n t/t1410-reflog.sh | 4 ++--\n 2 files changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 95f264989b..762719315e 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -383,7 +383,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,\n \t\tstruct expire_reflog_policy_cb cb = { .cmd = cmd };\n \n \t\tif (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) {\n-\t\t\tstatus |= error(_(\"%s points nowhere!\"), argv[i]);\n+\t\t\tstatus |= error(_(\"reflog could not be found: '%s'\"), argv[i]);\n \t\t\tcontinue;\n \t\t}\n \t\tset_reflog_expiry_param(&cb.cmd, ref);\ndiff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh\nindex 388fdf9ae5..1f7249be76 100755\n--- a/t/t1410-reflog.sh\n+++ b/t/t1410-reflog.sh\n@@ -315,9 +315,9 @@ test_expect_success 'git reflog expire unknown reference' '\n \ttest_config gc.reflogexpireunreachable never &&\n \n \ttest_must_fail git reflog expire main@{123} 2>stderr &&\n-\ttest_grep \"points nowhere\" stderr &&\n+\ttest_grep \"error: reflog could not be found: ${SQ}main@{123}${SQ}\" stderr &&\n \ttest_must_fail git reflog expire does-not-exist 2>stderr &&\n-\ttest_grep \"points nowhere\" stderr\n+\ttest_grep \"error: reflog could not be found: ${SQ}does-not-exist${SQ}\" stderr\n '\n \n test_expect_success 'checkout should not delete log for packed ref' '\n\n-- \n2.48.1\n\n"},{"id":"514257","messageId":"20250314-493-add-command-to-purge-reflog-entries-v3-2-c24e23a6146d@gmail.com","threadId":"63072","inReplyTo":"20250314-493-add-command-to-purge-reflog-entries-v3-0-c24e23a6146d@gmail.com","subject":"[PATCH v3 2/2] reflog: implement subcommand to drop reflogs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-14T08:40:35Z","receivedAt":"2025-03-14T08:40:51Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"While 'git-reflog(1)' currently allows users to expire reflogs and\ndelete individual entries, it lacks functionality to completely remove\nreflogs for specific references. This becomes problematic in\nrepositories where reflogs are not needed but continue to accumulate\nentries despite setting 'core.logAllRefUpdates=false'.\n\nAdd a new 'drop' subcommand to git-reflog that allows users to delete\nthe entire reflog for a specified reference. Include an '--all' flag to\nenable dropping all reflogs from all worktrees and an addon flag\n'--single-worktree', to only drop all reflogs from the current worktree.\n\nWhile here, remove an extraneous newline in the file.\n\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\n---\n Documentation/git-reflog.adoc |  23 ++++++--\n builtin/reflog.c              |  66 ++++++++++++++++++++++-\n t/t1410-reflog.sh             | 122 ++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 206 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-reflog.adoc b/Documentation/git-reflog.adoc\nindex a929c52982..b55c060569 100644\n--- a/Documentation/git-reflog.adoc\n+++ b/Documentation/git-reflog.adoc\n@@ -16,6 +16,7 @@ SYNOPSIS\n \t[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]\n 'git reflog delete' [--rewrite] [--updateref]\n \t[--dry-run | -n] [--verbose] <ref>@{<specifier>}...\n+'git reflog drop' [--all [--single-worktree] | <refs>...]\n 'git reflog exists' <ref>\n \n DESCRIPTION\n@@ -48,10 +49,14 @@ and not reachable from the current tip, are removed from the reflog.\n This is typically not used directly by end users -- instead, see\n linkgit:git-gc[1].\n \n-The \"delete\" subcommand deletes single entries from the reflog. Its\n-argument must be an _exact_ entry (e.g. \"`git reflog delete\n-master@{2}`\"). This subcommand is also typically not used directly by\n-end users.\n+The \"delete\" subcommand deletes single entries from the reflog, but\n+not the reflog itself. Its argument must be an _exact_ entry (e.g. \"`git\n+reflog delete master@{2}`\"). This subcommand is also typically not used\n+directly by end users.\n+\n+The \"drop\" subcommand completely removes the reflog for the specified\n+references. This is in contrast to \"expire\" and \"delete\", both of which\n+can be used to delete reflog entries, but not the reflog itself.\n \n The \"exists\" subcommand checks whether a ref has a reflog.  It exits\n with zero status if the reflog exists, and non-zero status if it does\n@@ -132,6 +137,16 @@ Options for `delete`\n `--dry-run`, and `--verbose`, with the same meanings as when they are\n used with `expire`.\n \n+Options for `drop`\n+~~~~~~~~~~~~~~~~~~~~\n+\n+--all::\n+\tDrop the reflogs of all references from all worktrees.\n+\n+--single-worktree::\n+\tBy default when `--all` is specified, reflogs from all working\n+\ttrees are dropped. This option limits the processing to reflogs\n+\tfrom the current working tree only.\n \n GIT\n ---\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 762719315e..a3652e69f1 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -29,6 +29,9 @@\n #define BUILTIN_REFLOG_EXISTS_USAGE \\\n \tN_(\"git reflog exists <ref>\")\n \n+#define BUILTIN_REFLOG_DROP_USAGE \\\n+\tN_(\"git reflog drop [--all [--single-worktree] | <refs>...]\")\n+\n static const char *const reflog_show_usage[] = {\n \tBUILTIN_REFLOG_SHOW_USAGE,\n \tNULL,\n@@ -54,11 +57,17 @@ static const char *const reflog_exists_usage[] = {\n \tNULL,\n };\n \n+static const char *const reflog_drop_usage[] = {\n+\tBUILTIN_REFLOG_DROP_USAGE,\n+\tNULL,\n+};\n+\n static const char *const reflog_usage[] = {\n \tBUILTIN_REFLOG_SHOW_USAGE,\n \tBUILTIN_REFLOG_LIST_USAGE,\n \tBUILTIN_REFLOG_EXPIRE_USAGE,\n \tBUILTIN_REFLOG_DELETE_USAGE,\n+\tBUILTIN_REFLOG_DROP_USAGE,\n \tBUILTIN_REFLOG_EXISTS_USAGE,\n \tNULL\n };\n@@ -449,10 +458,64 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,\n \t\t\t\t   refname);\n }\n \n+static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,\n+\t\t\t   struct repository *repo)\n+{\n+\tint ret = 0, do_all = 0, single_worktree = 0;\n+\tconst struct option options[] = {\n+\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"drop the reflogs of all references\")),\n+\t\tOPT_BOOL(0, \"single-worktree\", &single_worktree,\n+\t\t\t N_(\"drop reflogs from the current worktree only\")),\n+\t\tOPT_END()\n+\t};\n+\n+\targc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);\n+\n+\tif (argc && do_all)\n+\t\tusage(_(\"references specified along with --all\"));\n+\n+\tif (do_all) {\n+\t\tstruct worktree_reflogs collected = {\n+\t\t\t.reflogs = STRING_LIST_INIT_DUP,\n+\t\t};\n+\t\tstruct string_list_item *item;\n+\t\tstruct worktree **worktrees, **p;\n+\n+\t\tworktrees = get_worktrees();\n+\t\tfor (p = worktrees; *p; p++) {\n+\t\t\tif (single_worktree && !(*p)->is_current)\n+\t\t\t\tcontinue;\n+\t\t\tcollected.worktree = *p;\n+\t\t\trefs_for_each_reflog(get_worktree_ref_store(*p),\n+\t\t\t\t\t     collect_reflog, &collected);\n+\t\t}\n+\t\tfree_worktrees(worktrees);\n+\n+\t\tfor_each_string_list_item(item, &collected.reflogs)\n+\t\t\tret |= refs_delete_reflog(get_main_ref_store(repo),\n+\t\t\t\t\t\t     item->string);\n+\t\tstring_list_clear(&collected.reflogs, 0);\n+\n+\t\treturn ret;\n+\t}\n+\n+\tfor (int i = 0; i < argc; i++) {\n+\t\tchar *ref;\n+\t\tif (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {\n+\t\t\tret |= error(_(\"reflog could not be found: '%s'\"), argv[i]);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tret |= refs_delete_reflog(get_main_ref_store(repo), ref);\n+\t\tfree(ref);\n+\t}\n+\n+\treturn ret;\n+}\n+\n /*\n  * main \"reflog\"\n  */\n-\n int cmd_reflog(int argc,\n \t       const char **argv,\n \t       const char *prefix,\n@@ -465,6 +528,7 @@ int cmd_reflog(int argc,\n \t\tOPT_SUBCOMMAND(\"expire\", &fn, cmd_reflog_expire),\n \t\tOPT_SUBCOMMAND(\"delete\", &fn, cmd_reflog_delete),\n \t\tOPT_SUBCOMMAND(\"exists\", &fn, cmd_reflog_exists),\n+\t\tOPT_SUBCOMMAND(\"drop\", &fn, cmd_reflog_drop),\n \t\tOPT_END()\n \t};\n \ndiff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh\nindex 1f7249be76..42b501f163 100755\n--- a/t/t1410-reflog.sh\n+++ b/t/t1410-reflog.sh\n@@ -551,4 +551,126 @@ test_expect_success 'reflog with invalid object ID can be listed' '\n \t)\n '\n \n+test_expect_success 'reflog drop non-existent ref' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n+\t\ttest_must_fail git reflog drop refs/heads/non-existent 2>stderr &&\n+\t\ttest_grep \"error: reflog could not be found: ${SQ}refs/heads/non-existent${SQ}\" stderr\n+\t)\n+'\n+\n+test_expect_success 'reflog drop' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_commit A &&\n+\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n+\t\tgit reflog exists refs/heads/main &&\n+\t\tgit reflog exists refs/heads/branch &&\n+\t\tgit reflog drop refs/heads/main &&\n+\t\ttest_must_fail git reflog exists refs/heads/main &&\n+\t\tgit reflog exists refs/heads/branch\n+\t)\n+'\n+\n+test_expect_success 'reflog drop multiple references' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_commit A &&\n+\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n+\t\tgit reflog exists refs/heads/main &&\n+\t\tgit reflog exists refs/heads/branch &&\n+\t\tgit reflog drop refs/heads/main refs/heads/branch &&\n+\t\ttest_must_fail git reflog exists refs/heads/main &&\n+\t\ttest_must_fail git reflog exists refs/heads/branch\n+\t)\n+'\n+\n+test_expect_success 'reflog drop multiple references some non-existent' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_commit A &&\n+\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n+\t\tgit reflog exists refs/heads/main &&\n+\t\tgit reflog exists refs/heads/branch &&\n+\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n+\t\ttest_must_fail git reflog drop refs/heads/main refs/heads/non-existent refs/heads/branch 2>stderr &&\n+\t\ttest_must_fail git reflog exists refs/heads/main &&\n+\t\ttest_must_fail git reflog exists refs/heads/branch &&\n+\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n+\t\ttest_grep \"error: reflog could not be found: ${SQ}refs/heads/non-existent${SQ}\" stderr\n+\t)\n+'\n+\n+test_expect_success 'reflog drop --all' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_commit A &&\n+\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n+\t\tgit reflog exists refs/heads/main &&\n+\t\tgit reflog exists refs/heads/branch &&\n+\t\tgit reflog drop --all &&\n+\t\ttest_must_fail git reflog exists refs/heads/main &&\n+\t\ttest_must_fail git reflog exists refs/heads/branch\n+\t)\n+'\n+\n+test_expect_success 'reflog drop --all multiple worktrees' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\ttest_when_finished \"rm -rf wt\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_commit A &&\n+\t\tgit worktree add ../wt &&\n+\t\ttest_commit_bulk -C ../wt --ref=refs/heads/branch 1 &&\n+\t\tgit reflog exists refs/heads/main &&\n+\t\tgit reflog exists refs/heads/branch &&\n+\t\tgit reflog drop --all &&\n+\t\ttest_must_fail git reflog exists refs/heads/main &&\n+\t\ttest_must_fail git reflog exists refs/heads/branch\n+\t)\n+'\n+\n+test_expect_success 'reflog drop --all --single-worktree' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\ttest_when_finished \"rm -rf wt\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_commit A &&\n+\t\tgit worktree add ../wt &&\n+\t\ttest_commit -C ../wt foobar &&\n+\t\tgit reflog exists refs/heads/main &&\n+\t\tgit reflog exists refs/heads/wt &&\n+\t\ttest-tool ref-store worktree:wt reflog-exists HEAD &&\n+\t\tgit reflog drop --all --single-worktree &&\n+\t\ttest_must_fail git reflog exists refs/heads/main &&\n+\t\ttest_must_fail git reflog exists refs/heads/wt &&\n+\t\ttest_must_fail test-tool ref-store worktree:main reflog-exists HEAD &&\n+\t\ttest-tool ref-store worktree:wt reflog-exists HEAD\n+\t)\n+'\n+\n+test_expect_success 'reflog drop --all with reference' '\n+\ttest_when_finished \"rm -rf repo\" &&\n+\tgit init repo &&\n+\t(\n+\t\tcd repo &&\n+\t\ttest_commit A &&\n+\t\ttest_must_fail git reflog drop --all refs/heads/main 2>stderr &&\n+\t\ttest_grep \"usage: references specified along with --all\" stderr\n+\t)\n+'\n+\n test_done\n\n-- \n2.48.1\n\n"},{"id":"514526","messageId":"CAP8UFD2+xQQbUevHTQangg__dq2M615pHLhYYUf7b9shpShD9Q@mail.gmail.com","threadId":"63072","inReplyTo":"20250314-493-add-command-to-purge-reflog-entries-v3-2-c24e23a6146d@gmail.com","subject":"Re: [PATCH v3 2/2] reflog: implement subcommand to drop reflogs","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2025-03-18T14:01:16Z","receivedAt":"2025-03-18T14:01:31Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On Fri, Mar 14, 2025 at 9:41 AM Karthik Nayak <karthik.188@gmail.com> wrote:\n\n> +Options for `drop`\n> +~~~~~~~~~~~~~~~~~~~~\n> +\n> +--all::\n> +       Drop the reflogs of all references from all worktrees.\n> +\n> +--single-worktree::\n> +       By default when `--all` is specified, reflogs from all working\n> +       trees are dropped. This option limits the processing to reflogs\n> +       from the current working tree only.\n\nIt seems to me that \"--current-worktree\" would have been clearer than\n\"--single-worktree\", but I understand that it would have been\nconfusing to have a different name for basically the same option in\n`git reflog expire` and `git reflog drop`.\n\n> +       argc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);\n> +\n> +       if (argc && do_all)\n> +               usage(_(\"references specified along with --all\"));\n> +\n> +       if (do_all) {\n> +               struct worktree_reflogs collected = {\n> +                       .reflogs = STRING_LIST_INIT_DUP,\n> +               };\n> +               struct string_list_item *item;\n> +               struct worktree **worktrees, **p;\n> +\n> +               worktrees = get_worktrees();\n> +               for (p = worktrees; *p; p++) {\n> +                       if (single_worktree && !(*p)->is_current)\n\nIt looks like 'single_worktree' is only used here. This means that if\na user forgets to add --all and only uses --single-worktree, nothing\nwill happen and it seems to me that the command will exit with code 0.\nEven if `git reflog expire` already works like that, I think this is a\nbit unfortunate.\n\nOtherwise this patch series looks very well done to me.\n\nThanks!\n"},{"id":"514532","messageId":"8734fagwn2.fsf@iotcl.com","threadId":"63072","inReplyTo":"20250314-493-add-command-to-purge-reflog-entries-v3-2-c24e23a6146d@gmail.com","subject":"Re: [PATCH v3 2/2] reflog: implement subcommand to drop reflogs","fromName":"Toon Claes","fromEmail":"toon@iotcl.com","sentAt":"2025-03-18T15:56:49Z","receivedAt":"2025-03-18T15:57:02Z","isPatch":true,"sender":{"key":"toon@iotcl.com","avatar":"https://avatars.githubusercontent.com/u/121621?v=4"},"body":"Karthik Nayak <karthik.188@gmail.com> writes:\n\n> While 'git-reflog(1)' currently allows users to expire reflogs and\n> delete individual entries, it lacks functionality to completely remove\n> reflogs for specific references. This becomes problematic in\n> repositories where reflogs are not needed but continue to accumulate\n> entries despite setting 'core.logAllRefUpdates=false'.\n>\n> Add a new 'drop' subcommand to git-reflog that allows users to delete\n> the entire reflog for a specified reference. Include an '--all' flag to\n> enable dropping all reflogs from all worktrees and an addon flag\n> '--single-worktree', to only drop all reflogs from the current worktree.\n>\n> While here, remove an extraneous newline in the file.\n>\n> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n> ---\n>  Documentation/git-reflog.adoc |  23 ++++++--\n>  builtin/reflog.c              |  66 ++++++++++++++++++++++-\n>  t/t1410-reflog.sh             | 122 ++++++++++++++++++++++++++++++++++++++++++\n>  3 files changed, 206 insertions(+), 5 deletions(-)\n>\n> diff --git a/Documentation/git-reflog.adoc b/Documentation/git-reflog.adoc\n> index a929c52982..b55c060569 100644\n> --- a/Documentation/git-reflog.adoc\n> +++ b/Documentation/git-reflog.adoc\n> @@ -16,6 +16,7 @@ SYNOPSIS\n>  \t[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]\n>  'git reflog delete' [--rewrite] [--updateref]\n>  \t[--dry-run | -n] [--verbose] <ref>@{<specifier>}...\n> +'git reflog drop' [--all [--single-worktree] | <refs>...]\n>  'git reflog exists' <ref>\n>  \n>  DESCRIPTION\n> @@ -48,10 +49,14 @@ and not reachable from the current tip, are removed from the reflog.\n>  This is typically not used directly by end users -- instead, see\n>  linkgit:git-gc[1].\n>  \n> -The \"delete\" subcommand deletes single entries from the reflog. Its\n> -argument must be an _exact_ entry (e.g. \"`git reflog delete\n> -master@{2}`\"). This subcommand is also typically not used directly by\n> -end users.\n> +The \"delete\" subcommand deletes single entries from the reflog, but\n> +not the reflog itself. Its argument must be an _exact_ entry (e.g. \"`git\n> +reflog delete master@{2}`\"). This subcommand is also typically not used\n> +directly by end users.\n> +\n> +The \"drop\" subcommand completely removes the reflog for the specified\n> +references. This is in contrast to \"expire\" and \"delete\", both of which\n> +can be used to delete reflog entries, but not the reflog itself.\n>  \n>  The \"exists\" subcommand checks whether a ref has a reflog.  It exits\n>  with zero status if the reflog exists, and non-zero status if it does\n> @@ -132,6 +137,16 @@ Options for `delete`\n>  `--dry-run`, and `--verbose`, with the same meanings as when they are\n>  used with `expire`.\n>  \n> +Options for `drop`\n> +~~~~~~~~~~~~~~~~~~~~\n> +\n> +--all::\n> +\tDrop the reflogs of all references from all worktrees.\n> +\n> +--single-worktree::\n> +\tBy default when `--all` is specified, reflogs from all working\n> +\ttrees are dropped. This option limits the processing to reflogs\n> +\tfrom the current working tree only.\n>  \n>  GIT\n>  ---\n> diff --git a/builtin/reflog.c b/builtin/reflog.c\n> index 762719315e..a3652e69f1 100644\n> --- a/builtin/reflog.c\n> +++ b/builtin/reflog.c\n> @@ -29,6 +29,9 @@\n>  #define BUILTIN_REFLOG_EXISTS_USAGE \\\n>  \tN_(\"git reflog exists <ref>\")\n>  \n> +#define BUILTIN_REFLOG_DROP_USAGE \\\n> +\tN_(\"git reflog drop [--all [--single-worktree] | <refs>...]\")\n> +\n>  static const char *const reflog_show_usage[] = {\n>  \tBUILTIN_REFLOG_SHOW_USAGE,\n>  \tNULL,\n> @@ -54,11 +57,17 @@ static const char *const reflog_exists_usage[] = {\n>  \tNULL,\n>  };\n>  \n> +static const char *const reflog_drop_usage[] = {\n> +\tBUILTIN_REFLOG_DROP_USAGE,\n> +\tNULL,\n> +};\n> +\n>  static const char *const reflog_usage[] = {\n>  \tBUILTIN_REFLOG_SHOW_USAGE,\n>  \tBUILTIN_REFLOG_LIST_USAGE,\n>  \tBUILTIN_REFLOG_EXPIRE_USAGE,\n>  \tBUILTIN_REFLOG_DELETE_USAGE,\n> +\tBUILTIN_REFLOG_DROP_USAGE,\n>  \tBUILTIN_REFLOG_EXISTS_USAGE,\n>  \tNULL\n>  };\n> @@ -449,10 +458,64 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,\n>  \t\t\t\t   refname);\n>  }\n>  \n> +static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,\n> +\t\t\t   struct repository *repo)\n> +{\n> +\tint ret = 0, do_all = 0, single_worktree = 0;\n> +\tconst struct option options[] = {\n> +\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"drop the reflogs of all references\")),\n> +\t\tOPT_BOOL(0, \"single-worktree\", &single_worktree,\n> +\t\t\t N_(\"drop reflogs from the current worktree only\")),\n> +\t\tOPT_END()\n> +\t};\n> +\n> +\targc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);\n> +\n> +\tif (argc && do_all)\n> +\t\tusage(_(\"references specified along with --all\"));\n\nWhat is the intended behavior when both `--all` and `<refs>` are\nomitted? It seems nothing happens at the moment. And no error nor\nwarning is printed, that feels a bit odd to me.\n\nNow, when you do `git reflog expire --expire=all` it also seems to be\ndoing nothing at all. I also think this is weird. And I don't see any\ntest coverage for `git reflog expire` without `--all`.\n\nBut what is the expected behavior when you omit `--all` and `<refs>`?\nShould it give an error or warning? Should it use HEAD, just like `git\nreflog show` does?\n\n> +\n> +\tif (do_all) {\n> +\t\tstruct worktree_reflogs collected = {\n> +\t\t\t.reflogs = STRING_LIST_INIT_DUP,\n> +\t\t};\n> +\t\tstruct string_list_item *item;\n> +\t\tstruct worktree **worktrees, **p;\n> +\n> +\t\tworktrees = get_worktrees();\n> +\t\tfor (p = worktrees; *p; p++) {\n> +\t\t\tif (single_worktree && !(*p)->is_current)\n> +\t\t\t\tcontinue;\n> +\t\t\tcollected.worktree = *p;\n> +\t\t\trefs_for_each_reflog(get_worktree_ref_store(*p),\n> +\t\t\t\t\t     collect_reflog, &collected);\n> +\t\t}\n> +\t\tfree_worktrees(worktrees);\n> +\n> +\t\tfor_each_string_list_item(item, &collected.reflogs)\n> +\t\t\tret |= refs_delete_reflog(get_main_ref_store(repo),\n> +\t\t\t\t\t\t     item->string);\n> +\t\tstring_list_clear(&collected.reflogs, 0);\n> +\n> +\t\treturn ret;\n> +\t}\n> +\n> +\tfor (int i = 0; i < argc; i++) {\n> +\t\tchar *ref;\n> +\t\tif (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {\n> +\t\t\tret |= error(_(\"reflog could not be found: '%s'\"), argv[i]);\n> +\t\t\tcontinue;\n> +\t\t}\n> +\n> +\t\tret |= refs_delete_reflog(get_main_ref_store(repo), ref);\n> +\t\tfree(ref);\n> +\t}\n> +\n> +\treturn ret;\n> +}\n> +\n>  /*\n>   * main \"reflog\"\n>   */\n> -\n>  int cmd_reflog(int argc,\n>  \t       const char **argv,\n>  \t       const char *prefix,\n> @@ -465,6 +528,7 @@ int cmd_reflog(int argc,\n>  \t\tOPT_SUBCOMMAND(\"expire\", &fn, cmd_reflog_expire),\n>  \t\tOPT_SUBCOMMAND(\"delete\", &fn, cmd_reflog_delete),\n>  \t\tOPT_SUBCOMMAND(\"exists\", &fn, cmd_reflog_exists),\n> +\t\tOPT_SUBCOMMAND(\"drop\", &fn, cmd_reflog_drop),\n>  \t\tOPT_END()\n>  \t};\n>  \n> diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh\n> index 1f7249be76..42b501f163 100755\n> --- a/t/t1410-reflog.sh\n> +++ b/t/t1410-reflog.sh\n> @@ -551,4 +551,126 @@ test_expect_success 'reflog with invalid object ID can be listed' '\n>  \t)\n>  '\n>  \n> +test_expect_success 'reflog drop non-existent ref' '\n> +\ttest_when_finished \"rm -rf repo\" &&\n> +\tgit init repo &&\n> +\t(\n> +\t\tcd repo &&\n> +\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n> +\t\ttest_must_fail git reflog drop refs/heads/non-existent 2>stderr &&\n> +\t\ttest_grep \"error: reflog could not be found: ${SQ}refs/heads/non-existent${SQ}\" stderr\n> +\t)\n> +'\n> +\n> +test_expect_success 'reflog drop' '\n> +\ttest_when_finished \"rm -rf repo\" &&\n> +\tgit init repo &&\n> +\t(\n> +\t\tcd repo &&\n> +\t\ttest_commit A &&\n> +\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n> +\t\tgit reflog exists refs/heads/main &&\n> +\t\tgit reflog exists refs/heads/branch &&\n> +\t\tgit reflog drop refs/heads/main &&\n> +\t\ttest_must_fail git reflog exists refs/heads/main &&\n> +\t\tgit reflog exists refs/heads/branch\n> +\t)\n> +'\n> +\n> +test_expect_success 'reflog drop multiple references' '\n> +\ttest_when_finished \"rm -rf repo\" &&\n> +\tgit init repo &&\n> +\t(\n> +\t\tcd repo &&\n> +\t\ttest_commit A &&\n> +\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n> +\t\tgit reflog exists refs/heads/main &&\n> +\t\tgit reflog exists refs/heads/branch &&\n> +\t\tgit reflog drop refs/heads/main refs/heads/branch &&\n> +\t\ttest_must_fail git reflog exists refs/heads/main &&\n> +\t\ttest_must_fail git reflog exists refs/heads/branch\n> +\t)\n> +'\n> +\n> +test_expect_success 'reflog drop multiple references some non-existent' '\n> +\ttest_when_finished \"rm -rf repo\" &&\n> +\tgit init repo &&\n> +\t(\n> +\t\tcd repo &&\n> +\t\ttest_commit A &&\n> +\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n> +\t\tgit reflog exists refs/heads/main &&\n> +\t\tgit reflog exists refs/heads/branch &&\n> +\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n> +\t\ttest_must_fail git reflog drop refs/heads/main refs/heads/non-existent refs/heads/branch 2>stderr &&\n> +\t\ttest_must_fail git reflog exists refs/heads/main &&\n> +\t\ttest_must_fail git reflog exists refs/heads/branch &&\n> +\t\ttest_must_fail git reflog exists refs/heads/non-existent &&\n> +\t\ttest_grep \"error: reflog could not be found: ${SQ}refs/heads/non-existent${SQ}\" stderr\n> +\t)\n> +'\n> +\n> +test_expect_success 'reflog drop --all' '\n> +\ttest_when_finished \"rm -rf repo\" &&\n> +\tgit init repo &&\n> +\t(\n> +\t\tcd repo &&\n> +\t\ttest_commit A &&\n> +\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n> +\t\tgit reflog exists refs/heads/main &&\n> +\t\tgit reflog exists refs/heads/branch &&\n> +\t\tgit reflog drop --all &&\n> +\t\ttest_must_fail git reflog exists refs/heads/main &&\n> +\t\ttest_must_fail git reflog exists refs/heads/branch\n\nShould we test output of `git reflog list`?\n\n> +\t)\n> +'\n> +\n> +test_expect_success 'reflog drop --all multiple worktrees' '\n> +\ttest_when_finished \"rm -rf repo\" &&\n> +\ttest_when_finished \"rm -rf wt\" &&\n> +\tgit init repo &&\n> +\t(\n> +\t\tcd repo &&\n> +\t\ttest_commit A &&\n> +\t\tgit worktree add ../wt &&\n> +\t\ttest_commit_bulk -C ../wt --ref=refs/heads/branch 1 &&\n> +\t\tgit reflog exists refs/heads/main &&\n> +\t\tgit reflog exists refs/heads/branch &&\n> +\t\tgit reflog drop --all &&\n> +\t\ttest_must_fail git reflog exists refs/heads/main &&\n> +\t\ttest_must_fail git reflog exists refs/heads/branch\n\nShall we test HEAD in both worktrees does not exists?\n\n> +\t)\n> +'\n> +\n> +test_expect_success 'reflog drop --all --single-worktree' '\n> +\ttest_when_finished \"rm -rf repo\" &&\n> +\ttest_when_finished \"rm -rf wt\" &&\n> +\tgit init repo &&\n> +\t(\n> +\t\tcd repo &&\n> +\t\ttest_commit A &&\n> +\t\tgit worktree add ../wt &&\n> +\t\ttest_commit -C ../wt foobar &&\n> +\t\tgit reflog exists refs/heads/main &&\n> +\t\tgit reflog exists refs/heads/wt &&\n> +\t\ttest-tool ref-store worktree:wt reflog-exists HEAD &&\n> +\t\tgit reflog drop --all --single-worktree &&\n> +\t\ttest_must_fail git reflog exists refs/heads/main &&\n> +\t\ttest_must_fail git reflog exists refs/heads/wt &&\n> +\t\ttest_must_fail test-tool ref-store worktree:main reflog-exists HEAD &&\n> +\t\ttest-tool ref-store worktree:wt reflog-exists HEAD\n\nNaive question: why is `test-tool ref-store` used and not\n`git -C ../wt reflog exist`?\n\n> +\t)\n> +'\n> +\n> +test_expect_success 'reflog drop --all with reference' '\n> +\ttest_when_finished \"rm -rf repo\" &&\n> +\tgit init repo &&\n> +\t(\n> +\t\tcd repo &&\n> +\t\ttest_commit A &&\n> +\t\ttest_must_fail git reflog drop --all refs/heads/main 2>stderr &&\n> +\t\ttest_grep \"usage: references specified along with --all\" stderr\n> +\t)\n> +'\n> +\n>  test_done\n>\n> -- \n> 2.48.1\n"},{"id":"514542","messageId":"xmqqsena2pzh.fsf@gitster.g","threadId":"63072","inReplyTo":"CAP8UFD2+xQQbUevHTQangg__dq2M615pHLhYYUf7b9shpShD9Q@mail.gmail.com","subject":"Re: [PATCH v3 2/2] reflog: implement subcommand to drop reflogs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-03-18T17:44:18Z","receivedAt":"2025-03-18T17:44:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christian Couder <christian.couder@gmail.com> writes:\n\n> It looks like 'single_worktree' is only used here. This means that if\n> a user forgets to add --all and only uses --single-worktree, nothing\n> will happen and it seems to me that the command will exit with code 0.\n> Even if `git reflog expire` already works like that, I think this is a\n> bit unfortunate.\n>\n> Otherwise this patch series looks very well done to me.\n\nIn the thread Toon too seems to have noticed the same \"what if there\nis no --all and --single-worktree is given?\" gotcha.  Together with\nthe \"current would be better name than single\", we can consider that\nthese funnies are to be \"consistent\" with the \"expire\" thing, and I\nam OK to see us move on.  An alternative is to \"fix\" the behaviour\nand naming of the \"expire\" first, and then use the same improved\nbehaviour and naming when adding \"drop\" as a new thing.\n\nThanks.\n\n"},{"id":"514622","messageId":"CAP8UFD1vTkPH=5b++6+=koozC31=wD1gQszvdFmzb73B9HM4Aw@mail.gmail.com","threadId":"63072","inReplyTo":"xmqqsena2pzh.fsf@gitster.g","subject":"Re: [PATCH v3 2/2] reflog: implement subcommand to drop reflogs","fromName":"Christian Couder","fromEmail":"christian.couder@gmail.com","sentAt":"2025-03-19T08:17:42Z","receivedAt":"2025-03-19T08:17:56Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On Tue, Mar 18, 2025 at 6:44 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Christian Couder <christian.couder@gmail.com> writes:\n>\n> > It looks like 'single_worktree' is only used here. This means that if\n> > a user forgets to add --all and only uses --single-worktree, nothing\n> > will happen and it seems to me that the command will exit with code 0.\n> > Even if `git reflog expire` already works like that, I think this is a\n> > bit unfortunate.\n> >\n> > Otherwise this patch series looks very well done to me.\n>\n> In the thread Toon too seems to have noticed the same \"what if there\n> is no --all and --single-worktree is given?\" gotcha.  Together with\n> the \"current would be better name than single\", we can consider that\n> these funnies are to be \"consistent\" with the \"expire\" thing, and I\n> am OK to see us move on.\n\nI am OK with moving on too. We can \"fix\" the behavior and naming later\nin a dedicated separate patch series.\n\n> An alternative is to \"fix\" the behaviour\n> and naming of the \"expire\" first, and then use the same improved\n> behaviour and naming when adding \"drop\" as a new thing.\n\nI would be OK with that too.\n\nThanks.\n"},{"id":"514623","messageId":"CAOLa=ZSj11TSTs6CywSX1Q9AAfW28zssS2yrGf8PmBOgd06Etg@mail.gmail.com","threadId":"63072","inReplyTo":"CAP8UFD1vTkPH=5b++6+=koozC31=wD1gQszvdFmzb73B9HM4Aw@mail.gmail.com","subject":"Re: [PATCH v3 2/2] reflog: implement subcommand to drop reflogs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-19T09:06:14Z","receivedAt":"2025-03-19T09:06:16Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Christian Couder <christian.couder@gmail.com> writes:\n\n> On Tue, Mar 18, 2025 at 6:44 PM Junio C Hamano <gitster@pobox.com> wrote:\n>>\n>> Christian Couder <christian.couder@gmail.com> writes:\n>>\n>> > It looks like 'single_worktree' is only used here. This means that if\n>> > a user forgets to add --all and only uses --single-worktree, nothing\n>> > will happen and it seems to me that the command will exit with code 0.\n>> > Even if `git reflog expire` already works like that, I think this is a\n>> > bit unfortunate.\n>> >\n>> > Otherwise this patch series looks very well done to me.\n>>\n>> In the thread Toon too seems to have noticed the same \"what if there\n>> is no --all and --single-worktree is given?\" gotcha.  Together with\n>> the \"current would be better name than single\", we can consider that\n>> these funnies are to be \"consistent\" with the \"expire\" thing, and I\n>> am OK to see us move on.\n>\n> I am OK with moving on too. We can \"fix\" the behavior and naming later\n> in a dedicated separate patch series.\n>\n\nSeems good, let's do that then. I'll see if I can follow up but this\ncould also be #leftoverbits if someone wants to pick it up!\n\n>> An alternative is to \"fix\" the behaviour\n>> and naming of the \"expire\" first, and then use the same improved\n>> behaviour and naming when adding \"drop\" as a new thing.\n>\n> I would be OK with that too.\n>\n> Thanks.\n"},{"id":"514624","messageId":"CAOLa=ZQ-ndKuYMABLveNjLnbR181+RD9_NGHAJsZbdc9eV2nEw@mail.gmail.com","threadId":"63072","inReplyTo":"8734fagwn2.fsf@iotcl.com","subject":"Re: [PATCH v3 2/2] reflog: implement subcommand to drop reflogs","fromName":"Karthik Nayak","fromEmail":"karthik.188@gmail.com","sentAt":"2025-03-19T09:16:09Z","receivedAt":"2025-03-19T09:16:11Z","isPatch":true,"sender":{"key":"karthik.188@gmail.com","avatar":"https://avatars.githubusercontent.com/u/1786334?v=4"},"body":"Toon Claes <toon@iotcl.com> writes:\n\n> Karthik Nayak <karthik.188@gmail.com> writes:\n>\n\n[snip]\n\n>> +static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,\n>> +\t\t\t   struct repository *repo)\n>> +{\n>> +\tint ret = 0, do_all = 0, single_worktree = 0;\n>> +\tconst struct option options[] = {\n>> +\t\tOPT_BOOL(0, \"all\", &do_all, N_(\"drop the reflogs of all references\")),\n>> +\t\tOPT_BOOL(0, \"single-worktree\", &single_worktree,\n>> +\t\t\t N_(\"drop reflogs from the current worktree only\")),\n>> +\t\tOPT_END()\n>> +\t};\n>> +\n>> +\targc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);\n>> +\n>> +\tif (argc && do_all)\n>> +\t\tusage(_(\"references specified along with --all\"));\n>\n> What is the intended behavior when both `--all` and `<refs>` are\n> omitted? It seems nothing happens at the moment. And no error nor\n> warning is printed, that feels a bit odd to me.\n>\n> Now, when you do `git reflog expire --expire=all` it also seems to be\n> doing nothing at all. I also think this is weird. And I don't see any\n> test coverage for `git reflog expire` without `--all`.\n>\n> But what is the expected behavior when you omit `--all` and `<refs>`?\n> Should it give an error or warning? Should it use HEAD, just like `git\n> reflog show` does?\n>\n\nAs discussed in the other thread [1], ideally this should be raised as\nan error. I'm leaving it for now.\n\n[snip]\n\n>> +\n>> +test_expect_success 'reflog drop --all' '\n>> +\ttest_when_finished \"rm -rf repo\" &&\n>> +\tgit init repo &&\n>> +\t(\n>> +\t\tcd repo &&\n>> +\t\ttest_commit A &&\n>> +\t\ttest_commit_bulk --ref=refs/heads/branch 1 &&\n>> +\t\tgit reflog exists refs/heads/main &&\n>> +\t\tgit reflog exists refs/heads/branch &&\n>> +\t\tgit reflog drop --all &&\n>> +\t\ttest_must_fail git reflog exists refs/heads/main &&\n>> +\t\ttest_must_fail git reflog exists refs/heads/branch\n>\n> Should we test output of `git reflog list`?\n>\n\nI don't see why, we're concerned with individual reflogs and 'exists'\nhelp check against those individual reflogs.\n\n>> +\t)\n>> +'\n>> +\n>> +test_expect_success 'reflog drop --all multiple worktrees' '\n>> +\ttest_when_finished \"rm -rf repo\" &&\n>> +\ttest_when_finished \"rm -rf wt\" &&\n>> +\tgit init repo &&\n>> +\t(\n>> +\t\tcd repo &&\n>> +\t\ttest_commit A &&\n>> +\t\tgit worktree add ../wt &&\n>> +\t\ttest_commit_bulk -C ../wt --ref=refs/heads/branch 1 &&\n>> +\t\tgit reflog exists refs/heads/main &&\n>> +\t\tgit reflog exists refs/heads/branch &&\n>> +\t\tgit reflog drop --all &&\n>> +\t\ttest_must_fail git reflog exists refs/heads/main &&\n>> +\t\ttest_must_fail git reflog exists refs/heads/branch\n>\n> Shall we test HEAD in both worktrees does not exists?\n>\n\nI think it would be a good addition, but I'm not sure if its worthy of a\nre-roll.\n\n>> +\t)\n>> +'\n>> +\n>> +test_expect_success 'reflog drop --all --single-worktree' '\n>> +\ttest_when_finished \"rm -rf repo\" &&\n>> +\ttest_when_finished \"rm -rf wt\" &&\n>> +\tgit init repo &&\n>> +\t(\n>> +\t\tcd repo &&\n>> +\t\ttest_commit A &&\n>> +\t\tgit worktree add ../wt &&\n>> +\t\ttest_commit -C ../wt foobar &&\n>> +\t\tgit reflog exists refs/heads/main &&\n>> +\t\tgit reflog exists refs/heads/wt &&\n>> +\t\ttest-tool ref-store worktree:wt reflog-exists HEAD &&\n>> +\t\tgit reflog drop --all --single-worktree &&\n>> +\t\ttest_must_fail git reflog exists refs/heads/main &&\n>> +\t\ttest_must_fail git reflog exists refs/heads/wt &&\n>> +\t\ttest_must_fail test-tool ref-store worktree:main reflog-exists HEAD &&\n>> +\t\ttest-tool ref-store worktree:wt reflog-exists HEAD\n>\n> Naive question: why is `test-tool ref-store` used and not\n> `git -C ../wt reflog exist`?\n>\n\nThat should work too :)\n\n>> +\t)\n>> +'\n>> +\n>> +test_expect_success 'reflog drop --all with reference' '\n>> +\ttest_when_finished \"rm -rf repo\" &&\n>> +\tgit init repo &&\n>> +\t(\n>> +\t\tcd repo &&\n>> +\t\ttest_commit A &&\n>> +\t\ttest_must_fail git reflog drop --all refs/heads/main 2>stderr &&\n>> +\t\ttest_grep \"usage: references specified along with --all\" stderr\n>> +\t)\n>> +'\n>> +\n>>  test_done\n>>\n>> --\n>> 2.48.1\n\n[1]: CAOLa=ZSj11TSTs6CywSX1Q9AAfW28zssS2yrGf8PmBOgd06Etg@mail.gmail.com\n"}]}