builtin/refs: add '--no-reflog' flag to drop reflogs

The "git refs migrate" subcommand converts the backend used for ref storage. It always migrates reflog data as well as refs. Introduce an option to exclude reflogs from migration, allowing them to be discarded when they are unnecessary. This is particularly useful in server-side repositories, where reflogs are typically not expected. However, some repositories may still have them due to historical reasons, such as bugs, misconfigurations, or administrative decisions to enable reflogs for debugging. In such repositories, it would be optimal to drop reflogs during the migration. To address this, introduce the '--no-reflog' flag, which prevents reflog migration. When this flag is used, reflogs from the original reference backend are migrated. Since only the new reference backend remains in the repository, all previous reflogs are permanently discarded. Helped-by: Junio C Hamano <gitster@pobox.com> Helped-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Karthik Nayak committed Feb 21, 2025 at 11:04 UTC 89be7d2774f81f3d8663999975a6ab64d46bf42e
5 files changed +44 -11
Documentation/git-refs.txt
+8 -3
@@ -8,9 +8,9 @@ git-refs - Low-level access to refs
8
9 SYNOPSIS
10 --------
11 -[verse]
12 -'git refs migrate' --ref-format=<format> [--dry-run]
13 -'git refs verify' [--strict] [--verbose]
11 +[synopsis]
12 +git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]
13 +git refs verify [--strict] [--verbose]
14
15 DESCRIPTION
16 -----------
@@ -43,6 +43,11 @@ include::ref-storage-format.txt[]
43 can be used to double check that the migration works as expected before
44 performing the actual migration.
45
46 +--reflog::
47 +--no-reflog::
48 + Choose between migrating the reflog data to the new backend,
49 + and discarding them. The default is "--reflog", to migrate.
50 +
51 The following options are specific to 'git refs verify':
52
53 --strict::
builtin/refs.c
+3
@@ -30,6 +30,9 @@ static int cmd_refs_migrate(int argc, const char **argv, const char *prefix,
30 OPT_BIT(0, "dry-run", &flags,
31 N_("perform a non-destructive dry-run"),
32 REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN),
33 + OPT_BIT(0, "no-reflog", &flags,
34 + N_("drop reflogs entirely during the migration"),
35 + REPO_MIGRATE_REF_STORAGE_FORMAT_SKIP_REFLOG),
36 OPT_END(),
37 };
38 struct strbuf errbuf = STRBUF_INIT;
refs.c
+5 -3
@@ -3035,9 +3035,11 @@ int repo_migrate_ref_storage_format(struct repository *repo,
3035 if (ret < 0)
3036 goto done;
3037
3038 - ret = refs_for_each_reflog(old_refs, migrate_one_reflog, &data);
3039 - if (ret < 0)
3040 - goto done;
3038 + if (!(flags & REPO_MIGRATE_REF_STORAGE_FORMAT_SKIP_REFLOG)) {
3039 + ret = refs_for_each_reflog(old_refs, migrate_one_reflog, &data);
3040 + if (ret < 0)
3041 + goto done;
3042 + }
3043
3044 ret = ref_transaction_commit(transaction, errbuf);
3045 if (ret < 0)
refs.h
+4 -1
@@ -1157,8 +1157,11 @@ int is_pseudo_ref(const char *refname);
1157 * - REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN: perform a dry-run migration
1158 * without touching the main repository. The result will be written into a
1159 * temporary ref storage directory.
1160 + *
1161 + * - REPO_MIGRATE_REF_STORAGE_FORMAT_SKIP_REFLOG: skip migration of reflogs.
1162 */
1161 -#define REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN (1 << 0)
1163 +#define REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN (1 << 0)
1164 +#define REPO_MIGRATE_REF_STORAGE_FORMAT_SKIP_REFLOG (1 << 1)
1165
1166 /*
1167 * Migrate the ref storage format used by the repository to the
t/t1460-refs-migrate.sh
+24 -4
@@ -9,14 +9,21 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
9
10 # Migrate the provided repository from one format to the other and
11 # verify that the references and logs are migrated over correctly.
12 -# Usage: test_migration <repo> <format> <skip_reflog_verify>
12 +# Usage: test_migration <repo> <format> [<skip_reflog_verify> [<options...>]]
13 # <repo> is the relative path to the repo to be migrated.
14 # <format> is the ref format to be migrated to.
15 -# <skip_reflog_verify> (true or false) whether to skip reflog verification.
15 +# <skip_reflog_verify> (default: false) whether to skip reflog verification.
16 +# <options...> are other options be passed directly to 'git refs migrate'.
17 test_migration () {
18 repo=$1 &&
19 format=$2 &&
19 - skip_reflog_verify=${3:-false} &&
20 + shift 2 &&
21 + skip_reflog_verify=false &&
22 + if test $# -ge 1
23 + then
24 + skip_reflog_verify=$1
25 + shift
26 + fi &&
27 git -C "$repo" for-each-ref --include-root-refs \
28 --format='%(refname) %(objectname) %(symref)' >expect &&
29 if ! $skip_reflog_verify
@@ -25,7 +32,7 @@ test_migration () {
32 git -C "$repo" reflog list >expect_log_list
33 fi &&
34
28 - git -C "$repo" refs migrate --ref-format="$2" &&
35 + git -C "$repo" refs migrate --ref-format="$format" "$@" &&
36
37 git -C "$repo" for-each-ref --include-root-refs \
38 --format='%(refname) %(objectname) %(symref)' >actual &&
@@ -241,6 +248,19 @@ do
248 test_cmp expect.reflog actual.reflog
249 )
250 '
251 +
252 + test_expect_success "$from_format -> $to_format: skip reflog with --skip-reflog" '
253 + test_when_finished "rm -rf repo" &&
254 + git init --ref-format=$from_format repo &&
255 + test_commit -C repo initial &&
256 + # we see that the repository contains reflogs.
257 + git -C repo reflog --all >reflogs &&
258 + test_line_count = 2 reflogs &&
259 + test_migration repo "$to_format" true --no-reflog &&
260 + # there should be no reflogs post migration.
261 + git -C repo reflog --all >reflogs &&
262 + test_must_be_empty reflogs
263 + '
264 done
265 done
266