Skip to content

Commit

Permalink
Merge branch 'hn/reftable' into seen
Browse files Browse the repository at this point in the history
The "reftable" backend for the refs API.

* hn/reftable: (28 commits)
  t1404: annotate test cases with REFFILES
  t1401,t2011: parameterize HEAD.lock for REFTABLE
  t1301: document what needs to be done for REFTABLE
  Add "test-tool dump-reftable" command.
  git-prompt: prepare for reftable refs backend
  Reftable support for git-core
  reftable: add dump utility
  reftable: implement stack, a mutable database of reftable files.
  reftable: implement refname validation
  reftable: add merged table view
  reftable: add a heap-based priority queue for reftable records
  reftable: reftable file level tests
  reftable: read reftable files
  reftable: generic interface to tables
  reftable: write reftable files
  reftable: a generic binary tree implementation
  reftable: reading/writing blocks
  Provide zlib's uncompress2 from compat/zlib-compat.c
  reftable: (de)serialization for the polymorphic record type.
  reftable: add blocksource, an abstraction for random access reads
  ...
  • Loading branch information
gitster committed May 22, 2021
2 parents 01720bd + c57c75a commit f675f29
Show file tree
Hide file tree
Showing 95 changed files with 13,008 additions and 70 deletions.
9 changes: 9 additions & 0 deletions Documentation/config/extensions.txt
Expand Up @@ -6,3 +6,12 @@ extensions.objectFormat::
Note that this setting should only be set by linkgit:git-init[1] or
linkgit:git-clone[1]. Trying to change it after initialization will not
work and will produce hard-to-diagnose issues.
+
extensions.refStorage::
Specify the ref storage mechanism to use. The acceptable values are `files` and
`reftable`. If not specified, `files` is assumed. It is an error to specify
this key unless `core.repositoryFormatVersion` is 1.
+
Note that this setting should only be set by linkgit:git-init[1] or
linkgit:git-clone[1]. Trying to change it after initialization will not
work and will produce hard-to-diagnose issues.
7 changes: 7 additions & 0 deletions Documentation/technical/repository-version.txt
Expand Up @@ -100,3 +100,10 @@ If set, by default "git config" reads from both "config" and
multiple working directory mode, "config" file is shared while
"config.worktree" is per-working directory (i.e., it's in
GIT_COMMON_DIR/worktrees/<id>/config.worktree)

==== `refStorage`

Specifies the file format for the ref database. Values are `files`
(for the traditional packed + loose ref format) and `reftable` for the
binary reftable format. See https://github.com/google/reftable for
more information.
54 changes: 51 additions & 3 deletions Makefile
Expand Up @@ -256,6 +256,8 @@ all::
#
# Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.
#
# Define NO_UNCOMPRESS2 if your zlib does not have uncompress2.
#
# Define NO_NORETURN if using buggy versions of gcc 4.6+ and profile feedback,
# as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
#
Expand Down Expand Up @@ -738,6 +740,7 @@ TEST_BUILTINS_OBJS += test-read-cache.o
TEST_BUILTINS_OBJS += test-read-graph.o
TEST_BUILTINS_OBJS += test-read-midx.o
TEST_BUILTINS_OBJS += test-ref-store.o
TEST_BUILTINS_OBJS += test-reftable.o
TEST_BUILTINS_OBJS += test-regex.o
TEST_BUILTINS_OBJS += test-repository.o
TEST_BUILTINS_OBJS += test-revision-walking.o
Expand Down Expand Up @@ -816,6 +819,8 @@ TEST_SHELL_PATH = $(SHELL_PATH)

LIB_FILE = libgit.a
XDIFF_LIB = xdiff/lib.a
REFTABLE_LIB = reftable/libreftable.a
REFTABLE_TEST_LIB = reftable/libreftable_test.a

GENERATED_H += command-list.h
GENERATED_H += config-list.h
Expand Down Expand Up @@ -980,6 +985,7 @@ LIB_OBJS += reflog-walk.o
LIB_OBJS += refs.o
LIB_OBJS += refs/debug.o
LIB_OBJS += refs/files-backend.o
LIB_OBJS += refs/reftable-backend.o
LIB_OBJS += refs/iterator.o
LIB_OBJS += refs/packed-backend.o
LIB_OBJS += refs/ref-cache.o
Expand Down Expand Up @@ -1194,7 +1200,7 @@ THIRD_PARTY_SOURCES += compat/regex/%
THIRD_PARTY_SOURCES += sha1collisiondetection/%
THIRD_PARTY_SOURCES += sha1dc/%

GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB)
GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB)
EXTLIBS =

GIT_USER_AGENT = git/$(GIT_VERSION)
Expand Down Expand Up @@ -1734,6 +1740,11 @@ ifdef NO_DEFLATE_BOUND
BASIC_CFLAGS += -DNO_DEFLATE_BOUND
endif

ifdef NO_UNCOMPRESS2
BASIC_CFLAGS += -DNO_UNCOMPRESS2
LIB_OBJS += compat/zlib-uncompress2.o
endif

ifdef NO_POSIX_GOODIES
BASIC_CFLAGS += -DNO_POSIX_GOODIES
endif
Expand Down Expand Up @@ -2438,7 +2449,36 @@ XDIFF_OBJS += xdiff/xutils.o
.PHONY: xdiff-objs
xdiff-objs: $(XDIFF_OBJS)

REFTABLE_OBJS += reftable/basics.o
REFTABLE_OBJS += reftable/error.o
REFTABLE_OBJS += reftable/block.o
REFTABLE_OBJS += reftable/blocksource.o
REFTABLE_OBJS += reftable/iter.o
REFTABLE_OBJS += reftable/publicbasics.o
REFTABLE_OBJS += reftable/merged.o
REFTABLE_OBJS += reftable/pq.o
REFTABLE_OBJS += reftable/reader.o
REFTABLE_OBJS += reftable/record.o
REFTABLE_OBJS += reftable/refname.o
REFTABLE_OBJS += reftable/generic.o
REFTABLE_OBJS += reftable/stack.o
REFTABLE_OBJS += reftable/tree.o
REFTABLE_OBJS += reftable/writer.o

REFTABLE_TEST_OBJS += reftable/basics_test.o
REFTABLE_TEST_OBJS += reftable/block_test.o
REFTABLE_TEST_OBJS += reftable/dump.o
REFTABLE_TEST_OBJS += reftable/merged_test.o
REFTABLE_TEST_OBJS += reftable/pq_test.o
REFTABLE_TEST_OBJS += reftable/record_test.o
REFTABLE_TEST_OBJS += reftable/readwrite_test.o
REFTABLE_TEST_OBJS += reftable/refname_test.o
REFTABLE_TEST_OBJS += reftable/stack_test.o
REFTABLE_TEST_OBJS += reftable/test_framework.o
REFTABLE_TEST_OBJS += reftable/tree_test.o

TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))

.PHONY: test-objs
test-objs: $(TEST_OBJS)

Expand All @@ -2454,6 +2494,8 @@ OBJECTS += $(PROGRAM_OBJS)
OBJECTS += $(TEST_OBJS)
OBJECTS += $(XDIFF_OBJS)
OBJECTS += $(FUZZ_OBJS)
OBJECTS += $(REFTABLE_OBJS) $(REFTABLE_TEST_OBJS)

ifndef NO_CURL
OBJECTS += http.o http-walker.o remote-curl.o
endif
Expand Down Expand Up @@ -2605,6 +2647,12 @@ $(LIB_FILE): $(LIB_OBJS)
$(XDIFF_LIB): $(XDIFF_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^

$(REFTABLE_LIB): $(REFTABLE_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^

$(REFTABLE_TEST_LIB): $(REFTABLE_TEST_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^

export DEFAULT_EDITOR DEFAULT_PAGER

Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
Expand Down Expand Up @@ -2889,7 +2937,7 @@ perf: all

t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))

t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS)
t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) $(REFTABLE_TEST_LIB)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)

check-sha1:: t/helper/test-tool$X
Expand Down Expand Up @@ -3219,7 +3267,7 @@ cocciclean:
clean: profile-clean coverage-clean cocciclean
$(RM) *.res
$(RM) $(OBJECTS)
$(RM) $(LIB_FILE) $(XDIFF_LIB)
$(RM) $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB)
$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
$(RM) $(TEST_PROGRAMS)
$(RM) $(FUZZ_PROGRAMS)
Expand Down
5 changes: 3 additions & 2 deletions builtin/clone.c
Expand Up @@ -1148,7 +1148,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}

init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL,
INIT_DB_QUIET);
default_ref_storage(), INIT_DB_QUIET);

