git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 2/2] reflog: implement subcommand to drop reflogs

From
Karthik Nayak <karthik.188@gmail.com>
Date
Mar 7, 2025, 11:17 UTC
Message-ID
<20250307-493-add-command-to-purge-reflog-entries-v1-2-84ab8529cf9e@gmail.com>
In-Reply-To
<20250307-493-add-command-to-purge-reflog-entries-v1-0-84ab8529cf9e@gmail.com>

Add a new 'drop' subcommand to git-reflog that allows users to delete the entire reflog for a specified reference. Include a '--all' flag to enable dropping all reflogs in a repository.

While 'git-reflog(1)' currently allows users to expire reflogs and delete individual entries, it lacks functionality to completely remove reflogs for specific references. This becomes problematic in repositories where reflogs are not needed but continue to accumulate entries despite setting 'core.logAllRefUpdates=false'.

While here, remove an erranous newline in the file.
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 Documentation/git-reflog.adoc |  6 +++++
 builtin/reflog.c              | 58 ++++++++++++++++++++++++++++++++++++++++++-
 t/t1410-reflog.sh             | 55 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 118 insertions(+), 1 deletion(-)
diff --git a/Documentation/git-reflog.adoc b/Documentation/git-reflog.adoc
index a929c52982..4ecee297de 100644
--- a/Documentation/git-reflog.adoc
+++ b/Documentation/git-reflog.adoc
@@ -17,6 +17,7 @@ SYNOPSIS
 'git reflog delete' [--rewrite] [--updateref]
 	[--dry-run | -n] [--verbose] <ref>@{<specifier>}...
 'git reflog exists' <ref>
+'git reflog drop' [--all | <refs>...]
 
 DESCRIPTION
 -----------
@@ -57,6 +58,11 @@ The "exists" subcommand checks whether a ref has a reflog.  It exits
 with zero status if the reflog exists, and non-zero status if it does
 not.
 
+The "drop" subcommand removes the reflog for the specified references.
+In contrast, "expire" can be used to prune all entries from a reflog,
+but the reflog itself will still exist for that reference. To fully
+remove the reflog for specific references, use the "drop" subcommand.
+
 OPTIONS
 -------
 
diff --git a/builtin/reflog.c b/builtin/reflog.c
index f92258f6b6..232602c1a6 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -27,6 +27,9 @@
 #define BUILTIN_REFLOG_EXISTS_USAGE \
 	N_("git reflog exists <ref>")
 
+#define BUILTIN_REFLOG_DROP_USAGE \
+	N_("git reflog drop [--all | <refs>...]")
+
 static const char *const reflog_show_usage[] = {
 	BUILTIN_REFLOG_SHOW_USAGE,
 	NULL,
@@ -52,12 +55,18 @@ static const char *const reflog_exists_usage[] = {
 	NULL,
 };
 
+static const char *const reflog_drop_usage[] = {
+	BUILTIN_REFLOG_DROP_USAGE,
+	NULL,
+};
+
 static const char *const reflog_usage[] = {
 	BUILTIN_REFLOG_SHOW_USAGE,
 	BUILTIN_REFLOG_LIST_USAGE,
 	BUILTIN_REFLOG_EXPIRE_USAGE,
 	BUILTIN_REFLOG_DELETE_USAGE,
 	BUILTIN_REFLOG_EXISTS_USAGE,
+	BUILTIN_REFLOG_DROP_USAGE,
 	NULL
 };
 
@@ -447,10 +456,56 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix,
 				   refname);
 }
 
+static int cmd_reflog_drop(int argc, const char **argv, const char *prefix,
+			   struct repository *repo)
+{
+	int i, ret, do_all;
+	const struct option options[] = {
+		OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")),
+		OPT_END()
+	};
+
+	do_all = ret = 0;
+	argc = parse_options(argc, argv, prefix, options, reflog_drop_usage, 0);
+
+	if (do_all) {
+		struct worktree_reflogs collected = {
+			.reflogs = STRING_LIST_INIT_DUP,
+		};
+		struct string_list_item *item;
+		struct worktree **worktrees, **p;
+
+		worktrees = get_worktrees();
+		for (p = worktrees; *p; p++) {
+			collected.worktree = *p;
+			refs_for_each_reflog(get_worktree_ref_store(*p),
+					     collect_reflog, &collected);
+		}
+		free_worktrees(worktrees);
+
+		for_each_string_list_item(item, &collected.reflogs)
+			ret |= refs_delete_reflog(get_main_ref_store(repo),
+						     item->string);
+		string_list_clear(&collected.reflogs, 0);
+	}
+
+	for (i = 0; i < argc; i++) {
+		char *ref;
+		if (!repo_dwim_log(repo, argv[i], strlen(argv[i]), NULL, &ref)) {
+			ret |= error(_("%s points nowhere!"), argv[i]);
+			continue;
+		}
+
+		ret |= refs_delete_reflog(get_main_ref_store(repo), ref);
+		free(ref);
+	}
+
+	return ret;
+}
+
 /*
  * main "reflog"
  */
