Skip to content

Commit

Permalink
builtin/refs: new command to migrate ref storage formats
Browse files Browse the repository at this point in the history
Introduce a new command that allows the user to migrate a repository
between ref storage formats. This new command is implemented as part of
a new git-refs(1) executable. This is due to two reasons:

  - There is no good place to put the migration logic in existing
    commands. git-maintenance(1) felt unwieldy, and git-pack-refs(1) is
    not the correct place to put it, either.

  - I had it in my mind to create a new low-level command for accessing
    refs for quite a while already. git-refs(1) is that command and can
    over time grow more functionality relating to refs. This should help
    discoverability by consolidating low-level access to refs into a
    single executable.

As mentioned in the preceding commit that introduces the ref storage
format migration logic, the new `git refs migrate` command still has a
bunch of restrictions. These restrictions are documented accordingly.

Signed-off-by: Patrick Steinhardt <[email protected]>
  • Loading branch information
pks-t committed Jun 6, 2024
1 parent 893d99e commit ec0c6d3
Show file tree
Hide file tree
Showing 8 changed files with 384 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
/git-rebase
/git-receive-pack
/git-reflog
/git-refs
/git-remote
/git-remote-http
/git-remote-https
Expand Down
61 changes: 61 additions & 0 deletions Documentation/git-refs.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
git-refs(1)
===========

NAME
----
git-refs - Low-level access to refs


SYNOPSIS
--------
[verse]
'git refs migrate' --ref-format=<format> [--dry-run]

DESCRIPTION
-----------

This command provides low-level access to refs.

COMMANDS
--------

migrate::
Migrate ref store between different formats.

OPTIONS
-------

The following options are specific to 'git refs migrate':

--ref-format=<format>::
The ref format to migrate the ref store to. Can be one of:
+
include::ref-storage-format.txt[]

--dry-run::
Perform the migration, but do not modify the repository. The migrated
refs will be written into a separate directory that can be inspected
separately. The name of the directory will be reported on stdout. This
can be used to double check that the migration works as expected before
performing the actual migration.

KNOWN LIMITATIONS
-----------------

The ref format migration has several known limitations in its current form:

* It is not possible to migrate repositories that have reflogs.

* It is not possible to migrate repositories that have worktrees.

* There is no way to block concurrent writes to the repository during an
ongoing migration. Concurrent writes can lead to an inconsistent migrated
state. Users are expected to block writes on a higher level. If your
repository is registered for scheduled maintenance, it is recommended to
unregister it first with git-maintenance(1).

These limitations may eventually be lifted.

GIT
---
Part of the linkgit:git[1] suite
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1283,6 +1283,7 @@ BUILTIN_OBJS += builtin/read-tree.o
BUILTIN_OBJS += builtin/rebase.o
BUILTIN_OBJS += builtin/receive-pack.o
BUILTIN_OBJS += builtin/reflog.o
BUILTIN_OBJS += builtin/refs.o
BUILTIN_OBJS += builtin/remote-ext.o
BUILTIN_OBJS += builtin/remote-fd.o
BUILTIN_OBJS += builtin/remote.o
Expand Down
1 change: 1 addition & 0 deletions builtin.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix);
int cmd_rebase__interactive(int argc, const char **argv, const char *prefix);
int cmd_receive_pack(int argc, const char **argv, const char *prefix);
int cmd_reflog(int argc, const char **argv, const char *prefix);
int cmd_refs(int argc, const char **argv, const char *prefix);
int cmd_remote(int argc, const char **argv, const char *prefix);
int cmd_remote_ext(int argc, const char **argv, const char *prefix);
int cmd_remote_fd(int argc, const char **argv, const char *prefix);
Expand Down
75 changes: 75 additions & 0 deletions builtin/refs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include "builtin.h"
#include "parse-options.h"
#include "refs.h"
#include "repository.h"
#include "strbuf.h"

#define REFS_MIGRATE_USAGE \
N_("git refs migrate --ref-format=<format> [--dry-run]")

static int cmd_refs_migrate(int argc, const char **argv, const char *prefix)
{
const char * const migrate_usage[] = {
REFS_MIGRATE_USAGE,
NULL,
};
const char *format_str = NULL;
enum ref_storage_format format;
unsigned int flags = 0;
struct option options[] = {
OPT_STRING_F(0, "ref-format", &format_str, N_("format"),
N_("specify the reference format to convert to"),
PARSE_OPT_NONEG),
OPT_BIT(0, "dry-run", &flags,
N_("perform a non-destructive dry-run"),
REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN),
OPT_END(),
};
struct strbuf errbuf = STRBUF_INIT;
int err;

argc = parse_options(argc, argv, prefix, options, migrate_usage, 0);
if (argc)
usage(_("too many arguments"));
if (!format_str)
usage(_("missing --ref-format=<format>"));

format = ref_storage_format_by_name(format_str);
if (format == REF_STORAGE_FORMAT_UNKNOWN) {
err = error(_("unknown ref storage format '%s'"), format_str);
goto out;
}

if (the_repository->ref_storage_format == format) {
err = error(_("repository already uses '%s' format"),
ref_storage_format_to_name(format));
goto out;
}

if (repo_migrate_ref_storage_format(the_repository, format, flags, &errbuf) < 0) {
err = error("%s", errbuf.buf);
goto out;
}

err = 0;

out:
strbuf_release(&errbuf);
return err;
}

int cmd_refs(int argc, const char **argv, const char *prefix)
{
const char * const refs_usage[] = {
REFS_MIGRATE_USAGE,
NULL,
};
parse_opt_subcommand_fn *fn = NULL;
struct option opts[] = {
OPT_SUBCOMMAND("migrate", &fn, cmd_refs_migrate),
OPT_END(),
};

argc = parse_options(argc, argv, prefix, opts, refs_usage, 0);
return fn(argc, argv, prefix);
}
1 change: 1 addition & 0 deletions command-list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ git-read-tree plumbingmanipulators
git-rebase mainporcelain history
git-receive-pack synchelpers
git-reflog ancillarymanipulators complete
git-refs ancillarymanipulators complete
git-remote ancillarymanipulators complete
git-repack ancillarymanipulators complete
git-replace ancillarymanipulators complete
Expand Down
1 change: 1 addition & 0 deletions git.c
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ static struct cmd_struct commands[] = {
{ "rebase", cmd_rebase, RUN_SETUP | NEED_WORK_TREE },
{ "receive-pack", cmd_receive_pack },
{ "reflog", cmd_reflog, RUN_SETUP },
{ "refs", cmd_refs, RUN_SETUP },
{ "remote", cmd_remote, RUN_SETUP },
{ "remote-ext", cmd_remote_ext, NO_PARSEOPT },
{ "remote-fd", cmd_remote_fd, NO_PARSEOPT },
Expand Down
Loading

0 comments on commit ec0c6d3

Please sign in to comment.