if (real_git_dir)
git_dir = real_git_dir;
Expand Down Expand Up @@ -1299,7 +1299,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
* Now that we know what algorithm the remote side is using,
* let's set ours to the same thing.
*/
initialize_repository_version(hash_algo, 1);
initialize_repository_version(hash_algo, 1,
default_ref_storage());
repo_set_hash_algo(the_repository, hash_algo);

mapped_refs = wanted_peer_refs(refs, &remote->fetch);
Expand Down
76 changes: 62 additions & 14 deletions builtin/init-db.c
Expand Up @@ -167,12 +167,14 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
return 1;
}

void initialize_repository_version(int hash_algo, int reinit)
void initialize_repository_version(int hash_algo, int reinit,
const char *ref_storage_format)
{
char repo_version_string[10];
int repo_version = GIT_REPO_VERSION;

if (hash_algo != GIT_HASH_SHA1)
if (hash_algo != GIT_HASH_SHA1 ||
!strcmp(ref_storage_format, "reftable"))
repo_version = GIT_REPO_VERSION_READ;

/* This forces creation of new config file */
Expand Down Expand Up @@ -225,6 +227,7 @@ static int create_default_files(const char *template_path,
is_bare_repository_cfg = init_is_bare_repository || !work_tree;
if (init_shared_repository != -1)
set_shared_repository(init_shared_repository);
the_repository->ref_storage_format = xstrdup(fmt->ref_storage);

/*
* We would have created the above under user's umask -- under
Expand All @@ -234,6 +237,24 @@ static int create_default_files(const char *template_path,
adjust_shared_perm(get_git_dir());
}

/*
* Check to see if .git/HEAD exists; this must happen before
* initializing the ref db, because we want to see if there is an
* existing HEAD.
*/
path = git_path_buf(&buf, "HEAD");
reinit = (!access(path, R_OK) ||
readlink(path, junk, sizeof(junk) - 1) != -1);

/*
* refs/heads is a file when using reftable. We can't reinitialize with
* a reftable because it will overwrite HEAD
*/
if (reinit && (!strcmp(fmt->ref_storage, "reftable")) ==
is_directory(git_path_buf(&buf, "refs/heads"))) {
die("cannot switch ref storage format.");
}

/*
* We need to create a "refs" dir in any case so that older
* versions of git can tell that this is a repository.
Expand All @@ -248,9 +269,6 @@ static int create_default_files(const char *template_path,
* Point the HEAD symref to the initial branch with if HEAD does
* not yet exist.
*/
path = git_path_buf(&buf, "HEAD");
reinit = (!access(path, R_OK)
|| readlink(path, junk, sizeof(junk)-1) != -1);
if (!reinit) {
char *ref;

Expand All @@ -267,7 +285,7 @@ static int create_default_files(const char *template_path,
free(ref);
}

initialize_repository_version(fmt->hash_algo, 0);
initialize_repository_version(fmt->hash_algo, 0, fmt->ref_storage);

/* Check filemode trustability */
path = git_path_buf(&buf, "config");
Expand Down Expand Up @@ -382,7 +400,7 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash

int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, int hash, const char *initial_branch,
unsigned int flags)
const char *ref_storage_format, unsigned int flags)
{
int reinit;
int exist_ok = flags & INIT_DB_EXIST_OK;
Expand Down Expand Up @@ -421,9 +439,31 @@ int init_db(const char *git_dir, const char *real_git_dir,
* is an attempt to reinitialize new repository with an old tool.
*/
check_repository_format(&repo_fmt);
repo_fmt.ref_storage = xstrdup(ref_storage_format);

validate_hash_algorithm(&repo_fmt, hash);

/*
* At this point, the_repository we have in-core does not look
* anything like one that we would see initialized in an already
* working repository after calling setup_git_directory().
*
* Calling repository.c::initialize_the_repository() may have
* prepared the .index .objects and .parsed_objects members, but
* other members like .gitdir, .commondir, etc. have not been
* initialized.
*
* Many API functions assume they are working with the_repository
* that has sensibly been initialized, but because we haven't
* really read from an existing repository, we need to hand-craft
* the necessary members of the structure to get out of this
* chicken-and-egg situation.
*
* For now, we update the hash algorithm member to what the
* validate_hash_algorithm() call decided for us.
*/
repo_set_hash_algo(the_repository, repo_fmt.hash_algo);

reinit = create_default_files(template_dir, original_git_dir,
initial_branch, &repo_fmt,
flags & INIT_DB_QUIET);
Expand Down Expand Up @@ -454,6 +494,9 @@ int init_db(const char *git_dir, const char *real_git_dir,
git_config_set("receive.denyNonFastforwards", "true");
}

if (!strcmp(ref_storage_format, "reftable"))
git_config_set("extensions.refStorage", ref_storage_format);

if (!(flags & INIT_DB_QUIET)) {
int len = strlen(git_dir);

Expand Down Expand Up @@ -527,6 +570,7 @@ static const char *const init_db_usage[] = {
int cmd_init_db(int argc, const char **argv, const char *prefix)
{
const char *git_dir;
const char *ref_storage_format = default_ref_storage();
const char *real_git_dir = NULL;
const char *work_tree;
const char *template_dir = NULL;
Expand All @@ -535,15 +579,18 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
const char *initial_branch = NULL;
int hash_algo = GIT_HASH_UNKNOWN;
const struct option init_db_options[] = {
OPT_STRING(0, "template", &template_dir, N_("template-directory"),
N_("directory from which templates will be used")),
OPT_STRING(0, "template", &template_dir,
N_("template-directory"),
N_("directory from which templates will be used")),
OPT_SET_INT(0, "bare", &is_bare_repository_cfg,
N_("create a bare repository"), 1),
N_("create a bare repository"), 1),
{ OPTION_CALLBACK, 0, "shared", &init_shared_repository,
N_("permissions"),
N_("specify that the git repository is to be shared amongst several users"),
PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
N_("permissions"),
N_("specify that the git repository is to be shared amongst several users"),
PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0 },
OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET),
OPT_STRING(0, "ref-storage", &ref_storage_format, N_("backend"),
N_("the ref storage format to use")),
OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
N_("separate git dir from working tree")),
OPT_STRING('b', "initial-branch", &initial_branch, N_("name"),
Expand Down Expand Up @@ -686,10 +733,11 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
}

UNLEAK(real_git_dir);
UNLEAK(ref_storage_format);
UNLEAK(git_dir);
UNLEAK(work_tree);

flags |= INIT_DB_EXIST_OK;
return init_db(git_dir, real_git_dir, template_dir, hash_algo,
initial_branch, flags);
initial_branch, ref_storage_format, flags);
}
8 changes: 7 additions & 1 deletion builtin/stash.c
Expand Up @@ -207,10 +207,16 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
static int do_clear_stash(void)
{
struct object_id obj;
int result;
if (get_oid(ref_stash, &obj))
return 0;

return delete_ref(NULL, ref_stash, &obj, 0);
result = delete_ref(NULL, ref_stash, &obj, 0);

/* Ignore error; this is necessary for reftable, which keeps reflogs
* even when refs are deleted. */
delete_reflog(ref_stash);
return result;
}

static int clear_stash(int argc, const char **argv, const char *prefix)
Expand Down
27 changes: 24 additions & 3 deletions builtin/worktree.c
Expand Up @@ -13,6 +13,7 @@
#include "utf8.h"
#include "worktree.h"
#include "quote.h"
#include "../refs/refs-internal.h"

static const char * const worktree_usage[] = {
N_("git worktree add [<options>] <path> [<commit-ish>]"),
Expand Down Expand Up @@ -328,9 +329,29 @@ static int add_worktree(const char *path, const char *refname,
* worktree.
*/
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
write_file(sb.buf, "%s", oid_to_hex(null_oid()));
strbuf_reset(&sb);
if (get_main_ref_store(the_repository)->be == &refs_be_reftable) {
/* XXX this is cut & paste from reftable_init_db. */
strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
write_file(sb.buf, "%s", "ref: refs/heads/.invalid\n");
strbuf_reset(&sb);

strbuf_addf(&sb, "%s/refs", sb_repo.buf);
safe_create_dir(sb.buf, 1);
strbuf_reset(&sb);

strbuf_addf(&sb, "%s/refs/heads", sb_repo.buf);
write_file(sb.buf, "this repository uses the reftable format");
strbuf_reset(&sb);

strbuf_addf(&sb, "%s/reftable", sb_repo.buf);
safe_create_dir(sb.buf, 1);
strbuf_reset(&sb);
} else {
strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
write_file(sb.buf, "%s", oid_to_hex(null_oid()));
strbuf_reset(&sb);
}

strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
write_file(sb.buf, "../..");

Expand Down

0 comments on commit f675f29

Please sign in to comment.