-
 int cmd_reflog(int argc,
 	       const char **argv,
 	       const char *prefix,
@@ -463,6 +518,7 @@ int cmd_reflog(int argc,
 		OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire),
 		OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete),
 		OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists),
+		OPT_SUBCOMMAND("drop", &fn, cmd_reflog_drop),
 		OPT_END()
 	};
 
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
index 388fdf9ae5..b6e44ce6b9 100755
--- a/t/t1410-reflog.sh
+++ b/t/t1410-reflog.sh
@@ -551,4 +551,59 @@ test_expect_success 'reflog with invalid object ID can be listed' '
 	)
 '
 
+test_expect_success 'reflog drop non-existent ref' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_must_fail git reflog exists refs/heads/non-existent &&
+		test_must_fail git reflog drop refs/heads/non-existent
+	)
+'
+
+test_expect_success 'reflog drop' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit A &&
+		test_commit_bulk --ref=refs/heads/branch 1 &&
+		git reflog exists refs/heads/main &&
+		git reflog exists refs/heads/branch &&
+		git reflog drop refs/heads/main &&
+		test_must_fail git reflog exists refs/heads/main &&
+		git reflog exists refs/heads/branch
+	)
+'
+
+test_expect_success 'reflog drop multiple references' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit A &&
+		test_commit_bulk --ref=refs/heads/branch 1 &&
+		git reflog exists refs/heads/main &&
+		git reflog exists refs/heads/branch &&
+		git reflog drop refs/heads/main refs/heads/branch &&
+		test_must_fail git reflog exists refs/heads/main &&
+		test_must_fail git reflog exists refs/heads/branch
+	)
+'
+
+test_expect_success 'reflog drop --all' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		test_commit A &&
+		test_commit_bulk --ref=refs/heads/branch 1 &&
+		git reflog exists refs/heads/main &&
+		git reflog exists refs/heads/branch &&
+		git reflog drop --all &&
+		test_must_fail git reflog exists refs/heads/main &&
+		test_must_fail git reflog exists refs/heads/branch
+	)
+'
+
 test_done
-- 
2.48.1
Previous: Karthik NayakNext: Patrick Steinhardt
Message 7 of 30 in “EDITME: cover title for 493-add-command-to-purge-reflog-entries”
  1. 0/2 EDITME: cover title for 493-add-command-to-purge-reflog-entriesKarthik Nayak, Mar 7, 2025
  2. 1/2 reflog: drop usage of global variablesKarthik Nayak, Mar 7, 2025
  3. Junio C HamanoMar 7, 2025
  4. Karthik NayakMar 10, 2025
  5. Junio C HamanoMar 10, 2025
  6. Karthik NayakMar 13, 2025
  7. 2/2 reflog: implement subcommand to drop reflogsKarthik Nayak, Mar 7, 2025
  8. Patrick SteinhardtMar 7, 2025
  9. Karthik NayakMar 7, 2025
  10. Patrick SteinhardtMar 7, 2025
  11. Karthik NayakMar 7, 2025
  12. Karthik NayakMar 7, 2025
  13. Junio C HamanoMar 7, 2025
  14. Karthik NayakMar 10, 2025
  15. Kristoffer HaugsbakkMar 10, 2025
  16. Karthik NayakMar 10, 2025
  17. Junio C HamanoMar 10, 2025
  18. reflog: implement subcommand to drop reflogsKarthik Nayak, Mar 10, 2025
  19. Patrick SteinhardtMar 12, 2025
  20. Karthik NayakMar 13, 2025
  21. Patrick SteinhardtMar 13, 2025
  22. 0/2 reflog: implement subcommand to drop reflogsKarthik Nayak, Mar 14, 2025
  23. 1/2 reflog: improve error for when reflog is not foundKarthik Nayak, Mar 14, 2025
  24. 2/2 reflog: implement subcommand to drop reflogsKarthik Nayak, Mar 14, 2025
  25. Christian CouderMar 18, 2025
  26. Junio C HamanoMar 18, 2025
  27. Christian CouderMar 19, 2025
  28. Karthik NayakMar 19, 2025
  29. Toon ClaesMar 18, 2025
  30. Karthik NayakMar 19, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.