Reftable support git-core
Subsystems (2)
PATCH v17 11/17 Add reftable library
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
Reftable is a format for storing the ref database. Its rationale and specification is in the preceding commit.
This imports the upstream library as one big commit. For understanding the code, it is suggested to read the code in the following order:
* The specification under Documentation/technical/reftable.txt
* reftable.h - the public API
* record.{c,h} - reading and writing records
* block.{c,h} - reading and writing blocks.
* writer.{c,h} - writing a complete reftable file.
* merged.{c,h} and pq.{c,h} - reading a stack of reftables
* stack.{c,h} - writing and compacting stacks of reftable on the filesystem.
--- reftable/LICENSE | 31 + reftable/README.md | 11 + reftable/VERSION | 1 + reftable/basics.c | 215 +++++++ reftable/basics.h | 53 ++ reftable/block.c | 437 +++++++++++++ reftable/block.h | 129 ++++ reftable/block_test.c | 157 +++++ reftable/compat.c | 97 +++ reftable/compat.h | 48 ++ reftable/constants.h | 21 + reftable/dump.c | 212 +++++++ reftable/file.c | 95 +++ reftable/iter.c | 242 ++++++++ reftable/iter.h | 72 +++ reftable/merged.c | 320 ++++++++++ reftable/merged.h | 39 ++ reftable/merged_test.c | 273 +++++++++ reftable/pq.c | 113 ++++ reftable/pq.h | 34 ++ reftable/reader.c | 742 +++++++++++++++++++++++ reftable/reader.h | 65 ++ reftable/record.c | 1118 ++++++++++++++++++++++++++++++++++ reftable/record.h | 128 ++++ reftable/record_test.c | 403 ++++++++++++ reftable/refname.c | 211 +++++++ reftable/refname.h | 38 ++ reftable/refname_test.c | 99 +++ reftable/reftable-tests.h | 22 + reftable/reftable.c | 90 +++ reftable/reftable.h | 571 +++++++++++++++++ reftable/reftable_test.c | 631 +++++++++++++++++++ reftable/slice.c | 217 +++++++ reftable/slice.h | 85 +++ reftable/slice_test.c | 39 ++ reftable/stack.c | 1214 +++++++++++++++++++++++++++++++++++++ reftable/stack.h | 48 ++ reftable/stack_test.c | 787 ++++++++++++++++++++++++ reftable/system.h | 55 ++ reftable/test_framework.c | 69 +++ reftable/test_framework.h | 64 ++ reftable/tree.c | 63 ++ reftable/tree.h | 34 ++ reftable/tree_test.c | 62 ++ reftable/update.sh | 22 + reftable/writer.c | 661 ++++++++++++++++++++ reftable/writer.h | 60 ++ reftable/zlib-compat.c | 92 +++ 48 files changed, 10290 insertions(+) create mode 100644 reftable/LICENSE create mode 100644 reftable/README.md create mode 100644 reftable/VERSION create mode 100644 reftable/basics.c create mode 100644 reftable/basics.h create mode 100644 reftable/block.c create mode 100644 reftable/block.h create mode 100644 reftable/block_test.c create mode 100644 reftable/compat.c create mode 100644 reftable/compat.h create mode 100644 reftable/constants.h create mode 100644 reftable/dump.c create mode 100644 reftable/file.c create mode 100644 reftable/iter.c create mode 100644 reftable/iter.h create mode 100644 reftable/merged.c create mode 100644 reftable/merged.h create mode 100644 reftable/merged_test.c create mode 100644 reftable/pq.c create mode 100644 reftable/pq.h create mode 100644 reftable/reader.c create mode 100644 reftable/reader.h create mode 100644 reftable/record.c create mode 100644 reftable/record.h create mode 100644 reftable/record_test.c create mode 100644 reftable/refname.c create mode 100644 reftable/refname.h create mode 100644 reftable/refname_test.c create mode 100644 reftable/reftable-tests.h create mode 100644 reftable/reftable.c create mode 100644 reftable/reftable.h create mode 100644 reftable/reftable_test.c create mode 100644 reftable/slice.c create mode 100644 reftable/slice.h create mode 100644 reftable/slice_test.c create mode 100644 reftable/stack.c create mode 100644 reftable/stack.h create mode 100644 reftable/stack_test.c create mode 100644 reftable/system.h create mode 100644 reftable/test_framework.c create mode 100644 reftable/test_framework.h create mode 100644 reftable/tree.c create mode 100644 reftable/tree.h create mode 100644 reftable/tree_test.c create mode 100755 reftable/update.sh create mode 100644 reftable/writer.c create mode 100644 reftable/writer.h create mode 100644 reftable/zlib-compat.c
reftable/LICENSE new file +31 -0
@@ -0,0 +1,31 @@ +BSD License + +Copyright (c) 2020, Google LLC +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +* Neither the name of Google LLC nor the names of its contributors may +be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
reftable/README.md new file +11 -0
@@ -0,0 +1,11 @@ + +The source code in this directory comes from https://github.com/google/reftable. + +The VERSION file keeps track of the current version of the reftable library. + +To update the library, do: + + sh reftable/update.sh + +Bugfixes should be accompanied by a test and applied to upstream project at +https://github.com/google/reftable.
reftable/VERSION new file +1 -0
@@ -0,0 +1 @@ +2c91c4b305dcbf6500c0806bb1a7fbcfc668510c C: include system.h in compat.h
reftable/basics.c new file +215 -0
215 lines not on this page · raw message
reftable/basics.h new file +53 -0
53 lines not on this page · raw message
reftable/block.c new file +437 -0
437 lines not on this page · raw message
reftable/block.h new file +129 -0
129 lines not on this page · raw message
reftable/block_test.c new file +157 -0
157 lines not on this page · raw message
reftable/compat.c new file +97 -0
97 lines not on this page · raw message
reftable/compat.h new file +48 -0
48 lines not on this page · raw message
reftable/constants.h new file +21 -0
@@ -0,0 +1,21 @@ +/* +Copyright 2020 Google LLC + +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file or at +https://developers.google.com/open-source/licenses/bsd +*/ + +#ifndef CONSTANTS_H +#define CONSTANTS_H + +#define BLOCK_TYPE_LOG 'g' +#define BLOCK_TYPE_INDEX 'i' +#define BLOCK_TYPE_REF 'r' +#define BLOCK_TYPE_OBJ 'o' +#define BLOCK_TYPE_ANY 0 + +#define MAX_RESTARTS ((1 << 16) - 1) +#define DEFAULT_BLOCK_SIZE 4096 + +#endif
reftable/dump.c new file +212 -0
212 lines not on this page · raw message
reftable/file.c new file +95 -0
95 lines not on this page · raw message
reftable/iter.c new file +242 -0
242 lines not on this page · raw message
reftable/iter.h new file +72 -0
72 lines not on this page · raw message
reftable/merged.c new file +320 -0
320 lines not on this page · raw message
reftable/merged.h new file +39 -0
@@ -0,0 +1,39 @@ +/* +Copyright 2020 Google LLC + +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file or at +https://developers.google.com/open-source/licenses/bsd +*/ + +#ifndef MERGED_H +#define MERGED_H + +#include "pq.h" +#include "reftable.h" + +struct reftable_merged_table { +struct reftable_reader **stack; +int stack_len; +uint32_t hash_id; +bool suppress_deletions; + +uint64_t min; +uint64_t max; +}; + +struct merged_iter { +struct reftable_iterator *stack; +uint32_t hash_id; +int stack_len; +byte typ; +bool suppress_deletions; +struct merged_iter_pqueue pq; +}; + +void merged_table_clear (struct reftable_merged_table *mt); +int merged_table_seek_record (struct reftable_merged_table *mt, +struct reftable_iterator *it, +struct reftable_record *rec); + +#endif
reftable/merged_test.c new file +273 -0
273 lines not on this page · raw message
reftable/pq.c new file +113 -0
113 lines not on this page · raw message
reftable/pq.h new file +34 -0
@@ -0,0 +1,34 @@ +/* +Copyright 2020 Google LLC + +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file or at +https://developers.google.com/open-source/licenses/bsd +*/ + +#ifndef PQ_H +#define PQ_H + +#include "record.h" + +struct pq_entry { +int index; +struct reftable_record rec; +}; + +int pq_less (struct pq_entry a,struct pq_entry b); + +struct merged_iter_pqueue { +struct pq_entry *heap; +int len; +int cap; +}; + +struct pq_entry merged_iter_pqueue_top (struct merged_iter_pqueue pq); +bool merged_iter_pqueue_is_empty (struct merged_iter_pqueue pq); +void merged_iter_pqueue_check (struct merged_iter_pqueue pq); +struct pq_entry merged_iter_pqueue_remove (struct merged_iter_pqueue *pq); +void merged_iter_pqueue_add (struct merged_iter_pqueue *pq,struct pq_entry e); +void merged_iter_pqueue_clear (struct merged_iter_pqueue *pq); + +#endif
reftable/reader.c new file +742 -0
400 lines not on this page · raw message
reftable/reader.h new file +65 -0
65 lines not on this page · raw message
reftable/record.c new file +1118 -0
1,118 lines not on this page · raw message
reftable/record.h new file +128 -0
128 lines not on this page · raw message
reftable/record_test.c new file +403 -0
403 lines not on this page · raw message
reftable/refname.c new file +211 -0
211 lines not on this page · raw message
reftable/refname_test.c new file +99 -0
99 lines not on this page · raw message
reftable/reftable.c new file +90 -0
90 lines not on this page · raw message
reftable/reftable.h new file +571 -0
571 lines not on this page · raw message
reftable/reftable_test.c new file +631 -0
631 lines not on this page · raw message
reftable/slice.c new file +217 -0
217 lines not on this page · raw message
reftable/slice.h new file +85 -0
85 lines not on this page · raw message
reftable/stack.c new file +1214 -0
1,214 lines not on this page · raw message
reftable/stack.h new file +48 -0
48 lines not on this page · raw message
reftable/stack_test.c new file +787 -0
787 lines not on this page · raw message
reftable/system.h new file +55 -0
55 lines not on this page · raw message
reftable/test_framework.c new file +69 -0
69 lines not on this page · raw message
reftable/test_framework.h new file +64 -0
64 lines not on this page · raw message
reftable/tree.c new file +63 -0
63 lines not on this page · raw message
reftable/tree_test.c new file +62 -0
62 lines not on this page · raw message
reftable/writer.c new file +661 -0
661 lines not on this page · raw message
reftable/writer.h new file +60 -0
60 lines not on this page · raw message
reftable/zlib-compat.c new file +92 -0
92 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
gitgitgadget
Part of the diff is not shown here. View the raw message.
PATCH v17 12/17 Reftable support for git-core
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
For background, see the previous commit introducing the library.
This introduces the refs/reftable-backend.c containing reftable powered ref storage backend.
It can be activated by passing --ref-storage=reftable to "git init".
TODO:
* Fix worktree commands
* Spots marked XXX
Example use: see t/t0031-reftable.sh
--- .../technical/repository-version.txt | 7 + Makefile | 28 +- builtin/clone.c | 3 +- builtin/init-db.c | 56 +- cache.h | 6 +- refs.c | 27 +- refs.h | 3 + refs/refs-internal.h | 1 + refs/reftable-backend.c | 1327 +++++++++++++++++ repository.c | 2 + repository.h | 3 + setup.c | 12 +- t/t0031-reftable.sh | 155 ++ 13 files changed, 1600 insertions(+), 30 deletions(-) create mode 100644 refs/reftable-backend.c create mode 100755 t/t0031-reftable.sh
Documentation/technical/repository-version.txt +7 -0
@@ -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.
Makefile +26 -2
@@ -807,6 +807,7 @@ TEST_SHELL_PATH = $(SHELL_PATH) LIB_FILE = libgit.a XDIFF_LIB = xdiff/lib.a VCSSVN_LIB = vcs-svn/lib.a +REFTABLE_LIB = reftable/libreftable.a GENERATED_H += config-list.h GENERATED_H += command-list.h
@@ -959,6 +960,7 @@ LIB_OBJS += ref-filter.o LIB_OBJS += reflog-walk.o LIB_OBJS += refs.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
@@ -1162,7 +1164,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)
@@ -2352,11 +2354,30 @@ VCSSVN_OBJS += vcs-svn/sliding_window.o VCSSVN_OBJS += vcs-svn/svndiff.o VCSSVN_OBJS += vcs-svn/svndump.o +REFTABLE_OBJS += reftable/basics.o +REFTABLE_OBJS += reftable/block.o +REFTABLE_OBJS += reftable/compat.o +REFTABLE_OBJS += reftable/file.o +REFTABLE_OBJS += reftable/iter.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/reftable.o +REFTABLE_OBJS += reftable/slice.o +REFTABLE_OBJS += reftable/stack.o +REFTABLE_OBJS += reftable/tree.o +REFTABLE_OBJS += reftable/writer.o +REFTABLE_OBJS += reftable/zlib-compat.o + + TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \ $(XDIFF_OBJS) \ $(VCSSVN_OBJS) \ $(FUZZ_OBJS) \ + $(REFTABLE_OBJS) \ common-main.o \ git.o ifndef NO_CURL
@@ -2497,6 +2518,9 @@ $(XDIFF_LIB): $(XDIFF_OBJS) $(VCSSVN_LIB): $(VCSSVN_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ +$(REFTABLE_LIB): $(REFTABLE_OBJS) + $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ + export DEFAULT_EDITOR DEFAULT_PAGER Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
@@ -3112,7 +3136,7 @@ cocciclean: clean: profile-clean coverage-clean cocciclean $(RM) *.res $(RM) $(OBJECTS) - $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) + $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) $(REFTABLE_LIB) $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X $(RM) $(TEST_PROGRAMS) $(RM) $(FUZZ_PROGRAMS)
builtin/clone.c +2 -1
@@ -1111,7 +1111,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } } -init_db (git_dir, real_git_dir, option_template,GIT_HASH_UNKNOWN ,INIT_DB_QUIET ); +init_db (git_dir, real_git_dir, option_template,GIT_HASH_UNKNOWN , +DEFAULT_REF_STORAGE ,INIT_DB_QUIET );if (real_git_dir) git_dir = real_git_dir;
builtin/init-db.c +42 -14
132 lines not on this page · raw message
cache.h +4 -2
@@ -628,8 +628,9 @@ int path_inside_repo(const char *prefix, const char *path);int init_db (const char *git_dir,const char *real_git_dir,const char *template_dir,int hash_algo, -unsigned int flags); -void initialize_repository_version (int hash_algo); +const char *ref_storage_format,unsigned int flags); +void initialize_repository_version (int hash_algo, +const char *ref_storage_format);void sanitize_stdfds (void );int daemonize (void );
@@ -1043,6 +1044,7 @@ struct repository_format {int is_bare;int hash_algo;char *work_tree; +char *ref_storage;struct string_list unknown_extensions; };
refs.c +19 -8
@@ -17,10 +17,16 @@#include "argv-array.h" #include "repository.h" +const char *default_ref_storage (void ) +{ +const char *test =getenv ("GIT_TEST_REFTABLE" ); +return test ?"reftable" :"files" ; +} +/* * List of all available backends */ -static struct ref_storage_be *refs_backends = &refs_be_files; +static struct ref_storage_be *refs_backends = &refs_be_reftable;static struct ref_storage_be *find_ref_storage_backend (const char *name) {
@@ -1717,13 +1723,13 @@ static struct ref_store *lookup_ref_store_map(struct hashmap *map,* Create, record, and return a ref_store instance for the specified * gitdir. */ -static struct ref_store *ref_store_init (const char *gitdir, +static struct ref_store *ref_store_init (const char *gitdir,const char *be_name,unsigned int flags) { -const char *be_name ="files" ; -struct ref_storage_be *be =find_ref_storage_backend (be_name); +struct ref_storage_be *be;struct ref_store *refs; + be =find_ref_storage_backend (be_name);if (!be)BUG ("reference backend %s is unknown" , be_name);
@@ -1739,7 +1745,11 @@ struct ref_store *get_main_ref_store(struct repository *r)if (!r->gitdir)BUG ("attempting to get main_ref_store outside of repository" ); - r->refs_private =ref_store_init (r->gitdir,REF_STORE_ALL_CAPS ); + r->refs_private =ref_store_init (r->gitdir, + r->ref_storage_format ? + r->ref_storage_format : +DEFAULT_REF_STORAGE , +REF_STORE_ALL_CAPS );return r->refs_private; }
@@ -1794,7 +1804,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule) goto done;/* assume that add_submodule_odb() has been called */ - refs =ref_store_init (submodule_sb.buf, + refs =ref_store_init (submodule_sb.buf,DEFAULT_REF_STORAGE ,/* XXX */ REF_STORE_READ |REF_STORE_ODB );register_ref_store_map (&submodule_ref_stores,"submodule" , refs, submodule);
@@ -1808,6 +1818,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule)struct ref_store *get_worktree_ref_store (const struct worktree *wt) { +const char *format =DEFAULT_REF_STORAGE ;/* XXX */ struct ref_store *refs;const char *id;
@@ -1821,9 +1832,9 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)if (wt->id) refs =ref_store_init (git_common_path ("worktrees/%s" , wt->id), -REF_STORE_ALL_CAPS ); + format,REF_STORE_ALL_CAPS );else - refs =ref_store_init (get_git_common_dir (), + refs =ref_store_init (get_git_common_dir (), format,REF_STORE_ALL_CAPS );if (refs)
refs.h +3 -0
@@ -9,6 +9,9 @@ struct string_list;struct string_list_item ;struct worktree ; +/* Returns the ref storage backend to use by default. */ +const char *default_ref_storage (void ); +/* * Resolve a reference, recursively following symbolic refererences. *
refs/refs-internal.h +1 -0
@@ -693,6 +693,7 @@ struct ref_storage_be { };extern struct ref_storage_be refs_be_files; +extern struct ref_storage_be refs_be_reftable;extern struct ref_storage_be refs_be_packed;/*
refs/reftable-backend.c new file +1327 -0
1,327 lines not on this page · raw message
repository.c +2 -0
@@ -178,6 +178,8 @@ int repo_init(struct repository *repo,if (worktree)repo_set_worktree (repo, worktree); + repo->ref_storage_format =xstrdup_or_null (format.ref_storage); +clear_repository_format (&format);return 0 ;
repository.h +3 -0
@@ -74,6 +74,9 @@ struct repository {*/ struct ref_store *refs_private; +/* The format to use for the ref database. */ +char *ref_storage_format; +/* * Contains path to often used file names. */
setup.c +9 -3
@@ -468,9 +468,11 @@ static int check_repo_format(const char *var, const char *value, void *vdata)if (!value)return config_error_nonbool (var); data->partial_clone =xstrdup (value); - }else if (!strcmp (ext,"worktreeconfig" )) + }else if (!strcmp (ext,"worktreeconfig" )) { data->worktree_config =git_config_bool (var, value); -else + }else if (!strcmp (ext,"refstorage" )) { + data->ref_storage =xstrdup (value); + }else string_list_append (&data->unknown_extensions, ext); }
@@ -559,6 +561,7 @@ void clear_repository_format(struct repository_format *format)string_list_clear (&format->unknown_extensions,0 );free (format->work_tree);free (format->partial_clone); +free (format->ref_storage);init_repository_format (format); }
@@ -1204,8 +1207,11 @@ const char *setup_git_directory_gently(int *nongit_ok) gitdir =DEFAULT_GIT_DIR_ENVIRONMENT ;setup_git_env (gitdir); } -if (startup_info->have_repository) +if (startup_info->have_repository) {repo_set_hash_algo (the_repository, repo_fmt.hash_algo); + the_repository->ref_storage_format = +xstrdup_or_null (repo_fmt.ref_storage); + } }strbuf_release (&dir);
t/t0031-reftable.sh new file +155 -0
155 lines not on this page · raw message
Signature · 1 line
gitgitgadget
Re: [PATCH v17 12/17] Reftable support for git-core
refs/reftable-backend.c line 95
Quoted 10 lines · Han-Wen Nienhuys via GitGitGadget
refs/reftable-backend.c new file +10
-0+static int reftable_init_db (struct ref_store *ref_store,struct strbuf *err) +{ +struct git_reftable_ref_store *refs = + (struct git_reftable_ref_store *)ref_store; +struct strbuf sb =STRBUF_INIT ; + +safe_create_dir (refs->reftable_dir,1 ); + +strbuf_addf (&sb,"%s/HEAD" , refs->repo_dir); +write_file (sb.buf,"ref: refs/.invalid" );
The "Backward compatibility" section of the specs says:
* .git/HEAD, a regular file containing ref: refs/heads/.invalid.
not "refs/.invalid".
Quoted 11 lines
refs/reftable-backend.c new file +11
-0+strbuf_reset (&sb); + +strbuf_addf (&sb,"%s/refs" , refs->repo_dir); +safe_create_dir (sb.buf,1 ); +strbuf_reset (&sb); + +strbuf_addf (&sb,"%s/refs/heads" , refs->repo_dir); +write_file (sb.buf,"this repository uses the reftable format" ); + +return 0 ; +}
PATCH v17 13/17 Hookup unittests for the reftable library.
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
The unittests are under reftable/*_test.c, so all of the reftable code stays in one directory. They are called from t/helpers/test-reftable.c in t0031-reftable.sh
--- Makefile | 17 ++++++++++++++++- t/helper/test-reftable.c | 15 +++++++++++++++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + t/t0031-reftable.sh | 5 +++++ 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 t/helper/test-reftable.c
Makefile +16 -1
@@ -725,6 +725,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
@@ -808,6 +809,7 @@ LIB_FILE = libgit.a XDIFF_LIB = xdiff/lib.a VCSSVN_LIB = vcs-svn/lib.a REFTABLE_LIB = reftable/libreftable.a +REFTABLE_TEST_LIB = reftable/libreftable_test.a GENERATED_H += config-list.h GENERATED_H += command-list.h
@@ -2371,6 +2373,15 @@ REFTABLE_OBJS += reftable/tree.o REFTABLE_OBJS += reftable/writer.o REFTABLE_OBJS += reftable/zlib-compat.o +REFTABLE_TEST_OBJS += reftable/block_test.o +REFTABLE_TEST_OBJS += reftable/merged_test.o +REFTABLE_TEST_OBJS += reftable/record_test.o +REFTABLE_TEST_OBJS += reftable/refname_test.o +REFTABLE_TEST_OBJS += reftable/reftable_test.o +REFTABLE_TEST_OBJS += reftable/slice_test.o +REFTABLE_TEST_OBJS += reftable/stack_test.o +REFTABLE_TEST_OBJS += reftable/tree_test.o +REFTABLE_TEST_OBJS += reftable/test_framework.o TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
@@ -2378,6 +2389,7 @@ OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \ $(VCSSVN_OBJS) \ $(FUZZ_OBJS) \ $(REFTABLE_OBJS) \ + $(REFTABLE_TEST_OBJS) \ common-main.o \ git.o ifndef NO_CURL
@@ -2521,6 +2533,9 @@ $(VCSSVN_LIB): $(VCSSVN_OBJS) $(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
@@ -2803,7 +2818,7 @@ t/helper/test-svn-fe$X: $(VCSSVN_LIB) 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
t/helper/test-reftable.c new file +15 -0
@@ -0,0 +1,15 @@ +#include "reftable/reftable-tests.h" +#include "test-tool.h" + +int cmd__reftable (int argc,const char **argv) +{ +block_test_main (argc, argv); +merged_test_main (argc, argv); +record_test_main (argc, argv); +refname_test_main (argc, argv); +reftable_test_main (argc, argv); +slice_test_main (argc, argv); +stack_test_main (argc, argv); +tree_test_main (argc, argv); +return 0 ; +}
t/helper/test-tool.c +1 -0
@@ -52,6 +52,7 @@ static struct test_cmd cmds[] = { { "read-graph", cmd__read_graph }, { "read-midx", cmd__read_midx }, { "ref-store", cmd__ref_store }, + { "reftable", cmd__reftable }, { "regex", cmd__regex }, { "repository", cmd__repository }, { "revision-walking", cmd__revision_walking },
t/helper/test-tool.h +1 -0
@@ -41,6 +41,7 @@ int cmd__read_cache(int argc, const char **argv);int cmd__read_graph (int argc,const char **argv);int cmd__read_midx (int argc,const char **argv);int cmd__ref_store (int argc,const char **argv); +int cmd__reftable (int argc,const char **argv);int cmd__regex (int argc,const char **argv);int cmd__repository (int argc,const char **argv);int cmd__revision_walking (int argc,const char **argv);
t/t0031-reftable.sh +5 -0
@@ -15,6 +15,11 @@ initialize () { mv .git/hooks .git/hooks-disabled } +test_expect_success 'unittests' ' + test-tool reftable +' + + test_expect_success 'delete ref' ' initialize && test_commit file &&
Signature · 1 line
gitgitgadget
PATCH v17 14/17 Add GIT_DEBUG_REFS debugging mechanism
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
When set in the environment, GIT_DEBUG_REFS makes git print operations and results as they flow through the ref storage backend. This helps debug discrepancies between different ref backends.
--- Makefile | 1 + refs.c | 3 + refs/debug.c | 358 ++++++++++++++++++++++++++++++++++++++++++ refs/refs-internal.h | 6 + t/t0033-debug-refs.sh | 18 +++ 5 files changed, 386 insertions(+) create mode 100644 refs/debug.c create mode 100755 t/t0033-debug-refs.sh
Makefile +1 -0
@@ -961,6 +961,7 @@ LIB_OBJS += rebase.o LIB_OBJS += ref-filter.o 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
refs.c +3 -0
@@ -1750,6 +1750,9 @@ struct ref_store *get_main_ref_store(struct repository *r) r->ref_storage_format :DEFAULT_REF_STORAGE , REF_STORE_ALL_CAPS); +if (getenv ("GIT_DEBUG_REFS" )) { + r->refs_private =debug_wrap (r->refs_private); + }return r->refs_private; }
refs/debug.c new file +358 -0
358 lines not on this page · raw message
refs/refs-internal.h +6 -0
@@ -713,4 +713,10 @@ struct ref_store {void base_ref_store_init (struct ref_store *refs,const struct ref_storage_be *be); +/* +* Print out ref operations as they occur. Useful for debugging alternate ref +* backends. +*/ +struct ref_store *debug_wrap (struct ref_store *store); +#endif /* REFS_REFS_INTERNAL_H */
t/t0033-debug-refs.sh new file +18 -0
@@ -0,0 +1,18 @@ +#!/bin/sh +# +# Copyright (c) 2020 Google LLC +# + +test_description='cross-check reftable with files, using GIT_DEBUG_REFS output' + +. ./test-lib.sh + +test_expect_success 'GIT_DEBUG_REFS' ' + git init --ref-storage=files files && + git init --ref-storage=reftable reftable && + (cd files && GIT_DEBUG_REFS=1 test_commit message file) > files.txt && + (cd reftable && GIT_DEBUG_REFS=1 test_commit message file) > reftable.txt && + test_cmp files.txt reftable.txt +' + +test_done
Signature · 1 line
gitgitgadget
PATCH v17 15/17 vcxproj: adjust for the reftable changes
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
This allows Git to be compiled via Visual Studio again after integrating the hn/reftable branch.
--- config.mak.uname | 2 +- contrib/buildsystems/Generators/Vcxproj.pm | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-)
config.mak.uname +1 -1
@@ -709,7 +709,7 @@ vcxproj: # Make .vcxproj files and add them unset QUIET_GEN QUIET_BUILT_IN; \ perl contrib/buildsystems/generate -g Vcxproj - git add -f git.sln {*,*/lib,t/helper/*}/*.vcxproj + git add -f git.sln {*,*/lib,*/libreftable,t/helper/*}/*.vcxproj # Generate the LinkOrCopyBuiltins.targets and LinkOrCopyRemoteHttp.targets file (echo '<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">' && \
contrib/buildsystems/Generators/Vcxproj.pm +10 -1
@@ -77,7 +77,7 @@ sub createProject { my $libs_release = "\n "; my $libs_debug = "\n "; if (!$static_library) { - $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}})); + $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib|reftable\/libreftable\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}})); $libs_debug = $libs_release; $libs_debug =~ s/zlib\.lib/zlibd\.lib/g; $libs_debug =~ s/libcurl\.lib/libcurl-d\.lib/g;
@@ -231,6 +231,7 @@ sub createProject { EOM if (!$static_library || $target =~ 'vcs-svn' || $target =~ 'xdiff') { my $uuid_libgit = $$build_structure{"LIBS_libgit_GUID"}; + my $uuid_libreftable = $$build_structure{"LIBS_reftable/libreftable_GUID"}; my $uuid_xdiff_lib = $$build_structure{"LIBS_xdiff/lib_GUID"}; print F << "EOM";
@@ -240,6 +241,14 @@ sub createProject { <ReferenceOutputAssembly>false</ReferenceOutputAssembly> </ProjectReference> EOM + if (!($name =~ /xdiff|libreftable/)) { + print F << "EOM"; + <ProjectReference Include="$cdup\\reftable\\libreftable\\libreftable.vcxproj"> + <Project>$uuid_libreftable</Project> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + </ProjectReference> +EOM + } if (!($name =~ 'xdiff')) { print F << "EOM"; <ProjectReference Include="$cdup\\xdiff\\lib\\xdiff_lib.vcxproj">
Signature · 1 line
gitgitgadget
PATCH v17 16/17 Add reftable testing infrastructure
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
* Add GIT_TEST_REFTABLE environment var to control default ref storage
* Add test_prerequisite REFTABLE.
* Skip some tests that are incompatible:
* t3210-pack-refs.sh - does not apply
* t9903-bash-prompt - The bash mode reads .git/HEAD directly
* t1450-fsck.sh - manipulates .git/ directly to create invalid state
Major test failures:
* t1400-update-ref.sh - Reads from .git/{refs,logs} directly
* t1404-update-ref-errors.sh - Manipulates .git/refs/ directly
* t1405 - inspecs .git/ directly.
--- builtin/clone.c | 2 +- refs.c | 6 +++--- t/t1409-avoid-packing-refs.sh | 6 ++++++ t/t1450-fsck.sh | 6 ++++++ t/t3210-pack-refs.sh | 6 ++++++ t/t9903-bash-prompt.sh | 6 ++++++ t/test-lib.sh | 5 +++++ 7 files changed, 33 insertions(+), 4 deletions(-)
builtin/clone.c +1 -1
@@ -1112,7 +1112,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) }init_db (git_dir, real_git_dir, option_template,GIT_HASH_UNKNOWN , -DEFAULT_REF_STORAGE ,INIT_DB_QUIET ); +default_ref_storage (),INIT_DB_QUIET );if (real_git_dir) git_dir = real_git_dir;
refs.c +3 -3
@@ -1748,7 +1748,7 @@ struct ref_store *get_main_ref_store(struct repository *r) r->refs_private =ref_store_init (r->gitdir, r->ref_storage_format ? r->ref_storage_format : -DEFAULT_REF_STORAGE , +default_ref_storage (),REF_STORE_ALL_CAPS );if (getenv ("GIT_DEBUG_REFS" )) { r->refs_private =debug_wrap (r->refs_private);
@@ -1807,7 +1807,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule) goto done;/* assume that add_submodule_odb() has been called */ - refs =ref_store_init (submodule_sb.buf,DEFAULT_REF_STORAGE ,/* XXX */ + refs =ref_store_init (submodule_sb.buf,default_ref_storage (),REF_STORE_READ |REF_STORE_ODB );register_ref_store_map (&submodule_ref_stores,"submodule" , refs, submodule);
@@ -1821,7 +1821,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule)struct ref_store *get_worktree_ref_store (const struct worktree *wt) { -const char *format =DEFAULT_REF_STORAGE ;/* XXX */ +const char *format =default_ref_storage ();struct ref_store *refs;const char *id;
t/t1409-avoid-packing-refs.sh +6 -0
@@ -4,6 +4,12 @@ test_description='avoid rewriting packed-refs unnecessarily' . ./test-lib.sh +if test_have_prereq REFTABLE +then + skip_all='skipping pack-refs tests; incompatible with reftable' + test_done +fi + # Add an identifying mark to the packed-refs file header line. This # shouldn't upset readers, and it should be omitted if the file is # ever rewritten.
t/t1450-fsck.sh +6 -0
@@ -8,6 +8,12 @@ test_description='git fsck random collection of tests . ./test-lib.sh +if test_have_prereq REFTABLE +then + skip_all='skipping tests; incompatible with reftable' + test_done +fi + test_expect_success setup ' test_oid_init && git config gc.auto 0 &&
t/t3210-pack-refs.sh +6 -0
@@ -11,6 +11,12 @@ semantic is still the same. ' . ./test-lib.sh +if test_have_prereq REFTABLE +then + skip_all='skipping pack-refs tests; incompatible with reftable' + test_done +fi + test_expect_success 'enable reflogs' ' git config core.logallrefupdates true '
t/t9903-bash-prompt.sh +6 -0
@@ -7,6 +7,12 @@ test_description='test git-specific bash prompt functions' . ./lib-bash.sh +if test_have_prereq REFTABLE +then + skip_all='skipping tests; incompatible with reftable' + test_done +fi + . "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh" actual="$TRASH_DIRECTORY/actual"
t/test-lib.sh +5 -0
@@ -1504,6 +1504,11 @@ parisc* | hppa*) ;; esac +if test -n "$GIT_TEST_REFTABLE" +then + test_set_prereq REFTABLE +fi + ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1 test -z "$NO_PERL" && test_set_prereq PERL test -z "$NO_PTHREADS" && test_set_prereq PTHREADS
Signature · 1 line
gitgitgadget
Re: [PATCH v17 16/17] Add reftable testing infrastructure
Quoted 7 lines · Han-Wen Nienhuys via GitGitGadget
* Add
GIT_TEST_REFTABLEenvironment var to control default ref storage* Add
test_prerequisiteREFTABLE.* Skip some tests that are incompatible:
* t9903-bash-prompt - The bash mode reads .git/HEAD directly
The patch below fixes this incompatibility, relying on the fact that the reftable specs clearly specify what must be written to the placeholder '.git/HEAD'. You can queue this as a preparatory patch to this series.
Note, however, that, as poined out in my other email, the current reftable code doesn't follow the specs WRT what must be written to '.git/HEAD'. The patch below follows the code, not the specs, so it will have to be updated as well.
--- >8 ---
Subject: [PATCH] git-prompt: prepare for reftable refs backend
In our git-prompt script we strive to use Bash builtins wherever possible, because fork()-ing subshells for command substitutions and fork()+exec()-ing Git commands are expensive on some platforms. We even read and parse '.git/HEAD' using Bash builtins to get the name of the current branch [1]. However, the upcoming reftable refs backend won't use '.git/HEAD' at all, but will write an invalid refname as placeholder for backwards compatibility instead, which will break our git-prompt script.
Update the git-prompt script to recognize the placeholder '.git/HEAD' written by the reftable backend (its content is specified in the reftable specs), and then fall back to use 'git symbolic-ref' to get the name of the current branch.
[1] 3a43c4b5bd (bash prompt: use bash builtins to find out current
branch, 2011-03-31)
--- contrib/completion/git-prompt.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
contrib/completion/git-prompt.sh +6 -1
@@ -460,10 +460,15 @@ __git_ps1 () if ! __git_eread "$g/HEAD" head; then return $exit fi - # is it a symbolic ref? b="${head#ref: }" if [ "$head" = "$b" ]; then detached=yes + elif [ "$b" = "refs/.invalid" ]; then + # Reftable + b="$(git symbolic-ref HEAD 2>/dev/null)" || + detached=yes + fi + if [ "$detached" = yes ]; then b="$( case "${GIT_PS1_DESCRIBE_STYLE-}" in (contains)
Signature · 1 line
2.27.0.365.g477ada09ef
PATCH v17 17/17 Add "test-tool dump-reftable" command.
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This command dumps individual tables or a stack of of tables.
--- Makefile | 1 + t/helper/test-reftable.c | 5 +++++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + 4 files changed, 8 insertions(+)
Makefile +1 -0
@@ -2375,6 +2375,7 @@ REFTABLE_OBJS += reftable/writer.o REFTABLE_OBJS += reftable/zlib-compat.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/record_test.o REFTABLE_TEST_OBJS += reftable/refname_test.o
t/helper/test-reftable.c +5 -0
@@ -13,3 +13,8 @@ int cmd__reftable(int argc, const char **argv)tree_test_main (argc, argv);return 0 ; } + +int cmd__dump_reftable (int argc,const char **argv) +{ +return reftable_dump_main (argc, (char *const *)argv); +}
t/helper/test-tool.c +1 -0
@@ -53,6 +53,7 @@ static struct test_cmd cmds[] = { { "read-midx", cmd__read_midx }, { "ref-store", cmd__ref_store }, { "reftable", cmd__reftable }, + { "dump-reftable", cmd__dump_reftable }, { "regex", cmd__regex }, { "repository", cmd__repository }, { "revision-walking", cmd__revision_walking },
t/helper/test-tool.h +1 -0
@@ -17,6 +17,7 @@ int cmd__dump_cache_tree(int argc, const char **argv);int cmd__dump_fsmonitor (int argc,const char **argv);int cmd__dump_split_index (int argc,const char **argv);int cmd__dump_untracked_cache (int argc,const char **argv); +int cmd__dump_reftable (int argc,const char **argv);int cmd__example_decorate (int argc,const char **argv);int cmd__genrandom (int argc,const char **argv);int cmd__genzeros (int argc,const char **argv);
Signature · 1 line
gitgitgadget
v18 cover Reftable support git-core
In reply to Han-Wen Nienhuys via GitGitGadgetThis adds the reftable library, and hooks it up as a ref backend. Based on hn/refs-cleanup.
Includes testing support, to test: make -C t/ GIT_TEST_REFTABLE=1
Summary 21561 tests pass 899 tests fail
Some issues:
* many tests inspect .git/{logs,heads}/ directly.
* worktrees broken.
v21
* reftable/ is now fully imported from upstream.
* reftable/ uses strbuf
* fix of the HEAD file.
* check for reload on every read
Han-Wen Nienhuys (17):
lib-t6000.sh: write tag using git-update-ref
checkout: add '\n' to reflog message
Write pseudorefs through ref backends.
Make refs_ref_exists public
Treat BISECT_HEAD as a pseudo ref
Treat CHERRY_PICK_HEAD as a pseudo ref
Treat REVERT_HEAD as a pseudo ref
Move REF_LOG_ONLY to refs-internal.h
Iterate over the "refs/" namespace in for_each_[raw]ref Add .gitattributes for the reftable/ directory Add reftable library
Add standalone build infrastructure for reftable Reftable support for git-core
Hookup unittests for the reftable library.
Add GIT_DEBUG_REFS debugging mechanism
Add reftable testing infrastructure
Add "test-tool dump-reftable" command.
Johannes Schindelin (1):
vcxproj: adjust for the reftable changes
SZEDER Gábor (1):
git-prompt: prepare for reftable refs backend
.../technical/repository-version.txt | 7 + Makefile | 47 +- builtin/bisect--helper.c | 3 +- builtin/checkout.c | 5 +- builtin/clone.c | 3 +- builtin/commit.c | 34 +- builtin/init-db.c | 56 +- builtin/merge.c | 2 +- cache.h | 6 +- config.mak.uname | 2 +- contrib/buildsystems/Generators/Vcxproj.pm | 11 +- contrib/completion/git-prompt.sh | 7 +- git-bisect.sh | 4 +- path.c | 2 - path.h | 9 +- refs.c | 157 +- refs.h | 16 + refs/debug.c | 411 +++++ refs/files-backend.c | 121 +- refs/packed-backend.c | 21 +- refs/refs-internal.h | 32 + refs/reftable-backend.c | 1335 +++++++++++++++++ reftable/.gitattributes | 1 + reftable/BUILD | 203 +++ reftable/LICENSE | 31 + reftable/README.md | 33 + reftable/VERSION | 1 + reftable/WORKSPACE | 14 + reftable/basics.c | 215 +++ reftable/basics.h | 53 + reftable/block.c | 432 ++++++ reftable/block.h | 129 ++ reftable/block_test.c | 157 ++ reftable/compat.c | 98 ++ reftable/compat.h | 48 + reftable/constants.h | 21 + reftable/dump.c | 212 +++ reftable/file.c | 95 ++ reftable/iter.c | 242 +++ reftable/iter.h | 72 + reftable/merged.c | 321 ++++ reftable/merged.h | 39 + reftable/merged_test.c | 273 ++++ reftable/pq.c | 115 ++ reftable/pq.h | 34 + reftable/reader.c | 744 +++++++++ reftable/reader.h | 65 + reftable/record.c | 1126 ++++++++++++++ reftable/record.h | 143 ++ reftable/record_test.c | 410 +++++ reftable/refname.c | 209 +++ reftable/refname.h | 38 + reftable/refname_test.c | 99 ++ reftable/reftable-tests.h | 22 + reftable/reftable.c | 90 ++ reftable/reftable.h | 571 +++++++ reftable/reftable_test.c | 631 ++++++++ reftable/stack.c | 1209 +++++++++++++++ reftable/stack.h | 48 + reftable/stack_test.c | 787 ++++++++++ reftable/strbuf.c | 206 +++ reftable/strbuf.h | 88 ++ reftable/strbuf_test.c | 39 + reftable/system.h | 53 + reftable/test_framework.c | 69 + reftable/test_framework.h | 64 + reftable/tree.c | 63 + reftable/tree.h | 34 + reftable/tree_test.c | 62 + reftable/update.sh | 19 + reftable/writer.c | 662 ++++++++ reftable/writer.h | 60 + reftable/zlib-compat.c | 92 ++ reftable/zlib.BUILD | 36 + repository.c | 2 + repository.h | 3 + sequencer.c | 56 +- setup.c | 12 +- t/helper/test-reftable.c | 20 + t/helper/test-tool.c | 2 + t/helper/test-tool.h | 2 + t/lib-t6000.sh | 5 +- t/t0031-reftable.sh | 160 ++ t/t0033-debug-refs.sh | 18 + t/t1409-avoid-packing-refs.sh | 6 + t/t1450-fsck.sh | 6 + t/t3210-pack-refs.sh | 6 + t/test-lib.sh | 5 + wt-status.c | 6 +- 89 files changed, 12977 insertions(+), 201 deletions(-) create mode 100644 refs/debug.c create mode 100644 refs/reftable-backend.c create mode 100644 reftable/.gitattributes create mode 100644 reftable/BUILD create mode 100644 reftable/LICENSE create mode 100644 reftable/README.md create mode 100644 reftable/VERSION create mode 100644 reftable/WORKSPACE create mode 100644 reftable/basics.c create mode 100644 reftable/basics.h create mode 100644 reftable/block.c create mode 100644 reftable/block.h create mode 100644 reftable/block_test.c create mode 100644 reftable/compat.c create mode 100644 reftable/compat.h create mode 100644 reftable/constants.h create mode 100644 reftable/dump.c create mode 100644 reftable/file.c create mode 100644 reftable/iter.c create mode 100644 reftable/iter.h create mode 100644 reftable/merged.c create mode 100644 reftable/merged.h create mode 100644 reftable/merged_test.c create mode 100644 reftable/pq.c create mode 100644 reftable/pq.h create mode 100644 reftable/reader.c create mode 100644 reftable/reader.h create mode 100644 reftable/record.c create mode 100644 reftable/record.h create mode 100644 reftable/record_test.c create mode 100644 reftable/refname.c create mode 100644 reftable/refname.h create mode 100644 reftable/refname_test.c create mode 100644 reftable/reftable-tests.h create mode 100644 reftable/reftable.c create mode 100644 reftable/reftable.h create mode 100644 reftable/reftable_test.c create mode 100644 reftable/stack.c create mode 100644 reftable/stack.h create mode 100644 reftable/stack_test.c create mode 100644 reftable/strbuf.c create mode 100644 reftable/strbuf.h create mode 100644 reftable/strbuf_test.c create mode 100644 reftable/system.h create mode 100644 reftable/test_framework.c create mode 100644 reftable/test_framework.h create mode 100644 reftable/tree.c create mode 100644 reftable/tree.h create mode 100644 reftable/tree_test.c create mode 100755 reftable/update.sh create mode 100644 reftable/writer.c create mode 100644 reftable/writer.h create mode 100644 reftable/zlib-compat.c create mode 100644 reftable/zlib.BUILD create mode 100644 t/helper/test-reftable.c create mode 100755 t/t0031-reftable.sh create mode 100755 t/t0033-debug-refs.sh
base-commit: 4d328a12d95bbea4e742463974ca03e49933bf7e
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-539%2Fhanwen%2Freftable-v18
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-539/hanwen/reftable-v18
Pull-Request: https://github.com/gitgitgadget/git/pull/539
Range-diff vs v17:
1: 8304c3d6379 = 1: b968b795af9 lib-t6000.sh: write tag using git-update-ref
2: 4012d801e3c = 2: 52102259773 checkout: add '\n' to reflog message
3: 95a6a1d968e = 3: 15c9dd66e17 Write pseudorefs through ref backends.
4: 1f8865f4b3e = 4: a5bce2e3fe6 Make refs_ref_exists public
5: 7f376a76d84 = 5: a29d898907a Treat BISECT_HEAD as a pseudo ref
6: 959c69b5ee4 = 6: 11a690d2b8e Treat CHERRY_PICK_HEAD as a pseudo ref
7: 3f18475d0d3 = 7: 4e52ec0dbc1 Treat REVERT_HEAD as a pseudo ref
8: 4981e5395c6 = 8: 37e350af159 Move REF_LOG_ONLY to refs-internal.h
9: f452c48ae44 = 9: 468f00eaf67 Iterate over the "refs/" namespace in for_each_[raw]ref
10: 1fa68d5d34f = 10: 21febeaa81f Add .gitattributes for the reftable/ directory
11: 86646c834c2 ! 11: 3c84f43cfa0 Add reftable library
@@ Metadata
## Commit message ##
Add reftable library
- Reftable is a format for storing the ref database. Its rationale and
- specification is in the preceding commit.
+ Reftable is a format for storing the ref database. The rationale and format
+ layout is detailed in commit 35e6c47404 ("reftable: file format documentation",
+ 2020-05-20).
- This imports the upstream library as one big commit. For understanding
- the code, it is suggested to read the code in the following order:
+ This commits imports a fully functioning library for reading and writing
+ reftables, along with unittests.
+
+ It is suggested to read the code in the following order:
* The specification under Documentation/technical/reftable.txt
@@ Commit message
* block.{c,h} - reading and writing blocks.
+ * reader.{c,h} - reading a complete reftable file.
+
* writer.{c,h} - writing a complete reftable file.
* merged.{c,h} and pq.{c,h} - reading a stack of reftables
@@ reftable/LICENSE (new)
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- ## reftable/README.md (new) ##
-@@
-+
-+The source code in this directory comes from https://github.com/google/reftable.
-+
-+The VERSION file keeps track of the current version of the reftable library.
-+
-+To update the library, do:
-+
-+ sh reftable/update.sh
-+
-+Bugfixes should be accompanied by a test and applied to upstream project at
-+https://github.com/google/reftable.
-
## reftable/VERSION (new) ##
@@
-+2c91c4b305dcbf6500c0806bb1a7fbcfc668510c C: include system.h in compat.h
++d7472cbd1afe6bf3da53e94971b1cc79ce183fa8 Remove outdated bits of the README file
## reftable/basics.c (new) ##
@@
@@ reftable/basics.c (new)
+
+#include "system.h"
+
-+void put_be24(byte *out, uint32_t i)
++void put_be24(uint8_t *out, uint32_t i)
+{
-+ out[0] = (byte)((i >> 16) & 0xff);
-+ out[1] = (byte)((i >> 8) & 0xff);
-+ out[2] = (byte)(i & 0xff);
++ out[0] = (uint8_t)((i >> 16) & 0xff);
++ out[1] = (uint8_t)((i >> 8) & 0xff);
++ out[2] = (uint8_t)(i & 0xff);
+}
+
-+uint32_t get_be24(byte *in)
++uint32_t get_be24(uint8_t *in)
+{
+ return (uint32_t)(in[0]) << 16 | (uint32_t)(in[1]) << 8 |
+ (uint32_t)(in[2]);
@@ reftable/basics.h (new)
+
+/* Bigendian en/decoding of integers */
+
-+void put_be24(byte *out, uint32_t i);
-+uint32_t get_be24(byte *in);
++void put_be24(uint8_t *out, uint32_t i);
++uint32_t get_be24(uint8_t *in);
+void put_be16(uint8_t *out, uint16_t i);
+
+/*
@@ reftable/block.c (new)
+}
+
+int block_writer_register_restart(struct block_writer *w, int n, bool restart,
-+ struct slice *key);
++ struct strbuf *key);
+
-+void block_writer_init(struct block_writer *bw, byte typ, byte *buf,
++void block_writer_init(struct block_writer *bw, uint8_t typ, uint8_t *buf,
+ uint32_t block_size, uint32_t header_off, int hash_size)
+{
+ bw->buf = buf;
@@ reftable/block.c (new)
+ bw->entries = 0;
+ bw->restart_len = 0;
+ bw->last_key.len = 0;
-+ bw->last_key.canary = SLICE_CANARY;
+}
+
-+byte block_writer_type(struct block_writer *bw)
++uint8_t block_writer_type(struct block_writer *bw)
+{
+ return bw->buf[bw->header_off];
+}
@@ reftable/block.c (new)
+ success */
+int block_writer_add(struct block_writer *w, struct reftable_record *rec)
+{
-+ struct slice empty = SLICE_INIT;
-+ struct slice last = w->entries % w->restart_interval == 0 ? empty :
-+ w->last_key;
-+ struct slice out = {
++ struct strbuf empty = STRBUF_INIT;
++ struct strbuf last =
++ w->entries % w->restart_interval == 0 ? empty : w->last_key;
++ struct string_view out = {
+ .buf = w->buf + w->next,
+ .len = w->block_size - w->next,
-+ .canary = SLICE_CANARY,
+ };
+
-+ struct slice start = out;
++ struct string_view start = out;
+
+ bool restart = false;
-+ struct slice key = SLICE_INIT;
++ struct strbuf key = STRBUF_INIT;
+ int n = 0;
+
+ reftable_record_key(rec, &key);
@@ reftable/block.c (new)
+ reftable_record_val_type(rec));
+ if (n < 0)
+ goto done;
-+ slice_consume(&out, n);
++ string_view_consume(&out, n);
+
+ n = reftable_record_encode(rec, out, w->hash_size);
+ if (n < 0)
+ goto done;
-+ slice_consume(&out, n);
++ string_view_consume(&out, n);
+
+ if (block_writer_register_restart(w, start.len - out.len, restart,
+ &key) < 0)
+ goto done;
+
-+ slice_release(&key);
++ strbuf_release(&key);
+ return 0;
+
+done:
-+ slice_release(&key);
++ strbuf_release(&key);
+ return -1;
+}
+
+int block_writer_register_restart(struct block_writer *w, int n, bool restart,
-+ struct slice *key)
++ struct strbuf *key)
+{
+ int rlen = w->restart_len;
+ if (rlen >= MAX_RESTARTS) {
@@ reftable/block.c (new)
+
+ w->next += n;
+
-+ slice_reset(&w->last_key);
-+ slice_addbuf(&w->last_key, key);
++ strbuf_reset(&w->last_key);
++ strbuf_addbuf(&w->last_key, key);
+ w->entries++;
+ return 0;
+}
@@ reftable/block.c (new)
+
+ if (block_writer_type(w) == BLOCK_TYPE_LOG) {
+ int block_header_skip = 4 + w->header_off;
-+ byte *compressed = NULL;
++ uint8_t *compressed = NULL;
+ int zresult = 0;
+ uLongf src_len = w->next - block_header_skip;
+ size_t dest_cap = src_len;
@@ reftable/block.c (new)
+ return w->next;
+}
+
-+byte block_reader_type(struct block_reader *r)
++uint8_t block_reader_type(struct block_reader *r)
+{
+ return r->block.data[r->header_off];
+}
@@ reftable/block.c (new)
+ int hash_size)
+{
+ uint32_t full_block_size = table_block_size;
-+ byte typ = block->data[header_off];
++ uint8_t typ = block->data[header_off];
+ uint32_t sz = get_be24(block->data + header_off + 1);
+
+ uint16_t restart_count = 0;
+ uint32_t restart_start = 0;
-+ byte *restart_bytes = NULL;
++ uint8_t *restart_bytes = NULL;
+
+ if (!reftable_is_block_type(typ))
+ return REFTABLE_FORMAT_ERROR;
@@ reftable/block.c (new)
+ uLongf src_len = block->len - block_header_skip;
+ /* Log blocks specify the *uncompressed* size in their header.
+ */
-+ byte *uncompressed = reftable_malloc(sz);
++ uint8_t *uncompressed = reftable_malloc(sz);
+
+ /* Copy over the block header verbatim. It's not compressed. */
+ memcpy(uncompressed, block->data, block_header_skip);
@@ reftable/block.c (new)
+void block_reader_start(struct block_reader *br, struct block_iter *it)
+{
+ it->br = br;
-+ slice_reset(&it->last_key);
++ strbuf_reset(&it->last_key);
+ it->next_off = br->header_off + 4;
+}
+
+struct restart_find_args {
+ int error;
-+ struct slice key;
++ struct strbuf key;
+ struct block_reader *r;
+};
+
@@ reftable/block.c (new)
+{
+ struct restart_find_args *a = (struct restart_find_args *)args;
+ uint32_t off = block_reader_restart_offset(a->r, idx);
-+ struct slice in = {
++ struct string_view in = {
+ .buf = a->r->block.data + off,
+ .len = a->r->block_len - off,
-+ .canary = SLICE_CANARY,
+ };
+
+ /* the restart key is verbatim in the block, so this could avoid the
+ alloc for decoding the key */
-+ struct slice rkey = SLICE_INIT;
-+ struct slice last_key = SLICE_INIT;
-+ byte unused_extra;
++ struct strbuf rkey = STRBUF_INIT;
++ struct strbuf last_key = STRBUF_INIT;
++ uint8_t unused_extra;
+ int n = reftable_decode_key(&rkey, &unused_extra, last_key, in);
+ int result;
+ if (n < 0) {
@@ reftable/block.c (new)
+ return -1;
+ }
+
-+ result = slice_cmp(&a->key, &rkey);
-+ slice_release(&rkey);
++ result = strbuf_cmp(&a->key, &rkey);
++ strbuf_release(&rkey);
+ return result;
+}
+
@@ reftable/block.c (new)
+{
+ dest->br = src->br;
+ dest->next_off = src->next_off;
-+ slice_reset(&dest->last_key);
-+ slice_addbuf(&dest->last_key, &src->last_key);
++ strbuf_reset(&dest->last_key);
++ strbuf_addbuf(&dest->last_key, &src->last_key);
+}
+
+int block_iter_next(struct block_iter *it, struct reftable_record *rec)
+{
-+ struct slice in = {
++ struct string_view in = {
+ .buf = it->br->block.data + it->next_off,
+ .len = it->br->block_len - it->next_off,
-+ .canary = SLICE_CANARY,
+ };
-+ struct slice start = in;
-+ struct slice key = SLICE_INIT;
-+ byte extra = 0;
++ struct string_view start = in;
++ struct strbuf key = STRBUF_INIT;
++ uint8_t extra = 0;
+ int n = 0;
+
+ if (it->next_off >= it->br->block_len)
@@ reftable/block.c (new)
+ if (n < 0)
+ return -1;
+
-+ slice_consume(&in, n);
++ string_view_consume(&in, n);
+ n = reftable_record_decode(rec, key, extra, in, it->br->hash_size);
+ if (n < 0)
+ return -1;
-+ slice_consume(&in, n);
++ string_view_consume(&in, n);
+
-+ slice_reset(&it->last_key);
-+ slice_addbuf(&it->last_key, &key);
++ strbuf_reset(&it->last_key);
++ strbuf_addbuf(&it->last_key, &key);
+ it->next_off += start.len - in.len;
-+ slice_release(&key);
++ strbuf_release(&key);
+ return 0;
+}
+
-+int block_reader_first_key(struct block_reader *br, struct slice *key)
++int block_reader_first_key(struct block_reader *br, struct strbuf *key)
+{
-+ struct slice empty = SLICE_INIT;
++ struct strbuf empty = STRBUF_INIT;
+ int off = br->header_off + 4;
-+ struct slice in = {
++ struct string_view in = {
+ .buf = br->block.data + off,
+ .len = br->block_len - off,
-+ .canary = SLICE_CANARY,
+ };
+
-+ byte extra = 0;
++ uint8_t extra = 0;
+ int n = reftable_decode_key(key, &extra, empty, in);
+ if (n < 0)
+ return n;
@@ reftable/block.c (new)
+ return 0;
+}
+
-+int block_iter_seek(struct block_iter *it, struct slice *want)
++int block_iter_seek(struct block_iter *it, struct strbuf *want)
+{
+ return block_reader_seek(it->br, it, want);
+}
+
+void block_iter_close(struct block_iter *it)
+{
-+ slice_release(&it->last_key);
++ strbuf_release(&it->last_key);
+}
+
+int block_reader_seek(struct block_reader *br, struct block_iter *it,
-+ struct slice *want)
++ struct strbuf *want)
+{
+ struct restart_find_args args = {
+ .key = *want,
+ .r = br,
+ };
+ struct reftable_record rec = reftable_new_record(block_reader_type(br));
-+ struct slice key = SLICE_INIT;
++ struct strbuf key = STRBUF_INIT;
+ int err = 0;
+ struct block_iter next = {
-+ .last_key = SLICE_INIT,
++ .last_key = STRBUF_INIT,
+ };
+
+ int i = binsearch(br->restart_count, &restart_key_less, &args);
@@ reftable/block.c (new)
+ goto done;
+
+ reftable_record_key(&rec, &key);
-+ if (err > 0 || slice_cmp(&key, want) >= 0) {
++ if (err > 0 || strbuf_cmp(&key, want) >= 0) {
+ err = 0;
+ goto done;
+ }
@@ reftable/block.c (new)
+ }
+
+done:
-+ slice_release(&key);
-+ slice_release(&next.last_key);
++ strbuf_release(&key);
++ strbuf_release(&next.last_key);
+ reftable_record_destroy(&rec);
+
+ return err;
@@ reftable/block.c (new)
+void block_writer_clear(struct block_writer *bw)
+{
+ FREE_AND_NULL(bw->restarts);
-+ slice_release(&bw->last_key);
++ strbuf_release(&bw->last_key);
+ /* the block is not owned. */
+}
@@ reftable/block.h (new)
+ allocation overhead.
+*/
+struct block_writer {
-+ byte *buf;
++ uint8_t *buf;
+ uint32_t block_size;
+
+ /* Offset ofof the global header. Nonzero in the first block only. */
@@ reftable/block.h (new)
+ int restart_interval;
+ int hash_size;
+
-+ /* Offset of next byte to write. */
++ /* Offset of next uint8_t to write. */
+ uint32_t next;
+ uint32_t *restarts;
+ uint32_t restart_len;
+ uint32_t restart_cap;
+
-+ struct slice last_key;
++ struct strbuf last_key;
+ int entries;
+};
+
+/*
+ initializes the blockwriter to write `typ` entries, using `buf` as temporary
+ storage. `buf` is not owned by the block_writer. */
-+void block_writer_init(struct block_writer *bw, byte typ, byte *buf,
++void block_writer_init(struct block_writer *bw, uint8_t typ, uint8_t *buf,
+ uint32_t block_size, uint32_t header_off, int hash_size);
+
+/*
+ returns the block type (eg. 'r' for ref records.
+*/
-+byte block_writer_type(struct block_writer *bw);
++uint8_t block_writer_type(struct block_writer *bw);
+
+/* appends the record, or -1 if it doesn't fit. */
+int block_writer_add(struct block_writer *w, struct reftable_record *rec);
@@ reftable/block.h (new)
+
+ /* size of the data, excluding restart data. */
+ uint32_t block_len;
-+ byte *restart_bytes;
++ uint8_t *restart_bytes;
+ uint16_t restart_count;
+
+ /* size of the data in the file. For log blocks, this is the compressed
@@ reftable/block.h (new)
+ struct block_reader *br;
+
+ /* key for last entry we read. */
-+ struct slice last_key;
++ struct strbuf last_key;
+};
+
+/* initializes a block reader. */
@@ reftable/block.h (new)
+
+/* Position `it` to the `want` key in the block */
+int block_reader_seek(struct block_reader *br, struct block_iter *it,
-+ struct slice *want);
++ struct strbuf *want);
+
+/* Returns the block type (eg. 'r' for refs) */
-+byte block_reader_type(struct block_reader *r);
++uint8_t block_reader_type(struct block_reader *r);
+
+/* Decodes the first key in the block */
-+int block_reader_first_key(struct block_reader *br, struct slice *key);
++int block_reader_first_key(struct block_reader *br, struct strbuf *key);
+
+void block_iter_copy_from(struct block_iter *dest, struct block_iter *src);
+
@@ reftable/block.h (new)
+int block_iter_next(struct block_iter *it, struct reftable_record *rec);
+
+/* Seek to `want` with in the block pointed to by `it` */
-+int block_iter_seek(struct block_iter *it, struct slice *want);
++int block_iter_seek(struct block_iter *it, struct strbuf *want);
+
+/* deallocate memory for `it`. The block reader and its block is left intact. */
+void block_iter_close(struct block_iter *it);
@@ reftable/block_test.c (new)
+ const int block_size = 1024;
+ struct reftable_block block = { 0 };
+ struct block_writer bw = {
-+ .last_key = SLICE_INIT,
++ .last_key = STRBUF_INIT,
+ };
+ struct reftable_ref_record ref = { 0 };
+ struct reftable_record rec = { 0 };
+ int i = 0;
+ int n;
+ struct block_reader br = { 0 };
-+ struct block_iter it = { .last_key = SLICE_INIT };
++ struct block_iter it = { .last_key = STRBUF_INIT };
+ int j = 0;
-+ struct slice want = SLICE_INIT;
++ struct strbuf want = STRBUF_INIT;
+
+ block.data = reftable_calloc(block_size);
+ block.len = block_size;
@@ reftable/block_test.c (new)
+
+ for (i = 0; i < N; i++) {
+ char name[100];
-+ byte hash[SHA1_SIZE];
++ uint8_t hash[SHA1_SIZE];
+ snprintf(name, sizeof(name), "branch%02d", i);
+ memset(hash, i, sizeof(hash));
+
@@ reftable/block_test.c (new)
+ block_iter_close(&it);
+
+ for (i = 0; i < N; i++) {
-+ struct block_iter it = { .last_key = SLICE_INIT };
-+ slice_reset(&want);
-+ slice_addstr(&want, names[i]);
++ struct block_iter it = { .last_key = STRBUF_INIT };
++ strbuf_reset(&want);
++ strbuf_addstr(&want, names[i]);
+
+ n = block_reader_seek(&br, &it, &want);
+ assert(n == 0);
@@ reftable/block_test.c (new)
+
+ reftable_record_clear(&rec);
+ reftable_block_done(&br.block);
-+ slice_release(&want);
++ strbuf_release(&want);
+ for (i = 0; i < N; i++) {
+ reftable_free(names[i]);
+ }
@@ reftable/compat.c (new)
+#include "system.h"
+#include "basics.h"
+
-+#ifndef REFTABLE_IN_GITCORE
++#ifdef REFTABLE_STANDALONE
+
+#include <dirent.h>
+
+void put_be32(void *p, uint32_t i)
+{
-+ byte *out = (byte *)p;
++ uint8_t *out = (uint8_t *)p;
+
+ out[0] = (uint8_t)((i >> 24) & 0xff);
+ out[1] = (uint8_t)((i >> 16) & 0xff);
@@ reftable/compat.c (new)
+
+void put_be64(void *p, uint64_t v)
+{
-+ byte *out = (byte *)p;
++ uint8_t *out = (uint8_t *)p;
+ int i = sizeof(uint64_t);
+ while (i--) {
+ out[i] = (uint8_t)(v & 0xff);
@@ reftable/compat.c (new)
+ }
+}
+
-+uint64_t get_be64(uint8_t *out)
++uint64_t get_be64(void *out)
+{
++ uint8_t *bytes = (uint8_t *)out;
+ uint64_t v = 0;
+ int i = 0;
+ for (i = 0; i < sizeof(uint64_t); i++) {
-+ v = (v << 8) | (uint8_t)(out[i] & 0xff);
++ v = (v << 8) | (uint8_t)(bytes[i] & 0xff);
+ }
+ return v;
+}
@@ reftable/compat.h (new)
+
+#include "system.h"
+
-+#ifndef REFTABLE_IN_GITCORE
++#ifdef REFTABLE_STANDALONE
+
+/* functions that git-core provides, for standalone compilation */
+#include <stdint.h>
+
-+uint64_t get_be64(uint8_t *in);
++uint64_t get_be64(void *in);
+void put_be64(void *out, uint64_t i);
+
+void put_be32(void *out, uint32_t i);
@@ reftable/file.c (new)
+ return 0;
+}
+
-+int reftable_fd_write(void *arg, byte *data, size_t sz)
++int reftable_fd_write(void *arg, const void *data, size_t sz)
+{
+ int *fdp = (int *)arg;
+ return write(*fdp, data, sz);
@@ reftable/iter.c (new)
+{
+ struct filtering_ref_iterator *fri =
+ (struct filtering_ref_iterator *)iter_arg;
-+ slice_release(&fri->oid);
++ strbuf_release(&fri->oid);
+ reftable_iterator_destroy(&fri->it);
+}
+
@@ reftable/iter.c (new)
+ struct indexed_table_ref_iter *it = (struct indexed_table_ref_iter *)p;
+ block_iter_close(&it->cur);
+ reftable_block_done(&it->block_reader.block);
-+ slice_release(&it->oid);
++ strbuf_release(&it->oid);
+}
+
+static int indexed_table_ref_iter_next_block(struct indexed_table_ref_iter *it)
@@ reftable/iter.c (new)
+}
+
+int new_indexed_table_ref_iter(struct indexed_table_ref_iter **dest,
-+ struct reftable_reader *r, byte *oid,
++ struct reftable_reader *r, uint8_t *oid,
+ int oid_len, uint64_t *offsets, int offset_len)
+{
+ struct indexed_table_ref_iter empty = INDEXED_TABLE_REF_ITER_INIT;
@@ reftable/iter.c (new)
+
+ *itr = empty;
+ itr->r = r;
-+ slice_add(&itr->oid, oid, oid_len);
++ strbuf_add(&itr->oid, oid, oid_len);
+
+ itr->offsets = offsets;
+ itr->offset_len = offset_len;
@@ reftable/iter.h (new)
+
+#include "block.h"
+#include "record.h"
-+#include "slice.h"
++#include "strbuf.h"
+
+struct reftable_iterator_vtable {
+ int (*next)(void *iter_arg, struct reftable_record *rec);
@@ reftable/iter.h (new)
+struct filtering_ref_iterator {
+ bool double_check;
+ struct reftable_table tab;
-+ struct slice oid;
++ struct strbuf oid;
+ struct reftable_iterator it;
+};
+#define FILTERING_REF_ITERATOR_INIT \
+ { \
-+ .oid = SLICE_INIT \
++ .oid = STRBUF_INIT \
+ }
+
+void iterator_from_filtering_ref_iterator(struct reftable_iterator *,
@@ reftable/iter.h (new)
+ */
+struct indexed_table_ref_iter {
+ struct reftable_reader *r;
-+ struct slice oid;
++ struct strbuf oid;
+
+ /* mutable */
+ uint64_t *offsets;
@@ reftable/iter.h (new)
+ bool finished;
+};
+
-+#define INDEXED_TABLE_REF_ITER_INIT \
-+ { \
-+ .cur = { .last_key = SLICE_INIT }, .oid = SLICE_INIT, \
++#define INDEXED_TABLE_REF_ITER_INIT \
++ { \
++ .cur = { .last_key = STRBUF_INIT }, .oid = STRBUF_INIT, \
+ }
+
+void iterator_from_indexed_table_ref_iter(struct reftable_iterator *it,
+ struct indexed_table_ref_iter *itr);
+int new_indexed_table_ref_iter(struct indexed_table_ref_iter **dest,
-+ struct reftable_reader *r, byte *oid,
++ struct reftable_reader *r, uint8_t *oid,
+ int oid_len, uint64_t *offsets, int offset_len);
+
+#endif
@@ reftable/merged.c (new)
+#include "iter.h"
+#include "pq.h"
+#include "reader.h"
++#include "record.h"
+
+static int merged_iter_init(struct merged_iter *mi)
+{
@@ reftable/merged.c (new)
+static int merged_iter_next_entry(struct merged_iter *mi,
+ struct reftable_record *rec)
+{
-+ struct slice entry_key = SLICE_INIT;
++ struct strbuf entry_key = STRBUF_INIT;
+ struct pq_entry entry = { 0 };
+ int err = 0;
+
@@ reftable/merged.c (new)
+ reftable_record_key(&entry.rec, &entry_key);
+ while (!merged_iter_pqueue_is_empty(mi->pq)) {
+ struct pq_entry top = merged_iter_pqueue_top(mi->pq);
-+ struct slice k = SLICE_INIT;
++ struct strbuf k = STRBUF_INIT;
+ int err = 0, cmp = 0;
+
+ reftable_record_key(&top.rec, &k);
+
-+ cmp = slice_cmp(&k, &entry_key);
-+ slice_release(&k);
++ cmp = strbuf_cmp(&k, &entry_key);
++ strbuf_release(&k);
+
+ if (cmp > 0) {
+ break;
@@ reftable/merged.c (new)
+
+ reftable_record_copy_from(rec, &entry.rec, hash_size(mi->hash_id));
+ reftable_record_destroy(&entry.rec);
-+ slice_release(&entry_key);
++ strbuf_release(&entry_key);
+ return 0;
+}
+
@@ reftable/merged.h (new)
+ struct reftable_iterator *stack;
+ uint32_t hash_id;
+ int stack_len;
-+ byte typ;
++ uint8_t typ;
+ bool suppress_deletions;
+ struct merged_iter_pqueue pq;
+};
@@ reftable/merged_test.c (new)
+ merged_iter_pqueue_clear(&pq);
+}
+
-+static void write_test_table(struct slice *buf,
++static void write_test_table(struct strbuf *buf,
+ struct reftable_ref_record refs[], int n)
+{
+ int min = 0xffffffff;
@@ reftable/merged_test.c (new)
+ }
+ }
+
-+ w = reftable_new_writer(&slice_add_void, buf, &opts);
++ w = reftable_new_writer(&strbuf_add_void, buf, &opts);
+ reftable_writer_set_limits(w, min, max);
+
+ for (i = 0; i < n; i++) {
@@ reftable/merged_test.c (new)
+static struct reftable_merged_table *
+merged_table_from_records(struct reftable_ref_record **refs,
+ struct reftable_block_source **source, int *sizes,
-+ struct slice *buf, int n)
++ struct strbuf *buf, int n)
+{
+ struct reftable_reader **rd = reftable_calloc(n * sizeof(*rd));
+ int i = 0;
@@ reftable/merged_test.c (new)
+ *source = reftable_calloc(n * sizeof(**source));
+ for (i = 0; i < n; i++) {
+ write_test_table(&buf[i], refs[i], sizes[i]);
-+ block_source_from_slice(&(*source)[i], &buf[i]);
++ block_source_from_strbuf(&(*source)[i], &buf[i]);
+
+ err = reftable_new_reader(&rd[i], &(*source)[i], "name");
+ assert_err(err);
@@ reftable/merged_test.c (new)
+
+static void test_merged_between(void)
+{
-+ byte hash1[SHA1_SIZE] = { 1, 2, 3, 0 };
++ uint8_t hash1[SHA1_SIZE] = { 1, 2, 3, 0 };
+
+ struct reftable_ref_record r1[] = { {
+ .ref_name = "b",
@@ reftable/merged_test.c (new)
+
+ struct reftable_ref_record *refs[] = { r1, r2 };
+ int sizes[] = { 1, 1 };
-+ struct slice bufs[2] = { SLICE_INIT, SLICE_INIT };
++ struct strbuf bufs[2] = { STRBUF_INIT, STRBUF_INIT };
+ struct reftable_block_source *bs = NULL;
+ struct reftable_merged_table *mt =
+ merged_table_from_records(refs, &bs, sizes, bufs, 2);
@@ reftable/merged_test.c (new)
+ reftable_merged_table_close(mt);
+ reftable_merged_table_free(mt);
+ for (i = 0; i < ARRAY_SIZE(bufs); i++) {
-+ slice_release(&bufs[i]);
++ strbuf_release(&bufs[i]);
+ }
+ reftable_free(bs);
+}
+
+static void test_merged(void)
+{
-+ byte hash1[SHA1_SIZE] = { 1 };
-+ byte hash2[SHA1_SIZE] = { 2 };
++ uint8_t hash1[SHA1_SIZE] = { 1 };
++ uint8_t hash2[SHA1_SIZE] = { 2 };
+ struct reftable_ref_record r1[] = { {
+ .ref_name = "a",
+ .update_index = 1,
@@ reftable/merged_test.c (new)
+
+ struct reftable_ref_record *refs[] = { r1, r2, r3 };
+ int sizes[3] = { 3, 1, 2 };
-+ struct slice bufs[3] = { SLICE_INIT, SLICE_INIT, SLICE_INIT };
++ struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT };
+ struct reftable_block_source *bs = NULL;
+
+ struct reftable_merged_table *mt =
@@ reftable/merged_test.c (new)
+ reftable_free(out);
+
+ for (i = 0; i < 3; i++) {
-+ slice_release(&bufs[i]);
++ strbuf_release(&bufs[i]);
+ }
+ reftable_merged_table_close(mt);
+ reftable_merged_table_free(mt);
@@ reftable/pq.c (new)
+
+#include "pq.h"
+
++#include "reftable.h"
+#include "system.h"
++#include "basics.h"
+
+int pq_less(struct pq_entry a, struct pq_entry b)
+{
-+ struct slice ak = SLICE_INIT;
-+ struct slice bk = SLICE_INIT;
++ struct strbuf ak = STRBUF_INIT;
++ struct strbuf bk = STRBUF_INIT;
+ int cmp = 0;
+ reftable_record_key(&a.rec, &ak);
+ reftable_record_key(&b.rec, &bk);
+
-+ cmp = slice_cmp(&ak, &bk);
++ cmp = strbuf_cmp(&ak, &bk);
+
-+ slice_release(&ak);
-+ slice_release(&bk);
++ strbuf_release(&ak);
++ strbuf_release(&bk);
+
+ if (cmp == 0)
+ return a.index > b.index;
@@ reftable/reader.c (new)
+}
+
+static struct reftable_reader_offsets *
-+reader_offsets_for(struct reftable_reader *r, byte typ)
++reader_offsets_for(struct reftable_reader *r, uint8_t typ)
+{
+ switch (typ) {
+ case BLOCK_TYPE_REF:
@@ reftable/reader.c (new)
+ return r->name;
+}
+
-+static int parse_footer(struct reftable_reader *r, byte *footer, byte *header)
++static int parse_footer(struct reftable_reader *r, uint8_t *footer,
++ uint8_t *header)
+{
-+ byte *f = footer;
-+ byte first_block_typ;
++ uint8_t *f = footer;
++ uint8_t first_block_typ;
+ int err = 0;
+ uint32_t computed_crc;
+ uint32_t file_crc;
@@ reftable/reader.c (new)
+
+struct table_iter {
+ struct reftable_reader *r;
-+ byte typ;
++ uint8_t typ;
+ uint64_t block_off;
+ struct block_iter bi;
+ bool finished;
+};
-+#define TABLE_ITER_INIT \
-+ { \
-+ .bi = {.last_key = SLICE_INIT } \
++#define TABLE_ITER_INIT \
++ { \
++ .bi = {.last_key = STRBUF_INIT } \
+ }
+
+static void table_iter_copy_from(struct table_iter *dest,
@@ reftable/reader.c (new)
+ ti->bi.next_off = 0;
+}
+
-+static int32_t extract_block_size(byte *data, byte *typ, uint64_t off,
++static int32_t extract_block_size(uint8_t *data, uint8_t *typ, uint64_t off,
+ int version)
+{
+ int32_t result = 0;
@@ reftable/reader.c (new)
+}
+
+int reader_init_block_reader(struct reftable_reader *r, struct block_reader *br,
-+ uint64_t next_off, byte want_typ)
++ uint64_t next_off, uint8_t want_typ)
+{
+ int32_t guess_block_size = r->block_size ? r->block_size :
+ DEFAULT_BLOCK_SIZE;
+ struct reftable_block block = { 0 };
-+ byte block_typ = 0;
++ uint8_t block_typ = 0;
+ int err = 0;
+ uint32_t header_off = next_off ? 0 : header_size(r->version);
+ int32_t block_size = 0;
@@ reftable/reader.c (new)
+}
+
+static int reader_table_iter_at(struct reftable_reader *r,
-+ struct table_iter *ti, uint64_t off, byte typ)
++ struct table_iter *ti, uint64_t off,
++ uint8_t typ)
+{
+ struct block_reader br = { 0 };
+ struct block_reader *brp = NULL;
@@ reftable/reader.c (new)
+}
+
+static int reader_start(struct reftable_reader *r, struct table_iter *ti,
-+ byte typ, bool index)
++ uint8_t typ, bool index)
+{
+ struct reftable_reader_offsets *offs = reader_offsets_for(r, typ);
+ uint64_t off = offs->offset;
@@ reftable/reader.c (new)
+{
+ struct reftable_record rec =
+ reftable_new_record(reftable_record_type(want));
-+ struct slice want_key = SLICE_INIT;
-+ struct slice got_key = SLICE_INIT;
++ struct strbuf want_key = STRBUF_INIT;
++ struct strbuf got_key = STRBUF_INIT;
+ struct table_iter next = TABLE_ITER_INIT;
+ int err = -1;
+
@@ reftable/reader.c (new)
+ if (err < 0)
+ goto done;
+
-+ if (slice_cmp(&got_key, &want_key) > 0) {
++ if (strbuf_cmp(&got_key, &want_key) > 0) {
+ table_iter_block_done(&next);
+ break;
+ }
@@ reftable/reader.c (new)
+done:
+ block_iter_close(&next.bi);
+ reftable_record_destroy(&rec);
-+ slice_release(&want_key);
-+ slice_release(&got_key);
++ strbuf_release(&want_key);
++ strbuf_release(&got_key);
+ return err;
+}
+
@@ reftable/reader.c (new)
+ struct reftable_iterator *it,
+ struct reftable_record *rec)
+{
-+ struct reftable_index_record want_index = { .last_key = SLICE_INIT };
++ struct reftable_index_record want_index = { .last_key = STRBUF_INIT };
+ struct reftable_record want_index_rec = { 0 };
-+ struct reftable_index_record index_result = { .last_key = SLICE_INIT };
++ struct reftable_index_record index_result = { .last_key = STRBUF_INIT };
+ struct reftable_record index_result_rec = { 0 };
+ struct table_iter index_iter = TABLE_ITER_INIT;
+ struct table_iter next = TABLE_ITER_INIT;
@@ reftable/reader.c (new)
+int reader_seek(struct reftable_reader *r, struct reftable_iterator *it,
+ struct reftable_record *rec)
+{
-+ byte typ = reftable_record_type(rec);
++ uint8_t typ = reftable_record_type(rec);
+
+ struct reftable_reader_offsets *offs = reader_offsets_for(r, typ);
+ if (!offs->present) {
@@ reftable/reader.c (new)
+
+static int reftable_reader_refs_for_indexed(struct reftable_reader *r,
+ struct reftable_iterator *it,
-+ byte *oid)
++ uint8_t *oid)
+{
+ struct reftable_obj_record want = {
+ .hash_prefix = oid,
@@ reftable/reader.c (new)
+
+static int reftable_reader_refs_for_unindexed(struct reftable_reader *r,
+ struct reftable_iterator *it,
-+ byte *oid)
++ uint8_t *oid)
+{
+ struct table_iter ti_empty = TABLE_ITER_INIT;
+ struct table_iter *ti = reftable_calloc(sizeof(struct table_iter));
@@ reftable/reader.c (new)
+ filter = reftable_malloc(sizeof(struct filtering_ref_iterator));
+ *filter = empty;
+
-+ slice_add(&filter->oid, oid, oid_len);
++ strbuf_add(&filter->oid, oid, oid_len);
+ reftable_table_from_reader(&filter->tab, r);
+ filter->double_check = false;
+ iterator_from_table_iter(&filter->it, ti);
@@ reftable/reader.c (new)
+}
+
+int reftable_reader_refs_for(struct reftable_reader *r,
-+ struct reftable_iterator *it, byte *oid)
++ struct reftable_iterator *it, uint8_t *oid)
+{
+ if (r->obj_offsets.present)
+ return reftable_reader_refs_for_indexed(r, it, oid);
@@ reftable/reader.h (new)
+
+/* initialize a block reader to read from `r` */
+int reader_init_block_reader(struct reftable_reader *r, struct block_reader *br,
-+ uint64_t next_off, byte want_typ);
++ uint64_t next_off, uint8_t want_typ);
+
+#endif
@@ reftable/record.c (new)
+#include "record.h"
+
+#include "system.h"
-+
+#include "constants.h"
+#include "reftable.h"
++#include "basics.h"
+
-+int get_var_int(uint64_t *dest, struct slice *in)
++int get_var_int(uint64_t *dest, struct string_view *in)
+{
+ int ptr = 0;
+ uint64_t val;
@@ reftable/record.c (new)
+ return ptr + 1;
+}
+
-+int put_var_int(struct slice *dest, uint64_t val)
++int put_var_int(struct string_view *dest, uint64_t val)
+{
-+ byte buf[10] = { 0 };
++ uint8_t buf[10] = { 0 };
+ int i = 9;
+ int n = 0;
-+ buf[i] = (byte)(val & 0x7f);
++ buf[i] = (uint8_t)(val & 0x7f);
+ i--;
+ while (true) {
+ val >>= 7;
@@ reftable/record.c (new)
+ break;
+ }
+ val--;
-+ buf[i] = 0x80 | (byte)(val & 0x7f);
++ buf[i] = 0x80 | (uint8_t)(val & 0x7f);
+ i--;
+ }
+
@@ reftable/record.c (new)
+ return n;
+}
+
-+int reftable_is_block_type(byte typ)
++int reftable_is_block_type(uint8_t typ)
+{
+ switch (typ) {
+ case BLOCK_TYPE_REF:
@@ reftable/record.c (new)
+ return false;
+}
+
-+static int decode_string(struct slice *dest, struct slice in)
++static int decode_string(struct strbuf *dest, struct string_view in)
+{
+ int start_len = in.len;
+ uint64_t tsize = 0;
+ int n = get_var_int(&tsize, &in);
+ if (n <= 0)
+ return -1;
-+ slice_consume(&in, n);
++ string_view_consume(&in, n);
+ if (in.len < tsize)
+ return -1;
+
-+ slice_reset(dest);
-+ slice_add(dest, in.buf, tsize);
-+ slice_consume(&in, tsize);
++ strbuf_reset(dest);
++ strbuf_add(dest, in.buf, tsize);
++ string_view_consume(&in, tsize);
+
+ return start_len - in.len;
+}
+
-+static int encode_string(char *str, struct slice s)
++static int encode_string(char *str, struct string_view s)
+{
-+ struct slice start = s;
++ struct string_view start = s;
+ int l = strlen(str);
+ int n = put_var_int(&s, l);
+ if (n < 0)
+ return -1;
-+ slice_consume(&s, n);
++ string_view_consume(&s, n);
+ if (s.len < l)
+ return -1;
+ memcpy(s.buf, str, l);
-+ slice_consume(&s, l);
++ string_view_consume(&s, l);
+
+ return start.len - s.len;
+}
+
-+int reftable_encode_key(bool *restart, struct slice dest, struct slice prev_key,
-+ struct slice key, byte extra)
++int reftable_encode_key(bool *restart, struct string_view dest,
++ struct strbuf prev_key, struct strbuf key,
++ uint8_t extra)
+{
-+ struct slice start = dest;
++ struct string_view start = dest;
+ int prefix_len = common_prefix_size(&prev_key, &key);
+ uint64_t suffix_len = key.len - prefix_len;
+ int n = put_var_int(&dest, (uint64_t)prefix_len);
+ if (n < 0)
+ return -1;
-+ slice_consume(&dest, n);
++ string_view_consume(&dest, n);
+
+ *restart = (prefix_len == 0);
+
+ n = put_var_int(&dest, suffix_len << 3 | (uint64_t)extra);
+ if (n < 0)
+ return -1;
-+ slice_consume(&dest, n);
++ string_view_consume(&dest, n);
+
+ if (dest.len < suffix_len)
+ return -1;
+ memcpy(dest.buf, key.buf + prefix_len, suffix_len);
-+ slice_consume(&dest, suffix_len);
++ string_view_consume(&dest, suffix_len);
+
+ return start.len - dest.len;
+}
+
-+int reftable_decode_key(struct slice *key, byte *extra, struct slice last_key,
-+ struct slice in)
++int reftable_decode_key(struct strbuf *key, uint8_t *extra,
++ struct strbuf last_key, struct string_view in)
+{
+ int start_len = in.len;
+ uint64_t prefix_len = 0;
@@ reftable/record.c (new)
+ int n = get_var_int(&prefix_len, &in);
+ if (n < 0)
+ return -1;
-+ slice_consume(&in, n);
++ string_view_consume(&in, n);
+
+ if (prefix_len > last_key.len)
+ return -1;
@@ reftable/record.c (new)
+ n = get_var_int(&suffix_len, &in);
+ if (n <= 0)
+ return -1;
-+ slice_consume(&in, n);
++ string_view_consume(&in, n);
+
-+ *extra = (byte)(suffix_len & 0x7);
++ *extra = (uint8_t)(suffix_len & 0x7);
+ suffix_len >>= 3;
+
+ if (in.len < suffix_len)
+ return -1;
+
-+ slice_reset(key);
-+ slice_add(key, last_key.buf, prefix_len);
-+ slice_add(key, in.buf, suffix_len);
-+ slice_consume(&in, suffix_len);
++ strbuf_reset(key);
++ strbuf_add(key, last_key.buf, prefix_len);
++ strbuf_add(key, in.buf, suffix_len);
++ string_view_consume(&in, suffix_len);
+
+ return start_len - in.len;
+}
+
-+static void reftable_ref_record_key(const void *r, struct slice *dest)
++static void reftable_ref_record_key(const void *r, struct strbuf *dest)
+{
+ const struct reftable_ref_record *rec =
+ (const struct reftable_ref_record *)r;
-+ slice_reset(dest);
-+ slice_addstr(dest, rec->ref_name);
++ strbuf_reset(dest);
++ strbuf_addstr(dest, rec->ref_name);
+}
+
+static void reftable_ref_record_copy_from(void *rec, const void *src_rec,
@@ reftable/record.c (new)
+ return 'a' + (c - 10);
+}
+
-+static void hex_format(char *dest, byte *src, int hash_size)
++static void hex_format(char *dest, uint8_t *src, int hash_size)
+{
+ assert(hash_size > 0);
+ if (src != NULL) {
@@ reftable/record.c (new)
+ memset(ref, 0, sizeof(struct reftable_ref_record));
+}
+
-+static byte reftable_ref_record_val_type(const void *rec)
++static uint8_t reftable_ref_record_val_type(const void *rec)
+{
+ const struct reftable_ref_record *r =
+ (const struct reftable_ref_record *)rec;
@@ reftable/record.c (new)
+ return 0;
+}
+
-+static int reftable_ref_record_encode(const void *rec, struct slice s,
++static int reftable_ref_record_encode(const void *rec, struct string_view s,
+ int hash_size)
+{
+ const struct reftable_ref_record *r =
+ (const struct reftable_ref_record *)rec;
-+ struct slice start = s;
++ struct string_view start = s;
+ int n = put_var_int(&s, r->update_index);
+ assert(hash_size > 0);
+ if (n < 0)
+ return -1;
-+ slice_consume(&s, n);
++ string_view_consume(&s, n);
+
+ if (r->value != NULL) {
+ if (s.len < hash_size) {
+ return -1;
+ }
+ memcpy(s.buf, r->value, hash_size);
-+ slice_consume(&s, hash_size);
++ string_view_consume(&s, hash_size);
+ }
+
+ if (r->target_value != NULL) {
@@ reftable/record.c (new)
+ return -1;
+ }
+ memcpy(s.buf, r->target_value, hash_size);
-+ slice_consume(&s, hash_size);
++ string_view_consume(&s, hash_size);
+ }
+
+ if (r->target != NULL) {
@@ reftable/record.c (new)
+ if (n < 0) {
+ return -1;
+ }
-+ slice_consume(&s, n);
++ string_view_consume(&s, n);
+ }
+
+ return start.len - s.len;
+}
+
-+static int reftable_ref_record_decode(void *rec, struct slice key,
-+ byte val_type, struct slice in,
++static int reftable_ref_record_decode(void *rec, struct strbuf key,
++ uint8_t val_type, struct string_view in,
+ int hash_size)
+{
+ struct reftable_ref_record *r = (struct reftable_ref_record *)rec;
-+ struct slice start = in;
++ struct string_view start = in;
+ bool seen_value = false;
+ bool seen_target_value = false;
+ bool seen_target = false;
@@ reftable/record.c (new)
+ return n;
+ assert(hash_size > 0);
+
-+ slice_consume(&in, n);
++ string_view_consume(&in, n);
+
+ r->ref_name = reftable_realloc(r->ref_name, key.len + 1);
+ memcpy(r->ref_name, key.buf, key.len);
@@ reftable/record.c (new)
+ }
+ seen_value = true;
+ memcpy(r->value, in.buf, hash_size);
-+ slice_consume(&in, hash_size);
++ string_view_consume(&in, hash_size);
+ if (val_type == 1) {
+ break;
+ }
@@ reftable/record.c (new)
+ }
+ seen_target_value = true;
+ memcpy(r->target_value, in.buf, hash_size);
-+ slice_consume(&in, hash_size);
++ string_view_consume(&in, hash_size);
+ break;
+ case 3: {
-+ struct slice dest = SLICE_INIT;
++ struct strbuf dest = STRBUF_INIT;
+ int n = decode_string(&dest, in);
+ if (n < 0) {
+ return -1;
+ }
-+ slice_consume(&in, n);
++ string_view_consume(&in, n);
+ seen_target = true;
+ if (r->target != NULL) {
+ reftable_free(r->target);
+ }
-+ r->target = (char *)slice_as_string(&dest);
++ r->target = dest.buf;
+ } break;
+
+ case 0:
@@ reftable/record.c (new)
+ .is_deletion = &reftable_ref_record_is_deletion_void,
+};
+
-+static void reftable_obj_record_key(const void *r, struct slice *dest)
++static void reftable_obj_record_key(const void *r, struct strbuf *dest)
+{
+ const struct reftable_obj_record *rec =
+ (const struct reftable_obj_record *)r;
-+ slice_reset(dest);
-+ slice_add(dest, rec->hash_prefix, rec->hash_prefix_len);
++ strbuf_reset(dest);
++ strbuf_add(dest, rec->hash_prefix, rec->hash_prefix_len);
+}
+
+static void reftable_obj_record_clear(void *rec)
@@ reftable/record.c (new)
+ memcpy(obj->offsets, src->offsets, olen);
+}
+
-+static byte reftable_obj_record_val_type(const void *rec)
++static uint8_t reftable_obj_record_val_type(const void *rec)
+{
+ struct reftable_obj_record *r = (struct reftable_obj_record *)rec;
+ if (r->offset_len > 0 && r->offset_len < 8)
@@ reftable/record.c (new)
+ return 0;
+}
+
-+static int reftable_obj_record_encode(const void *rec, struct slice s,
++static int reftable_obj_record_encode(const void *rec, struct string_view s,
+ int hash_size)
+{
+ struct reftable_obj_record *r = (struct reftable_obj_record *)rec;
-+ struct slice start = s;
++ struct string_view start = s;
+ int i = 0;
+ int n = 0;
+ uint64_t last = 0;
@@ reftable/record.c (new)
+ if (n < 0) {
+ return -1;
+ }
-+ slice_consume(&s, n);
++ string_view_consume(&s, n);
+ }
+ if (r->offset_len == 0)
+ return start.len - s.len;
+ n = put_var_int(&s, r->offsets[0]);
+ if (n < 0)
+ return -1;
-+ slice_consume(&s, n);
++ string_view_consume(&s, n);
+
+ last = r->offsets[0];
+ for (i = 1; i < r->offset_len; i++) {
@@ reftable/record.c (new)
+ if (n < 0) {
+ return -1;
+ }
-+ slice_consume(&s, n);
++ string_view_consume(&s, n);
+ last = r->offsets[i];
+ }
+ return start.len - s.len;
+}
+
-+static int reftable_obj_record_decode(void *rec, struct slice key,
-+ byte val_type, struct slice in,
++static int reftable_obj_record_decode(void *rec, struct strbuf key,
++ uint8_t val_type, struct string_view in,
+ int hash_size)
+{
-+ struct slice start = in;
++ struct string_view start = in;
+ struct reftable_obj_record *r = (struct reftable_obj_record *)rec;
+ uint64_t count = val_type;
+ int n = 0;
@@ reftable/record.c (new)
+ return n;
+ }
+
-+ slice_consume(&in, n);
++ string_view_consume(&in, n);
+ }
+
+ r->offsets = NULL;
@@ reftable/record.c (new)
+ n = get_var_int(&r->offsets[0], &in);
+ if (n < 0)
+ return n;
-+ slice_consume(&in, n);
++ string_view_consume(&in, n);
+
+ last = r->offsets[0];
+ j = 1;
@@ reftable/record.c (new)
+ if (n < 0) {
+ return n;
+ }
-+ slice_consume(&in, n);
++ string_view_consume(&in, n);
+
+ last = r->offsets[j] = (delta + last);
+ j++;
@@ reftable/record.c (new)
+ printf("%s\n\n%s\n}\n", hex, log->message);
+}
+
-+static void reftable_log_record_key(const void *r, struct slice *dest)
++static void reftable_log_record_key(const void *r, struct strbuf *dest)
+{
+ const struct reftable_log_record *rec =
+ (const struct reftable_log_record *)r;
+ int len = strlen(rec->ref_name);
-+ byte i64[8];
++ uint8_t i64[8];
+ uint64_t ts = 0;
-+ slice_reset(dest);
-+ slice_add(dest, (byte *)rec->ref_name, len + 1);
++ strbuf_reset(dest);
++ strbuf_add(dest, (uint8_t *)rec->ref_name, len + 1);
+
+ ts = (~ts) - rec->update_index;
+ put_be64(&i64[0], ts);
-+ slice_add(dest, i64, sizeof(i64));
++ strbuf_add(dest, i64, sizeof(i64));
+}
+
+static void reftable_log_record_copy_from(void *rec, const void *src_rec,
@@ reftable/record.c (new)
+ memset(r, 0, sizeof(struct reftable_log_record));
+}
+
-+static byte reftable_log_record_val_type(const void *rec)
++static uint8_t reftable_log_record_val_type(const void *rec)
+{
+ const struct reftable_log_record *log =
+ (const struct reftable_log_record *)rec;
@@ reftable/record.c (new)
+ return reftable_log_record_is_deletion(log) ? 0 : 1;
+}
+
-+static byte zero[SHA256_SIZE] = { 0 };
++static uint8_t zero[SHA256_SIZE] = { 0 };
+
-+static int reftable_log_record_encode(const void *rec, struct slice s,
++static int reftable_log_record_encode(const void *rec, struct string_view s,
+ int hash_size)
+{
+ struct reftable_log_record *r = (struct reftable_log_record *)rec;
-+ struct slice start = s;
++ struct string_view start = s;
+ int n = 0;
-+ byte *oldh = r->old_hash;
-+ byte *newh = r->new_hash;
++ uint8_t *oldh = r->old_hash;
++ uint8_t *newh = r->new_hash;
+ if (reftable_log_record_is_deletion(r))
+ return 0;
+
@@ reftable/record.c (new)
+
+ memcpy(s.buf, oldh, hash_size);
+ memcpy(s.buf + hash_size, newh, hash_size);
-+ slice_consume(&s, 2 * hash_size);
++ string_view_consume(&s, 2 * hash_size);
+
+ n = encode_string(r->name ? r->name : "", s);
+ if (n < 0)
+ return -1;
-+ slice_consume(&s, n);
++ string_view_consume(&s, n);
+
+ n = encode_string(r->email ? r->email : "", s);
+ if (n < 0)
+ return -1;
-+ slice_consume(&s, n);
++ string_view_consume(&s, n);
+
+ n = put_var_int(&s, r->time);
+ if (n < 0)
+ return -1;
-+ slice_consume(&s, n);
++ string_view_consume(&s, n);
+
+ if (s.len < 2)
+ return -1;
+
+ put_be16(s.buf, r->tz_offset);
-+ slice_consume(&s, 2);
++ string_view_consume(&s, 2);
+
+ n = encode_string(r->message ? r->message : "", s);
+ if (n < 0)
+ return -1;
-+ slice_consume(&s, n);
++ string_view_consume(&s, n);
+
+ return start.len - s.len;
+}
+
-+static int reftable_log_record_decode(void *rec, struct slice key,
-+ byte val_type, struct slice in,
++static int reftable_log_record_decode(void *rec, struct strbuf key,
++ uint8_t val_type, struct string_view in,
+ int hash_size)
+{
-+ struct slice start = in;
++ struct string_view start = in;
+ struct reftable_log_record *r = (struct reftable_log_record *)rec;
+ uint64_t max = 0;
+ uint64_t ts = 0;
-+ struct slice dest = SLICE_INIT;
++ struct strbuf dest = STRBUF_INIT;
+ int n;
+
+ if (key.len <= 9 || key.buf[key.len - 9] != 0)
@@ reftable/record.c (new)
+ memcpy(r->old_hash, in.buf, hash_size);
+ memcpy(r->new_hash, in.buf + hash_size, hash_size);
+
-+ slice_consume(&in, 2 * hash_size);
++ string_view_consume(&in, 2 * hash_size);
+
+ n = decode_string(&dest, in);
+ if (n < 0)
+ goto done;
-+ slice_consume(&in, n);
++ string_view_consume(&in, n);
+
+ r->name = reftable_realloc(r->name, dest.len + 1);
+ memcpy(r->name, dest.buf, dest.len);
+ r->name[dest.len] = 0;
+
-+ slice_reset(&dest);
++ strbuf_reset(&dest);
+ n = decode_string(&dest, in);
+ if (n < 0)
+ goto done;
-+ slice_consume(&in, n);
++ string_view_consume(&in, n);
+
+ r->email = reftable_realloc(r->email, dest.len + 1);
+ memcpy(r->email, dest.buf, dest.len);
@@ reftable/record.c (new)
+ n = get_var_int(&ts, &in);
+ if (n < 0)
+ goto done;
-+ slice_consume(&in, n);
++ string_view_consume(&in, n);
+ r->time = ts;
+ if (in.len < 2)
+ goto done;
+
+ r->tz_offset = get_be16(in.buf);
-+ slice_consume(&in, 2);
++ string_view_consume(&in, 2);
+
-+ slice_reset(&dest);
++ strbuf_reset(&dest);
+ n = decode_string(&dest, in);
+ if (n < 0)
+ goto done;
-+ slice_consume(&in, n);
++ string_view_consume(&in, n);
+
+ r->message = reftable_realloc(r->message, dest.len + 1);
+ memcpy(r->message, dest.buf, dest.len);
+ r->message[dest.len] = 0;
+
-+ slice_release(&dest);
++ strbuf_release(&dest);
+ return start.len - in.len;
+
+done:
-+ slice_release(&dest);
++ strbuf_release(&dest);
+ return REFTABLE_FORMAT_ERROR;
+}
+
@@ reftable/record.c (new)
+ return 0 == strcmp(a, b);
+}
+
-+static bool zero_hash_eq(byte *a, byte *b, int sz)
++static bool zero_hash_eq(uint8_t *a, uint8_t *b, int sz)
+{
+ if (a == NULL)
+ a = zero;
@@ reftable/record.c (new)
+ .is_deletion = &reftable_log_record_is_deletion_void,
+};
+
-+struct reftable_record reftable_new_record(byte typ)
++struct reftable_record reftable_new_record(uint8_t typ)
+{
+ struct reftable_record rec = { NULL };
+ switch (typ) {
@@ reftable/record.c (new)
+ return rec;
+ }
+ case BLOCK_TYPE_INDEX: {
-+ struct reftable_index_record empty = { .last_key = SLICE_INIT };
++ struct reftable_index_record empty = { .last_key =
++ STRBUF_INIT };
+ struct reftable_index_record *r =
+ reftable_calloc(sizeof(struct reftable_index_record));
+ *r = empty;
@@ reftable/record.c (new)
+ reftable_free(reftable_record_yield(rec));
+}
+
-+static void reftable_index_record_key(const void *r, struct slice *dest)
++static void reftable_index_record_key(const void *r, struct strbuf *dest)
+{
+ struct reftable_index_record *rec = (struct reftable_index_record *)r;
-+ slice_reset(dest);
-+ slice_addbuf(dest, &rec->last_key);
++ strbuf_reset(dest);
++ strbuf_addbuf(dest, &rec->last_key);
+}
+
+static void reftable_index_record_copy_from(void *rec, const void *src_rec,
@@ reftable/record.c (new)
+ struct reftable_index_record *src =
+ (struct reftable_index_record *)src_rec;
+
-+ slice_reset(&dst->last_key);
-+ slice_addbuf(&dst->last_key, &src->last_key);
++ strbuf_reset(&dst->last_key);
++ strbuf_addbuf(&dst->last_key, &src->last_key);
+ dst->offset = src->offset;
+}
+
+static void reftable_index_record_clear(void *rec)
+{
+ struct reftable_index_record *idx = (struct reftable_index_record *)rec;
-+ slice_release(&idx->last_key);
++ strbuf_release(&idx->last_key);
+}
+
-+static byte reftable_index_record_val_type(const void *rec)
++static uint8_t reftable_index_record_val_type(const void *rec)
+{
+ return 0;
+}
+
-+static int reftable_index_record_encode(const void *rec, struct slice out,
++static int reftable_index_record_encode(const void *rec, struct string_view out,
+ int hash_size)
+{
+ const struct reftable_index_record *r =
+ (const struct reftable_index_record *)rec;
-+ struct slice start = out;
++ struct string_view start = out;
+
+ int n = put_var_int(&out, r->offset);
+ if (n < 0)
+ return n;
+
-+ slice_consume(&out, n);
++ string_view_consume(&out, n);
+
+ return start.len - out.len;
+}
+
-+static int reftable_index_record_decode(void *rec, struct slice key,
-+ byte val_type, struct slice in,
++static int reftable_index_record_decode(void *rec, struct strbuf key,
++ uint8_t val_type, struct string_view in,
+ int hash_size)
+{
-+ struct slice start = in;
++ struct string_view start = in;
+ struct reftable_index_record *r = (struct reftable_index_record *)rec;
+ int n = 0;
+
-+ slice_reset(&r->last_key);
-+ slice_addbuf(&r->last_key, &key);
++ strbuf_reset(&r->last_key);
++ strbuf_addbuf(&r->last_key, &key);
+
+ n = get_var_int(&r->offset, &in);
+ if (n < 0)
+ return n;
+
-+ slice_consume(&in, n);
++ string_view_consume(&in, n);
+ return start.len - in.len;
+}
+
@@ reftable/record.c (new)
+ .is_deletion = ¬_a_deletion,
+};
+
-+void reftable_record_key(struct reftable_record *rec, struct slice *dest)
++void reftable_record_key(struct reftable_record *rec, struct strbuf *dest)
+{
+ rec->ops->key(rec->data, dest);
+}
+
-+byte reftable_record_type(struct reftable_record *rec)
++uint8_t reftable_record_type(struct reftable_record *rec)
+{
+ return rec->ops->type;
+}
+
-+int reftable_record_encode(struct reftable_record *rec, struct slice dest,
++int reftable_record_encode(struct reftable_record *rec, struct string_view dest,
+ int hash_size)
+{
+ return rec->ops->encode(rec->data, dest, hash_size);
@@ reftable/record.c (new)
+ rec->ops->copy_from(rec->data, src->data, hash_size);
+}
+
-+byte reftable_record_val_type(struct reftable_record *rec)
++uint8_t reftable_record_val_type(struct reftable_record *rec)
+{
+ return rec->ops->val_type(rec->data);
+}
+
-+int reftable_record_decode(struct reftable_record *rec, struct slice key,
-+ byte extra, struct slice src, int hash_size)
++int reftable_record_decode(struct reftable_record *rec, struct strbuf key,
++ uint8_t extra, struct string_view src, int hash_size)
+{
+ return rec->ops->decode(rec->data, key, extra, src, hash_size);
+}
@@ reftable/record.c (new)
+ return (struct reftable_log_record *)rec->data;
+}
+
-+static bool hash_equal(byte *a, byte *b, int hash_size)
++static bool hash_equal(uint8_t *a, uint8_t *b, int hash_size)
+{
+ if (a != NULL && b != NULL)
+ return !memcmp(a, b, hash_size);
@@ reftable/record.c (new)
+ return SHA256_SIZE;
+ }
+ abort();
++}
++
++void string_view_consume(struct string_view *s, int n)
++{
++ s->buf += n;
++ s->len -= n;
+}
## reftable/record.h (new) ##
@@ reftable/record.h (new)
+#define RECORD_H
+
+#include "reftable.h"
-+#include "slice.h"
++#include "strbuf.h"
++#include "system.h"
++
++/*
++ A substring of existing string data. This structure takes no responsibility
++ for the lifetime of the data it points to.
++*/
++struct string_view {
++ uint8_t *buf;
++ int len;
++};
++
++/* Advance `s.buf` by `n`, and decrease length. */
++void string_view_consume(struct string_view *s, int n);
+
+/* utilities for de/encoding varints */
+
-+int get_var_int(uint64_t *dest, struct slice *in);
-+int put_var_int(struct slice *dest, uint64_t val);
++int get_var_int(uint64_t *dest, struct string_view *in);
++int put_var_int(struct string_view *dest, uint64_t val);
+
+/* Methods for records. */
+struct reftable_record_vtable {
-+ /* encode the key of to a byte slice. */
-+ void (*key)(const void *rec, struct slice *dest);
++ /* encode the key of to a uint8_t strbuf. */
++ void (*key)(const void *rec, struct strbuf *dest);
+
+ /* The record type of ('r' for ref). */
-+ byte type;
++ uint8_t type;
+
+ void (*copy_from)(void *dest, const void *src, int hash_size);
+
+ /* a value of [0..7], indicating record subvariants (eg. ref vs. symref
+ * vs ref deletion) */
-+ byte (*val_type)(const void *rec);
++ uint8_t (*val_type)(const void *rec);
+
+ /* encodes rec into dest, returning how much space was used. */
-+ int (*encode)(const void *rec, struct slice dest, int hash_size);
++ int (*encode)(const void *rec, struct string_view dest, int hash_size);
+
+ /* decode data from `src` into the record. */
-+ int (*decode)(void *rec, struct slice key, byte extra, struct slice src,
-+ int hash_size);
++ int (*decode)(void *rec, struct strbuf key, uint8_t extra,
++ struct string_view src, int hash_size);
+
+ /* deallocate and null the record. */
+ void (*clear)(void *rec);
@@ reftable/record.h (new)
+};
+
+/* returns true for recognized block types. Block start with the block type. */
-+int reftable_is_block_type(byte typ);
++int reftable_is_block_type(uint8_t typ);
+
+/* creates a malloced record of the given type. Dispose with record_destroy */
-+struct reftable_record reftable_new_record(byte typ);
++struct reftable_record reftable_new_record(uint8_t typ);
+
+extern struct reftable_record_vtable reftable_ref_record_vtable;
+
+/* Encode `key` into `dest`. Sets `restart` to indicate a restart. Returns
+ number of bytes written. */
-+int reftable_encode_key(bool *restart, struct slice dest, struct slice prev_key,
-+ struct slice key, byte extra);
++int reftable_encode_key(bool *restart, struct string_view dest,
++ struct strbuf prev_key, struct strbuf key,
++ uint8_t extra);
+
+/* Decode into `key` and `extra` from `in` */
-+int reftable_decode_key(struct slice *key, byte *extra, struct slice last_key,
-+ struct slice in);
++int reftable_decode_key(struct strbuf *key, uint8_t *extra,
++ struct strbuf last_key, struct string_view in);
+
+/* reftable_index_record are used internally to speed up lookups. */
+struct reftable_index_record {
+ uint64_t offset; /* Offset of block */
-+ struct slice last_key; /* Last key of the block. */
++ struct strbuf last_key; /* Last key of the block. */
+};
+
+/* reftable_obj_record stores an object ID => ref mapping. */
+struct reftable_obj_record {
-+ byte *hash_prefix; /* leading bytes of the object ID */
++ uint8_t *hash_prefix; /* leading bytes of the object ID */
+ int hash_prefix_len; /* number of leading bytes. Constant
+ * across a single table. */
+ uint64_t *offsets; /* a vector of file offsets. */
@@ reftable/record.h (new)
+
+/* see struct record_vtable */
+
-+void reftable_record_key(struct reftable_record *rec, struct slice *dest);
-+byte reftable_record_type(struct reftable_record *rec);
++void reftable_record_key(struct reftable_record *rec, struct strbuf *dest);
++uint8_t reftable_record_type(struct reftable_record *rec);
+void reftable_record_copy_from(struct reftable_record *rec,
+ struct reftable_record *src, int hash_size);
-+byte reftable_record_val_type(struct reftable_record *rec);
-+int reftable_record_encode(struct reftable_record *rec, struct slice dest,
++uint8_t reftable_record_val_type(struct reftable_record *rec);
++int reftable_record_encode(struct reftable_record *rec, struct string_view dest,
++ int hash_size);
++int reftable_record_decode(struct reftable_record *rec, struct strbuf key,
++ uint8_t extra, struct string_view src,
+ int hash_size);
-+int reftable_record_decode(struct reftable_record *rec, struct slice key,
-+ byte extra, struct slice src, int hash_size);
+bool reftable_record_is_deletion(struct reftable_record *rec);
+
+/* zeroes out the embedded record */
@@ reftable/record_test.c (new)
+ ((uint64_t)1 << 63) + ((uint64_t)1 << 63) - 1 };
+ int i = 0;
+ for (i = 0; i < ARRAY_SIZE(inputs); i++) {
-+ byte dest[10];
-+
-+ struct slice out = { .buf = dest, .len = 10, .cap = 10 };
++ uint8_t dest[10];
+
++ struct string_view out = {
++ .buf = dest,
++ .len = sizeof(dest),
++ };
+ uint64_t in = inputs[i];
+ int n = put_var_int(&out, in);
+ uint64_t got = 0;
@@ reftable/record_test.c (new)
+
+ int i = 0;
+ for (i = 0; i < ARRAY_SIZE(cases); i++) {
-+ struct slice a = SLICE_INIT;
-+ struct slice b = SLICE_INIT;
-+ slice_addstr(&a, cases[i].a);
-+ slice_addstr(&b, cases[i].b);
++ struct strbuf a = STRBUF_INIT;
++ struct strbuf b = STRBUF_INIT;
++ strbuf_addstr(&a, cases[i].a);
++ strbuf_addstr(&b, cases[i].b);
+ assert(common_prefix_size(&a, &b) == cases[i].want);
+
-+ slice_release(&a);
-+ slice_release(&b);
++ strbuf_release(&a);
++ strbuf_release(&b);
+ }
+}
+
-+static void set_hash(byte *h, int j)
++static void set_hash(uint8_t *h, int j)
+{
+ int i = 0;
+ for (i = 0; i < hash_size(SHA1_ID); i++) {
@@ reftable/record_test.c (new)
+ .target = xstrdup("old value"),
+ };
+ struct reftable_record rec_out = { 0 };
-+ struct slice key = SLICE_INIT;
++ struct strbuf key = STRBUF_INIT;
+ struct reftable_record rec = { 0 };
-+ struct slice dest = SLICE_INIT;
++ uint8_t buffer[1024] = { 0 };
++ struct string_view dest = {
++ .buf = buffer,
++ .len = sizeof(buffer),
++ };
++
+ int n, m;
+
+ switch (i) {
@@ reftable/record_test.c (new)
+ assert(reftable_record_val_type(&rec) == i);
+
+ reftable_record_key(&rec, &key);
-+ slice_grow(&dest, 1024);
-+ slice_setlen(&dest, 1024);
+ n = reftable_record_encode(&rec, dest, SHA1_SIZE);
+ assert(n > 0);
+
@@ reftable/record_test.c (new)
+ assert((out.target != NULL) == (in.target != NULL));
+ reftable_record_clear(&rec_out);
+
-+ slice_release(&key);
-+ slice_release(&dest);
++ strbuf_release(&key);
+ reftable_ref_record_clear(&in);
+ }
+}
@@ reftable/record_test.c (new)
+ set_test_hash(in[0].old_hash, 2);
+ for (int i = 0; i < ARRAY_SIZE(in); i++) {
+ struct reftable_record rec = { 0 };
-+ struct slice key = SLICE_INIT;
-+ struct slice dest = SLICE_INIT;
++ struct strbuf key = STRBUF_INIT;
++ uint8_t buffer[1024] = { 0 };
++ struct string_view dest = {
++ .buf = buffer,
++ .len = sizeof(buffer),
++ };
+ /* populate out, to check for leaks. */
+ struct reftable_log_record out = {
+ .ref_name = xstrdup("old name"),
@@ reftable/record_test.c (new)
+
+ reftable_record_key(&rec, &key);
+
-+ slice_grow(&dest, 1024);
-+ slice_setlen(&dest, 1024);
-+
+ n = reftable_record_encode(&rec, dest, SHA1_SIZE);
+ assert(n >= 0);
+ reftable_record_from_log(&rec_out, &out);
@@ reftable/record_test.c (new)
+
+ assert(reftable_log_record_equal(&in[i], &out, SHA1_SIZE));
+ reftable_log_record_clear(&in[i]);
-+ slice_release(&key);
-+ slice_release(&dest);
++ strbuf_release(&key);
+ reftable_record_clear(&rec_out);
+ }
+}
@@ reftable/record_test.c (new)
+static void test_u24_roundtrip(void)
+{
+ uint32_t in = 0x112233;
-+ byte dest[3];
++ uint8_t dest[3];
+ uint32_t out;
+ put_be24(dest, in);
+ out = get_be24(dest);
@@ reftable/record_test.c (new)
+
+static void test_key_roundtrip(void)
+{
-+ struct slice dest = SLICE_INIT;
-+ struct slice last_key = SLICE_INIT;
-+ struct slice key = SLICE_INIT;
-+ struct slice roundtrip = SLICE_INIT;
++ uint8_t buffer[1024] = { 0 };
++ struct string_view dest = {
++ .buf = buffer,
++ .len = sizeof(buffer),
++ };
++ struct strbuf last_key = STRBUF_INIT;
++ struct strbuf key = STRBUF_INIT;
++ struct strbuf roundtrip = STRBUF_INIT;
+ bool restart;
-+ byte extra;
++ uint8_t extra;
+ int n, m;
-+ byte rt_extra;
++ uint8_t rt_extra;
+
-+ slice_grow(&dest, 1024);
-+ slice_setlen(&dest, 1024);
-+ slice_addstr(&last_key, "refs/heads/master");
-+ slice_addstr(&key, "refs/tags/bla");
++ strbuf_addstr(&last_key, "refs/heads/master");
++ strbuf_addstr(&key, "refs/tags/bla");
+ extra = 6;
+ n = reftable_encode_key(&restart, dest, last_key, key, extra);
+ assert(!restart);
@@ reftable/record_test.c (new)
+
+ m = reftable_decode_key(&roundtrip, &rt_extra, last_key, dest);
+ assert(n == m);
-+ assert(0 == slice_cmp(&key, &roundtrip));
++ assert(0 == strbuf_cmp(&key, &roundtrip));
+ assert(rt_extra == extra);
+
-+ slice_release(&last_key);
-+ slice_release(&key);
-+ slice_release(&dest);
-+ slice_release(&roundtrip);
++ strbuf_release(&last_key);
++ strbuf_release(&key);
++ strbuf_release(&roundtrip);
+}
+
+static void test_reftable_obj_record_roundtrip(void)
+{
-+ byte testHash1[SHA1_SIZE] = { 1, 2, 3, 4, 0 };
++ uint8_t testHash1[SHA1_SIZE] = { 1, 2, 3, 4, 0 };
+ uint64_t till9[] = { 1, 2, 3, 4, 500, 600, 700, 800, 9000 };
+ struct reftable_obj_record recs[3] = { {
+ .hash_prefix = testHash1,
@@ reftable/record_test.c (new)
+ int i = 0;
+ for (i = 0; i < ARRAY_SIZE(recs); i++) {
+ struct reftable_obj_record in = recs[i];
-+ struct slice dest = SLICE_INIT;
++ uint8_t buffer[1024] = { 0 };
++ struct string_view dest = {
++ .buf = buffer,
++ .len = sizeof(buffer),
++ };
+ struct reftable_record rec = { 0 };
-+ struct slice key = SLICE_INIT;
++ struct strbuf key = STRBUF_INIT;
+ struct reftable_obj_record out = { 0 };
+ struct reftable_record rec_out = { 0 };
+ int n, m;
-+ byte extra;
++ uint8_t extra;
+
+ reftable_record_from_obj(&rec, &in);
+ test_copy(&rec);
+ reftable_record_key(&rec, &key);
-+ slice_grow(&dest, 1024);
-+ slice_setlen(&dest, 1024);
+ n = reftable_record_encode(&rec, dest, SHA1_SIZE);
+ assert(n > 0);
+ extra = reftable_record_val_type(&rec);
@@ reftable/record_test.c (new)
+ in.hash_prefix_len));
+ assert(0 == memcmp(in.offsets, out.offsets,
+ sizeof(uint64_t) * in.offset_len));
-+ slice_release(&key);
-+ slice_release(&dest);
++ strbuf_release(&key);
+ reftable_record_clear(&rec_out);
+ }
+}
@@ reftable/record_test.c (new)
+{
+ struct reftable_index_record in = {
+ .offset = 42,
-+ .last_key = SLICE_INIT,
++ .last_key = STRBUF_INIT,
++ };
++ uint8_t buffer[1024] = { 0 };
++ struct string_view dest = {
++ .buf = buffer,
++ .len = sizeof(buffer),
+ };
-+ struct slice dest = SLICE_INIT;
-+ struct slice key = SLICE_INIT;
++ struct strbuf key = STRBUF_INIT;
+ struct reftable_record rec = { 0 };
-+ struct reftable_index_record out = { .last_key = SLICE_INIT };
++ struct reftable_index_record out = { .last_key = STRBUF_INIT };
+ struct reftable_record out_rec = { NULL };
+ int n, m;
-+ byte extra;
++ uint8_t extra;
+
-+ slice_addstr(&in.last_key, "refs/heads/master");
++ strbuf_addstr(&in.last_key, "refs/heads/master");
+ reftable_record_from_index(&rec, &in);
+ reftable_record_key(&rec, &key);
+ test_copy(&rec);
+
-+ assert(0 == slice_cmp(&key, &in.last_key));
-+ slice_grow(&dest, 1024);
-+ slice_setlen(&dest, 1024);
++ assert(0 == strbuf_cmp(&key, &in.last_key));
+ n = reftable_record_encode(&rec, dest, SHA1_SIZE);
+ assert(n > 0);
+
@@ reftable/record_test.c (new)
+ assert(in.offset == out.offset);
+
+ reftable_record_clear(&out_rec);
-+ slice_release(&key);
-+ slice_release(&in.last_key);
-+ slice_release(&dest);
++ strbuf_release(&key);
++ strbuf_release(&in.last_key);
+}
+
+int record_test_main(int argc, const char *argv[])
@@ reftable/refname.c (new)
+#include "reftable.h"
+#include "basics.h"
+#include "refname.h"
-+#include "slice.h"
++#include "strbuf.h"
+
+struct find_arg {
+ char **names;
@@ reftable/refname.c (new)
+ return err;
+}
+
-+static void slice_trim_component(struct slice *sl)
++static void strbuf_trim_component(struct strbuf *sl)
+{
+ while (sl->len > 0) {
+ bool is_slash = (sl->buf[sl->len - 1] == '/');
-+ slice_setlen(sl, sl->len - 1);
++ strbuf_setlen(sl, sl->len - 1);
+ if (is_slash)
+ break;
+ }
@@ reftable/refname.c (new)
+
+int modification_validate(struct modification *mod)
+{
-+ struct slice slashed = SLICE_INIT;
++ struct strbuf slashed = STRBUF_INIT;
+ int err = 0;
+ int i = 0;
+ for (; i < mod->add_len; i++) {
+ err = validate_ref_name(mod->add[i]);
+ if (err)
+ goto done;
-+ slice_reset(&slashed);
-+ slice_addstr(&slashed, mod->add[i]);
-+ slice_addstr(&slashed, "/");
++ strbuf_reset(&slashed);
++ strbuf_addstr(&slashed, mod->add[i]);
++ strbuf_addstr(&slashed, "/");
+
-+ err = modification_has_ref_with_prefix(
-+ mod, slice_as_string(&slashed));
++ err = modification_has_ref_with_prefix(mod, slashed.buf);
+ if (err == 0) {
+ err = REFTABLE_NAME_CONFLICT;
+ goto done;
@@ reftable/refname.c (new)
+ if (err < 0)
+ goto done;
+
-+ slice_reset(&slashed);
-+ slice_addstr(&slashed, mod->add[i]);
++ strbuf_reset(&slashed);
++ strbuf_addstr(&slashed, mod->add[i]);
+ while (slashed.len) {
-+ slice_trim_component(&slashed);
-+ err = modification_has_ref(mod,
-+ slice_as_string(&slashed));
++ strbuf_trim_component(&slashed);
++ err = modification_has_ref(mod, slashed.buf);
+ if (err == 0) {
+ err = REFTABLE_NAME_CONFLICT;
+ goto done;
@@ reftable/refname.c (new)
+ }
+ err = 0;
+done:
-+ slice_release(&slashed);
++ strbuf_release(&slashed);
+ return err;
+}
@@ reftable/refname_test.c (new)
+static void test_conflict(void)
+{
+ struct reftable_write_options opts = { 0 };
-+ struct slice buf = SLICE_INIT;
++ struct strbuf buf = STRBUF_INIT;
+ struct reftable_writer *w =
-+ reftable_new_writer(&slice_add_void, &buf, &opts);
++ reftable_new_writer(&strbuf_add_void, &buf, &opts);
+ struct reftable_ref_record rec = {
+ .ref_name = "a/b",
+ .target = "destination", /* make sure it's not a symref. */
@@ reftable/refname_test.c (new)
+ assert_err(err);
+ reftable_writer_free(w);
+
-+ block_source_from_slice(&source, &buf);
++ block_source_from_strbuf(&source, &buf);
+ err = reftable_new_reader(&rd, &source, "filename");
+ assert_err(err);
+
@@ reftable/refname_test.c (new)
+ }
+
+ reftable_reader_free(rd);
-+ slice_release(&buf);
++ strbuf_release(&buf);
+}
+
+int refname_test_main(int argc, const char *argv[])
@@ reftable/reftable-tests.h (new)
+int record_test_main(int argc, const char **argv);
+int refname_test_main(int argc, const char **argv);
+int reftable_test_main(int argc, const char **argv);
-+int slice_test_main(int argc, const char **argv);
++int strbuf_test_main(int argc, const char **argv);
+int stack_test_main(int argc, const char **argv);
+int tree_test_main(int argc, const char **argv);
+int reftable_dump_main(int argc, char *const *argv);
@@ reftable/reftable.h (new)
+
+/* reftable_new_writer creates a new writer */
+struct reftable_writer *
-+reftable_new_writer(int (*writer_func)(void *, uint8_t *, size_t),
++reftable_new_writer(int (*writer_func)(void *, const void *, size_t),
+ void *writer_arg, struct reftable_write_options *opts);
+
+/* write to a file descriptor. fdp should be an int* pointing to the fd. */
-+int reftable_fd_write(void *fdp, uint8_t *data, size_t size);
++int reftable_fd_write(void *fdp, const void *data, size_t size);
+
+/* Set the range of update indices for the records we will add. When
+ writing a table into a stack, the min should be at least
@@ reftable/reftable_test.c (new)
+
+static void test_buffer(void)
+{
-+ struct slice buf = SLICE_INIT;
++ struct strbuf buf = STRBUF_INIT;
+ struct reftable_block_source source = { NULL };
+ struct reftable_block out = { 0 };
+ int n;
-+ byte in[] = "hello";
-+ slice_add(&buf, in, sizeof(in));
-+ block_source_from_slice(&source, &buf);
++ uint8_t in[] = "hello";
++ strbuf_add(&buf, in, sizeof(in));
++ block_source_from_strbuf(&source, &buf);
+ assert(block_source_size(&source) == 6);
+ n = block_source_read_block(&source, &out, 0, sizeof(in));
+ assert(n == sizeof(in));
@@ reftable/reftable_test.c (new)
+
+ reftable_block_done(&out);
+ block_source_close(&source);
-+ slice_release(&buf);
++ strbuf_release(&buf);
+}
+
+static void test_default_write_opts(void)
+{
+ struct reftable_write_options opts = { 0 };
-+ struct slice buf = SLICE_INIT;
++ struct strbuf buf = STRBUF_INIT;
+ struct reftable_writer *w =
-+ reftable_new_writer(&slice_add_void, &buf, &opts);
++ reftable_new_writer(&strbuf_add_void, &buf, &opts);
+
+ struct reftable_ref_record rec = {
+ .ref_name = "master",
@@ reftable/reftable_test.c (new)
+ assert_err(err);
+ reftable_writer_free(w);
+
-+ block_source_from_slice(&source, &buf);
++ block_source_from_strbuf(&source, &buf);
+
+ err = reftable_new_reader(&rd, &source, "filename");
+ assert_err(err);
@@ reftable/reftable_test.c (new)
+
+ reftable_merged_table_close(merged);
+ reftable_merged_table_free(merged);
-+ slice_release(&buf);
++ strbuf_release(&buf);
+}
+
-+static void write_table(char ***names, struct slice *buf, int N, int block_size,
-+ uint32_t hash_id)
++static void write_table(char ***names, struct strbuf *buf, int N,
++ int block_size, uint32_t hash_id)
+{
+ struct reftable_write_options opts = {
+ .block_size = block_size,
+ .hash_id = hash_id,
+ };
+ struct reftable_writer *w =
-+ reftable_new_writer(&slice_add_void, buf, &opts);
++ reftable_new_writer(&strbuf_add_void, buf, &opts);
+ struct reftable_ref_record ref = { 0 };
+ int i = 0, n;
+ struct reftable_log_record log = { 0 };
@@ reftable/reftable_test.c (new)
+ *names = reftable_calloc(sizeof(char *) * (N + 1));
+ reftable_writer_set_limits(w, update_index, update_index);
+ for (i = 0; i < N; i++) {
-+ byte hash[SHA256_SIZE] = { 0 };
++ uint8_t hash[SHA256_SIZE] = { 0 };
+ char name[100];
+ int n;
+
@@ reftable/reftable_test.c (new)
+ }
+
+ for (i = 0; i < N; i++) {
-+ byte hash[SHA256_SIZE] = { 0 };
++ uint8_t hash[SHA256_SIZE] = { 0 };
+ char name[100];
+ int n;
+
@@ reftable/reftable_test.c (new)
+
+static void test_log_buffer_size(void)
+{
-+ struct slice buf = SLICE_INIT;
++ struct strbuf buf = STRBUF_INIT;
+ struct reftable_write_options opts = {
+ .block_size = 4096,
+ };
@@ reftable/reftable_test.c (new)
+ .message = "commit: 9\n",
+ };
+ struct reftable_writer *w =
-+ reftable_new_writer(&slice_add_void, &buf, &opts);
++ reftable_new_writer(&strbuf_add_void, &buf, &opts);
+
+ /* This tests buffer extension for log compression. Must use a random
+ hash, to ensure that the compressed part is larger than the original.
+ */
-+ byte hash1[SHA1_SIZE], hash2[SHA1_SIZE];
++ uint8_t hash1[SHA1_SIZE], hash2[SHA1_SIZE];
+ for (int i = 0; i < SHA1_SIZE; i++) {
-+ hash1[i] = (byte)(rand() % 256);
-+ hash2[i] = (byte)(rand() % 256);
++ hash1[i] = (uint8_t)(rand() % 256);
++ hash2[i] = (uint8_t)(rand() % 256);
+ }
+ log.old_hash = hash1;
+ log.new_hash = hash2;
@@ reftable/reftable_test.c (new)
+ err = reftable_writer_close(w);
+ assert_err(err);
+ reftable_writer_free(w);
-+ slice_release(&buf);
++ strbuf_release(&buf);
+}
+
+static void test_log_write_read(void)
@@ reftable/reftable_test.c (new)
+ struct reftable_iterator it = { 0 };
+ struct reftable_reader rd = { 0 };
+ struct reftable_block_source source = { 0 };
-+ struct slice buf = SLICE_INIT;
++ struct strbuf buf = STRBUF_INIT;
+ struct reftable_writer *w =
-+ reftable_new_writer(&slice_add_void, &buf, &opts);
++ reftable_new_writer(&strbuf_add_void, &buf, &opts);
+ const struct reftable_stats *stats = NULL;
+ reftable_writer_set_limits(w, 0, N);
+ for (i = 0; i < N; i++) {
@@ reftable/reftable_test.c (new)
+ assert_err(err);
+ }
+ for (i = 0; i < N; i++) {
-+ byte hash1[SHA1_SIZE], hash2[SHA1_SIZE];
++ uint8_t hash1[SHA1_SIZE], hash2[SHA1_SIZE];
+ struct reftable_log_record log = { 0 };
+ set_test_hash(hash1, i);
+ set_test_hash(hash2, i + 1);
@@ reftable/reftable_test.c (new)
+ reftable_writer_free(w);
+ w = NULL;
+
-+ block_source_from_slice(&source, &buf);
++ block_source_from_strbuf(&source, &buf);
+
+ err = init_reader(&rd, &source, "file.log");
+ assert_err(err);
@@ reftable/reftable_test.c (new)
+ reftable_iterator_destroy(&it);
+
+ /* cleanup. */
-+ slice_release(&buf);
++ strbuf_release(&buf);
+ free_names(names);
+ reader_close(&rd);
+}
@@ reftable/reftable_test.c (new)
+static void test_table_read_write_sequential(void)
+{
+ char **names;
-+ struct slice buf = SLICE_INIT;
++ struct strbuf buf = STRBUF_INIT;
+ int N = 50;
+ struct reftable_iterator it = { 0 };
+ struct reftable_block_source source = { 0 };
@@ reftable/reftable_test.c (new)
+
+ write_table(&names, &buf, N, 256, SHA1_ID);
+
-+ block_source_from_slice(&source, &buf);
++ block_source_from_strbuf(&source, &buf);
+
+ err = init_reader(&rd, &source, "file.ref");
+ assert_err(err);
@@ reftable/reftable_test.c (new)
+ }
+ assert(j == N);
+ reftable_iterator_destroy(&it);
-+ slice_release(&buf);
++ strbuf_release(&buf);
+ free_names(names);
+
+ reader_close(&rd);
@@ reftable/reftable_test.c (new)
+static void test_table_write_small_table(void)
+{
+ char **names;
-+ struct slice buf = SLICE_INIT;
++ struct strbuf buf = STRBUF_INIT;
+ int N = 1;
+ write_table(&names, &buf, N, 4096, SHA1_ID);
+ assert(buf.len < 200);
-+ slice_release(&buf);
++ strbuf_release(&buf);
+ free_names(names);
+}
+
+static void test_table_read_api(void)
+{
+ char **names;
-+ struct slice buf = SLICE_INIT;
++ struct strbuf buf = STRBUF_INIT;
+ int N = 50;
+ struct reftable_reader rd = { 0 };
+ struct reftable_block_source source = { 0 };
@@ reftable/reftable_test.c (new)
+
+ write_table(&names, &buf, N, 256, SHA1_ID);
+
-+ block_source_from_slice(&source, &buf);
++ block_source_from_strbuf(&source, &buf);
+
+ err = init_reader(&rd, &source, "file.ref");
+ assert_err(err);
@@ reftable/reftable_test.c (new)
+ err = reftable_iterator_next_log(&it, &log);
+ assert(err == REFTABLE_API_ERROR);
+
-+ slice_release(&buf);
++ strbuf_release(&buf);
+ for (i = 0; i < N; i++) {
+ reftable_free(names[i]);
+ }
+ reftable_iterator_destroy(&it);
+ reftable_free(names);
+ reader_close(&rd);
-+ slice_release(&buf);
++ strbuf_release(&buf);
+}
+
+static void test_table_read_write_seek(bool index, int hash_id)
+{
+ char **names;
-+ struct slice buf = SLICE_INIT;
++ struct strbuf buf = STRBUF_INIT;
+ int N = 50;
+ struct reftable_reader rd = { 0 };
+ struct reftable_block_source source = { 0 };
@@ reftable/reftable_test.c (new)
+ int i = 0;
+
+ struct reftable_iterator it = { 0 };
-+ struct slice pastLast = SLICE_INIT;
++ struct strbuf pastLast = STRBUF_INIT;
+ struct reftable_ref_record ref = { 0 };
+
+ write_table(&names, &buf, N, 256, hash_id);
+
-+ block_source_from_slice(&source, &buf);
++ block_source_from_strbuf(&source, &buf);
+
+ err = init_reader(&rd, &source, "file.ref");
+ assert_err(err);
@@ reftable/reftable_test.c (new)
+ reftable_iterator_destroy(&it);
+ }
+
-+ slice_addstr(&pastLast, names[N - 1]);
-+ slice_addstr(&pastLast, "/");
++ strbuf_addstr(&pastLast, names[N - 1]);
++ strbuf_addstr(&pastLast, "/");
+
-+ err = reftable_reader_seek_ref(&rd, &it, slice_as_string(&pastLast));
++ err = reftable_reader_seek_ref(&rd, &it, pastLast.buf);
+ if (err == 0) {
+ struct reftable_ref_record ref = { 0 };
+ int err = reftable_iterator_next_ref(&it, &ref);
@@ reftable/reftable_test.c (new)
+ assert(err > 0);
+ }
+
-+ slice_release(&pastLast);
++ strbuf_release(&pastLast);
+ reftable_iterator_destroy(&it);
+
-+ slice_release(&buf);
++ strbuf_release(&buf);
+ for (i = 0; i < N; i++) {
+ reftable_free(names[i]);
+ }
@@ reftable/reftable_test.c (new)
+ int N = 50;
+ char **want_names = reftable_calloc(sizeof(char *) * (N + 1));
+ int want_names_len = 0;
-+ byte want_hash[SHA1_SIZE];
++ uint8_t want_hash[SHA1_SIZE];
+
+ struct reftable_write_options opts = {
+ .block_size = 256,
@@ reftable/reftable_test.c (new)
+ struct reftable_reader rd;
+ struct reftable_block_source source = { 0 };
+
-+ struct slice buf = SLICE_INIT;
++ struct strbuf buf = STRBUF_INIT;
+ struct reftable_writer *w =
-+ reftable_new_writer(&slice_add_void, &buf, &opts);
++ reftable_new_writer(&strbuf_add_void, &buf, &opts);
+
+ struct reftable_iterator it = { 0 };
+ int j;
@@ reftable/reftable_test.c (new)
+ set_test_hash(want_hash, 4);
+
+ for (i = 0; i < N; i++) {
-+ byte hash[SHA1_SIZE];
++ uint8_t hash[SHA1_SIZE];
+ char fill[51] = { 0 };
+ char name[100];
-+ byte hash1[SHA1_SIZE];
-+ byte hash2[SHA1_SIZE];
++ uint8_t hash1[SHA1_SIZE];
++ uint8_t hash2[SHA1_SIZE];
+ struct reftable_ref_record ref = { 0 };
+
+ memset(hash, i, sizeof(hash));
@@ reftable/reftable_test.c (new)
+ reftable_writer_free(w);
+ w = NULL;
+
-+ block_source_from_slice(&source, &buf);
++ block_source_from_strbuf(&source, &buf);
+
+ err = init_reader(&rd, &source, "file.ref");
+ assert_err(err);
@@ reftable/reftable_test.c (new)
+ }
+ assert(j == want_names_len);
+
-+ slice_release(&buf);
++ strbuf_release(&buf);
+ free_names(want_names);
+ reftable_iterator_destroy(&it);
+ reader_close(&rd);
@@ reftable/reftable_test.c (new)
+static void test_table_empty(void)
+{
+ struct reftable_write_options opts = { 0 };
-+ struct slice buf = SLICE_INIT;
++ struct strbuf buf = STRBUF_INIT;
+ struct reftable_writer *w =
-+ reftable_new_writer(&slice_add_void, &buf, &opts);
++ reftable_new_writer(&strbuf_add_void, &buf, &opts);
+ struct reftable_block_source source = { 0 };
+ struct reftable_reader *rd = NULL;
+ struct reftable_ref_record rec = { 0 };
@@ reftable/reftable_test.c (new)
+
+ assert(buf.len == header_size(1) + footer_size(1));
+
-+ block_source_from_slice(&source, &buf);
++ block_source_from_strbuf(&source, &buf);
+
+ err = reftable_new_reader(&rd, &source, "filename");
+ assert_err(err);
@@ reftable/reftable_test.c (new)
+
+ reftable_iterator_destroy(&it);
+ reftable_reader_free(rd);
-+ slice_release(&buf);
++ strbuf_release(&buf);
+}
+
+int reftable_test_main(int argc, const char *argv[])
@@ reftable/reftable_test.c (new)
+ return test_main(argc, argv);
+}
- ## reftable/slice.c (new) ##
+ ## reftable/stack.c (new) ##
@@
+/*
+Copyright 2020 Google LLC
@@ reftable/slice.c (new)
+https://developers.google.com/open-source/licenses/bsd
+*/
+
-+#include "slice.h"
++#include "stack.h"
+
+#include "system.h"
-+
++#include "merged.h"
++#include "reader.h"
++#include "refname.h"
+#include "reftable.h"
++#include "writer.h"
+
-+struct slice reftable_empty_slice = SLICE_INIT;
-+
-+void slice_init(struct slice *s)
-+{
-+ struct slice empty = SLICE_INIT;
-+ *s = empty;
-+}
-+
-+void slice_grow(struct slice *s, size_t extra)
++int reftable_new_stack(struct reftable_stack **dest, const char *dir,
++ struct reftable_write_options config)
+{
-+ size_t newcap = s->len + extra + 1;
-+ if (newcap > s->cap) {
-+ s->buf = reftable_realloc(s->buf, newcap);
-+ s->cap = newcap;
-+ }
-+}
++ struct reftable_stack *p =
++ reftable_calloc(sizeof(struct reftable_stack));
++ struct strbuf list_file_name = STRBUF_INIT;
++ int err = 0;
+
-+static void slice_resize(struct slice *s, int l)
-+{
-+ int zl = l + 1; /* one byte for 0 termination. */
-+ assert(s->canary == SLICE_CANARY);
-+ if (s->cap < zl) {
-+ int c = s->cap * 2;
-+ if (c < zl) {
-+ c = zl;
-+ }
-+ s->cap = c;
-+ s->buf = reftable_realloc(s->buf, s->cap);
++ if (config.hash_id == 0) {
++ config.hash_id = SHA1_ID;
+ }
-+ s->len = l;
-+ s->buf[l] = 0;
-+}
-+
-+void slice_setlen(struct slice *s, size_t l)
-+{
-+ assert(s->cap >= l + 1);
-+ s->len = l;
-+ s->buf[l] = 0;
-+}
+
-+void slice_reset(struct slice *s)
-+{
-+ slice_resize(s, 0);
-+}
++ *dest = NULL;
+
-+void slice_addstr(struct slice *d, const char *s)
-+{
-+ int l1 = d->len;
-+ int l2 = strlen(s);
-+ assert(d->canary == SLICE_CANARY);
++ strbuf_reset(&list_file_name);
++ strbuf_addstr(&list_file_name, dir);
++ strbuf_addstr(&list_file_name, "/tables.list");
+
-+ slice_resize(d, l2 + l1);
-+ memcpy(d->buf + l1, s, l2);
-+}
++ p->list_file = strbuf_detach(&list_file_name, NULL);
++ p->reftable_dir = xstrdup(dir);
++ p->config = config;
+
-+void slice_addbuf(struct slice *s, struct slice *a)
-+{
-+ int end = s->len;
-+ assert(s->canary == SLICE_CANARY);
-+ slice_resize(s, s->len + a->len);
-+ memcpy(s->buf + end, a->buf, a->len);
++ err = reftable_stack_reload_maybe_reuse(p, true);
++ if (err < 0) {
++ reftable_stack_destroy(p);
++ } else {
++ *dest = p;
++ }
++ return err;
+}
+
-+void slice_consume(struct slice *s, int n)
++static int fd_read_lines(int fd, char ***namesp)
+{
-+ assert(s->canary == SLICE_CANARY);
-+ s->buf += n;
-+ s->len -= n;
-+}
++ off_t size = lseek(fd, 0, SEEK_END);
++ char *buf = NULL;
++ int err = 0;
++ if (size < 0) {
++ err = REFTABLE_IO_ERROR;
++ goto done;
++ }
++ err = lseek(fd, 0, SEEK_SET);
++ if (err < 0) {
++ err = REFTABLE_IO_ERROR;
++ goto done;
++ }
+
-+char *slice_detach(struct slice *s)
-+{
-+ char *p = NULL;
-+ slice_as_string(s);
-+ p = (char *)s->buf;
-+ s->buf = NULL;
-+ s->cap = 0;
-+ s->len = 0;
-+ return p;
-+}
++ buf = reftable_malloc(size + 1);
++ if (read(fd, buf, size) != size) {
++ err = REFTABLE_IO_ERROR;
++ goto done;
++ }
++ buf[size] = 0;
+
-+void slice_release(struct slice *s)
-+{
-+ byte *ptr = s->buf;
-+ assert(s->canary == SLICE_CANARY);
-+ s->buf = NULL;
-+ s->cap = 0;
-+ s->len = 0;
-+ reftable_free(ptr);
-+}
++ parse_names(buf, size, namesp);
+
-+/* return the underlying data as char*. len is left unchanged, but
-+ a \0 is added at the end. */
-+const char *slice_as_string(struct slice *s)
-+{
-+ return (const char *)s->buf;
++done:
++ reftable_free(buf);
++ return err;
+}
+
-+int slice_cmp(const struct slice *a, const struct slice *b)
++int read_lines(const char *filename, char ***namesp)
+{
-+ int min = a->len < b->len ? a->len : b->len;
-+ int res = memcmp(a->buf, b->buf, min);
-+ assert(a->canary == SLICE_CANARY);
-+ assert(b->canary == SLICE_CANARY);
-+ if (res != 0)
-+ return res;
-+ if (a->len < b->len)
-+ return -1;
-+ else if (a->len > b->len)
-+ return 1;
-+ else
-+ return 0;
-+}
++ int fd = open(filename, O_RDONLY, 0644);
++ int err = 0;
++ if (fd < 0) {
++ if (errno == ENOENT) {
++ *namesp = reftable_calloc(sizeof(char *));
++ return 0;
++ }
+
-+int slice_add(struct slice *b, const byte *data, size_t sz)
-+{
-+ assert(b->canary == SLICE_CANARY);
-+ slice_grow(b, sz);
-+ memcpy(b->buf + b->len, data, sz);
-+ b->len += sz;
-+ b->buf[b->len] = 0;
-+ return sz;
++ return REFTABLE_IO_ERROR;
++ }
++ err = fd_read_lines(fd, namesp);
++ close(fd);
++ return err;
+}
+
-+int slice_add_void(void *b, byte *data, size_t sz)
++struct reftable_merged_table *
++reftable_stack_merged_table(struct reftable_stack *st)
+{
-+ return slice_add((struct slice *)b, data, sz);
++ return st->merged;
+}
+
-+static uint64_t slice_size(void *b)
++/* Close and free the stack */
++void reftable_stack_destroy(struct reftable_stack *st)
+{
-+ return ((struct slice *)b)->len;
++ if (st->merged != NULL) {
++ reftable_merged_table_close(st->merged);
++ reftable_merged_table_free(st->merged);
++ st->merged = NULL;
++ }
++ FREE_AND_NULL(st->list_file);
++ FREE_AND_NULL(st->reftable_dir);
++ reftable_free(st);
+}
+
-+static void slice_return_block(void *b, struct reftable_block *dest)
++static struct reftable_reader **stack_copy_readers(struct reftable_stack *st,
++ int cur_len)
+{
-+ memset(dest->data, 0xff, dest->len);
-+ reftable_free(dest->data);
++ struct reftable_reader **cur =
++ reftable_calloc(sizeof(struct reftable_reader *) * cur_len);
++ int i = 0;
++ for (i = 0; i < cur_len; i++) {
++ cur[i] = st->merged->stack[i];
++ }
++ return cur;
+}
+
-+static void slice_close(void *b)
++static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
++ bool reuse_open)
+{
-+}
++ int cur_len = st->merged == NULL ? 0 : st->merged->stack_len;
++ struct reftable_reader **cur = stack_copy_readers(st, cur_len);
++ int err = 0;
++ int names_len = names_length(names);
++ struct reftable_reader **new_tables =
++ reftable_malloc(sizeof(struct reftable_reader *) * names_len);
++ int new_tables_len = 0;
++ struct reftable_merged_table *new_merged = NULL;
++ int i;
+
-+static int slice_read_block(void *v, struct reftable_block *dest, uint64_t off,
-+ uint32_t size)
-+{
-+ struct slice *b = (struct slice *)v;
-+ assert(off + size <= b->len);
-+ dest->data = reftable_calloc(size);
-+ memcpy(dest->data, b->buf + off, size);
-+ dest->len = size;
-+ return size;
-+}
++ while (*names) {
++ struct reftable_reader *rd = NULL;
++ char *name = *names++;
+
-+struct reftable_block_source_vtable slice_vtable = {
-+ .size = &slice_size,
-+ .read_block = &slice_read_block,
-+ .return_block = &slice_return_block,
-+ .close = &slice_close,
-+};
++ /* this is linear; we assume compaction keeps the number of
++ tables under control so this is not quadratic. */
++ int j = 0;
++ for (j = 0; reuse_open && j < cur_len; j++) {
++ if (cur[j] != NULL && 0 == strcmp(cur[j]->name, name)) {
++ rd = cur[j];
++ cur[j] = NULL;
++ break;
++ }
++ }
+
-+void block_source_from_slice(struct reftable_block_source *bs,
-+ struct slice *buf)
-+{
-+ assert(bs->ops == NULL);
-+ bs->ops = &slice_vtable;
-+ bs->arg = buf;
-+}
++ if (rd == NULL) {
++ struct reftable_block_source src = { 0 };
++ struct strbuf table_path = STRBUF_INIT;
++ strbuf_addstr(&table_path, st->reftable_dir);
++ strbuf_addstr(&table_path, "/");
++ strbuf_addstr(&table_path, name);
+
-+static void malloc_return_block(void *b, struct reftable_block *dest)
-+{
-+ memset(dest->data, 0xff, dest->len);
-+ reftable_free(dest->data);
-+}
++ err = reftable_block_source_from_file(&src,
++ table_path.buf);
++ strbuf_release(&table_path);
+
-+struct reftable_block_source_vtable malloc_vtable = {
-+ .return_block = &malloc_return_block,
-+};
++ if (err < 0)
++ goto done;
+
-+struct reftable_block_source malloc_block_source_instance = {
-+ .ops = &malloc_vtable,
-+};
++ err = reftable_new_reader(&rd, &src, name);
++ if (err < 0)
++ goto done;
++ }
+
-+struct reftable_block_source malloc_block_source(void)
-+{
-+ return malloc_block_source_instance;
-+}
-+
-+int common_prefix_size(struct slice *a, struct slice *b)
-+{
-+ int p = 0;
-+ assert(a->canary == SLICE_CANARY);
-+ assert(b->canary == SLICE_CANARY);
-+ while (p < a->len && p < b->len) {
-+ if (a->buf[p] != b->buf[p]) {
-+ break;
-+ }
-+ p++;
-+ }
-+
-+ return p;
-+}
-
- ## reftable/slice.h (new) ##
-@@
-+/*
-+Copyright 2020 Google LLC
-+
-+Use of this source code is governed by a BSD-style
-+license that can be found in the LICENSE file or at
-+https://developers.google.com/open-source/licenses/bsd
-+*/
-+
-+#ifndef SLICE_H
-+#define SLICE_H
-+
-+#include "basics.h"
-+#include "reftable.h"
-+
-+/*
-+ Provides a bounds-checked, growable byte ranges. To use, initialize as "slice
-+ x = SLICE_INIT;"
-+ */
-+struct slice {
-+ int len;
-+ int cap;
-+ byte *buf;
-+
-+ /* Used to enforce initialization with SLICE_INIT */
-+ byte canary;
-+};
-+#define SLICE_CANARY 0x42
-+#define SLICE_INIT \
-+ { \
-+ 0, 0, NULL, SLICE_CANARY \
-+ }
-+extern struct slice reftable_empty_slice;
-+
-+void slice_addstr(struct slice *dest, const char *src);
-+
-+/* Deallocate and clear slice */
-+void slice_release(struct slice *slice);
-+
-+/* Set slice to 0 length, but retain buffer. */
-+void slice_reset(struct slice *slice);
-+
-+/* Initializes a slice. Accepts a slice with random garbage. */
-+void slice_init(struct slice *slice);
-+
-+/* Ensure that `buf` is \0 terminated. */
-+const char *slice_as_string(struct slice *src);
-+
-+/* Return `buf`, clearing out `s` */
-+char *slice_detach(struct slice *s);
-+
-+/* Set length of the slace to `l`, but don't reallocated. */
-+void slice_setlen(struct slice *s, size_t l);
-+
-+/* Ensure `l` bytes beyond current length are available */
-+void slice_grow(struct slice *s, size_t l);
-+
-+/* Signed comparison */
-+int slice_cmp(const struct slice *a, const struct slice *b);
-+
-+/* Append `data` to the `dest` slice. */
-+int slice_add(struct slice *dest, const byte *data, size_t sz);
-+
-+/* Append `add` to `dest. */
-+void slice_addbuf(struct slice *dest, struct slice *add);
-+
-+/* Like slice_add, but suitable for passing to reftable_new_writer
-+ */
-+int slice_add_void(void *b, byte *data, size_t sz);
-+
-+/* Find the longest shared prefix size of `a` and `b` */
-+int common_prefix_size(struct slice *a, struct slice *b);
-+
-+struct reftable_block_source;
-+
-+/* Create an in-memory block source for reading reftables */
-+void block_source_from_slice(struct reftable_block_source *bs,
-+ struct slice *buf);
-+
-+struct reftable_block_source malloc_block_source(void);
-+
-+/* Advance `buf` by `n`, and decrease length. A copy of the slice
-+ should be kept for deallocating the slice. */
-+void slice_consume(struct slice *s, int n);
-+
-+#endif
-
- ## reftable/slice_test.c (new) ##
-@@
-+/*
-+Copyright 2020 Google LLC
-+
-+Use of this source code is governed by a BSD-style
-+license that can be found in the LICENSE file or at
-+https://developers.google.com/open-source/licenses/bsd
-+*/
-+
-+#include "slice.h"
-+
-+#include "system.h"
-+
-+#include "basics.h"
-+#include "record.h"
-+#include "reftable.h"
-+#include "test_framework.h"
-+#include "reftable-tests.h"
-+
-+static void test_slice(void)
-+{
-+ struct slice s = SLICE_INIT;
-+ struct slice t = SLICE_INIT;
-+
-+ slice_addstr(&s, "abc");
-+ assert(0 == strcmp("abc", slice_as_string(&s)));
-+
-+ slice_addstr(&t, "pqr");
-+ slice_addbuf(&s, &t);
-+ assert(0 == strcmp("abcpqr", slice_as_string(&s)));
-+
-+ slice_release(&s);
-+ slice_release(&t);
-+}
-+
-+int slice_test_main(int argc, const char *argv[])
-+{
-+ add_test_case("test_slice", &test_slice);
-+ return test_main(argc, argv);
-+}
-
- ## reftable/stack.c (new) ##
-@@
-+/*
-+Copyright 2020 Google LLC
-+
-+Use of this source code is governed by a BSD-style
-+license that can be found in the LICENSE file or at
-+https://developers.google.com/open-source/licenses/bsd
-+*/
-+
-+#include "stack.h"
-+
-+#include "system.h"
-+#include "merged.h"
-+#include "reader.h"
-+#include "refname.h"
-+#include "reftable.h"
-+#include "writer.h"
-+
-+int reftable_new_stack(struct reftable_stack **dest, const char *dir,
-+ struct reftable_write_options config)
-+{
-+ struct reftable_stack *p =
-+ reftable_calloc(sizeof(struct reftable_stack));
-+ struct slice list_file_name = SLICE_INIT;
-+ int err = 0;
-+
-+ if (config.hash_id == 0) {
-+ config.hash_id = SHA1_ID;
-+ }
-+
-+ *dest = NULL;
-+
-+ slice_reset(&list_file_name);
-+ slice_addstr(&list_file_name, dir);
-+ slice_addstr(&list_file_name, "/tables.list");
-+
-+ p->list_file = slice_detach(&list_file_name);
-+ p->reftable_dir = xstrdup(dir);
-+ p->config = config;
-+
-+ err = reftable_stack_reload_maybe_reuse(p, true);
-+ if (err < 0) {
-+ reftable_stack_destroy(p);
-+ } else {
-+ *dest = p;
-+ }
-+ return err;
-+}
-+
-+static int fd_read_lines(int fd, char ***namesp)
-+{
-+ off_t size = lseek(fd, 0, SEEK_END);
-+ char *buf = NULL;
-+ int err = 0;
-+ if (size < 0) {
-+ err = REFTABLE_IO_ERROR;
-+ goto done;
-+ }
-+ err = lseek(fd, 0, SEEK_SET);
-+ if (err < 0) {
-+ err = REFTABLE_IO_ERROR;
-+ goto done;
-+ }
-+
-+ buf = reftable_malloc(size + 1);
-+ if (read(fd, buf, size) != size) {
-+ err = REFTABLE_IO_ERROR;
-+ goto done;
-+ }
-+ buf[size] = 0;
-+
-+ parse_names(buf, size, namesp);
-+
-+done:
-+ reftable_free(buf);
-+ return err;
-+}
-+
-+int read_lines(const char *filename, char ***namesp)
-+{
-+ int fd = open(filename, O_RDONLY, 0644);
-+ int err = 0;
-+ if (fd < 0) {
-+ if (errno == ENOENT) {
-+ *namesp = reftable_calloc(sizeof(char *));
-+ return 0;
-+ }
-+
-+ return REFTABLE_IO_ERROR;
-+ }
-+ err = fd_read_lines(fd, namesp);
-+ close(fd);
-+ return err;
-+}
-+
-+struct reftable_merged_table *
-+reftable_stack_merged_table(struct reftable_stack *st)
-+{
-+ return st->merged;
-+}
-+
-+/* Close and free the stack */
-+void reftable_stack_destroy(struct reftable_stack *st)
-+{
-+ if (st->merged != NULL) {
-+ reftable_merged_table_close(st->merged);
-+ reftable_merged_table_free(st->merged);
-+ st->merged = NULL;
-+ }
-+ FREE_AND_NULL(st->list_file);
-+ FREE_AND_NULL(st->reftable_dir);
-+ reftable_free(st);
-+}
-+
-+static struct reftable_reader **stack_copy_readers(struct reftable_stack *st,
-+ int cur_len)
-+{
-+ struct reftable_reader **cur =
-+ reftable_calloc(sizeof(struct reftable_reader *) * cur_len);
-+ int i = 0;
-+ for (i = 0; i < cur_len; i++) {
-+ cur[i] = st->merged->stack[i];
-+ }
-+ return cur;
-+}
-+
-+static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
-+ bool reuse_open)
-+{
-+ int cur_len = st->merged == NULL ? 0 : st->merged->stack_len;
-+ struct reftable_reader **cur = stack_copy_readers(st, cur_len);
-+ int err = 0;
-+ int names_len = names_length(names);
-+ struct reftable_reader **new_tables =
-+ reftable_malloc(sizeof(struct reftable_reader *) * names_len);
-+ int new_tables_len = 0;
-+ struct reftable_merged_table *new_merged = NULL;
-+ int i;
-+
-+ while (*names) {
-+ struct reftable_reader *rd = NULL;
-+ char *name = *names++;
-+
-+ /* this is linear; we assume compaction keeps the number of
-+ tables under control so this is not quadratic. */
-+ int j = 0;
-+ for (j = 0; reuse_open && j < cur_len; j++) {
-+ if (cur[j] != NULL && 0 == strcmp(cur[j]->name, name)) {
-+ rd = cur[j];
-+ cur[j] = NULL;
-+ break;
-+ }
-+ }
-+
-+ if (rd == NULL) {
-+ struct reftable_block_source src = { 0 };
-+ struct slice table_path = SLICE_INIT;
-+ slice_addstr(&table_path, st->reftable_dir);
-+ slice_addstr(&table_path, "/");
-+ slice_addstr(&table_path, name);
-+
-+ err = reftable_block_source_from_file(
-+ &src, slice_as_string(&table_path));
-+ slice_release(&table_path);
-+
-+ if (err < 0)
-+ goto done;
-+
-+ err = reftable_new_reader(&rd, &src, name);
-+ if (err < 0)
-+ goto done;
-+ }
-+
-+ new_tables[new_tables_len++] = rd;
-+ }
++ new_tables[new_tables_len++] = rd;
++ }
+
+ /* success! */
+ err = reftable_new_merged_table(&new_merged, new_tables, new_tables_len,
@@ reftable/stack.c (new)
+ return 0;
+}
+
-+static void format_name(struct slice *dest, uint64_t min, uint64_t max)
++static void format_name(struct strbuf *dest, uint64_t min, uint64_t max)
+{
+ char buf[100];
+ snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64, min, max);
-+ slice_reset(dest);
-+ slice_addstr(dest, buf);
++ strbuf_reset(dest);
++ strbuf_addstr(dest, buf);
+}
+
+struct reftable_addition {
+ int lock_file_fd;
-+ struct slice lock_file_name;
++ struct strbuf lock_file_name;
+ struct reftable_stack *stack;
+ char **names;
+ char **new_tables;
@@ reftable/stack.c (new)
+ uint64_t next_update_index;
+};
+
-+#define REFTABLE_ADDITION_INIT \
-+ { \
-+ .lock_file_name = SLICE_INIT \
++#define REFTABLE_ADDITION_INIT \
++ { \
++ .lock_file_name = STRBUF_INIT \
+ }
+
+static int reftable_stack_init_addition(struct reftable_addition *add,
@@ reftable/stack.c (new)
+ int err = 0;
+ add->stack = st;
+
-+ slice_reset(&add->lock_file_name);
-+ slice_addstr(&add->lock_file_name, st->list_file);
-+ slice_addstr(&add->lock_file_name, ".lock");
++ strbuf_reset(&add->lock_file_name);
++ strbuf_addstr(&add->lock_file_name, st->list_file);
++ strbuf_addstr(&add->lock_file_name, ".lock");
+
-+ add->lock_file_fd = open(slice_as_string(&add->lock_file_name),
++ add->lock_file_fd = open(add->lock_file_name.buf,
+ O_EXCL | O_CREAT | O_WRONLY, 0644);
+ if (add->lock_file_fd < 0) {
+ if (errno == EEXIST) {
@@ reftable/stack.c (new)
+void reftable_addition_close(struct reftable_addition *add)
+{
+ int i = 0;
-+ struct slice nm = SLICE_INIT;
++ struct strbuf nm = STRBUF_INIT;
+ for (i = 0; i < add->new_tables_len; i++) {
-+ slice_reset(&nm);
-+ slice_addstr(&nm, add->stack->list_file);
-+ slice_addstr(&nm, "/");
-+ slice_addstr(&nm, add->new_tables[i]);
-+ unlink(slice_as_string(&nm));
++ strbuf_reset(&nm);
++ strbuf_addstr(&nm, add->stack->list_file);
++ strbuf_addstr(&nm, "/");
++ strbuf_addstr(&nm, add->new_tables[i]);
++ unlink(nm.buf);
+ reftable_free(add->new_tables[i]);
+ add->new_tables[i] = NULL;
+ }
@@ reftable/stack.c (new)
+ add->lock_file_fd = 0;
+ }
+ if (add->lock_file_name.len > 0) {
-+ unlink(slice_as_string(&add->lock_file_name));
-+ slice_release(&add->lock_file_name);
++ unlink(add->lock_file_name.buf);
++ strbuf_release(&add->lock_file_name);
+ }
+
+ free_names(add->names);
+ add->names = NULL;
-+ slice_release(&nm);
++ strbuf_release(&nm);
+}
+
+void reftable_addition_destroy(struct reftable_addition *add)
@@ reftable/stack.c (new)
+
+int reftable_addition_commit(struct reftable_addition *add)
+{
-+ struct slice table_list = SLICE_INIT;
++ struct strbuf table_list = STRBUF_INIT;
+ int i = 0;
+ int err = 0;
+ if (add->new_tables_len == 0)
+ goto done;
+
+ for (i = 0; i < add->stack->merged->stack_len; i++) {
-+ slice_addstr(&table_list, add->stack->merged->stack[i]->name);
-+ slice_addstr(&table_list, "\n");
++ strbuf_addstr(&table_list, add->stack->merged->stack[i]->name);
++ strbuf_addstr(&table_list, "\n");
+ }
+ for (i = 0; i < add->new_tables_len; i++) {
-+ slice_addstr(&table_list, add->new_tables[i]);
-+ slice_addstr(&table_list, "\n");
++ strbuf_addstr(&table_list, add->new_tables[i]);
++ strbuf_addstr(&table_list, "\n");
+ }
+
+ err = write(add->lock_file_fd, table_list.buf, table_list.len);
-+ slice_release(&table_list);
++ strbuf_release(&table_list);
+ if (err < 0) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
@@ reftable/stack.c (new)
+ goto done;
+ }
+
-+ err = rename(slice_as_string(&add->lock_file_name),
-+ add->stack->list_file);
++ err = rename(add->lock_file_name.buf, add->stack->list_file);
+ if (err < 0) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
@@ reftable/stack.c (new)
+ void *arg),
+ void *arg)
+{
-+ struct slice temp_tab_file_name = SLICE_INIT;
-+ struct slice tab_file_name = SLICE_INIT;
-+ struct slice next_name = SLICE_INIT;
++ struct strbuf temp_tab_file_name = STRBUF_INIT;
++ struct strbuf tab_file_name = STRBUF_INIT;
++ struct strbuf next_name = STRBUF_INIT;
+ struct reftable_writer *wr = NULL;
+ int err = 0;
+ int tab_fd = 0;
+
-+ slice_reset(&next_name);
++ strbuf_reset(&next_name);
+ format_name(&next_name, add->next_update_index, add->next_update_index);
+
-+ slice_addstr(&temp_tab_file_name, add->stack->reftable_dir);
-+ slice_addstr(&temp_tab_file_name, "/");
-+ slice_addbuf(&temp_tab_file_name, &next_name);
-+ slice_addstr(&temp_tab_file_name, ".temp.XXXXXX");
++ strbuf_addstr(&temp_tab_file_name, add->stack->reftable_dir);
++ strbuf_addstr(&temp_tab_file_name, "/");
++ strbuf_addbuf(&temp_tab_file_name, &next_name);
++ strbuf_addstr(&temp_tab_file_name, ".temp.XXXXXX");
+
-+ tab_fd = mkstemp((char *)slice_as_string(&temp_tab_file_name));
++ tab_fd = mkstemp(temp_tab_file_name.buf);
+ if (tab_fd < 0) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
@@ reftable/stack.c (new)
+ goto done;
+ }
+
-+ err = stack_check_addition(add->stack,
-+ slice_as_string(&temp_tab_file_name));
++ err = stack_check_addition(add->stack, temp_tab_file_name.buf);
+ if (err < 0)
+ goto done;
+
@@ reftable/stack.c (new)
+ }
+
+ format_name(&next_name, wr->min_update_index, wr->max_update_index);
-+ slice_addstr(&next_name, ".ref");
++ strbuf_addstr(&next_name, ".ref");
+
-+ slice_addstr(&tab_file_name, add->stack->reftable_dir);
-+ slice_addstr(&tab_file_name, "/");
-+ slice_addbuf(&tab_file_name, &next_name);
++ strbuf_addstr(&tab_file_name, add->stack->reftable_dir);
++ strbuf_addstr(&tab_file_name, "/");
++ strbuf_addbuf(&tab_file_name, &next_name);
+
+ /* TODO: should check destination out of paranoia */
-+ err = rename(slice_as_string(&temp_tab_file_name),
-+ slice_as_string(&tab_file_name));
++ err = rename(temp_tab_file_name.buf, tab_file_name.buf);
+ if (err < 0) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
@@ reftable/stack.c (new)
+ add->new_tables = reftable_realloc(add->new_tables,
+ sizeof(*add->new_tables) *
+ (add->new_tables_len + 1));
-+ add->new_tables[add->new_tables_len] = slice_detach(&next_name);
++ add->new_tables[add->new_tables_len] = strbuf_detach(&next_name, NULL);
+ add->new_tables_len++;
+done:
+ if (tab_fd > 0) {
@@ reftable/stack.c (new)
+ tab_fd = 0;
+ }
+ if (temp_tab_file_name.len > 0) {
-+ unlink(slice_as_string(&temp_tab_file_name));
++ unlink(temp_tab_file_name.buf);
+ }
+
-+ slice_release(&temp_tab_file_name);
-+ slice_release(&tab_file_name);
-+ slice_release(&next_name);
++ strbuf_release(&temp_tab_file_name);
++ strbuf_release(&tab_file_name);
++ strbuf_release(&next_name);
+ reftable_writer_free(wr);
+ return err;
+}
@@ reftable/stack.c (new)
+}
+
+static int stack_compact_locked(struct reftable_stack *st, int first, int last,
-+ struct slice *temp_tab,
++ struct strbuf *temp_tab,
+ struct reftable_log_expiry_config *config)
+{
-+ struct slice next_name = SLICE_INIT;
++ struct strbuf next_name = STRBUF_INIT;
+ int tab_fd = -1;
+ struct reftable_writer *wr = NULL;
+ int err = 0;
@@ reftable/stack.c (new)
+ reftable_reader_min_update_index(st->merged->stack[first]),
+ reftable_reader_max_update_index(st->merged->stack[first]));
+
-+ slice_reset(temp_tab);
-+ slice_addstr(temp_tab, st->reftable_dir);
-+ slice_addstr(temp_tab, "/");
-+ slice_addbuf(temp_tab, &next_name);
-+ slice_addstr(temp_tab, ".temp.XXXXXX");
++ strbuf_reset(temp_tab);
++ strbuf_addstr(temp_tab, st->reftable_dir);
++ strbuf_addstr(temp_tab, "/");
++ strbuf_addbuf(temp_tab, &next_name);
++ strbuf_addstr(temp_tab, ".temp.XXXXXX");
+
-+ tab_fd = mkstemp((char *)slice_as_string(temp_tab));
++ tab_fd = mkstemp(temp_tab->buf);
+ wr = reftable_new_writer(reftable_fd_write, &tab_fd, &st->config);
+
+ err = stack_write_compact(st, wr, first, last, config);
@@ reftable/stack.c (new)
+ tab_fd = 0;
+ }
+ if (err != 0 && temp_tab->len > 0) {
-+ unlink(slice_as_string(temp_tab));
-+ slice_release(temp_tab);
++ unlink(temp_tab->buf);
++ strbuf_release(temp_tab);
+ }
-+ slice_release(&next_name);
++ strbuf_release(&next_name);
+ return err;
+}
+
@@ reftable/stack.c (new)
+static int stack_compact_range(struct reftable_stack *st, int first, int last,
+ struct reftable_log_expiry_config *expiry)
+{
-+ struct slice temp_tab_file_name = SLICE_INIT;
-+ struct slice new_table_name = SLICE_INIT;
-+ struct slice lock_file_name = SLICE_INIT;
-+ struct slice ref_list_contents = SLICE_INIT;
-+ struct slice new_table_path = SLICE_INIT;
++ struct strbuf temp_tab_file_name = STRBUF_INIT;
++ struct strbuf new_table_name = STRBUF_INIT;
++ struct strbuf lock_file_name = STRBUF_INIT;
++ struct strbuf ref_list_contents = STRBUF_INIT;
++ struct strbuf new_table_path = STRBUF_INIT;
+ int err = 0;
+ bool have_lock = false;
+ int lock_file_fd = 0;
@@ reftable/stack.c (new)
+
+ st->stats.attempts++;
+
-+ slice_reset(&lock_file_name);
-+ slice_addstr(&lock_file_name, st->list_file);
-+ slice_addstr(&lock_file_name, ".lock");
++ strbuf_reset(&lock_file_name);
++ strbuf_addstr(&lock_file_name, st->list_file);
++ strbuf_addstr(&lock_file_name, ".lock");
+
-+ lock_file_fd = open(slice_as_string(&lock_file_name),
-+ O_EXCL | O_CREAT | O_WRONLY, 0644);
++ lock_file_fd =
++ open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0644);
+ if (lock_file_fd < 0) {
+ if (errno == EEXIST) {
+ err = 1;
@@ reftable/stack.c (new)
+ goto done;
+
+ for (i = first, j = 0; i <= last; i++) {
-+ struct slice subtab_file_name = SLICE_INIT;
-+ struct slice subtab_lock = SLICE_INIT;
++ struct strbuf subtab_file_name = STRBUF_INIT;
++ struct strbuf subtab_lock = STRBUF_INIT;
+ int sublock_file_fd = -1;
+
-+ slice_addstr(&subtab_file_name, st->reftable_dir);
-+ slice_addstr(&subtab_file_name, "/");
-+ slice_addstr(&subtab_file_name,
-+ reader_name(st->merged->stack[i]));
++ strbuf_addstr(&subtab_file_name, st->reftable_dir);
++ strbuf_addstr(&subtab_file_name, "/");
++ strbuf_addstr(&subtab_file_name,
++ reader_name(st->merged->stack[i]));
+
-+ slice_reset(&subtab_lock);
-+ slice_addbuf(&subtab_lock, &subtab_file_name);
-+ slice_addstr(&subtab_lock, ".lock");
++ strbuf_reset(&subtab_lock);
++ strbuf_addbuf(&subtab_lock, &subtab_file_name);
++ strbuf_addstr(&subtab_lock, ".lock");
+
-+ sublock_file_fd = open(slice_as_string(&subtab_lock),
++ sublock_file_fd = open(subtab_lock.buf,
+ O_EXCL | O_CREAT | O_WRONLY, 0644);
+ if (sublock_file_fd > 0) {
+ close(sublock_file_fd);
@@ reftable/stack.c (new)
+ }
+ }
+
-+ subtable_locks[j] = (char *)slice_as_string(&subtab_lock);
-+ delete_on_success[j] =
-+ (char *)slice_as_string(&subtab_file_name);
++ subtable_locks[j] = subtab_lock.buf;
++ delete_on_success[j] = subtab_file_name.buf;
+ j++;
+
+ if (err != 0)
+ goto done;
+ }
+
-+ err = unlink(slice_as_string(&lock_file_name));
++ err = unlink(lock_file_name.buf);
+ if (err < 0)
+ goto done;
+ have_lock = false;
@@ reftable/stack.c (new)
+ if (err < 0)
+ goto done;
+
-+ lock_file_fd = open(slice_as_string(&lock_file_name),
-+ O_EXCL | O_CREAT | O_WRONLY, 0644);
++ lock_file_fd =
++ open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0644);
+ if (lock_file_fd < 0) {
+ if (errno == EEXIST) {
+ err = 1;
@@ reftable/stack.c (new)
+
+ format_name(&new_table_name, st->merged->stack[first]->min_update_index,
+ st->merged->stack[last]->max_update_index);
-+ slice_addstr(&new_table_name, ".ref");
++ strbuf_addstr(&new_table_name, ".ref");
+
-+ slice_reset(&new_table_path);
-+ slice_addstr(&new_table_path, st->reftable_dir);
-+ slice_addstr(&new_table_path, "/");
-+ slice_addbuf(&new_table_path, &new_table_name);
++ strbuf_reset(&new_table_path);
++ strbuf_addstr(&new_table_path, st->reftable_dir);
++ strbuf_addstr(&new_table_path, "/");
++ strbuf_addbuf(&new_table_path, &new_table_name);
+
+ if (!is_empty_table) {
-+ err = rename(slice_as_string(&temp_tab_file_name),
-+ slice_as_string(&new_table_path));
++ err = rename(temp_tab_file_name.buf, new_table_path.buf);
+ if (err < 0) {
+ err = REFTABLE_IO_ERROR;
+ goto done;
@@ reftable/stack.c (new)
+ }
+
+ for (i = 0; i < first; i++) {
-+ slice_addstr(&ref_list_contents, st->merged->stack[i]->name);
-+ slice_addstr(&ref_list_contents, "\n");
++ strbuf_addstr(&ref_list_contents, st->merged->stack[i]->name);
++ strbuf_addstr(&ref_list_contents, "\n");
+ }
+ if (!is_empty_table) {
-+ slice_addbuf(&ref_list_contents, &new_table_name);
-+ slice_addstr(&ref_list_contents, "\n");
++ strbuf_addbuf(&ref_list_contents, &new_table_name);
++ strbuf_addstr(&ref_list_contents, "\n");
+ }
+ for (i = last + 1; i < st->merged->stack_len; i++) {
-+ slice_addstr(&ref_list_contents, st->merged->stack[i]->name);
-+ slice_addstr(&ref_list_contents, "\n");
++ strbuf_addstr(&ref_list_contents, st->merged->stack[i]->name);
++ strbuf_addstr(&ref_list_contents, "\n");
+ }
+
+ err = write(lock_file_fd, ref_list_contents.buf, ref_list_contents.len);
+ if (err < 0) {
+ err = REFTABLE_IO_ERROR;
-+ unlink(slice_as_string(&new_table_path));
++ unlink(new_table_path.buf);
+ goto done;
+ }
+ err = close(lock_file_fd);
+ lock_file_fd = 0;
+ if (err < 0) {
+ err = REFTABLE_IO_ERROR;
-+ unlink(slice_as_string(&new_table_path));
++ unlink(new_table_path.buf);
+ goto done;
+ }
+
-+ err = rename(slice_as_string(&lock_file_name), st->list_file);
++ err = rename(lock_file_name.buf, st->list_file);
+ if (err < 0) {
+ err = REFTABLE_IO_ERROR;
-+ unlink(slice_as_string(&new_table_path));
++ unlink(new_table_path.buf);
+ goto done;
+ }
+ have_lock = false;
@@ reftable/stack.c (new)
+
+ listp = delete_on_success;
+ while (*listp) {
-+ if (strcmp(*listp, slice_as_string(&new_table_path))) {
++ if (strcmp(*listp, new_table_path.buf)) {
+ unlink(*listp);
+ }
+ listp++;
@@ reftable/stack.c (new)
+ lock_file_fd = 0;
+ }
+ if (have_lock) {
-+ unlink(slice_as_string(&lock_file_name));
++ unlink(lock_file_name.buf);
+ }
-+ slice_release(&new_table_name);
-+ slice_release(&new_table_path);
-+ slice_release(&ref_list_contents);
-+ slice_release(&temp_tab_file_name);
-+ slice_release(&lock_file_name);
++ strbuf_release(&new_table_name);
++ strbuf_release(&new_table_path);
++ strbuf_release(&ref_list_contents);
++ strbuf_release(&temp_tab_file_name);
++ strbuf_release(&lock_file_name);
+ return err;
+}
+
@@ reftable/stack_test.c (new)
+ struct reftable_stack *st = NULL;
+ char dir[256] = "/tmp/stack_test.XXXXXX";
+
-+ byte h1[SHA1_SIZE] = { 0x01 }, h2[SHA1_SIZE] = { 0x02 };
++ uint8_t h1[SHA1_SIZE] = { 0x01 }, h2[SHA1_SIZE] = { 0x02 };
+
+ struct reftable_log_record input = {
+ .ref_name = "branch",
@@ reftable/stack_test.c (new)
+ add_test_case("test_names_equal", &test_names_equal);
+ add_test_case("test_reftable_stack_add", &test_reftable_stack_add);
+ return test_main(argc, argv);
++}
+
+ ## reftable/strbuf.c (new) ##
+@@
++/*
++Copyright 2020 Google LLC
++
++Use of this source code is governed by a BSD-style
++license that can be found in the LICENSE file or at
++https://developers.google.com/open-source/licenses/bsd
++*/
++
++#include "strbuf.h"
++
++#include "system.h"
++#include "reftable.h"
++#include "basics.h"
++
++#ifdef REFTABLE_STANDALONE
++
++void strbuf_init(struct strbuf *s, size_t alloc)
++{
++ struct strbuf empty = STRBUF_INIT;
++ *s = empty;
++}
++
++void strbuf_grow(struct strbuf *s, size_t extra)
++{
++ size_t newcap = s->len + extra + 1;
++ if (newcap > s->cap) {
++ s->buf = reftable_realloc(s->buf, newcap);
++ s->cap = newcap;
++ }
++}
++
++static void strbuf_resize(struct strbuf *s, int l)
++{
++ int zl = l + 1; /* one uint8_t for 0 termination. */
++ assert(s->canary == STRBUF_CANARY);
++ if (s->cap < zl) {
++ int c = s->cap * 2;
++ if (c < zl) {
++ c = zl;
++ }
++ s->cap = c;
++ s->buf = reftable_realloc(s->buf, s->cap);
++ }
++ s->len = l;
++ s->buf[l] = 0;
++}
++
++void strbuf_setlen(struct strbuf *s, size_t l)
++{
++ assert(s->cap >= l + 1);
++ s->len = l;
++ s->buf[l] = 0;
++}
++
++void strbuf_reset(struct strbuf *s)
++{
++ strbuf_resize(s, 0);
++}
++
++void strbuf_addstr(struct strbuf *d, const char *s)
++{
++ int l1 = d->len;
++ int l2 = strlen(s);
++ assert(d->canary == STRBUF_CANARY);
++
++ strbuf_resize(d, l2 + l1);
++ memcpy(d->buf + l1, s, l2);
++}
++
++void strbuf_addbuf(struct strbuf *s, struct strbuf *a)
++{
++ int end = s->len;
++ assert(s->canary == STRBUF_CANARY);
++ strbuf_resize(s, s->len + a->len);
++ memcpy(s->buf + end, a->buf, a->len);
++}
++
++char *strbuf_detach(struct strbuf *s, size_t *sz)
++{
++ char *p = NULL;
++ p = (char *)s->buf;
++ if (sz)
++ *sz = s->len;
++ s->buf = NULL;
++ s->cap = 0;
++ s->len = 0;
++ return p;
++}
++
++void strbuf_release(struct strbuf *s)
++{
++ assert(s->canary == STRBUF_CANARY);
++ s->cap = 0;
++ s->len = 0;
++ reftable_free(s->buf);
++ s->buf = NULL;
++}
++
++int strbuf_cmp(const struct strbuf *a, const struct strbuf *b)
++{
++ int min = a->len < b->len ? a->len : b->len;
++ int res = memcmp(a->buf, b->buf, min);
++ assert(a->canary == STRBUF_CANARY);
++ assert(b->canary == STRBUF_CANARY);
++ if (res != 0)
++ return res;
++ if (a->len < b->len)
++ return -1;
++ else if (a->len > b->len)
++ return 1;
++ else
++ return 0;
++}
++
++int strbuf_add(struct strbuf *b, const void *data, size_t sz)
++{
++ assert(b->canary == STRBUF_CANARY);
++ strbuf_grow(b, sz);
++ memcpy(b->buf + b->len, data, sz);
++ b->len += sz;
++ b->buf[b->len] = 0;
++ return sz;
++}
++
++#endif
++
++static uint64_t strbuf_size(void *b)
++{
++ return ((struct strbuf *)b)->len;
++}
++
++int strbuf_add_void(void *b, const void *data, size_t sz)
++{
++ strbuf_add((struct strbuf *)b, data, sz);
++ return sz;
++}
++
++static void strbuf_return_block(void *b, struct reftable_block *dest)
++{
++ memset(dest->data, 0xff, dest->len);
++ reftable_free(dest->data);
++}
++
++static void strbuf_close(void *b)
++{
++}
++
++static int strbuf_read_block(void *v, struct reftable_block *dest, uint64_t off,
++ uint32_t size)
++{
++ struct strbuf *b = (struct strbuf *)v;
++ assert(off + size <= b->len);
++ dest->data = reftable_calloc(size);
++ memcpy(dest->data, b->buf + off, size);
++ dest->len = size;
++ return size;
++}
++
++struct reftable_block_source_vtable strbuf_vtable = {
++ .size = &strbuf_size,
++ .read_block = &strbuf_read_block,
++ .return_block = &strbuf_return_block,
++ .close = &strbuf_close,
++};
++
++void block_source_from_strbuf(struct reftable_block_source *bs,
++ struct strbuf *buf)
++{
++ assert(bs->ops == NULL);
++ bs->ops = &strbuf_vtable;
++ bs->arg = buf;
++}
++
++static void malloc_return_block(void *b, struct reftable_block *dest)
++{
++ memset(dest->data, 0xff, dest->len);
++ reftable_free(dest->data);
++}
++
++struct reftable_block_source_vtable malloc_vtable = {
++ .return_block = &malloc_return_block,
++};
++
++struct reftable_block_source malloc_block_source_instance = {
++ .ops = &malloc_vtable,
++};
++
++struct reftable_block_source malloc_block_source(void)
++{
++ return malloc_block_source_instance;
++}
++
++int common_prefix_size(struct strbuf *a, struct strbuf *b)
++{
++ int p = 0;
++ while (p < a->len && p < b->len) {
++ if (a->buf[p] != b->buf[p]) {
++ break;
++ }
++ p++;
++ }
++
++ return p;
++}
++
++struct strbuf reftable_empty_strbuf = STRBUF_INIT;
+
+ ## reftable/strbuf.h (new) ##
+@@
++/*
++Copyright 2020 Google LLC
++
++Use of this source code is governed by a BSD-style
++license that can be found in the LICENSE file or at
++https://developers.google.com/open-source/licenses/bsd
++*/
++
++#ifndef SLICE_H
++#define SLICE_H
++
++#ifdef REFTABLE_STANDALONE
++
++#include "basics.h"
++#include "reftable.h"
++
++/*
++ Provides a bounds-checked, growable byte ranges. To use, initialize as "strbuf
++ x = STRBUF_INIT;"
++ */
++struct strbuf {
++ int len;
++ int cap;
++ char *buf;
++
++ /* Used to enforce initialization with STRBUF_INIT */
++ uint8_t canary;
++};
++#define STRBUF_CANARY 0x42
++#define STRBUF_INIT \
++ { \
++ 0, 0, NULL, STRBUF_CANARY \
++ }
++
++void strbuf_addstr(struct strbuf *dest, const char *src);
++
++/* Deallocate and clear strbuf */
++void strbuf_release(struct strbuf *strbuf);
++
++/* Set strbuf to 0 length, but retain buffer. */
++void strbuf_reset(struct strbuf *strbuf);
++
++/* Initializes a strbuf. Accepts a strbuf with random garbage. */
++void strbuf_init(struct strbuf *strbuf, size_t alloc);
++
++/* Return `buf`, clearing out `s`. Optionally return len (not cap) in `sz`. */
++char *strbuf_detach(struct strbuf *s, size_t *sz);
++
++/* Set length of the slace to `l`, but don't reallocated. */
++void strbuf_setlen(struct strbuf *s, size_t l);
++
++/* Ensure `l` bytes beyond current length are available */
++void strbuf_grow(struct strbuf *s, size_t l);
++
++/* Signed comparison */
++int strbuf_cmp(const struct strbuf *a, const struct strbuf *b);
++
++/* Append `data` to the `dest` strbuf. */
++int strbuf_add(struct strbuf *dest, const void *data, size_t sz);
++
++/* Append `add` to `dest. */
++void strbuf_addbuf(struct strbuf *dest, struct strbuf *add);
++
++#else
++
++#include "../git-compat-util.h"
++#include "../strbuf.h"
++
++#endif
++
++extern struct strbuf reftable_empty_strbuf;
++
++/* Like strbuf_add, but suitable for passing to reftable_new_writer
++ */
++int strbuf_add_void(void *b, const void *data, size_t sz);
++
++/* Find the longest shared prefix size of `a` and `b` */
++int common_prefix_size(struct strbuf *a, struct strbuf *b);
++
++struct reftable_block_source;
++
++/* Create an in-memory block source for reading reftables */
++void block_source_from_strbuf(struct reftable_block_source *bs,
++ struct strbuf *buf);
++
++struct reftable_block_source malloc_block_source(void);
++
++#endif
+
+ ## reftable/strbuf_test.c (new) ##
+@@
++/*
++Copyright 2020 Google LLC
++
++Use of this source code is governed by a BSD-style
++license that can be found in the LICENSE file or at
++https://developers.google.com/open-source/licenses/bsd
++*/
++
++#include "strbuf.h"
++
++#include "system.h"
++
++#include "basics.h"
++#include "record.h"
++#include "reftable.h"
++#include "test_framework.h"
++#include "reftable-tests.h"
++
++static void test_strbuf(void)
++{
++ struct strbuf s = STRBUF_INIT;
++ struct strbuf t = STRBUF_INIT;
++
++ strbuf_addstr(&s, "abc");
++ assert(0 == strcmp("abc", s.buf));
++
++ strbuf_addstr(&t, "pqr");
++ strbuf_addbuf(&s, &t);
++ assert(0 == strcmp("abcpqr", s.buf));
++
++ strbuf_release(&s);
++ strbuf_release(&t);
++}
++
++int strbuf_test_main(int argc, const char *argv[])
++{
++ add_test_case("test_strbuf", &test_strbuf);
++ return test_main(argc, argv);
+}
## reftable/system.h (new) ##
@@ reftable/system.h (new)
+#ifndef SYSTEM_H
+#define SYSTEM_H
+
-+#if 1 /* REFTABLE_IN_GITCORE */
-+#define REFTABLE_IN_GITCORE
++#ifndef REFTABLE_STANDALONE
+
+#include "git-compat-util.h"
+#include "cache.h"
@@ reftable/system.h (new)
+
+#include "compat.h"
+
-+#endif /* REFTABLE_IN_GITCORE */
++#endif /* REFTABLE_STANDALONE */
+
+void reftable_clear_dir(const char *dirname);
+
@@ reftable/system.h (new)
+#define SHA1_SIZE 20
+#define SHA256_SIZE 32
+
-+typedef uint8_t byte;
+typedef int bool;
+
+/* This is uncompress2, which is only available in zlib as of 2017.
@@ reftable/test_framework.c (new)
+ return 0;
+}
+
-+void set_test_hash(byte *p, int i)
++void set_test_hash(uint8_t *p, int i)
+{
-+ memset(p, (byte)i, hash_size(SHA1_ID));
++ memset(p, (uint8_t)i, hash_size(SHA1_ID));
+}
## reftable/test_framework.h (new) ##
@@ reftable/test_framework.h (new)
+struct test_case *add_test_case(const char *name, void (*testfunc)(void));
+int test_main(int argc, const char *argv[]);
+
-+void set_test_hash(byte *p, int i);
++void set_test_hash(uint8_t *p, int i);
+
+#endif
@@ reftable/writer.c (new)
+#include "tree.h"
+
+static struct reftable_block_stats *
-+writer_reftable_block_stats(struct reftable_writer *w, byte typ)
++writer_reftable_block_stats(struct reftable_writer *w, uint8_t typ)
+{
+ switch (typ) {
+ case 'r':
@@ reftable/writer.c (new)
+
+/* write data, queuing the padding for the next write. Returns negative for
+ * error. */
-+static int padded_write(struct reftable_writer *w, byte *data, size_t len,
++static int padded_write(struct reftable_writer *w, uint8_t *data, size_t len,
+ int padding)
+{
+ int n = 0;
+ if (w->pending_padding > 0) {
-+ byte *zeroed = reftable_calloc(w->pending_padding);
++ uint8_t *zeroed = reftable_calloc(w->pending_padding);
+ int n = w->write(w->write_arg, zeroed, w->pending_padding);
+ if (n < 0)
+ return n;
@@ reftable/writer.c (new)
+ return (w->opts.hash_id == 0 || w->opts.hash_id == SHA1_ID) ? 1 : 2;
+}
+
-+static int writer_write_header(struct reftable_writer *w, byte *dest)
++static int writer_write_header(struct reftable_writer *w, uint8_t *dest)
+{
+ memcpy((char *)dest, "REFT", 4);
+
@@ reftable/writer.c (new)
+ return header_size(writer_version(w));
+}
+
-+static void writer_reinit_block_writer(struct reftable_writer *w, byte typ)
++static void writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
+{
+ int block_start = 0;
+ if (w->next == 0) {
+ block_start = header_size(writer_version(w));
+ }
+
-+ slice_release(&w->last_key);
++ strbuf_release(&w->last_key);
+ block_writer_init(&w->block_writer_data, typ, w->block,
+ w->opts.block_size, block_start,
+ hash_size(w->opts.hash_id));
@@ reftable/writer.c (new)
+}
+
+struct reftable_writer *
-+reftable_new_writer(int (*writer_func)(void *, byte *, size_t),
++reftable_new_writer(int (*writer_func)(void *, const void *, size_t),
+ void *writer_arg, struct reftable_write_options *opts)
+{
+ struct reftable_writer *wp =
+ reftable_calloc(sizeof(struct reftable_writer));
-+ slice_init(&wp->block_writer_data.last_key);
++ strbuf_init(&wp->block_writer_data.last_key, 0);
+ options_set_defaults(opts);
+ if (opts->block_size >= (1 << 24)) {
+ /* TODO - error return? */
+ abort();
+ }
-+ wp->last_key = reftable_empty_slice;
++ wp->last_key = reftable_empty_strbuf;
+ wp->block = reftable_calloc(opts->block_size);
+ wp->write = writer_func;
+ wp->write_arg = writer_arg;
@@ reftable/writer.c (new)
+}
+
+struct obj_index_tree_node {
-+ struct slice hash;
++ struct strbuf hash;
+ uint64_t *offsets;
+ int offset_len;
+ int offset_cap;
+};
-+#define OBJ_INDEX_TREE_NODE_INIT \
-+ { \
-+ .hash = SLICE_INIT \
++#define OBJ_INDEX_TREE_NODE_INIT \
++ { \
++ .hash = STRBUF_INIT \
+ }
+
+static int obj_index_tree_node_compare(const void *a, const void *b)
+{
-+ return slice_cmp(&((const struct obj_index_tree_node *)a)->hash,
-+ &((const struct obj_index_tree_node *)b)->hash);
++ return strbuf_cmp(&((const struct obj_index_tree_node *)a)->hash,
++ &((const struct obj_index_tree_node *)b)->hash);
+}
+
-+static void writer_index_hash(struct reftable_writer *w, struct slice *hash)
++static void writer_index_hash(struct reftable_writer *w, struct strbuf *hash)
+{
+ uint64_t off = w->next;
+
@@ reftable/writer.c (new)
+ key = reftable_malloc(sizeof(struct obj_index_tree_node));
+ *key = empty;
+
-+ slice_reset(&key->hash);
-+ slice_addbuf(&key->hash, hash);
++ strbuf_reset(&key->hash);
++ strbuf_addbuf(&key->hash, hash);
+ tree_search((void *)key, &w->obj_index_tree,
+ &obj_index_tree_node_compare, 1);
+ } else {
@@ reftable/writer.c (new)
+ struct reftable_record *rec)
+{
+ int result = -1;
-+ struct slice key = SLICE_INIT;
++ struct strbuf key = STRBUF_INIT;
+ int err = 0;
+ reftable_record_key(rec, &key);
-+ if (slice_cmp(&w->last_key, &key) >= 0)
++ if (strbuf_cmp(&w->last_key, &key) >= 0)
+ goto done;
+
-+ slice_reset(&w->last_key);
-+ slice_addbuf(&w->last_key, &key);
++ strbuf_reset(&w->last_key);
++ strbuf_addbuf(&w->last_key, &key);
+ if (w->block_writer == NULL) {
+ writer_reinit_block_writer(w, reftable_record_type(rec));
+ }
@@ reftable/writer.c (new)
+
+ result = 0;
+done:
-+ slice_release(&key);
++ strbuf_release(&key);
+ return result;
+}
+
@@ reftable/writer.c (new)
+ return err;
+
+ if (!w->opts.skip_index_objects && ref->value != NULL) {
-+ struct slice h = SLICE_INIT;
-+ slice_add(&h, ref->value, hash_size(w->opts.hash_id));
++ struct strbuf h = STRBUF_INIT;
++ strbuf_add(&h, (char *)ref->value, hash_size(w->opts.hash_id));
+ writer_index_hash(w, &h);
-+ slice_release(&h);
++ strbuf_release(&h);
+ }
+
+ if (!w->opts.skip_index_objects && ref->target_value != NULL) {
-+ struct slice h = SLICE_INIT;
-+ slice_add(&h, ref->target_value, hash_size(w->opts.hash_id));
++ struct strbuf h = STRBUF_INIT;
++ strbuf_add(&h, ref->target_value, hash_size(w->opts.hash_id));
+ writer_index_hash(w, &h);
+ }
+ return 0;
@@ reftable/writer.c (new)
+{
+ struct reftable_record rec = { 0 };
+ char *input_log_message = log->message;
-+ struct slice cleaned_message = SLICE_INIT;
++ struct strbuf cleaned_message = STRBUF_INIT;
+ int err;
+ if (log->ref_name == NULL)
+ return REFTABLE_API_ERROR;
@@ reftable/writer.c (new)
+ }
+
+ if (!w->opts.exact_log_message && log->message != NULL) {
-+ slice_addstr(&cleaned_message, log->message);
++ strbuf_addstr(&cleaned_message, log->message);
+ while (cleaned_message.len &&
+ cleaned_message.buf[cleaned_message.len - 1] == '\n')
-+ slice_setlen(&cleaned_message, cleaned_message.len - 1);
-+ if (strchr(slice_as_string(&cleaned_message), '\n')) {
++ strbuf_setlen(&cleaned_message,
++ cleaned_message.len - 1);
++ if (strchr(cleaned_message.buf, '\n')) {
+ // multiple lines not allowed.
+ err = REFTABLE_API_ERROR;
+ goto done;
+ }
-+ slice_addstr(&cleaned_message, "\n");
-+ log->message = (char *)slice_as_string(&cleaned_message);
++ strbuf_addstr(&cleaned_message, "\n");
++ log->message = cleaned_message.buf;
+ }
+
+ w->next -= w->pending_padding;
@@ reftable/writer.c (new)
+
+done:
+ log->message = input_log_message;
-+ slice_release(&cleaned_message);
++ strbuf_release(&cleaned_message);
+ return err;
+}
+
@@ reftable/writer.c (new)
+
+static int writer_finish_section(struct reftable_writer *w)
+{
-+ byte typ = block_writer_type(w->block_writer);
++ uint8_t typ = block_writer_type(w->block_writer);
+ uint64_t index_start = 0;
+ int max_level = 0;
+ int threshold = w->opts.unpadded ? 1 : 3;
@@ reftable/writer.c (new)
+ }
+ }
+ for (i = 0; i < idx_len; i++) {
-+ slice_release(&idx[i].last_key);
++ strbuf_release(&idx[i].last_key);
+ }
+ reftable_free(idx);
+ }
@@ reftable/writer.c (new)
+}
+
+struct common_prefix_arg {
-+ struct slice *last;
++ struct strbuf *last;
+ int max;
+};
+
@@ reftable/writer.c (new)
+ struct write_record_arg *arg = (struct write_record_arg *)void_arg;
+ struct obj_index_tree_node *entry = (struct obj_index_tree_node *)key;
+ struct reftable_obj_record obj_rec = {
-+ .hash_prefix = entry->hash.buf,
++ .hash_prefix = (uint8_t *)entry->hash.buf,
+ .hash_prefix_len = arg->w->stats.object_id_len,
+ .offsets = entry->offsets,
+ .offset_len = entry->offset_len,
@@ reftable/writer.c (new)
+ struct obj_index_tree_node *entry = (struct obj_index_tree_node *)key;
+
+ FREE_AND_NULL(entry->offsets);
-+ slice_release(&entry->hash);
++ strbuf_release(&entry->hash);
+ reftable_free(entry);
+}
+
@@ reftable/writer.c (new)
+
+int writer_finish_public_section(struct reftable_writer *w)
+{
-+ byte typ = 0;
++ uint8_t typ = 0;
+ int err = 0;
+
+ if (w->block_writer == NULL)
@@ reftable/writer.c (new)
+
+int reftable_writer_close(struct reftable_writer *w)
+{
-+ byte footer[72];
-+ byte *p = footer;
++ uint8_t footer[72];
++ uint8_t *p = footer;
+ int err = writer_finish_public_section(w);
+ int empty_table = w->next == 0;
+ if (err != 0)
@@ reftable/writer.c (new)
+ w->pending_padding = 0;
+ if (empty_table) {
+ /* Empty tables need a header anyway. */
-+ byte header[28];
++ uint8_t header[28];
+ int n = writer_write_header(w, header);
+ err = padded_write(w, header, n, 0);
+ if (err < 0)
@@ reftable/writer.c (new)
+ /* free up memory. */
+ block_writer_clear(&w->block_writer_data);
+ writer_clear_index(w);
-+ slice_release(&w->last_key);
++ strbuf_release(&w->last_key);
+ return err;
+}
+
@@ reftable/writer.c (new)
+{
+ int i = 0;
+ for (i = 0; i < w->index_len; i++) {
-+ slice_release(&w->index[i].last_key);
++ strbuf_release(&w->index[i].last_key);
+ }
+
+ FREE_AND_NULL(w->index);
@@ reftable/writer.c (new)
+
+static int writer_flush_nonempty_block(struct reftable_writer *w)
+{
-+ byte typ = block_writer_type(w->block_writer);
++ uint8_t typ = block_writer_type(w->block_writer);
+ struct reftable_block_stats *bstats =
+ writer_reftable_block_stats(w, typ);
+ uint64_t block_typ_off = (bstats->blocks == 0) ? w->next : 0;
+ int raw_bytes = block_writer_finish(w->block_writer);
+ int padding = 0;
+ int err = 0;
-+ struct reftable_index_record ir = { .last_key = SLICE_INIT };
++ struct reftable_index_record ir = { .last_key = STRBUF_INIT };
+ if (raw_bytes < 0)
+ return raw_bytes;
+
@@ reftable/writer.c (new)
+ }
+
+ ir.offset = w->next;
-+ slice_reset(&ir.last_key);
-+ slice_addbuf(&ir.last_key, &w->block_writer->last_key);
++ strbuf_reset(&ir.last_key);
++ strbuf_addbuf(&ir.last_key, &w->block_writer->last_key);
+ w->index[w->index_len] = ir;
+
+ w->index_len++;
@@ reftable/writer.h (new)
+#include "basics.h"
+#include "block.h"
+#include "reftable.h"
-+#include "slice.h"
++#include "strbuf.h"
+#include "tree.h"
+
+struct reftable_writer {
-+ int (*write)(void *, byte *, size_t);
++ int (*write)(void *, const void *, size_t);
+ void *write_arg;
+ int pending_padding;
-+ struct slice last_key;
++ struct strbuf last_key;
+
+ /* offset of next block to write. */
+ uint64_t next;
@@ reftable/writer.h (new)
+ struct reftable_write_options opts;
+
+ /* memory buffer for writing */
-+ byte *block;
++ uint8_t *block;
+
+ /* writer for the current section. NULL or points to
+ * block_writer_data */
-: ----------- > 12: c92b8d12ec6 Add standalone build infrastructure for reftable
12: e1b01927454 ! 13: 479fe884e98 Reftable support for git-core
@@ Commit message
It can be activated by passing --ref-storage=reftable to "git init".
- TODO:
-
- * Fix worktree commands
-
- * Spots marked XXX
-
Example use: see t/t0031-reftable.sh
Signed-off-by: Han-Wen Nienhuys <[email protected]>
@@ Makefile: VCSSVN_OBJS += vcs-svn/sliding_window.o
+REFTABLE_OBJS += reftable/record.o
+REFTABLE_OBJS += reftable/refname.o
+REFTABLE_OBJS += reftable/reftable.o
-+REFTABLE_OBJS += reftable/slice.o
++REFTABLE_OBJS += reftable/strbuf.o
+REFTABLE_OBJS += reftable/stack.o
+REFTABLE_OBJS += reftable/tree.o
+REFTABLE_OBJS += reftable/writer.o
@@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)
- init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, INIT_DB_QUIET);
+ init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN,
-+ DEFAULT_REF_STORAGE, INIT_DB_QUIET);
++ default_ref_storage(), INIT_DB_QUIET);
if (real_git_dir)
git_dir = real_git_dir;
@@ cache.h: int path_inside_repo(const char *prefix, const char *path);
void sanitize_stdfds(void);
int daemonize(void);
@@ cache.h: struct repository_format {
- int is_bare;
int hash_algo;
+ int has_extensions;
char *work_tree;
+ char *ref_storage;
struct string_list unknown_extensions;
@@ refs.c: struct ref_store *get_main_ref_store(struct repository *r)
+ r->refs_private = ref_store_init(r->gitdir,
+ r->ref_storage_format ?
+ r->ref_storage_format :
-+ DEFAULT_REF_STORAGE,
++ default_ref_storage(),
+ REF_STORE_ALL_CAPS);
return r->refs_private;
}
@@ refs.c: struct ref_store *get_submodule_ref_store(const char *submodule)
/* assume that add_submodule_odb() has been called */
- refs = ref_store_init(submodule_sb.buf,
-+ refs = ref_store_init(submodule_sb.buf, DEFAULT_REF_STORAGE, /* XXX */
++ refs = ref_store_init(submodule_sb.buf, default_ref_storage(),
REF_STORE_READ | REF_STORE_ODB);
register_ref_store_map(&submodule_ref_stores, "submodule",
refs, submodule);
@@ refs.c: struct ref_store *get_submodule_ref_store(const char *submodule)
struct ref_store *get_worktree_ref_store(const struct worktree *wt)
{
-+ const char *format = DEFAULT_REF_STORAGE; /* XXX */
++ const char *format = default_ref_storage();
struct ref_store *refs;
const char *id;
@@ refs/reftable-backend.c (new)
+ safe_create_dir(refs->reftable_dir, 1);
+
+ strbuf_addf(&sb, "%s/HEAD", refs->repo_dir);
-+ write_file(sb.buf, "ref: refs/.invalid");
++ write_file(sb.buf, "ref: refs/heads/.invalid");
+ strbuf_reset(&sb);
+
+ strbuf_addf(&sb, "%s/refs", refs->repo_dir);
@@ refs/reftable-backend.c (new)
+ return refs->err;
+ }
+
++ /* This is usually not needed, but Git doesn't signal to ref backend if
++ a subprocess updated the ref DB. So we always check.
++ */
++ err = reftable_stack_reload(refs->stack);
++ if (err) {
++ goto done;
++ }
++
+ err = reftable_stack_read_ref(refs->stack, refname, &ref);
+ if (err > 0) {
+ errno = ENOENT;
@@ refs/reftable-backend.c (new)
+ reftable_reflog_expire,
+};
+ ## reftable/update.sh ##
+@@ reftable/update.sh: cp reftable-repo/LICENSE reftable/
+ git --git-dir reftable-repo/.git show --no-patch --format=oneline HEAD \
+ > reftable/VERSION
+
+-mv reftable/system.h reftable/system.h~
+-sed 's|if REFTABLE_IN_GITCORE|if 1 /* REFTABLE_IN_GITCORE */|' < reftable/system.h~ > reftable/system.h
+-
+ git add reftable/*.[ch] reftable/LICENSE reftable/VERSION
+
## repository.c ##
@@ repository.c: int repo_init(struct repository *repo,
if (worktree)
@@ t/t0031-reftable.sh (new)
+ for count in $(test_seq 1 10)
+ do
+ test_commit "number $count" file.t $count number-$count ||
-+ return 1
++ return 1
+ done &&
+ git pack-refs &&
+ ls -1 .git/reftable >table-files &&
@@ t/t0031-reftable.sh (new)
+ git branch source &&
+ git checkout HEAD^ &&
+ test_commit message3 file3 &&
-+ git rebase source &&
++ git rebase source &&
+ test -f file2
+'
+
13: 0359fe416fa ! 14: eafd8eeefcc Hookup unittests for the reftable library.
@@ Makefile: REFTABLE_OBJS += reftable/tree.o
+REFTABLE_TEST_OBJS += reftable/record_test.o
+REFTABLE_TEST_OBJS += reftable/refname_test.o
+REFTABLE_TEST_OBJS += reftable/reftable_test.o
-+REFTABLE_TEST_OBJS += reftable/slice_test.o
++REFTABLE_TEST_OBJS += reftable/strbuf_test.o
+REFTABLE_TEST_OBJS += reftable/stack_test.o
+REFTABLE_TEST_OBJS += reftable/tree_test.o
+REFTABLE_TEST_OBJS += reftable/test_framework.o
@@ t/helper/test-reftable.c (new)
+ record_test_main(argc, argv);
+ refname_test_main(argc, argv);
+ reftable_test_main(argc, argv);
-+ slice_test_main(argc, argv);
++ strbuf_test_main(argc, argv);
+ stack_test_main(argc, argv);
+ tree_test_main(argc, argv);
+ return 0;
14: 88640ea13f9 ! 15: 46af142f33b Add GIT_DEBUG_REFS debugging mechanism
@@ Makefile: LIB_OBJS += rebase.o
## refs.c ##
@@ refs.c: struct ref_store *get_main_ref_store(struct repository *r)
r->ref_storage_format :
- DEFAULT_REF_STORAGE,
+ default_ref_storage(),
REF_STORE_ALL_CAPS);
+ if (getenv("GIT_DEBUG_REFS")) {
+ r->refs_private = debug_wrap(r->refs_private);
@@ refs/debug.c (new)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
+ int res = drefs->refs->be->init_db(drefs->refs, err);
++ fprintf(stderr, "init_db: %d\n", res);
+ return res;
+}
+
@@ refs/debug.c (new)
+ transaction->ref_store = drefs->refs;
+ res = drefs->refs->be->transaction_prepare(drefs->refs, transaction,
+ err);
++ fprintf(stderr, "transaction_prepare: %d\n", res);
+ return res;
+}
+
@@ refs/debug.c (new)
+ type &= 0xf; /* see refs.h REF_* */
+ flags &= REF_HAVE_NEW | REF_HAVE_OLD | REF_NO_DEREF |
+ REF_FORCE_CREATE_REFLOG | REF_LOG_ONLY;
-+ printf("%d: %s %s -> %s (F=0x%x, T=0x%x) \"%s\"\n", i, refname, o, n,
-+ flags, type, msg);
++ fprintf(stderr, "%d: %s %s -> %s (F=0x%x, T=0x%x) \"%s\"\n", i, refname,
++ o, n, flags, type, msg);
+}
+
+static void print_transaction(struct ref_transaction *transaction)
+{
-+ printf("transaction {\n");
++ fprintf(stderr, "transaction {\n");
+ for (int i = 0; i < transaction->nr; i++) {
+ struct ref_update *u = transaction->updates[i];
+ print_update(i, u->refname, &u->old_oid, &u->new_oid, u->flags,
+ u->type, u->msg);
+ }
-+ printf("}\n");
++ fprintf(stderr, "}\n");
+}
+
+static int debug_transaction_finish(struct ref_store *refs,
@@ refs/debug.c (new)
+ res = drefs->refs->be->transaction_finish(drefs->refs, transaction,
+ err);
+ print_transaction(transaction);
-+ printf("finish: %d\n", res);
++ fprintf(stderr, "finish: %d\n", res);
+ return res;
+}
+
@@ refs/debug.c (new)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res = drefs->refs->be->pack_refs(drefs->refs, flags);
++ fprintf(stderr, "pack_refs: %d\n", res);
+ return res;
+}
+
@@ refs/debug.c (new)
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res = drefs->refs->be->create_symref(drefs->refs, ref_name, target,
+ logmsg);
-+ printf("create_symref: %s -> %s \"%s\": %d\n", ref_name, target, logmsg,
-+ res);
++ fprintf(stderr, "create_symref: %s -> %s \"%s\": %d\n", ref_name,
++ target, logmsg, res);
+ return res;
+}
+
@@ refs/debug.c (new)
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res = drefs->refs->be->rename_ref(drefs->refs, oldref, newref,
+ logmsg);
-+ printf("rename_ref: %s -> %s \"%s\": %d\n", oldref, newref, logmsg,
-+ res);
++ fprintf(stderr, "rename_ref: %s -> %s \"%s\": %d\n", oldref, newref,
++ logmsg, res);
+ return res;
+}
+
@@ refs/debug.c (new)
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res =
+ drefs->refs->be->copy_ref(drefs->refs, oldref, newref, logmsg);
-+ printf("copy_ref: %s -> %s \"%s\": %d\n", oldref, newref, logmsg, res);
++ fprintf(stderr, "copy_ref: %s -> %s \"%s\": %d\n", oldref, newref,
++ logmsg, res);
+ return res;
+}
+
@@ refs/debug.c (new)
+ oid_to_hex_r(o, oid);
+ if (old_oid)
+ oid_to_hex_r(n, old_oid);
-+ printf("write_pseudoref: %s, %s => %s, err %s: %d\n", pseudoref, o, n,
-+ err->buf, res);
++ fprintf(stderr, "write_pseudoref: %s, %s => %s, err %s: %d\n",
++ pseudoref, o, n, err->buf, res);
+ return res;
+}
+
@@ refs/debug.c (new)
+ char hex[100] = "null";
+ if (old_oid)
+ oid_to_hex_r(hex, old_oid);
-+ printf("delete_pseudoref: %s (%s): %d\n", pseudoref, hex, res);
++ fprintf(stderr, "delete_pseudoref: %s (%s): %d\n", pseudoref, hex, res);
+ return res;
+}
+
++struct debug_ref_iterator {
++ struct ref_iterator base;
++ struct ref_iterator *iter;
++};
++
++static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator)
++{
++ struct debug_ref_iterator *diter =
++ (struct debug_ref_iterator *)ref_iterator;
++ int res = diter->iter->vtable->advance(diter->iter);
++ fprintf(stderr, "iterator_advance: %s: %d\n", diter->iter->refname,
++ res);
++ diter->base.ordered = diter->iter->ordered;
++ diter->base.refname = diter->iter->refname;
++ diter->base.oid = diter->iter->oid;
++ diter->base.flags = diter->iter->flags;
++ return res;
++}
++static int debug_ref_iterator_peel(struct ref_iterator *ref_iterator,
++ struct object_id *peeled)
++{
++ struct debug_ref_iterator *diter =
++ (struct debug_ref_iterator *)ref_iterator;
++ int res = diter->iter->vtable->peel(diter->iter, peeled);
++ fprintf(stderr, "iterator_peel: %s: %d\n", diter->iter->refname, res);
++ return res;
++}
++
++static int debug_ref_iterator_abort(struct ref_iterator *ref_iterator)
++{
++ struct debug_ref_iterator *diter =
++ (struct debug_ref_iterator *)ref_iterator;
++ int res = diter->iter->vtable->abort(diter->iter);
++ fprintf(stderr, "iterator_abort: %d\n", res);
++ return res;
++}
++
++static struct ref_iterator_vtable debug_ref_iterator_vtable = {
++ debug_ref_iterator_advance, debug_ref_iterator_peel,
++ debug_ref_iterator_abort
++};
++
+static struct ref_iterator *
+debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
+ unsigned int flags)
@@ refs/debug.c (new)
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ struct ref_iterator *res =
+ drefs->refs->be->iterator_begin(drefs->refs, prefix, flags);
-+ return res;
++ struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter));
++ base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable, 1);
++ diter->iter = res;
++ fprintf(stderr, "ref_iterator_begin: %s (0x%x)\n", prefix, flags);
++ return &diter->base;
+}
+
+static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname,
@@ refs/debug.c (new)
+ type);
+
+ if (res == 0) {
-+ printf("read_raw_ref: %s: %s (=> %s) type %x: %d\n", refname,
-+ oid_to_hex(oid), referent->buf, *type, res);
++ fprintf(stderr, "read_raw_ref: %s: %s (=> %s) type %x: %d\n",
++ refname, oid_to_hex(oid), referent->buf, *type, res);
+ } else {
-+ printf("read_raw_ref: %s err %d\n", refname, res);
++ fprintf(stderr, "read_raw_ref: %s err %d\n", refname, res);
+ }
+ return res;
+}
@@ refs/debug.c (new)
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ struct ref_iterator *res =
+ drefs->refs->be->reflog_iterator_begin(drefs->refs);
-+ printf("for_each_reflog_iterator_begin\n");
++ fprintf(stderr, "for_each_reflog_iterator_begin\n");
+ return res;
+}
+
@@ refs/debug.c (new)
+
+ ret = dbg->fn(old_oid, new_oid, committer, timestamp, tz, msg,
+ dbg->cb_data);
-+ printf("reflog_ent %s (ret %d): %s -> %s, %s %ld \"%s\"\n",
-+ dbg->refname, ret, o, n, committer, (long int)timestamp, msg);
++ fprintf(stderr, "reflog_ent %s (ret %d): %s -> %s, %s %ld \"%s\"\n",
++ dbg->refname, ret, o, n, committer, (long int)timestamp, msg);
+ return ret;
+}
+
@@ refs/debug.c (new)
+
+ int res = drefs->refs->be->for_each_reflog_ent(
+ drefs->refs, refname, &debug_print_reflog_ent, &dbg);
-+ printf("for_each_reflog: %s: %d\n", refname, res);
++ fprintf(stderr, "for_each_reflog: %s: %d\n", refname, res);
+ return res;
+}
+
@@ refs/debug.c (new)
+ };
+ int res = drefs->refs->be->for_each_reflog_ent_reverse(
+ drefs->refs, refname, &debug_print_reflog_ent, &dbg);
-+ printf("for_each_reflog_reverse: %s: %d\n", refname, res);
++ fprintf(stderr, "for_each_reflog_reverse: %s: %d\n", refname, res);
+ return res;
+}
+
@@ refs/debug.c (new)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res = drefs->refs->be->reflog_exists(drefs->refs, refname);
-+ printf("reflog_exists: %s: %d\n", refname, res);
++ fprintf(stderr, "reflog_exists: %s: %d\n", refname, res);
+ return res;
+}
+
@@ refs/debug.c (new)
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res = drefs->refs->be->create_reflog(drefs->refs, refname,
+ force_create, err);
++ fprintf(stderr, "create_reflog: %s: %d\n", refname, res);
+ return res;
+}
+
@@ refs/debug.c (new)
+{
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res = drefs->refs->be->delete_reflog(drefs->refs, refname);
++ fprintf(stderr, "delete_reflog: %s: %d\n", refname, res);
+ return res;
+}
+
@@ refs/debug.c (new)
+ flags, prepare_fn,
+ should_prune_fn, cleanup_fn,
+ policy_cb_data);
++ fprintf(stderr, "reflog_expire: %s: %d\n", refname, res);
+ return res;
+}
+
15: 1c0cc646084 = 16: 5211c643104 vcxproj: adjust for the reftable changes
-: ----------- > 17: 9724854088c git-prompt: prepare for reftable refs backend
16: 4f24b5f73de ! 18: ece1fa1f625 Add reftable testing infrastructure @@ Commit message
* Skip some tests that are incompatible:
* t3210-pack-refs.sh - does not apply
- * t9903-bash-prompt - The bash mode reads .git/HEAD directly
* t1450-fsck.sh - manipulates .git/ directly to create invalid state
Major test failures:
@@ Commit message
- ## builtin/clone.c ##
-@@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)
- }
-
- init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN,
-- DEFAULT_REF_STORAGE, INIT_DB_QUIET);
-+ default_ref_storage(), INIT_DB_QUIET);
-
- if (real_git_dir)
- git_dir = real_git_dir;
-
- ## refs.c ##
-@@ refs.c: struct ref_store *get_main_ref_store(struct repository *r)
- r->refs_private = ref_store_init(r->gitdir,
- r->ref_storage_format ?
- r->ref_storage_format :
-- DEFAULT_REF_STORAGE,
-+ default_ref_storage(),
- REF_STORE_ALL_CAPS);
- if (getenv("GIT_DEBUG_REFS")) {
- r->refs_private = debug_wrap(r->refs_private);
-@@ refs.c: struct ref_store *get_submodule_ref_store(const char *submodule)
- goto done;
-
- /* assume that add_submodule_odb() has been called */
-- refs = ref_store_init(submodule_sb.buf, DEFAULT_REF_STORAGE, /* XXX */
-+ refs = ref_store_init(submodule_sb.buf, default_ref_storage(),
- REF_STORE_READ | REF_STORE_ODB);
- register_ref_store_map(&submodule_ref_stores, "submodule",
- refs, submodule);
-@@ refs.c: struct ref_store *get_submodule_ref_store(const char *submodule)
-
- struct ref_store *get_worktree_ref_store(const struct worktree *wt)
- {
-- const char *format = DEFAULT_REF_STORAGE; /* XXX */
-+ const char *format = default_ref_storage();
- struct ref_store *refs;
- const char *id;
-
-
## t/t1409-avoid-packing-refs.sh ##
@@ t/t1409-avoid-packing-refs.sh: test_description='avoid rewriting packed-refs unnecessarily'
@@ t/t3210-pack-refs.sh: semantic is still the same.
git config core.logallrefupdates true
'
- ## t/t9903-bash-prompt.sh ##
-@@ t/t9903-bash-prompt.sh: test_description='test git-specific bash prompt functions' -
- . ./lib-bash.sh
-
-+if test_have_prereq REFTABLE
-+then
-+ skip_all='skipping tests; incompatible with reftable'
-+ test_done
-+fi
-+
- . "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
-
- actual="$TRASH_DIRECTORY/actual"
-
## t/test-lib.sh ##
@@ t/test-lib.sh: parisc* | hppa*)
;;
17: ad5658ffc51 = 19: 991abf9e1b2 Add "test-tool dump-reftable" command.
Signature · 1 line
gitgitgadget
PATCH v18 1/19 lib-t6000.sh: write tag using git-update-ref
From: Han-Wen Nienhuys <[email protected]>
--- t/lib-t6000.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
t/lib-t6000.sh +2 -3
@@ -1,7 +1,5 @@ : included from 6002 and others -mkdir -p .git/refs/tags - >sed.script # Answer the sha1 has associated with the tag. The tag must exist under refs/tags
@@ -26,7 +24,8 @@ save_tag () { _tag=$1 test -n "$_tag" || error "usage: save_tag tag commit-args ..." shift 1 - "$@" >".git/refs/tags/$_tag" + + git update-ref "refs/tags/$_tag" $("$@") echo "s/$(tag $_tag)/$_tag/g" >sed.script.tmp cat sed.script >>sed.script.tmp
Signature · 1 line
gitgitgadget
PATCH v18 2/19 checkout: add '\n' to reflog message
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
Reftable precisely reproduces the given message. This leads to differences, because the files backend implicitly adds a trailing '\n' to all messages.
--- builtin/checkout.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
builtin/checkout.c +3 -2
@@ -884,8 +884,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts, reflog_msg =getenv ("GIT_REFLOG_ACTION" );if (!reflog_msg) -strbuf_addf (&msg,"checkout: moving from %s to %s" , - old_desc ? old_desc :"(invalid)" , new_branch_info->name); +strbuf_addf (&msg,"checkout: moving from %s to %s\n" , + old_desc ? old_desc :"(invalid)" , + new_branch_info->name);else strbuf_insertstr (&msg,0 , reflog_msg);
Signature · 1 line
gitgitgadget
PATCH v18 3/19 Write pseudorefs through ref backends.
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
Pseudorefs store transient data in in the repository. Examples are HEAD, CHERRY_PICK_HEAD, etc.
These refs have always been read through the ref backends, but they were written in a one-off routine that wrote an object ID or symref directly into .git/<pseudo_ref_name>.
This causes problems when introducing a new ref storage backend. To remedy this, extend the ref backend implementation with a write_pseudoref_fn and update_pseudoref_fn.
--- refs.c | 119 ++++++++---------------------------------- refs.h | 11 ++++ refs/files-backend.c | 114 +++++++++++++++++++++++++++++++++++++++- refs/packed-backend.c | 21 +++++++- refs/refs-internal.h | 18 +++++++ 5 files changed, 184 insertions(+), 99 deletions(-)
refs.c +22 -97
@@ -321,6 +321,12 @@ int ref_exists(const char *refname)return refs_ref_exists (get_main_ref_store (the_repository), refname); } +int delete_pseudoref (const char *pseudoref,const struct object_id *old_oid) +{ +return refs_delete_pseudoref (get_main_ref_store (the_repository), + pseudoref, old_oid); +} +static int filter_refs (const char *refname,const struct object_id *oid,int flags,void *data) {
@@ -739,101 +745,6 @@ long get_files_ref_lock_timeout_ms(void)return timeout_ms; } -static int write_pseudoref (const char *pseudoref,const struct object_id *oid, -const struct object_id *old_oid,struct strbuf *err) -{ -const char *filename; -int fd; -struct lock_file lock =LOCK_INIT ; -struct strbuf buf =STRBUF_INIT ; -int ret =-1 ; - -if (!oid) -return 0 ; - -strbuf_addf (&buf,"%s\n" ,oid_to_hex (oid)); - - filename =git_path ("%s" , pseudoref); - fd =hold_lock_file_for_update_timeout (&lock, filename,0 , -get_files_ref_lock_timeout_ms ()); -if (fd <0 ) { -strbuf_addf (err,_ ("could not open '%s' for writing: %s" ), - filename,strerror (errno)); - goto done; - } - -if (old_oid) { -struct object_id actual_old_oid; - -if (read_ref (pseudoref, &actual_old_oid)) { -if (!is_null_oid (old_oid)) { -strbuf_addf (err,_ ("could not read ref '%s'" ), - pseudoref); -rollback_lock_file (&lock); - goto done; - } - }else if (is_null_oid (old_oid)) { -strbuf_addf (err,_ ("ref '%s' already exists" ), - pseudoref); -rollback_lock_file (&lock); - goto done; - }else if (!oideq (&actual_old_oid, old_oid)) { -strbuf_addf (err,_ ("unexpected object ID when writing '%s'" ), - pseudoref); -rollback_lock_file (&lock); - goto done; - } - } - -if (write_in_full (fd, buf.buf, buf.len) <0 ) { -strbuf_addf (err,_ ("could not write to '%s'" ), filename); -rollback_lock_file (&lock); - goto done; - } - -commit_lock_file (&lock); - ret =0 ; -done: -strbuf_release (&buf); -return ret; -} - -static int delete_pseudoref (const char *pseudoref,const struct object_id *old_oid) -{ -const char *filename; - - filename =git_path ("%s" , pseudoref); - -if (old_oid && !is_null_oid (old_oid)) { -struct lock_file lock =LOCK_INIT ; -int fd; -struct object_id actual_old_oid; - - fd =hold_lock_file_for_update_timeout ( - &lock, filename,0 , -get_files_ref_lock_timeout_ms ()); -if (fd <0 ) { -error_errno (_ ("could not open '%s' for writing" ), - filename); -return -1 ; - } -if (read_ref (pseudoref, &actual_old_oid)) -die (_ ("could not read ref '%s'" ), pseudoref); -if (!oideq (&actual_old_oid, old_oid)) { -error (_ ("unexpected object ID when deleting '%s'" ), - pseudoref); -rollback_lock_file (&lock); -return -1 ; - } - -unlink (filename); -rollback_lock_file (&lock); - }else { -unlink (filename); - } - -return 0 ; -} int refs_delete_ref(struct ref_store *refs, const char *msg, const char *refname,
@@ -845,7 +756,7 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,if (ref_type (refname) ==REF_TYPE_PSEUDOREF ) {assert (refs ==get_main_ref_store (the_repository)); -return delete_pseudoref (refname, old_oid); +return refs_delete_pseudoref (refs, refname, old_oid); } transaction =ref_store_transaction_begin (refs, &err);
@@ -1172,7 +1083,8 @@ int refs_update_ref(struct ref_store *refs, const char *msg,if (ref_type (refname) ==REF_TYPE_PSEUDOREF ) {assert (refs ==get_main_ref_store (the_repository)); - ret =write_pseudoref (refname, new_oid, old_oid, &err); + ret =refs_write_pseudoref (refs, refname, new_oid, old_oid, + &err); }else { t =ref_store_transaction_begin (refs, &err); if (!t ||
@@ -1441,6 +1353,19 @@ int head_ref(each_ref_fn fn, void *cb_data)return refs_head_ref (get_main_ref_store (the_repository), fn, cb_data); } +int refs_write_pseudoref (struct ref_store *refs,const char *pseudoref, +const struct object_id *oid, +const struct object_id *old_oid,struct strbuf *err) +{ +return refs->be->write_pseudoref (refs, pseudoref, oid, old_oid, err); +} + +int refs_delete_pseudoref (struct ref_store *refs,const char *pseudoref, +const struct object_id *old_oid) +{ +return refs->be->delete_pseudoref (refs, pseudoref, old_oid); +} +struct ref_iterator *refs_ref_iterator_begin (struct ref_store *refs,const char *prefix,int trim,int flags)
refs.h +11 -0
@@ -732,6 +732,17 @@ int update_ref(const char *msg, const char *refname, const struct object_id *new_oid, const struct object_id *old_oid, unsigned int flags, enum action_on_err onerr); +/* Pseudorefs (eg. HEAD, CHERRY_PICK_HEAD) have a separate routines for updating +and deletion as they cannot take part in normal transactional updates. +Pseudorefs should only be written for the main repository. +*/ +int refs_write_pseudoref (struct ref_store *refs,const char *pseudoref, +const struct object_id *oid, +const struct object_id *old_oid,struct strbuf *err); +int refs_delete_pseudoref (struct ref_store *refs,const char *pseudoref, +const struct object_id *old_oid); +int delete_pseudoref (const char *pseudoref,const struct object_id *old_oid); +int parse_hide_refs_config (const char *var,const char *value,const char *);/*
refs/files-backend.c +113 -1
130 lines not on this page · raw message
refs/packed-backend.c +20 -1
@@ -1590,6 +1590,22 @@ static int packed_copy_ref(struct ref_store *ref_store,BUG ("packed reference store does not support copying references" ); } +static int packed_write_pseudoref (struct ref_store *ref_store, +const char *pseudoref, +const struct object_id *oid, +const struct object_id *old_oid, +struct strbuf *err) +{ +BUG ("packed reference store does not support writing pseudo-references" ); +} + +static int packed_delete_pseudoref (struct ref_store *ref_store, +const char *pseudoref, +const struct object_id *old_oid) +{ +BUG ("packed reference store does not support deleting pseudo-references" ); +} +static struct ref_iterator *packed_reflog_iterator_begin (struct ref_store *ref_store) {return empty_ref_iterator_begin ();
@@ -1656,6 +1672,9 @@ struct ref_storage_be refs_be_packed = { packed_rename_ref, packed_copy_ref, + packed_write_pseudoref, + packed_delete_pseudoref, + packed_ref_iterator_begin, packed_read_raw_ref,
@@ -1665,5 +1684,5 @@ struct ref_storage_be refs_be_packed = { packed_reflog_exists, packed_create_reflog, packed_delete_reflog, - packed_reflog_expire + packed_reflog_expire, };
refs/refs-internal.h +18 -0
@@ -556,6 +556,21 @@ typedef int copy_ref_fn(struct ref_store *ref_store, const char *oldref, const char *newref, const char *logmsg); +typedef int write_pseudoref_fn (struct ref_store *ref_store, +const char *pseudoref, +const struct object_id *oid, +const struct object_id *old_oid, +struct strbuf *err); + +/* +* Deletes a pseudoref. Deletion always succeeds (even if the pseudoref doesn't +* exist.), except if old_oid is specified. If it is, it can fail due to lock +* failure, failure reading the old OID, or an OID mismatch +*/ +typedef int delete_pseudoref_fn (struct ref_store *ref_store, +const char *pseudoref, +const struct object_id *old_oid); +/* * Iterate over the references in `ref_store` whose names start with * `prefix`. `prefix` is matched as a literal string, without regard
@@ -655,6 +670,9 @@ struct ref_storage_be {rename_ref_fn *rename_ref;copy_ref_fn *copy_ref; +write_pseudoref_fn *write_pseudoref; +delete_pseudoref_fn *delete_pseudoref; +ref_iterator_begin_fn *iterator_begin;read_raw_ref_fn *read_raw_ref;
Signature · 1 line
gitgitgadget
PATCH v18 4/19 Make refs_ref_exists public
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- refs.c | 2 +- refs.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-)
refs.c +1 -1
@@ -311,7 +311,7 @@ int read_ref(const char *refname, struct object_id *oid)return read_ref_full (refname,RESOLVE_REF_READING , oid,NULL ); } -static int refs_ref_exists (struct ref_store *refs,const char *refname) +int refs_ref_exists (struct ref_store *refs,const char *refname) {return !!refs_resolve_ref_unsafe (refs, refname,RESOLVE_REF_READING ,NULL ,NULL ); }
refs.h +2 -0
@@ -105,6 +105,8 @@ int refs_verify_refname_available(struct ref_store *refs,const struct string_list *skip, struct strbuf *err); +int refs_ref_exists (struct ref_store *refs,const char *refname); +int ref_exists (const char *refname);int should_autocreate_reflog (const char *refname);
Signature · 1 line
gitgitgadget
PATCH v18 5/19 Treat BISECT_HEAD as a pseudo ref
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
Both the git-bisect.sh as bisect--helper inspected the file system directly.
--- builtin/bisect--helper.c | 3 +-- git-bisect.sh | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-)
builtin/bisect--helper.c +1 -2
@@ -13,7 +13,6 @@ static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS") static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV") static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK") static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START") -static GIT_PATH_FUNC(git_path_bisect_head, "BISECT_HEAD") static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG") static GIT_PATH_FUNC(git_path_head_name, "head-name") static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
@@ -164,7 +163,7 @@ static int bisect_reset(const char *commit)strbuf_addstr (&branch, commit); } -if (!file_exists (git_path_bisect_head ())) { +if (!ref_exists ("BISECT_HEAD" )) {struct argv_array argv =ARGV_ARRAY_INIT ;argv_array_pushl (&argv,"checkout" , branch.buf,"--" ,NULL );
git-bisect.sh +2 -2
@@ -41,7 +41,7 @@ TERM_GOOD=good bisect_head() { - if test -f "$GIT_DIR/BISECT_HEAD" + if git rev-parse --verify -q BISECT_HEAD > /dev/null then echo BISECT_HEAD else
@@ -153,7 +153,7 @@ bisect_next() { git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD $TERM_GOOD|| exit # Perform all bisection computation, display and checkout - git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout) + git bisect--helper --next-all $(git rev-parse --verify -q BISECT_HEAD > /dev/null && echo --no-checkout) res=$? # Check if we should exit because bisection is finished
Signature · 1 line
gitgitgadget
PATCH v18 6/19 Treat CHERRY_PICK_HEAD as a pseudo ref
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
Check for existence and delete CHERRY_PICK_HEAD through pseudo ref functions. This will help cherry-pick work with alternate ref storage backends.
--- builtin/commit.c | 34 +++++++++++++++++++--------------- builtin/merge.c | 2 +- path.c | 1 - path.h | 7 ++++--- sequencer.c | 42 ++++++++++++++++++++++++++---------------- wt-status.c | 4 ++-- 6 files changed, 52 insertions(+), 38 deletions(-)
builtin/commit.c +19 -15
@@ -847,21 +847,25 @@ static int prepare_to_commit(const char *index_file, const char *prefix,if (cleanup_mode ==COMMIT_MSG_CLEANUP_SCISSORS && !merge_contains_scissors)wt_status_add_cut_line (s->fp); -status_printf_ln (s,GIT_COLOR_NORMAL , - whence ==FROM_MERGE - ?_ ("\n" -"It looks like you may be committing a merge.\n" -"If this is not correct, please remove the file\n" -" %s\n" -"and try again.\n" ) - :_ ("\n" -"It looks like you may be committing a cherry-pick.\n" -"If this is not correct, please remove the file\n" -" %s\n" -"and try again.\n" ), - whence ==FROM_MERGE ? -git_path_merge_head (the_repository) : -git_path_cherry_pick_head (the_repository)); +if (whence ==FROM_MERGE ) +status_printf_ln ( + s,GIT_COLOR_NORMAL , + +_ ("\n" +"It looks like you may be committing a merge.\n" +"If this is not correct, please remove the file\n" +" %s\n" +"and try again.\n" ), +git_path_merge_head (the_repository)); +else +status_printf_ln ( + s,GIT_COLOR_NORMAL , + +_ ("\n" +"It looks like you may be committing a cherry-pick.\n" +"If this is not correct, please run\n" +" git cherry-pick --abort\n" +"and try again.\n" )); }fprintf (s->fp,"\n" );
builtin/merge.c +1 -1
@@ -1352,7 +1352,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)else die (_ ("You have not concluded your merge (MERGE_HEAD exists)." )); } -if (file_exists (git_path_cherry_pick_head (the_repository))) { +if (ref_exists ("CHERRY_PICK_HEAD" )) {if (advice_resolve_conflict)die (_ ("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n" "Please, commit your changes before you merge." ));
path.c +0 -1
@@ -1528,7 +1528,6 @@ char *xdg_cache_home(const char *filename)return NULL ; } -REPO_GIT_PATH_FUNC(cherry_pick_head, "CHERRY_PICK_HEAD") REPO_GIT_PATH_FUNC(revert_head, "REVERT_HEAD") REPO_GIT_PATH_FUNC(squash_msg, "SQUASH_MSG")REPO_GIT_PATH_FUNC (merge_msg,"MERGE_MSG" )
path.h +4 -3
@@ -170,7 +170,6 @@ void report_linked_checkout_garbage(void); }struct path_cache { -const char *cherry_pick_head;const char *revert_head;const char *squash_msg;const char *merge_msg;
@@ -182,9 +181,11 @@ struct path_cache {const char *shallow; }; -#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +#define PATH_CACHE_INIT \ + { \ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL \ + } -const char *git_path_cherry_pick_head (struct repository *r);const char *git_path_revert_head (struct repository *r);const char *git_path_squash_msg (struct repository *r);const char *git_path_merge_msg (struct repository *r);
sequencer.c +26 -16
114 lines not on this page · raw message
wt-status.c +2 -2
@@ -1636,8 +1636,8 @@ void wt_status_get_state(struct repository *r, state->merge_in_progress =1 ; }else if (wt_status_check_rebase (NULL , state)) { ;/* all set */ - }else if (!stat (git_path_cherry_pick_head (r), &st) && - !get_oid ("CHERRY_PICK_HEAD" , &oid)) { + }else if (refs_ref_exists (get_main_ref_store (r),"CHERRY_PICK_HEAD" ) && + !get_oid ("CHERRY_PICK_HEAD" , &oid)) { state->cherry_pick_in_progress =1 ;oidcpy (&state->cherry_pick_head_oid, &oid); }
Signature · 1 line
gitgitgadget
PATCH v18 7/19 Treat REVERT_HEAD as a pseudo ref
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- path.c | 1 - path.h | 8 +++----- sequencer.c | 16 +++++++++------- wt-status.c | 2 +- 4 files changed, 13 insertions(+), 14 deletions(-)
path.c +0 -1
@@ -1528,7 +1528,6 @@ char *xdg_cache_home(const char *filename)return NULL ; } -REPO_GIT_PATH_FUNC(revert_head, "REVERT_HEAD") REPO_GIT_PATH_FUNC(squash_msg, "SQUASH_MSG") REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")REPO_GIT_PATH_FUNC (merge_rr,"MERGE_RR" )
path.h +3 -5
@@ -170,7 +170,6 @@ void report_linked_checkout_garbage(void); }struct path_cache { -const char *revert_head;const char *squash_msg;const char *merge_msg;const char *merge_rr;
@@ -181,12 +180,11 @@ struct path_cache {const char *shallow; }; -#define PATH_CACHE_INIT \ - { \ - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL \ +#define PATH_CACHE_INIT \ + { \ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL \ } -const char *git_path_revert_head (struct repository *r);const char *git_path_squash_msg (struct repository *r);const char *git_path_merge_msg (struct repository *r);const char *git_path_merge_rr (struct repository *r);
sequencer.c +9 -7
@@ -2317,8 +2317,10 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose) need_cleanup =1 ; } -if (file_exists (git_path_revert_head (r))) { -if (!unlink (git_path_revert_head (r)) && verbose) +if (refs_ref_exists (get_main_ref_store (r),"REVERT_HEAD" )) { +if (!refs_delete_pseudoref (get_main_ref_store (r),"REVERT_HEAD" , +NULL ) && + verbose)warning (_ ("cancelling a revert in progress" )); opts.action =REPLAY_REVERT ; need_cleanup =1 ;
@@ -2677,7 +2679,7 @@ static int create_seq_dir(struct repository *r)const char *in_progress_error =NULL ;const char *in_progress_advice =NULL ;unsigned int advise_skip = -file_exists (git_path_revert_head (r)) || +refs_ref_exists (get_main_ref_store (r),"REVERT_HEAD" ) ||refs_ref_exists (get_main_ref_store (r),"CHERRY_PICK_HEAD" );if (!sequencer_get_last_command (r, &action)) {
@@ -2778,7 +2780,7 @@ static int rollback_single_pick(struct repository *r)struct object_id head_oid;if (!refs_ref_exists (get_main_ref_store (r),"CHERRY_PICK_HEAD" ) && - !file_exists (git_path_revert_head (r))) + !refs_ref_exists (get_main_ref_store (r),"REVERT_HEAD" ))return error (_ ("no cherry-pick or revert in progress" ));if (read_ref_full ("HEAD" ,0 , &head_oid,NULL ))return error (_ ("cannot resolve HEAD" ));
@@ -2872,7 +2874,7 @@ int sequencer_skip(struct repository *r, struct replay_opts *opts)*/ switch (opts->action) {case REPLAY_REVERT : -if (!file_exists (git_path_revert_head (r))) { +if (!refs_ref_exists (get_main_ref_store (r),"REVERT_HEAD" )) {if (action !=REPLAY_REVERT )return error (_ ("no revert in progress" ));if (!rollback_is_safe ())
@@ -4210,7 +4212,7 @@ static int continue_single_pick(struct repository *r)const char *argv[] = {"commit" ,NULL };if (!refs_ref_exists (get_main_ref_store (r),"CHERRY_PICK_HEAD" ) && - !file_exists (git_path_revert_head (r))) + !refs_ref_exists (get_main_ref_store (r),"REVERT_HEAD" ))return error (_ ("no cherry-pick or revert in progress" ));return run_command_v_opt (argv,RUN_GIT_CMD ); }
@@ -4390,7 +4392,7 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)/* Verify that the conflict has been resolved */ if (refs_ref_exists (get_main_ref_store (r),"CHERRY_PICK_HEAD" ) || -file_exists (git_path_revert_head (r))) { +refs_ref_exists (get_main_ref_store (r),"REVERT_HEAD" )) { res =continue_single_pick (r);if (res) goto release_todo_list;
wt-status.c +1 -1
@@ -1642,7 +1642,7 @@ void wt_status_get_state(struct repository *r,oidcpy (&state->cherry_pick_head_oid, &oid); }wt_status_check_bisect (NULL , state); -if (!stat (git_path_revert_head (r), &st) && +if (refs_ref_exists (get_main_ref_store (r),"REVERT_HEAD" ) && !get_oid ("REVERT_HEAD" , &oid)) { state->revert_in_progress =1 ;oidcpy (&state->revert_head_oid, &oid);
Signature · 1 line
gitgitgadget
PATCH v18 8/19 Move REF_LOG_ONLY to refs-internal.h
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
REF_LOG_ONLY is used in the transaction preparation: if a symref is involved in a transaction, the referent of the symref should be updated, and the symref itself should only be updated in the reflog. Other ref backends will need to duplicate this logic.
--- refs/files-backend.c | 7 ------- refs/refs-internal.h | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-)
refs/files-backend.c +0 -7
@@ -38,13 +38,6 @@*/ #define REF_NEEDS_COMMIT (1 << 6) -/* -* Used as a flag in ref_update::flags when we want to log a ref -* update but not actually perform it. This is used when a symbolic -* ref update is split up. -*/ -#define REF_LOG_ONLY (1 << 7) -/* * Used as a flag in ref_update::flags when the ref_update was via an * update to HEAD.
refs/refs-internal.h +7 -0
@@ -31,6 +31,13 @@ struct ref_transaction;*/ #define REF_HAVE_OLD (1 << 3) +/* +* Used as a flag in ref_update::flags when we want to log a ref +* update but not actually perform it. This is used when a symbolic +* ref update is split up. +*/ +#define REF_LOG_ONLY (1 << 7) +/* * Return the length of time to retry acquiring a loose reference lock * before giving up, in milliseconds:
Signature · 1 line
gitgitgadget
PATCH v18 9/19 Iterate over the "refs/" namespace in for_each_[raw]ref
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This happens implicitly in the files/packed ref backend; making it explicit simplifies adding alternate ref storage backends, such as reftable.
--- refs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
refs.c +3 -3
@@ -1450,7 +1450,7 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix,int refs_for_each_ref (struct ref_store *refs,each_ref_fn fn,void *cb_data) { -return do_for_each_ref (refs,"" , fn,0 ,0 , cb_data); +return do_for_each_ref (refs,"refs/" , fn,0 ,0 , cb_data); }int for_each_ref (each_ref_fn fn,void *cb_data)
@@ -1510,8 +1510,8 @@ int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)int refs_for_each_rawref (struct ref_store *refs,each_ref_fn fn,void *cb_data) { -return do_for_each_ref (refs,"" , fn,0 , -DO_FOR_EACH_INCLUDE_BROKEN , cb_data); +return do_for_each_ref (refs,"refs/" , fn,0 ,DO_FOR_EACH_INCLUDE_BROKEN , + cb_data); }int for_each_rawref (each_ref_fn fn,void *cb_data)
Signature · 1 line
gitgitgadget
PATCH v18 10/19 Add .gitattributes for the reftable/ directory
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- reftable/.gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 reftable/.gitattributes
reftable/.gitattributes new file +1 -0
@@ -0,0 +1 @@ +/zlib-compat.c whitespace=-indent-with-non-tab,-trailing-space
Signature · 1 line
gitgitgadget
PATCH v18 11/19 Add reftable library
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
Reftable is a format for storing the ref database. The rationale and format layout is detailed in commit 35e6c47404 ("reftable: file format documentation", 2020-05-20).
This commits imports a fully functioning library for reading and writing reftables, along with unittests.
It is suggested to read the code in the following order:
* The specification under Documentation/technical/reftable.txt
* reftable.h - the public API
* record.{c,h} - reading and writing records
* block.{c,h} - reading and writing blocks.
* reader.{c,h} - reading a complete reftable file.
* writer.{c,h} - writing a complete reftable file.
* merged.{c,h} and pq.{c,h} - reading a stack of reftables
* stack.{c,h} - writing and compacting stacks of reftable on the filesystem.
--- reftable/LICENSE | 31 + reftable/VERSION | 1 + reftable/basics.c | 215 +++++++ reftable/basics.h | 53 ++ reftable/block.c | 432 +++++++++++++ reftable/block.h | 129 ++++ reftable/block_test.c | 157 +++++ reftable/compat.c | 98 +++ reftable/compat.h | 48 ++ reftable/constants.h | 21 + reftable/dump.c | 212 +++++++ reftable/file.c | 95 +++ reftable/iter.c | 242 ++++++++ reftable/iter.h | 72 +++ reftable/merged.c | 321 ++++++++++ reftable/merged.h | 39 ++ reftable/merged_test.c | 273 +++++++++ reftable/pq.c | 115 ++++ reftable/pq.h | 34 ++ reftable/reader.c | 744 +++++++++++++++++++++++ reftable/reader.h | 65 ++ reftable/record.c | 1126 ++++++++++++++++++++++++++++++++++ reftable/record.h | 143 +++++ reftable/record_test.c | 410 +++++++++++++ reftable/refname.c | 209 +++++++ reftable/refname.h | 38 ++ reftable/refname_test.c | 99 +++ reftable/reftable-tests.h | 22 + reftable/reftable.c | 90 +++ reftable/reftable.h | 571 ++++++++++++++++++ reftable/reftable_test.c | 631 +++++++++++++++++++ reftable/stack.c | 1209 +++++++++++++++++++++++++++++++++++++ reftable/stack.h | 48 ++ reftable/stack_test.c | 787 ++++++++++++++++++++++++ reftable/strbuf.c | 206 +++++++ reftable/strbuf.h | 88 +++ reftable/strbuf_test.c | 39 ++ reftable/system.h | 53 ++ reftable/test_framework.c | 69 +++ reftable/test_framework.h | 64 ++ reftable/tree.c | 63 ++ reftable/tree.h | 34 ++ reftable/tree_test.c | 62 ++ reftable/update.sh | 22 + reftable/writer.c | 662 ++++++++++++++++++++ reftable/writer.h | 60 ++ reftable/zlib-compat.c | 92 +++ 47 files changed, 10294 insertions(+) create mode 100644 reftable/LICENSE create mode 100644 reftable/VERSION create mode 100644 reftable/basics.c create mode 100644 reftable/basics.h create mode 100644 reftable/block.c create mode 100644 reftable/block.h create mode 100644 reftable/block_test.c create mode 100644 reftable/compat.c create mode 100644 reftable/compat.h create mode 100644 reftable/constants.h create mode 100644 reftable/dump.c create mode 100644 reftable/file.c create mode 100644 reftable/iter.c create mode 100644 reftable/iter.h create mode 100644 reftable/merged.c create mode 100644 reftable/merged.h create mode 100644 reftable/merged_test.c create mode 100644 reftable/pq.c create mode 100644 reftable/pq.h create mode 100644 reftable/reader.c create mode 100644 reftable/reader.h create mode 100644 reftable/record.c create mode 100644 reftable/record.h create mode 100644 reftable/record_test.c create mode 100644 reftable/refname.c create mode 100644 reftable/refname.h create mode 100644 reftable/refname_test.c create mode 100644 reftable/reftable-tests.h create mode 100644 reftable/reftable.c create mode 100644 reftable/reftable.h create mode 100644 reftable/reftable_test.c create mode 100644 reftable/stack.c create mode 100644 reftable/stack.h create mode 100644 reftable/stack_test.c create mode 100644 reftable/strbuf.c create mode 100644 reftable/strbuf.h create mode 100644 reftable/strbuf_test.c create mode 100644 reftable/system.h create mode 100644 reftable/test_framework.c create mode 100644 reftable/test_framework.h create mode 100644 reftable/tree.c create mode 100644 reftable/tree.h create mode 100644 reftable/tree_test.c create mode 100755 reftable/update.sh create mode 100644 reftable/writer.c create mode 100644 reftable/writer.h create mode 100644 reftable/zlib-compat.c
reftable/LICENSE new file +31 -0
@@ -0,0 +1,31 @@ +BSD License + +Copyright (c) 2020, Google LLC +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +* Neither the name of Google LLC nor the names of its contributors may +be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
reftable/VERSION new file +1 -0
@@ -0,0 +1 @@ +d7472cbd1afe6bf3da53e94971b1cc79ce183fa8 Remove outdated bits of the README file
reftable/basics.c new file +215 -0
215 lines not on this page · raw message
reftable/basics.h new file +53 -0
53 lines not on this page · raw message
reftable/block.c new file +432 -0
432 lines not on this page · raw message
reftable/block.h new file +129 -0
129 lines not on this page · raw message
reftable/block_test.c new file +157 -0
157 lines not on this page · raw message
reftable/compat.c new file +98 -0
98 lines not on this page · raw message
reftable/compat.h new file +48 -0
48 lines not on this page · raw message
reftable/constants.h new file +21 -0
@@ -0,0 +1,21 @@ +/* +Copyright 2020 Google LLC + +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file or at +https://developers.google.com/open-source/licenses/bsd +*/ + +#ifndef CONSTANTS_H +#define CONSTANTS_H + +#define BLOCK_TYPE_LOG 'g' +#define BLOCK_TYPE_INDEX 'i' +#define BLOCK_TYPE_REF 'r' +#define BLOCK_TYPE_OBJ 'o' +#define BLOCK_TYPE_ANY 0 + +#define MAX_RESTARTS ((1 << 16) - 1) +#define DEFAULT_BLOCK_SIZE 4096 + +#endif
reftable/dump.c new file +212 -0
212 lines not on this page · raw message
reftable/file.c new file +95 -0
95 lines not on this page · raw message
reftable/iter.c new file +242 -0
242 lines not on this page · raw message
reftable/iter.h new file +72 -0
72 lines not on this page · raw message
reftable/merged.c new file +321 -0
321 lines not on this page · raw message
reftable/merged.h new file +39 -0
@@ -0,0 +1,39 @@ +/* +Copyright 2020 Google LLC + +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file or at +https://developers.google.com/open-source/licenses/bsd +*/ + +#ifndef MERGED_H +#define MERGED_H + +#include "pq.h" +#include "reftable.h" + +struct reftable_merged_table { +struct reftable_reader **stack; +int stack_len; +uint32_t hash_id; +bool suppress_deletions; + +uint64_t min; +uint64_t max; +}; + +struct merged_iter { +struct reftable_iterator *stack; +uint32_t hash_id; +int stack_len; +uint8_t typ; +bool suppress_deletions; +struct merged_iter_pqueue pq; +}; + +void merged_table_clear (struct reftable_merged_table *mt); +int merged_table_seek_record (struct reftable_merged_table *mt, +struct reftable_iterator *it, +struct reftable_record *rec); + +#endif
reftable/merged_test.c new file +273 -0
273 lines not on this page · raw message
reftable/pq.c new file +115 -0
115 lines not on this page · raw message
reftable/pq.h new file +34 -0
@@ -0,0 +1,34 @@ +/* +Copyright 2020 Google LLC + +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file or at +https://developers.google.com/open-source/licenses/bsd +*/ + +#ifndef PQ_H +#define PQ_H + +#include "record.h" + +struct pq_entry { +int index; +struct reftable_record rec; +}; + +int pq_less (struct pq_entry a,struct pq_entry b); + +struct merged_iter_pqueue { +struct pq_entry *heap; +int len; +int cap; +}; + +struct pq_entry merged_iter_pqueue_top (struct merged_iter_pqueue pq); +bool merged_iter_pqueue_is_empty (struct merged_iter_pqueue pq); +void merged_iter_pqueue_check (struct merged_iter_pqueue pq); +struct pq_entry merged_iter_pqueue_remove (struct merged_iter_pqueue *pq); +void merged_iter_pqueue_add (struct merged_iter_pqueue *pq,struct pq_entry e); +void merged_iter_pqueue_clear (struct merged_iter_pqueue *pq); + +#endif
reftable/reader.c new file +744 -0
412 lines not on this page · raw message
reftable/reader.h new file +65 -0
65 lines not on this page · raw message
reftable/record.c new file +1126 -0
1,126 lines not on this page · raw message
reftable/record.h new file +143 -0
143 lines not on this page · raw message
reftable/record_test.c new file +410 -0
410 lines not on this page · raw message
reftable/refname.c new file +209 -0
209 lines not on this page · raw message
reftable/refname_test.c new file +99 -0
99 lines not on this page · raw message
reftable/reftable.c new file +90 -0
90 lines not on this page · raw message
reftable/reftable.h new file +571 -0
571 lines not on this page · raw message
reftable/reftable_test.c new file +631 -0
631 lines not on this page · raw message
reftable/stack.c new file +1209 -0
1,209 lines not on this page · raw message
reftable/stack.h new file +48 -0
48 lines not on this page · raw message
reftable/stack_test.c new file +787 -0
787 lines not on this page · raw message
reftable/strbuf.c new file +206 -0
206 lines not on this page · raw message
reftable/strbuf.h new file +88 -0
88 lines not on this page · raw message
reftable/system.h new file +53 -0
53 lines not on this page · raw message
reftable/test_framework.c new file +69 -0
69 lines not on this page · raw message
reftable/test_framework.h new file +64 -0
64 lines not on this page · raw message
reftable/tree.c new file +63 -0
63 lines not on this page · raw message
reftable/tree_test.c new file +62 -0
62 lines not on this page · raw message
reftable/writer.c new file +662 -0
662 lines not on this page · raw message
reftable/writer.h new file +60 -0
60 lines not on this page · raw message
reftable/zlib-compat.c new file +92 -0
92 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
gitgitgadget
Part of the diff is not shown here. View the raw message.
PATCH v18 12/19 Add standalone build infrastructure for reftable
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
The reftable library is integrates with Git, so it become a first class supported storage mechanism, but is sufficiently complex that other projects (e.g. libgit2) may want to consume the same source code.
To make this possible, the library also compiles completely standalone, which is demonstrated with the Bazel BUILD/WORKSPACE files that this commit adds.
--- reftable/BUILD | 203 ++++++++++++++++++++++++++++++++++++++++++++ reftable/README.md | 33 +++++++ reftable/WORKSPACE | 14 +++ reftable/zlib.BUILD | 36 ++++++++ 4 files changed, 286 insertions(+) create mode 100644 reftable/BUILD create mode 100644 reftable/README.md create mode 100644 reftable/WORKSPACE create mode 100644 reftable/zlib.BUILD
reftable/BUILD new file +203 -0
@@ -0,0 +1,203 @@ +# Mirror core-git COPTS so reftable compiles without warning in CGit. +GIT_COPTS = [ + "-DREFTABLE_STANDALONE", + "-Wall", + "-Werror", + "-Wdeclaration-after-statement", + "-Wstrict-prototypes", + "-Wformat-security", + "-Wno-format-zero-length", + "-Wold-style-definition", + "-Woverflow", + "-Wpointer-arith", + "-Wstrict-prototypes", + "-Wunused", + "-Wvla", + "-Wextra", + "-Wmissing-prototypes", + "-Wno-empty-body", + "-Wno-missing-field-initializers", + "-Wno-sign-compare", + "-Werror=strict-aliasing", + "-Wno-unused-parameter", +] + +cc_library( + name = "reftable", + srcs = [ + "basics.c", + "block.c", + "compat.c", + "file.c", + "iter.c", + "merged.c", + "pq.c", + "reader.c", + "record.c", + "refname.c", + "reftable.c", + "strbuf.c", + "stack.c", + "tree.c", + "writer.c", + "zlib-compat.c", + "basics.h", + "block.h", + "compat.h", + "constants.h", + "iter.h", + "merged.h", + "pq.h", + "reader.h", + "refname.h", + "record.h", + "strbuf.h", + "stack.h", + "system.h", + "tree.h", + "writer.h", + ], + hdrs = [ + "reftable.h", + ], + includes = [ + "include", + ], + copts = [ + "-fvisibility=protected", + ] + GIT_COPTS, + deps = ["@zlib"], + visibility = ["//visibility:public"] +) + +cc_library( + name = "testlib", + srcs = [ + "test_framework.c", + "dump.c", + "test_framework.h", + ], + hdrs = [ + "reftable-tests.h", + ], + copts = GIT_COPTS, + deps = [":reftable"], + visibility = ["//visibility:public"] +) + +cc_test( + name = "record_test", + srcs = ["record_test.c"], + deps = [ + ":reftable", + ":testlib", + ], + copts = [ + "-Drecord_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +cc_test( + name = "reftable_test", + srcs = ["reftable_test.c"], + deps = [ + ":reftable", + ":testlib", + ], + copts = [ + "-Dreftable_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +cc_test( + name = "strbuf_test", + srcs = ["strbuf_test.c"], + deps = [ + ":reftable", + ":testlib", + ] , + copts = [ + "-Dstrbuf_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +cc_test( + name = "stack_test", + srcs = ["stack_test.c"], + deps = [ + ":reftable", + ":testlib", + ], + copts = [ + "-Dstack_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +cc_test( + name = "tree_test", + srcs = ["tree_test.c"], + deps = [ + ":reftable", + ":testlib", + ], + copts = [ + "-Dtree_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +cc_test( + name = "block_test", + srcs = ["block_test.c"], + deps = [ + ":reftable", + ":testlib", + ], + copts = [ + "-Dblock_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +cc_test( + name = "refname_test", + srcs = ["refname_test.c"], + deps = [ + ":reftable", + ":testlib", + ], + copts = [ + "-Drefname_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +cc_test( + name = "merged_test", + srcs = ["merged_test.c"], + deps = [ + ":reftable", + ":testlib", + ], + copts = [ + "-Dmerged_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +[sh_test( + name = "%s_valgrind_test" % t, + srcs = [ "valgrind_test.sh" ], + args = [ t ], + data = [ t ]) + for t in ["record_test", + "merged_test", + "refname_test", + "tree_test", + "block_test", + "strbuf_test", + "stack_test"]]
reftable/README.md new file +33 -0
@@ -0,0 +1,33 @@ +This directory contains an implementation of the reftable library. + +The library is integrated into the git-core project, but can also be built +standalone, by compiling with -DREFTABLE_STANDALONE. The standalone build is +exercised by the accompanying BUILD/WORKSPACE files, and can be run as + + bazel test :all + +It includes a fragment of the zlib library to provide uncompress2(), which is a +recent addition to the API. zlib is licensed as follows: + +``` + (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + [email protected] [email protected] +```
reftable/WORKSPACE new file +14 -0
@@ -0,0 +1,14 @@ +workspace(name = "reftable") + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "zlib", + build_file = "@//:zlib.BUILD", + sha256 = "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1", + strip_prefix = "zlib-1.2.11", + urls = [ + "https://mirror.bazel.build/zlib.net/zlib-1.2.11.tar.gz", + "https://zlib.net/zlib-1.2.11.tar.gz", + ], +)
reftable/zlib.BUILD new file +36 -0
@@ -0,0 +1,36 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # BSD/MIT-like license (for zlib) + +cc_library( + name = "zlib", + srcs = [ + "adler32.c", + "compress.c", + "crc32.c", + "crc32.h", + "deflate.c", + "deflate.h", + "gzclose.c", + "gzguts.h", + "gzlib.c", + "gzread.c", + "gzwrite.c", + "infback.c", + "inffast.c", + "inffast.h", + "inffixed.h", + "inflate.c", + "inflate.h", + "inftrees.c", + "inftrees.h", + "trees.c", + "trees.h", + "uncompr.c", + "zconf.h", + "zutil.c", + "zutil.h", + ], + hdrs = ["zlib.h"], + includes = ["."], +)
Signature · 1 line
gitgitgadget
PATCH v18 13/19 Reftable support for git-core
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
For background, see the previous commit introducing the library.
This introduces the refs/reftable-backend.c containing reftable powered ref storage backend.
It can be activated by passing --ref-storage=reftable to "git init".
Example use: see t/t0031-reftable.sh
--- .../technical/repository-version.txt | 7 + Makefile | 28 +- builtin/clone.c | 3 +- builtin/init-db.c | 56 +- cache.h | 6 +- refs.c | 27 +- refs.h | 3 + refs/refs-internal.h | 1 + refs/reftable-backend.c | 1335 +++++++++++++++++ reftable/update.sh | 3 - repository.c | 2 + repository.h | 3 + setup.c | 12 +- t/t0031-reftable.sh | 155 ++ 14 files changed, 1608 insertions(+), 33 deletions(-) create mode 100644 refs/reftable-backend.c create mode 100755 t/t0031-reftable.sh
Documentation/technical/repository-version.txt +7 -0
@@ -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.
Makefile +26 -2
@@ -807,6 +807,7 @@ TEST_SHELL_PATH = $(SHELL_PATH) LIB_FILE = libgit.a XDIFF_LIB = xdiff/lib.a VCSSVN_LIB = vcs-svn/lib.a +REFTABLE_LIB = reftable/libreftable.a GENERATED_H += config-list.h GENERATED_H += command-list.h
@@ -959,6 +960,7 @@ LIB_OBJS += ref-filter.o LIB_OBJS += reflog-walk.o LIB_OBJS += refs.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
@@ -1162,7 +1164,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)
@@ -2352,11 +2354,30 @@ VCSSVN_OBJS += vcs-svn/sliding_window.o VCSSVN_OBJS += vcs-svn/svndiff.o VCSSVN_OBJS += vcs-svn/svndump.o +REFTABLE_OBJS += reftable/basics.o +REFTABLE_OBJS += reftable/block.o +REFTABLE_OBJS += reftable/compat.o +REFTABLE_OBJS += reftable/file.o +REFTABLE_OBJS += reftable/iter.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/reftable.o +REFTABLE_OBJS += reftable/strbuf.o +REFTABLE_OBJS += reftable/stack.o +REFTABLE_OBJS += reftable/tree.o +REFTABLE_OBJS += reftable/writer.o +REFTABLE_OBJS += reftable/zlib-compat.o + + TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \ $(XDIFF_OBJS) \ $(VCSSVN_OBJS) \ $(FUZZ_OBJS) \ + $(REFTABLE_OBJS) \ common-main.o \ git.o ifndef NO_CURL
@@ -2497,6 +2518,9 @@ $(XDIFF_LIB): $(XDIFF_OBJS) $(VCSSVN_LIB): $(VCSSVN_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ +$(REFTABLE_LIB): $(REFTABLE_OBJS) + $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ + export DEFAULT_EDITOR DEFAULT_PAGER Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
@@ -3112,7 +3136,7 @@ cocciclean: clean: profile-clean coverage-clean cocciclean $(RM) *.res $(RM) $(OBJECTS) - $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) + $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) $(REFTABLE_LIB) $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X $(RM) $(TEST_PROGRAMS) $(RM) $(FUZZ_PROGRAMS)
builtin/clone.c +2 -1
@@ -1111,7 +1111,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } } -init_db (git_dir, real_git_dir, option_template,GIT_HASH_UNKNOWN ,INIT_DB_QUIET ); +init_db (git_dir, real_git_dir, option_template,GIT_HASH_UNKNOWN , +default_ref_storage (),INIT_DB_QUIET );if (real_git_dir) git_dir = real_git_dir;
builtin/init-db.c +42 -14
132 lines not on this page · raw message
cache.h +4 -2
@@ -628,8 +628,9 @@ int path_inside_repo(const char *prefix, const char *path);int init_db (const char *git_dir,const char *real_git_dir,const char *template_dir,int hash_algo, -unsigned int flags); -void initialize_repository_version (int hash_algo); +const char *ref_storage_format,unsigned int flags); +void initialize_repository_version (int hash_algo, +const char *ref_storage_format);void sanitize_stdfds (void );int daemonize (void );
@@ -1044,6 +1045,7 @@ struct repository_format {int hash_algo;int has_extensions;char *work_tree; +char *ref_storage;struct string_list unknown_extensions; };
refs.c +19 -8
@@ -17,10 +17,16 @@#include "argv-array.h" #include "repository.h" +const char *default_ref_storage (void ) +{ +const char *test =getenv ("GIT_TEST_REFTABLE" ); +return test ?"reftable" :"files" ; +} +/* * List of all available backends */ -static struct ref_storage_be *refs_backends = &refs_be_files; +static struct ref_storage_be *refs_backends = &refs_be_reftable;static struct ref_storage_be *find_ref_storage_backend (const char *name) {
@@ -1717,13 +1723,13 @@ static struct ref_store *lookup_ref_store_map(struct hashmap *map,* Create, record, and return a ref_store instance for the specified * gitdir. */ -static struct ref_store *ref_store_init (const char *gitdir, +static struct ref_store *ref_store_init (const char *gitdir,const char *be_name,unsigned int flags) { -const char *be_name ="files" ; -struct ref_storage_be *be =find_ref_storage_backend (be_name); +struct ref_storage_be *be;struct ref_store *refs; + be =find_ref_storage_backend (be_name);if (!be)BUG ("reference backend %s is unknown" , be_name);
@@ -1739,7 +1745,11 @@ struct ref_store *get_main_ref_store(struct repository *r)if (!r->gitdir)BUG ("attempting to get main_ref_store outside of repository" ); - r->refs_private =ref_store_init (r->gitdir,REF_STORE_ALL_CAPS ); + r->refs_private =ref_store_init (r->gitdir, + r->ref_storage_format ? + r->ref_storage_format : +default_ref_storage (), +REF_STORE_ALL_CAPS );return r->refs_private; }
@@ -1794,7 +1804,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule) goto done;/* assume that add_submodule_odb() has been called */ - refs =ref_store_init (submodule_sb.buf, + refs =ref_store_init (submodule_sb.buf,default_ref_storage (),REF_STORE_READ |REF_STORE_ODB );register_ref_store_map (&submodule_ref_stores,"submodule" , refs, submodule);
@@ -1808,6 +1818,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule)struct ref_store *get_worktree_ref_store (const struct worktree *wt) { +const char *format =default_ref_storage ();struct ref_store *refs;const char *id;
@@ -1821,9 +1832,9 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)if (wt->id) refs =ref_store_init (git_common_path ("worktrees/%s" , wt->id), -REF_STORE_ALL_CAPS ); + format,REF_STORE_ALL_CAPS );else - refs =ref_store_init (get_git_common_dir (), + refs =ref_store_init (get_git_common_dir (), format,REF_STORE_ALL_CAPS );if (refs)
refs.h +3 -0
@@ -9,6 +9,9 @@ struct string_list;struct string_list_item ;struct worktree ; +/* Returns the ref storage backend to use by default. */ +const char *default_ref_storage (void ); +/* * Resolve a reference, recursively following symbolic refererences. *
refs/refs-internal.h +1 -0
@@ -693,6 +693,7 @@ struct ref_storage_be { };extern struct ref_storage_be refs_be_files; +extern struct ref_storage_be refs_be_reftable;extern struct ref_storage_be refs_be_packed;/*
refs/reftable-backend.c new file +1335 -0
1,335 lines not on this page · raw message
reftable/update.sh +0 -3
@@ -16,7 +16,4 @@ cp reftable-repo/LICENSE reftable/ git --git-dir reftable-repo/.git show --no-patch --format=oneline HEAD \ > reftable/VERSION -mv reftable/system.h reftable/system.h~ -sed 's|if REFTABLE_IN_GITCORE|if 1 /* REFTABLE_IN_GITCORE */|' < reftable/system.h~ > reftable/system.h - git add reftable/*.[ch] reftable/LICENSE reftable/VERSION
repository.c +2 -0
@@ -178,6 +178,8 @@ int repo_init(struct repository *repo,if (worktree)repo_set_worktree (repo, worktree); + repo->ref_storage_format =xstrdup_or_null (format.ref_storage); +clear_repository_format (&format);return 0 ;
repository.h +3 -0
@@ -74,6 +74,9 @@ struct repository {*/ struct ref_store *refs_private; +/* The format to use for the ref database. */ +char *ref_storage_format; +/* * Contains path to often used file names. */
setup.c +9 -3
@@ -469,9 +469,11 @@ static int check_repo_format(const char *var, const char *value, void *vdata)if (!value)return config_error_nonbool (var); data->partial_clone =xstrdup (value); - }else if (!strcmp (ext,"worktreeconfig" )) + }else if (!strcmp (ext,"worktreeconfig" )) { data->worktree_config =git_config_bool (var, value); -else + }else if (!strcmp (ext,"refstorage" )) { + data->ref_storage =xstrdup (value); + }else string_list_append (&data->unknown_extensions, ext); }
@@ -594,6 +596,7 @@ void clear_repository_format(struct repository_format *format)string_list_clear (&format->unknown_extensions,0 );free (format->work_tree);free (format->partial_clone); +free (format->ref_storage);init_repository_format (format); }
@@ -1239,8 +1242,11 @@ const char *setup_git_directory_gently(int *nongit_ok) gitdir =DEFAULT_GIT_DIR_ENVIRONMENT ;setup_git_env (gitdir); } -if (startup_info->have_repository) +if (startup_info->have_repository) {repo_set_hash_algo (the_repository, repo_fmt.hash_algo); + the_repository->ref_storage_format = +xstrdup_or_null (repo_fmt.ref_storage); + } }strbuf_release (&dir);
t/t0031-reftable.sh new file +155 -0
155 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v18 14/19 Hookup unittests for the reftable library.
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
The unittests are under reftable/*_test.c, so all of the reftable code stays in one directory. They are called from t/helpers/test-reftable.c in t0031-reftable.sh
--- Makefile | 17 ++++++++++++++++- t/helper/test-reftable.c | 15 +++++++++++++++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + t/t0031-reftable.sh | 5 +++++ 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 t/helper/test-reftable.c
Makefile +16 -1
@@ -725,6 +725,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
@@ -808,6 +809,7 @@ LIB_FILE = libgit.a XDIFF_LIB = xdiff/lib.a VCSSVN_LIB = vcs-svn/lib.a REFTABLE_LIB = reftable/libreftable.a +REFTABLE_TEST_LIB = reftable/libreftable_test.a GENERATED_H += config-list.h GENERATED_H += command-list.h
@@ -2371,6 +2373,15 @@ REFTABLE_OBJS += reftable/tree.o REFTABLE_OBJS += reftable/writer.o REFTABLE_OBJS += reftable/zlib-compat.o +REFTABLE_TEST_OBJS += reftable/block_test.o +REFTABLE_TEST_OBJS += reftable/merged_test.o +REFTABLE_TEST_OBJS += reftable/record_test.o +REFTABLE_TEST_OBJS += reftable/refname_test.o +REFTABLE_TEST_OBJS += reftable/reftable_test.o +REFTABLE_TEST_OBJS += reftable/strbuf_test.o +REFTABLE_TEST_OBJS += reftable/stack_test.o +REFTABLE_TEST_OBJS += reftable/tree_test.o +REFTABLE_TEST_OBJS += reftable/test_framework.o TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
@@ -2378,6 +2389,7 @@ OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \ $(VCSSVN_OBJS) \ $(FUZZ_OBJS) \ $(REFTABLE_OBJS) \ + $(REFTABLE_TEST_OBJS) \ common-main.o \ git.o ifndef NO_CURL
@@ -2521,6 +2533,9 @@ $(VCSSVN_LIB): $(VCSSVN_OBJS) $(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
@@ -2803,7 +2818,7 @@ t/helper/test-svn-fe$X: $(VCSSVN_LIB) 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
t/helper/test-reftable.c new file +15 -0
@@ -0,0 +1,15 @@ +#include "reftable/reftable-tests.h" +#include "test-tool.h" + +int cmd__reftable (int argc,const char **argv) +{ +block_test_main (argc, argv); +merged_test_main (argc, argv); +record_test_main (argc, argv); +refname_test_main (argc, argv); +reftable_test_main (argc, argv); +strbuf_test_main (argc, argv); +stack_test_main (argc, argv); +tree_test_main (argc, argv); +return 0 ; +}
t/helper/test-tool.c +1 -0
@@ -52,6 +52,7 @@ static struct test_cmd cmds[] = { { "read-graph", cmd__read_graph }, { "read-midx", cmd__read_midx }, { "ref-store", cmd__ref_store }, + { "reftable", cmd__reftable }, { "regex", cmd__regex }, { "repository", cmd__repository }, { "revision-walking", cmd__revision_walking },
t/helper/test-tool.h +1 -0
@@ -41,6 +41,7 @@ int cmd__read_cache(int argc, const char **argv);int cmd__read_graph (int argc,const char **argv);int cmd__read_midx (int argc,const char **argv);int cmd__ref_store (int argc,const char **argv); +int cmd__reftable (int argc,const char **argv);int cmd__regex (int argc,const char **argv);int cmd__repository (int argc,const char **argv);int cmd__revision_walking (int argc,const char **argv);
t/t0031-reftable.sh +5 -0
@@ -15,6 +15,11 @@ initialize () { mv .git/hooks .git/hooks-disabled } +test_expect_success 'unittests' ' + test-tool reftable +' + + test_expect_success 'delete ref' ' initialize && test_commit file &&
Signature · 1 line
gitgitgadget
PATCH v18 15/19 Add GIT_DEBUG_REFS debugging mechanism
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
When set in the environment, GIT_DEBUG_REFS makes git print operations and results as they flow through the ref storage backend. This helps debug discrepancies between different ref backends.
--- Makefile | 1 + refs.c | 3 + refs/debug.c | 411 ++++++++++++++++++++++++++++++++++++++++++ refs/refs-internal.h | 6 + t/t0033-debug-refs.sh | 18 ++ 5 files changed, 439 insertions(+) create mode 100644 refs/debug.c create mode 100755 t/t0033-debug-refs.sh
Makefile +1 -0
@@ -961,6 +961,7 @@ LIB_OBJS += rebase.o LIB_OBJS += ref-filter.o 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
refs.c +3 -0
@@ -1750,6 +1750,9 @@ struct ref_store *get_main_ref_store(struct repository *r) r->ref_storage_format :default_ref_storage (), REF_STORE_ALL_CAPS); +if (getenv ("GIT_DEBUG_REFS" )) { + r->refs_private =debug_wrap (r->refs_private); + }return r->refs_private; }
refs/debug.c new file +411 -0
411 lines not on this page · raw message
refs/refs-internal.h +6 -0
@@ -713,4 +713,10 @@ struct ref_store {void base_ref_store_init (struct ref_store *refs,const struct ref_storage_be *be); +/* +* Print out ref operations as they occur. Useful for debugging alternate ref +* backends. +*/ +struct ref_store *debug_wrap (struct ref_store *store); +#endif /* REFS_REFS_INTERNAL_H */
t/t0033-debug-refs.sh new file +18 -0
@@ -0,0 +1,18 @@ +#!/bin/sh +# +# Copyright (c) 2020 Google LLC +# + +test_description='cross-check reftable with files, using GIT_DEBUG_REFS output' + +. ./test-lib.sh + +test_expect_success 'GIT_DEBUG_REFS' ' + git init --ref-storage=files files && + git init --ref-storage=reftable reftable && + (cd files && GIT_DEBUG_REFS=1 test_commit message file) > files.txt && + (cd reftable && GIT_DEBUG_REFS=1 test_commit message file) > reftable.txt && + test_cmp files.txt reftable.txt +' + +test_done
Signature · 1 line
gitgitgadget
PATCH v18 16/19 vcxproj: adjust for the reftable changes
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
This allows Git to be compiled via Visual Studio again after integrating the hn/reftable branch.
--- config.mak.uname | 2 +- contrib/buildsystems/Generators/Vcxproj.pm | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-)
config.mak.uname +1 -1
@@ -709,7 +709,7 @@ vcxproj: # Make .vcxproj files and add them unset QUIET_GEN QUIET_BUILT_IN; \ perl contrib/buildsystems/generate -g Vcxproj - git add -f git.sln {*,*/lib,t/helper/*}/*.vcxproj + git add -f git.sln {*,*/lib,*/libreftable,t/helper/*}/*.vcxproj # Generate the LinkOrCopyBuiltins.targets and LinkOrCopyRemoteHttp.targets file (echo '<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">' && \
contrib/buildsystems/Generators/Vcxproj.pm +10 -1
@@ -77,7 +77,7 @@ sub createProject { my $libs_release = "\n "; my $libs_debug = "\n "; if (!$static_library) { - $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}})); + $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib|reftable\/libreftable\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}})); $libs_debug = $libs_release; $libs_debug =~ s/zlib\.lib/zlibd\.lib/g; $libs_debug =~ s/libcurl\.lib/libcurl-d\.lib/g;
@@ -231,6 +231,7 @@ sub createProject { EOM if (!$static_library || $target =~ 'vcs-svn' || $target =~ 'xdiff') { my $uuid_libgit = $$build_structure{"LIBS_libgit_GUID"}; + my $uuid_libreftable = $$build_structure{"LIBS_reftable/libreftable_GUID"}; my $uuid_xdiff_lib = $$build_structure{"LIBS_xdiff/lib_GUID"}; print F << "EOM";
@@ -240,6 +241,14 @@ sub createProject { <ReferenceOutputAssembly>false</ReferenceOutputAssembly> </ProjectReference> EOM + if (!($name =~ /xdiff|libreftable/)) { + print F << "EOM"; + <ProjectReference Include="$cdup\\reftable\\libreftable\\libreftable.vcxproj"> + <Project>$uuid_libreftable</Project> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + </ProjectReference> +EOM + } if (!($name =~ 'xdiff')) { print F << "EOM"; <ProjectReference Include="$cdup\\xdiff\\lib\\xdiff_lib.vcxproj">
Signature · 1 line
gitgitgadget
PATCH v18 17/19 git-prompt: prepare for reftable refs backend
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: =?UTF-8?q?SZEDER=20G=C3=A1bor?= <[email protected]>
In our git-prompt script we strive to use Bash builtins wherever possible, because fork()-ing subshells for command substitutions and fork()+exec()-ing Git commands are expensive on some platforms. We even read and parse '.git/HEAD' using Bash builtins to get the name of the current branch [1]. However, the upcoming reftable refs backend won't use '.git/HEAD' at all, but will write an invalid refname as placeholder for backwards compatibility instead, which will break our git-prompt script.
Update the git-prompt script to recognize the placeholder '.git/HEAD' written by the reftable backend (its content is specified in the reftable specs), and then fall back to use 'git symbolic-ref' to get the name of the current branch.
[1] 3a43c4b5bd (bash prompt: use bash builtins to find out current
branch, 2011-03-31)
--- contrib/completion/git-prompt.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
contrib/completion/git-prompt.sh +6 -1
@@ -460,10 +460,15 @@ __git_ps1 () if ! __git_eread "$g/HEAD" head; then return $exit fi - # is it a symbolic ref? b="${head#ref: }" if [ "$head" = "$b" ]; then detached=yes + elif [ "$b" = "refs/heads/.invalid" ]; then + # Reftable + b="$(git symbolic-ref HEAD 2>/dev/null)" || + detached=yes + fi + if [ "$detached" = yes ]; then b="$( case "${GIT_PS1_DESCRIBE_STYLE-}" in (contains)
Signature · 1 line
gitgitgadget
PATCH v18 18/19 Add reftable testing infrastructure
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
* Add GIT_TEST_REFTABLE environment var to control default ref storage
* Add test_prerequisite REFTABLE.
* Skip some tests that are incompatible:
* t3210-pack-refs.sh - does not apply
* t1450-fsck.sh - manipulates .git/ directly to create invalid state
Major test failures:
* t1400-update-ref.sh - Reads from .git/{refs,logs} directly
* t1404-update-ref-errors.sh - Manipulates .git/refs/ directly
* t1405 - inspecs .git/ directly.
--- t/t1409-avoid-packing-refs.sh | 6 ++++++ t/t1450-fsck.sh | 6 ++++++ t/t3210-pack-refs.sh | 6 ++++++ t/test-lib.sh | 5 +++++ 4 files changed, 23 insertions(+)
t/t1409-avoid-packing-refs.sh +6 -0
@@ -4,6 +4,12 @@ test_description='avoid rewriting packed-refs unnecessarily' . ./test-lib.sh +if test_have_prereq REFTABLE +then + skip_all='skipping pack-refs tests; incompatible with reftable' + test_done +fi + # Add an identifying mark to the packed-refs file header line. This # shouldn't upset readers, and it should be omitted if the file is # ever rewritten.
t/t1450-fsck.sh +6 -0
@@ -8,6 +8,12 @@ test_description='git fsck random collection of tests . ./test-lib.sh +if test_have_prereq REFTABLE +then + skip_all='skipping tests; incompatible with reftable' + test_done +fi + test_expect_success setup ' test_oid_init && git config gc.auto 0 &&
t/t3210-pack-refs.sh +6 -0
@@ -11,6 +11,12 @@ semantic is still the same. ' . ./test-lib.sh +if test_have_prereq REFTABLE +then + skip_all='skipping pack-refs tests; incompatible with reftable' + test_done +fi + test_expect_success 'enable reflogs' ' git config core.logallrefupdates true '
t/test-lib.sh +5 -0
@@ -1504,6 +1504,11 @@ parisc* | hppa*) ;; esac +if test -n "$GIT_TEST_REFTABLE" +then + test_set_prereq REFTABLE +fi + ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1 test -z "$NO_PERL" && test_set_prereq PERL test -z "$NO_PTHREADS" && test_set_prereq PTHREADS
Signature · 1 line
gitgitgadget
PATCH v18 19/19 Add "test-tool dump-reftable" command.
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This command dumps individual tables or a stack of of tables.
--- Makefile | 1 + t/helper/test-reftable.c | 5 +++++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + 4 files changed, 8 insertions(+)
Makefile +1 -0
@@ -2375,6 +2375,7 @@ REFTABLE_OBJS += reftable/writer.o REFTABLE_OBJS += reftable/zlib-compat.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/record_test.o REFTABLE_TEST_OBJS += reftable/refname_test.o
t/helper/test-reftable.c +5 -0
@@ -13,3 +13,8 @@ int cmd__reftable(int argc, const char **argv)tree_test_main (argc, argv);return 0 ; } + +int cmd__dump_reftable (int argc,const char **argv) +{ +return reftable_dump_main (argc, (char *const *)argv); +}
t/helper/test-tool.c +1 -0
@@ -53,6 +53,7 @@ static struct test_cmd cmds[] = { { "read-midx", cmd__read_midx }, { "ref-store", cmd__ref_store }, { "reftable", cmd__reftable }, + { "dump-reftable", cmd__dump_reftable }, { "regex", cmd__regex }, { "repository", cmd__repository }, { "revision-walking", cmd__revision_walking },
t/helper/test-tool.h +1 -0
@@ -17,6 +17,7 @@ int cmd__dump_cache_tree(int argc, const char **argv);int cmd__dump_fsmonitor (int argc,const char **argv);int cmd__dump_split_index (int argc,const char **argv);int cmd__dump_untracked_cache (int argc,const char **argv); +int cmd__dump_reftable (int argc,const char **argv);int cmd__example_decorate (int argc,const char **argv);int cmd__genrandom (int argc,const char **argv);int cmd__genzeros (int argc,const char **argv);
Signature · 1 line
gitgitgadget
v19 cover Reftable support git-core
In reply to Han-Wen Nienhuys via GitGitGadgetThis adds the reftable library, and hooks it up as a ref backend.
Includes testing support, to test: make -C t/ GIT_TEST_REFTABLE=1
Summary 21852 tests pass 556 tests fail
v22
* worktree support
Han-Wen Nienhuys (18):
lib-t6000.sh: write tag using git-update-ref
t3432: use git-reflog to inspect the reflog for HEAD
checkout: add '\n' to reflog message
Write pseudorefs through ref backends.
Make refs_ref_exists public
Treat BISECT_HEAD as a pseudo ref
Treat CHERRY_PICK_HEAD as a pseudo ref
Treat REVERT_HEAD as a pseudo ref
Move REF_LOG_ONLY to refs-internal.h
Iterate over the "refs/" namespace in for_each_[raw]ref Add .gitattributes for the reftable/ directory Add reftable library
Add standalone build infrastructure for reftable Reftable support for git-core
Hookup unittests for the reftable library.
Add GIT_DEBUG_REFS debugging mechanism
Add reftable testing infrastructure
Add "test-tool dump-reftable" command.
Johannes Schindelin (1):
vcxproj: adjust for the reftable changes
SZEDER Gábor (1):
git-prompt: prepare for reftable refs backend
.../technical/repository-version.txt | 7 + Makefile | 47 +- builtin/bisect--helper.c | 3 +- builtin/checkout.c | 5 +- builtin/clone.c | 4 +- builtin/commit.c | 34 +- builtin/init-db.c | 54 +- builtin/merge.c | 2 +- builtin/worktree.c | 31 +- cache.h | 8 +- config.mak.uname | 2 +- contrib/buildsystems/Generators/Vcxproj.pm | 11 +- contrib/completion/git-prompt.sh | 7 +- git-bisect.sh | 4 +- path.c | 2 - path.h | 9 +- refs.c | 157 +- refs.h | 16 + refs/debug.c | 416 +++++ refs/files-backend.c | 121 +- refs/packed-backend.c | 21 +- refs/refs-internal.h | 32 + refs/reftable-backend.c | 1497 +++++++++++++++++ reftable/.gitattributes | 1 + reftable/BUILD | 203 +++ reftable/LICENSE | 31 + reftable/README.md | 33 + reftable/VERSION | 1 + reftable/WORKSPACE | 14 + reftable/basics.c | 215 +++ reftable/basics.h | 53 + reftable/block.c | 432 +++++ reftable/block.h | 129 ++ reftable/block_test.c | 157 ++ reftable/compat.c | 98 ++ reftable/compat.h | 48 + reftable/constants.h | 21 + reftable/dump.c | 212 +++ reftable/file.c | 95 ++ reftable/iter.c | 242 +++ reftable/iter.h | 72 + reftable/merged.c | 317 ++++ reftable/merged.h | 43 + reftable/merged_test.c | 286 ++++ reftable/pq.c | 115 ++ reftable/pq.h | 34 + reftable/reader.c | 744 ++++++++ reftable/reader.h | 65 + reftable/record.c | 1126 +++++++++++++ reftable/record.h | 143 ++ reftable/record_test.c | 410 +++++ reftable/refname.c | 209 +++ reftable/refname.h | 38 + reftable/refname_test.c | 99 ++ reftable/reftable-tests.h | 22 + reftable/reftable.c | 154 ++ reftable/reftable.h | 585 +++++++ reftable/reftable_test.c | 632 +++++++ reftable/stack.c | 1225 ++++++++++++++ reftable/stack.h | 50 + reftable/stack_test.c | 787 +++++++++ reftable/strbuf.c | 206 +++ reftable/strbuf.h | 88 + reftable/strbuf_test.c | 39 + reftable/system.h | 53 + reftable/test_framework.c | 69 + reftable/test_framework.h | 64 + reftable/tree.c | 63 + reftable/tree.h | 34 + reftable/tree_test.c | 62 + reftable/update.sh | 19 + reftable/writer.c | 663 ++++++++ reftable/writer.h | 60 + reftable/zlib-compat.c | 92 + reftable/zlib.BUILD | 36 + repository.c | 2 + repository.h | 3 + sequencer.c | 56 +- setup.c | 12 +- t/helper/test-reftable.c | 20 + t/helper/test-tool.c | 2 + t/helper/test-tool.h | 2 + t/lib-t6000.sh | 5 +- t/t0031-reftable.sh | 178 ++ t/t0033-debug-refs.sh | 18 + t/t1409-avoid-packing-refs.sh | 6 + t/t1450-fsck.sh | 6 + t/t3210-pack-refs.sh | 6 + t/t3432-rebase-fast-forward.sh | 7 +- t/test-lib.sh | 5 + wt-status.c | 6 +- 91 files changed, 13304 insertions(+), 209 deletions(-) create mode 100644 refs/debug.c create mode 100644 refs/reftable-backend.c create mode 100644 reftable/.gitattributes create mode 100644 reftable/BUILD create mode 100644 reftable/LICENSE create mode 100644 reftable/README.md create mode 100644 reftable/VERSION create mode 100644 reftable/WORKSPACE create mode 100644 reftable/basics.c create mode 100644 reftable/basics.h create mode 100644 reftable/block.c create mode 100644 reftable/block.h create mode 100644 reftable/block_test.c create mode 100644 reftable/compat.c create mode 100644 reftable/compat.h create mode 100644 reftable/constants.h create mode 100644 reftable/dump.c create mode 100644 reftable/file.c create mode 100644 reftable/iter.c create mode 100644 reftable/iter.h create mode 100644 reftable/merged.c create mode 100644 reftable/merged.h create mode 100644 reftable/merged_test.c create mode 100644 reftable/pq.c create mode 100644 reftable/pq.h create mode 100644 reftable/reader.c create mode 100644 reftable/reader.h create mode 100644 reftable/record.c create mode 100644 reftable/record.h create mode 100644 reftable/record_test.c create mode 100644 reftable/refname.c create mode 100644 reftable/refname.h create mode 100644 reftable/refname_test.c create mode 100644 reftable/reftable-tests.h create mode 100644 reftable/reftable.c create mode 100644 reftable/reftable.h create mode 100644 reftable/reftable_test.c create mode 100644 reftable/stack.c create mode 100644 reftable/stack.h create mode 100644 reftable/stack_test.c create mode 100644 reftable/strbuf.c create mode 100644 reftable/strbuf.h create mode 100644 reftable/strbuf_test.c create mode 100644 reftable/system.h create mode 100644 reftable/test_framework.c create mode 100644 reftable/test_framework.h create mode 100644 reftable/tree.c create mode 100644 reftable/tree.h create mode 100644 reftable/tree_test.c create mode 100755 reftable/update.sh create mode 100644 reftable/writer.c create mode 100644 reftable/writer.h create mode 100644 reftable/zlib-compat.c create mode 100644 reftable/zlib.BUILD create mode 100644 t/helper/test-reftable.c create mode 100755 t/t0031-reftable.sh create mode 100755 t/t0033-debug-refs.sh
base-commit: b9a2d1a0207fb9ded3fa524f54db3bc322a12cc4
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-539%2Fhanwen%2Freftable-v19
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-539/hanwen/reftable-v19
Pull-Request: https://github.com/gitgitgadget/git/pull/539
Range-diff vs v18:
1: b968b795af = 1: 596c316da4 lib-t6000.sh: write tag using git-update-ref
-: ---------- > 2: 277da0cf7e t3432: use git-reflog to inspect the reflog for HEAD
2: 5210225977 = 3: 125695ce92 checkout: add '\n' to reflog message
3: 15c9dd66e1 = 4: 5715681b3f Write pseudorefs through ref backends.
4: a5bce2e3fe = 5: c8cf2d2ce1 Make refs_ref_exists public
5: a29d898907 = 6: e36b29de79 Treat BISECT_HEAD as a pseudo ref
6: 11a690d2b8 = 7: 1676df9851 Treat CHERRY_PICK_HEAD as a pseudo ref
7: 4e52ec0dbc = 8: 0a5f97ea34 Treat REVERT_HEAD as a pseudo ref
8: 37e350af15 = 9: 9463ed9093 Move REF_LOG_ONLY to refs-internal.h
9: 468f00eaf6 = 10: a116aebe11 Iterate over the "refs/" namespace in for_each_[raw]ref
10: 21febeaa81 = 11: e4545658ed Add .gitattributes for the reftable/ directory
11: 3c84f43cfa ! 12: 169f6c7f54 Add reftable library
@@ reftable/LICENSE (new)
## reftable/VERSION (new) ##
@@
-+d7472cbd1afe6bf3da53e94971b1cc79ce183fa8 Remove outdated bits of the README file
++994cc2f626ff626ff04be5240413775f832110fb C: formatting tweaks
## reftable/basics.c (new) ##
@@
@@ reftable/basics.c (new)
+void parse_names(char *buf, int size, char ***namesp)
+{
+ char **names = NULL;
-+ int names_cap = 0;
-+ int names_len = 0;
++ size_t names_cap = 0;
++ size_t names_len = 0;
+
+ char *p = buf;
+ char *end = buf + size;
@@ reftable/block_test.c (new)
+ snprintf(name, sizeof(name), "branch%02d", i);
+ memset(hash, i, sizeof(hash));
+
-+ ref.ref_name = name;
++ ref.refname = name;
+ ref.value = hash;
+ names[i] = xstrdup(name);
+ n = block_writer_add(&bw, &rec);
-+ ref.ref_name = NULL;
++ ref.refname = NULL;
+ ref.value = NULL;
+ assert(n == 0);
+ }
@@ reftable/block_test.c (new)
+ if (r > 0) {
+ break;
+ }
-+ assert_streq(names[j], ref.ref_name);
++ assert_streq(names[j], ref.refname);
+ j++;
+ }
+
@@ reftable/block_test.c (new)
+ n = block_iter_next(&it, &rec);
+ assert(n == 0);
+
-+ assert_streq(names[i], ref.ref_name);
++ assert_streq(names[i], ref.refname);
+
+ want.len--;
+ n = block_reader_seek(&br, &it, &want);
@@ reftable/block_test.c (new)
+
+ n = block_iter_next(&it, &rec);
+ assert(n == 0);
-+ assert_streq(names[10 * (i / 10)], ref.ref_name);
++ assert_streq(names[10 * (i / 10)], ref.refname);
+
+ block_iter_close(&it);
+ }
@@ reftable/iter.c (new)
+ struct reftable_iterator it = { 0 };
+
+ err = reftable_table_seek_ref(&fri->tab, &it,
-+ ref->ref_name);
++ ref->refname);
+ if (err == 0) {
+ err = reftable_iterator_next_ref(&it, ref);
+ }
@@ reftable/merged.c (new)
+}
+
+int reftable_new_merged_table(struct reftable_merged_table **dest,
-+ struct reftable_reader **stack, int n,
++ struct reftable_table *stack, int n,
+ uint32_t hash_id)
+{
+ struct reftable_merged_table *m = NULL;
@@ reftable/merged.c (new)
+ uint64_t first_min = 0;
+ int i = 0;
+ for (i = 0; i < n; i++) {
-+ struct reftable_reader *r = stack[i];
-+ if (r->hash_id != hash_id) {
++ uint64_t min = reftable_table_min_update_index(&stack[i]);
++ uint64_t max = reftable_table_max_update_index(&stack[i]);
++
++ if (reftable_table_hash_id(&stack[i]) != hash_id) {
+ return REFTABLE_FORMAT_ERROR;
+ }
-+ if (i > 0 && last_max >= reftable_reader_min_update_index(r)) {
-+ return REFTABLE_FORMAT_ERROR;
++ if (i == 0 || min < first_min) {
++ first_min = min;
+ }
-+ if (i == 0) {
-+ first_min = reftable_reader_min_update_index(r);
++ if (i == 0 || max > last_max) {
++ last_max = max;
+ }
-+
-+ last_max = reftable_reader_max_update_index(r);
+ }
+
+ m = (struct reftable_merged_table *)reftable_calloc(
@@ reftable/merged.c (new)
+ return 0;
+}
+
-+void reftable_merged_table_close(struct reftable_merged_table *mt)
-+{
-+ int i = 0;
-+ for (i = 0; i < mt->stack_len; i++) {
-+ reftable_reader_free(mt->stack[i]);
-+ }
-+ FREE_AND_NULL(mt->stack);
-+ mt->stack_len = 0;
-+}
-+
+/* clears the list of subtable, without affecting the readers themselves. */
+void merged_table_clear(struct reftable_merged_table *mt)
+{
@@ reftable/merged.c (new)
+ int err = 0;
+ int i = 0;
+ for (i = 0; i < mt->stack_len && err == 0; i++) {
-+ int e = reader_seek(mt->stack[i], &iters[n], rec);
++ int e = reftable_table_seek_record(&mt->stack[i], &iters[n],
++ rec);
+ if (e < 0) {
+ err = e;
+ }
@@ reftable/merged.c (new)
+ const char *name)
+{
+ struct reftable_ref_record ref = {
-+ .ref_name = (char *)name,
++ .refname = (char *)name,
+ };
+ struct reftable_record rec = { 0 };
+ reftable_record_from_ref(&rec, &ref);
@@ reftable/merged.c (new)
+ const char *name, uint64_t update_index)
+{
+ struct reftable_log_record log = {
-+ .ref_name = (char *)name,
++ .refname = (char *)name,
+ .update_index = update_index,
+ };
+ struct reftable_record rec = { 0 };
@@ reftable/merged.c (new)
+{
+ uint64_t max = ~((uint64_t)0);
+ return reftable_merged_table_seek_log_at(mt, it, name, max);
++}
++
++uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *mt)
++{
++ return mt->hash_id;
+}
## reftable/merged.h (new) ##
@@ reftable/merged.h (new)
+#include "reftable.h"
+
+struct reftable_merged_table {
-+ struct reftable_reader **stack;
-+ int stack_len;
++ struct reftable_table *stack;
++ size_t stack_len;
+ uint32_t hash_id;
+ bool suppress_deletions;
+
@@ reftable/merged.h (new)
+struct merged_iter {
+ struct reftable_iterator *stack;
+ uint32_t hash_id;
-+ int stack_len;
++ size_t stack_len;
+ uint8_t typ;
+ bool suppress_deletions;
+ struct merged_iter_pqueue pq;
@@ reftable/merged.h (new)
+ struct reftable_iterator *it,
+ struct reftable_record *rec);
+
++int reftable_table_seek_record(struct reftable_table *tab,
++ struct reftable_iterator *it,
++ struct reftable_record *rec);
++
+#endif
## reftable/merged_test.c (new) ##
@@ reftable/merged_test.c (new)
+ reftable_new_record(BLOCK_TYPE_REF);
+ struct pq_entry e = { 0 };
+
-+ reftable_record_as_ref(&rec)->ref_name = names[i];
++ reftable_record_as_ref(&rec)->refname = names[i];
+ e.rec = rec;
+ merged_iter_pqueue_add(&pq, e);
+ merged_iter_pqueue_check(pq);
@@ reftable/merged_test.c (new)
+ merged_iter_pqueue_check(pq);
+
+ if (last != NULL) {
-+ assert(strcmp(last, ref->ref_name) < 0);
++ assert(strcmp(last, ref->refname) < 0);
+ }
-+ last = ref->ref_name;
-+ ref->ref_name = NULL;
++ last = ref->refname;
++ ref->refname = NULL;
+ reftable_free(ref);
+ }
+
@@ reftable/merged_test.c (new)
+
+static struct reftable_merged_table *
+merged_table_from_records(struct reftable_ref_record **refs,
-+ struct reftable_block_source **source, int *sizes,
++ struct reftable_block_source **source,
++ struct reftable_reader ***readers, int *sizes,
+ struct strbuf *buf, int n)
+{
-+ struct reftable_reader **rd = reftable_calloc(n * sizeof(*rd));
+ int i = 0;
+ struct reftable_merged_table *mt = NULL;
+ int err;
++ struct reftable_table *tabs =
++ reftable_calloc(n * sizeof(struct reftable_table));
++ *readers = reftable_calloc(n * sizeof(struct reftable_reader *));
+ *source = reftable_calloc(n * sizeof(**source));
+ for (i = 0; i < n; i++) {
+ write_test_table(&buf[i], refs[i], sizes[i]);
+ block_source_from_strbuf(&(*source)[i], &buf[i]);
+
-+ err = reftable_new_reader(&rd[i], &(*source)[i], "name");
++ err = reftable_new_reader(&(*readers)[i], &(*source)[i],
++ "name");
+ assert_err(err);
++ reftable_table_from_reader(&tabs[i], (*readers)[i]);
+ }
+
-+ err = reftable_new_merged_table(&mt, rd, n, SHA1_ID);
++ err = reftable_new_merged_table(&mt, tabs, n, SHA1_ID);
+ assert_err(err);
+ return mt;
+}
+
++static void readers_destroy(struct reftable_reader **readers, size_t n)
++{
++ int i = 0;
++ for (; i < n; i++)
++ reftable_reader_free(readers[i]);
++ reftable_free(readers);
++}
++
+static void test_merged_between(void)
+{
+ uint8_t hash1[SHA1_SIZE] = { 1, 2, 3, 0 };
+
+ struct reftable_ref_record r1[] = { {
-+ .ref_name = "b",
++ .refname = "b",
+ .update_index = 1,
+ .value = hash1,
+ } };
+ struct reftable_ref_record r2[] = { {
-+ .ref_name = "a",
++ .refname = "a",
+ .update_index = 2,
+ } };
+
@@ reftable/merged_test.c (new)
+ int sizes[] = { 1, 1 };
+ struct strbuf bufs[2] = { STRBUF_INIT, STRBUF_INIT };
+ struct reftable_block_source *bs = NULL;
++ struct reftable_reader **readers = NULL;
+ struct reftable_merged_table *mt =
-+ merged_table_from_records(refs, &bs, sizes, bufs, 2);
++ merged_table_from_records(refs, &bs, &readers, sizes, bufs, 2);
+ int i;
+ struct reftable_ref_record ref = { 0 };
+ struct reftable_iterator it = { 0 };
@@ reftable/merged_test.c (new)
+ assert_err(err);
+ assert(ref.update_index == 2);
+ reftable_ref_record_clear(&ref);
-+
+ reftable_iterator_destroy(&it);
-+ reftable_merged_table_close(mt);
++ readers_destroy(readers, 2);
+ reftable_merged_table_free(mt);
+ for (i = 0; i < ARRAY_SIZE(bufs); i++) {
+ strbuf_release(&bufs[i]);
@@ reftable/merged_test.c (new)
+ uint8_t hash1[SHA1_SIZE] = { 1 };
+ uint8_t hash2[SHA1_SIZE] = { 2 };
+ struct reftable_ref_record r1[] = { {
-+ .ref_name = "a",
++ .refname = "a",
+ .update_index = 1,
+ .value = hash1,
+ },
+ {
-+ .ref_name = "b",
++ .refname = "b",
+ .update_index = 1,
+ .value = hash1,
+ },
+ {
-+ .ref_name = "c",
++ .refname = "c",
+ .update_index = 1,
+ .value = hash1,
+ } };
+ struct reftable_ref_record r2[] = { {
-+ .ref_name = "a",
++ .refname = "a",
+ .update_index = 2,
+ } };
+ struct reftable_ref_record r3[] = {
+ {
-+ .ref_name = "c",
++ .refname = "c",
+ .update_index = 3,
+ .value = hash2,
+ },
+ {
-+ .ref_name = "d",
++ .refname = "d",
+ .update_index = 3,
+ .value = hash1,
+ },
@@ reftable/merged_test.c (new)
+ int sizes[3] = { 3, 1, 2 };
+ struct strbuf bufs[3] = { STRBUF_INIT, STRBUF_INIT, STRBUF_INIT };
+ struct reftable_block_source *bs = NULL;
-+
++ struct reftable_reader **readers = NULL;
+ struct reftable_merged_table *mt =
-+ merged_table_from_records(refs, &bs, sizes, bufs, 3);
++ merged_table_from_records(refs, &bs, &readers, sizes, bufs, 3);
+
+ struct reftable_iterator it = { 0 };
+ int err = reftable_merged_table_seek_ref(mt, &it, "a");
+ struct reftable_ref_record *out = NULL;
-+ int len = 0;
-+ int cap = 0;
++ size_t len = 0;
++ size_t cap = 0;
+ int i = 0;
+
+ assert_err(err);
@@ reftable/merged_test.c (new)
+ for (i = 0; i < 3; i++) {
+ strbuf_release(&bufs[i]);
+ }
-+ reftable_merged_table_close(mt);
++ readers_destroy(readers, 3);
+ reftable_merged_table_free(mt);
+ reftable_free(bs);
+}
@@ reftable/pq.h (new)
+
+struct merged_iter_pqueue {
+ struct pq_entry *heap;
-+ int len;
-+ int cap;
++ size_t len;
++ size_t cap;
+};
+
+struct pq_entry merged_iter_pqueue_top(struct merged_iter_pqueue pq);
@@ reftable/reader.c (new)
+ uint64_t next_off, uint8_t want_typ)
+{
+ int32_t guess_block_size = r->block_size ? r->block_size :
-+ DEFAULT_BLOCK_SIZE;
++ DEFAULT_BLOCK_SIZE;
+ struct reftable_block block = { 0 };
+ uint8_t block_typ = 0;
+ int err = 0;
@@ reftable/reader.c (new)
+ struct reftable_iterator *it, const char *name)
+{
+ struct reftable_ref_record ref = {
-+ .ref_name = (char *)name,
++ .refname = (char *)name,
+ };
+ struct reftable_record rec = { 0 };
+ reftable_record_from_ref(&rec, &ref);
@@ reftable/reader.c (new)
+ uint64_t update_index)
+{
+ struct reftable_log_record log = {
-+ .ref_name = (char *)name,
++ .refname = (char *)name,
+ .update_index = update_index,
+ };
+ struct reftable_record rec = { 0 };
@@ reftable/record.c (new)
+ const struct reftable_ref_record *rec =
+ (const struct reftable_ref_record *)r;
+ strbuf_reset(dest);
-+ strbuf_addstr(dest, rec->ref_name);
++ strbuf_addstr(dest, rec->refname);
+}
+
+static void reftable_ref_record_copy_from(void *rec, const void *src_rec,
@@ reftable/record.c (new)
+ /* This is simple and correct, but we could probably reuse the hash
+ fields. */
+ reftable_ref_record_clear(ref);
-+ if (src->ref_name != NULL) {
-+ ref->ref_name = xstrdup(src->ref_name);
++ if (src->refname != NULL) {
++ ref->refname = xstrdup(src->refname);
+ }
+
+ if (src->target != NULL) {
@@ reftable/record.c (new)
+ uint32_t hash_id)
+{
+ char hex[SHA256_SIZE + 1] = { 0 };
-+ printf("ref{%s(%" PRIu64 ") ", ref->ref_name, ref->update_index);
++ printf("ref{%s(%" PRIu64 ") ", ref->refname, ref->update_index);
+ if (ref->value != NULL) {
+ hex_format(hex, ref->value, hash_size(hash_id));
+ printf("%s", hex);
@@ reftable/record.c (new)
+
+void reftable_ref_record_clear(struct reftable_ref_record *ref)
+{
-+ reftable_free(ref->ref_name);
++ reftable_free(ref->refname);
+ reftable_free(ref->target);
+ reftable_free(ref->target_value);
+ reftable_free(ref->value);
@@ reftable/record.c (new)
+
+ string_view_consume(&in, n);
+
-+ r->ref_name = reftable_realloc(r->ref_name, key.len + 1);
-+ memcpy(r->ref_name, key.buf, key.len);
-+ r->ref_name[key.len] = 0;
++ r->refname = reftable_realloc(r->refname, key.len + 1);
++ memcpy(r->refname, key.buf, key.len);
++ r->refname[key.len] = 0;
+
+ switch (val_type) {
+ case 1:
@@ reftable/record.c (new)
+{
+ char hex[SHA256_SIZE + 1] = { 0 };
+
-+ printf("log{%s(%" PRIu64 ") %s <%s> %" PRIu64 " %04d\n", log->ref_name,
++ printf("log{%s(%" PRIu64 ") %s <%s> %" PRIu64 " %04d\n", log->refname,
+ log->update_index, log->name, log->email, log->time,
+ log->tz_offset);
+ hex_format(hex, log->old_hash, hash_size(hash_id));
@@ reftable/record.c (new)
+{
+ const struct reftable_log_record *rec =
+ (const struct reftable_log_record *)r;
-+ int len = strlen(rec->ref_name);
++ int len = strlen(rec->refname);
+ uint8_t i64[8];
+ uint64_t ts = 0;
+ strbuf_reset(dest);
-+ strbuf_add(dest, (uint8_t *)rec->ref_name, len + 1);
++ strbuf_add(dest, (uint8_t *)rec->refname, len + 1);
+
+ ts = (~ts) - rec->update_index;
+ put_be64(&i64[0], ts);
@@ reftable/record.c (new)
+
+ reftable_log_record_clear(dst);
+ *dst = *src;
-+ if (dst->ref_name != NULL) {
-+ dst->ref_name = xstrdup(dst->ref_name);
++ if (dst->refname != NULL) {
++ dst->refname = xstrdup(dst->refname);
+ }
+ if (dst->email != NULL) {
+ dst->email = xstrdup(dst->email);
@@ reftable/record.c (new)
+
+void reftable_log_record_clear(struct reftable_log_record *r)
+{
-+ reftable_free(r->ref_name);
++ reftable_free(r->refname);
+ reftable_free(r->new_hash);
+ reftable_free(r->old_hash);
+ reftable_free(r->name);
@@ reftable/record.c (new)
+ if (key.len <= 9 || key.buf[key.len - 9] != 0)
+ return REFTABLE_FORMAT_ERROR;
+
-+ r->ref_name = reftable_realloc(r->ref_name, key.len - 8);
-+ memcpy(r->ref_name, key.buf, key.len - 8);
++ r->refname = reftable_realloc(r->refname, key.len - 8);
++ memcpy(r->refname, key.buf, key.len - 8);
+ ts = get_be64(key.buf + key.len - 8);
+
+ r->update_index = (~max) - ts;
@@ reftable/record.c (new)
+ struct reftable_ref_record *b, int hash_size)
+{
+ assert(hash_size > 0);
-+ return 0 == strcmp(a->ref_name, b->ref_name) &&
++ return 0 == strcmp(a->refname, b->refname) &&
+ a->update_index == b->update_index &&
+ hash_equal(a->value, b->value, hash_size) &&
+ hash_equal(a->target_value, b->target_value, hash_size) &&
@@ reftable/record.c (new)
+
+int reftable_ref_record_compare_name(const void *a, const void *b)
+{
-+ return strcmp(((struct reftable_ref_record *)a)->ref_name,
-+ ((struct reftable_ref_record *)b)->ref_name);
++ return strcmp(((struct reftable_ref_record *)a)->refname,
++ ((struct reftable_ref_record *)b)->refname);
+}
+
+bool reftable_ref_record_is_deletion(const struct reftable_ref_record *ref)
@@ reftable/record.c (new)
+ struct reftable_log_record *la = (struct reftable_log_record *)a;
+ struct reftable_log_record *lb = (struct reftable_log_record *)b;
+
-+ int cmp = strcmp(la->ref_name, lb->ref_name);
++ int cmp = strcmp(la->refname, lb->refname);
+ if (cmp)
+ return cmp;
+ if (la->update_index > lb->update_index)
@@ reftable/record.h (new)
+*/
+struct string_view {
+ uint8_t *buf;
-+ int len;
++ size_t len;
+};
+
+/* Advance `s.buf` by `n`, and decrease length. */
@@ reftable/record_test.c (new)
+ for (i = 0; i <= 3; i++) {
+ struct reftable_ref_record in = { 0 };
+ struct reftable_ref_record out = {
-+ .ref_name = xstrdup("old name"),
++ .refname = xstrdup("old name"),
+ .value = reftable_calloc(SHA1_SIZE),
+ .target_value = reftable_calloc(SHA1_SIZE),
+ .target = xstrdup("old value"),
@@ reftable/record_test.c (new)
+ in.target = xstrdup("target");
+ break;
+ }
-+ in.ref_name = xstrdup("refs/heads/master");
++ in.refname = xstrdup("refs/heads/master");
+
+ reftable_record_from_ref(&rec, &in);
+ test_copy(&rec);
@@ reftable/record_test.c (new)
+{
+ struct reftable_log_record in[2] = {
+ {
-+ .ref_name = xstrdup("refs/heads/master"),
++ .refname = xstrdup("refs/heads/master"),
+ .update_index = 42,
+ },
+ {
-+ .ref_name = xstrdup("refs/heads/master"),
++ .refname = xstrdup("refs/heads/master"),
+ .update_index = 22,
+ }
+ };
@@ reftable/record_test.c (new)
+{
+ struct reftable_log_record in[2] = {
+ {
-+ .ref_name = xstrdup("refs/heads/master"),
++ .refname = xstrdup("refs/heads/master"),
+ .old_hash = reftable_malloc(SHA1_SIZE),
+ .new_hash = reftable_malloc(SHA1_SIZE),
+ .name = xstrdup("han-wen"),
@@ reftable/record_test.c (new)
+ .tz_offset = 100,
+ },
+ {
-+ .ref_name = xstrdup("refs/heads/master"),
++ .refname = xstrdup("refs/heads/master"),
+ .update_index = 22,
+ }
+ };
@@ reftable/record_test.c (new)
+ };
+ /* populate out, to check for leaks. */
+ struct reftable_log_record out = {
-+ .ref_name = xstrdup("old name"),
++ .refname = xstrdup("old name"),
+ .new_hash = reftable_calloc(SHA1_SIZE),
+ .old_hash = reftable_calloc(SHA1_SIZE),
+ .name = xstrdup("old name"),
@@ reftable/refname.c (new)
+ if (mod->del_len > 0) {
+ struct find_arg arg = {
+ .names = mod->del,
-+ .want = ref.ref_name,
++ .want = ref.refname,
+ };
+ int idx = binsearch(mod->del_len, find_name, &arg);
+ if (idx < mod->del_len &&
-+ !strcmp(ref.ref_name, mod->del[idx])) {
++ !strcmp(ref.refname, mod->del[idx])) {
+ continue;
+ }
+ }
+
-+ if (strncmp(ref.ref_name, prefix, strlen(prefix))) {
++ if (strncmp(ref.refname, prefix, strlen(prefix))) {
+ err = 1;
+ goto done;
+ }
@@ reftable/refname.c (new)
+ return err;
+}
+
-+int validate_ref_name(const char *name)
++int validate_refname(const char *name)
+{
+ while (true) {
+ char *next = strchr(name, '/');
@@ reftable/refname.c (new)
+ int err = 0;
+ for (; i < sz; i++) {
+ if (reftable_ref_record_is_deletion(&recs[i])) {
-+ mod.del[mod.del_len++] = recs[i].ref_name;
++ mod.del[mod.del_len++] = recs[i].refname;
+ } else {
-+ mod.add[mod.add_len++] = recs[i].ref_name;
++ mod.add[mod.add_len++] = recs[i].refname;
+ }
+ }
+
@@ reftable/refname.c (new)
+ int err = 0;
+ int i = 0;
+ for (; i < mod->add_len; i++) {
-+ err = validate_ref_name(mod->add[i]);
++ err = validate_refname(mod->add[i]);
+ if (err)
+ goto done;
+ strbuf_reset(&slashed);
@@ reftable/refname.h (new)
+ const char *prefix);
+
+// 0 = OK.
-+int validate_ref_name(const char *name);
++int validate_refname(const char *name);
+
+int validate_ref_record_addition(struct reftable_table tab,
+ struct reftable_ref_record *recs, size_t sz);
@@ reftable/refname_test.c (new)
+ struct reftable_writer *w =
+ reftable_new_writer(&strbuf_add_void, &buf, &opts);
+ struct reftable_ref_record rec = {
-+ .ref_name = "a/b",
++ .refname = "a/b",
+ .target = "destination", /* make sure it's not a symref. */
+ .update_index = 1,
+ };
@@ reftable/reftable.c (new)
+#include "merged.h"
+
+struct reftable_table_vtable {
-+ int (*seek)(void *tab, struct reftable_iterator *it,
-+ struct reftable_record *);
++ int (*seek_record)(void *tab, struct reftable_iterator *it,
++ struct reftable_record *);
++ uint32_t (*hash_id)(void *tab);
++ uint64_t (*min_update_index)(void *tab);
++ uint64_t (*max_update_index)(void *tab);
+};
+
+static int reftable_reader_seek_void(void *tab, struct reftable_iterator *it,
@@ reftable/reftable.c (new)
+ return reader_seek((struct reftable_reader *)tab, it, rec);
+}
+
++static uint32_t reftable_reader_hash_id_void(void *tab)
++{
++ return reftable_reader_hash_id((struct reftable_reader *)tab);
++}
++
++static uint64_t reftable_reader_min_update_index_void(void *tab)
++{
++ return reftable_reader_min_update_index((struct reftable_reader *)tab);
++}
++
++static uint64_t reftable_reader_max_update_index_void(void *tab)
++{
++ return reftable_reader_max_update_index((struct reftable_reader *)tab);
++}
++
+static struct reftable_table_vtable reader_vtable = {
-+ .seek = reftable_reader_seek_void,
++ .seek_record = reftable_reader_seek_void,
++ .hash_id = reftable_reader_hash_id_void,
++ .min_update_index = reftable_reader_min_update_index_void,
++ .max_update_index = reftable_reader_max_update_index_void,
+};
+
+static int reftable_merged_table_seek_void(void *tab,
@@ reftable/reftable.c (new)
+ rec);
+}
+
++static uint32_t reftable_merged_table_hash_id_void(void *tab)
++{
++ return reftable_merged_table_hash_id(
++ (struct reftable_merged_table *)tab);
++}
++
++static uint64_t reftable_merged_table_min_update_index_void(void *tab)
++{
++ return reftable_merged_table_min_update_index(
++ (struct reftable_merged_table *)tab);
++}
++
++static uint64_t reftable_merged_table_max_update_index_void(void *tab)
++{
++ return reftable_merged_table_max_update_index(
++ (struct reftable_merged_table *)tab);
++}
++
+static struct reftable_table_vtable merged_table_vtable = {
-+ .seek = reftable_merged_table_seek_void,
++ .seek_record = reftable_merged_table_seek_void,
++ .hash_id = reftable_merged_table_hash_id_void,
++ .min_update_index = reftable_merged_table_min_update_index_void,
++ .max_update_index = reftable_merged_table_max_update_index_void,
+};
+
+int reftable_table_seek_ref(struct reftable_table *tab,
+ struct reftable_iterator *it, const char *name)
+{
+ struct reftable_ref_record ref = {
-+ .ref_name = (char *)name,
++ .refname = (char *)name,
+ };
+ struct reftable_record rec = { 0 };
+ reftable_record_from_ref(&rec, &ref);
-+ return tab->ops->seek(tab->table_arg, it, &rec);
++ return tab->ops->seek_record(tab->table_arg, it, &rec);
+}
+
+void reftable_table_from_reader(struct reftable_table *tab,
@@ reftable/reftable.c (new)
+ if (err)
+ goto done;
+
-+ if (strcmp(ref->ref_name, name) ||
++ if (strcmp(ref->refname, name) ||
+ reftable_ref_record_is_deletion(ref)) {
+ reftable_ref_record_clear(ref);
+ err = 1;
@@ reftable/reftable.c (new)
+done:
+ reftable_iterator_destroy(&it);
+ return err;
++}
++
++int reftable_table_seek_record(struct reftable_table *tab,
++ struct reftable_iterator *it,
++ struct reftable_record *rec)
++{
++ return tab->ops->seek_record(tab->table_arg, it, rec);
++}
++
++uint64_t reftable_table_max_update_index(struct reftable_table *tab)
++{
++ return tab->ops->max_update_index(tab->table_arg);
++}
++
++uint64_t reftable_table_min_update_index(struct reftable_table *tab)
++{
++ return tab->ops->min_update_index(tab->table_arg);
++}
++
++uint32_t reftable_table_hash_id(struct reftable_table *tab)
++{
++ return tab->ops->hash_id(tab->table_arg);
+}
## reftable/reftable.h (new) ##
@@ reftable/reftable.h (new)
+
+/* reftable_ref_record holds a ref database entry target_value */
+struct reftable_ref_record {
-+ char *ref_name; /* Name of the ref, malloced. */
++ char *refname; /* Name of the ref, malloced. */
+ uint64_t update_index; /* Logical timestamp at which this value is
+ written */
+ uint8_t *value; /* SHA1, or NULL. malloced. */
@@ reftable/reftable.h (new)
+
+/* reftable_log_record holds a reflog entry */
+struct reftable_log_record {
-+ char *ref_name;
++ char *refname;
+ uint64_t update_index; /* logical timestamp of a transactional update.
+ */
+ uint8_t *new_hash;
@@ reftable/reftable.h (new)
+ REFTABLE_LOCK_ERROR = -5,
+
+ /* Misuse of the API:
-+ - on writing a record with NULL ref_name.
++ - on writing a record with NULL refname.
+ - on writing a reftable_ref_record outside the table limits
+ - on writing a ref or log record before the stack's next_update_index
+ - on writing a log record with multiline message with
@@ reftable/reftable.h (new)
+/* A merged table is implements seeking/iterating over a stack of tables. */
+struct reftable_merged_table;
+
++/* A generic reftable; see below. */
++struct reftable_table;
++
+/* reftable_new_merged_table creates a new merged table. It takes ownership of
+ the stack array.
+*/
+int reftable_new_merged_table(struct reftable_merged_table **dest,
-+ struct reftable_reader **stack, int n,
++ struct reftable_table *stack, int n,
+ uint32_t hash_id);
+
+/* returns an iterator positioned just before 'name' */
@@ reftable/reftable.h (new)
+uint64_t
+reftable_merged_table_min_update_index(struct reftable_merged_table *mt);
+
-+/* closes readers for the merged tables */
-+void reftable_merged_table_close(struct reftable_merged_table *mt);
-+
+/* releases memory for the merged_table */
+void reftable_merged_table_free(struct reftable_merged_table *m);
+
++/* return the hash ID of the merged table. */
++uint32_t reftable_merged_table_hash_id(struct reftable_merged_table *m);
++
+/****************************************************************
+ Generic tables
+
@@ reftable/reftable.h (new)
+
+void reftable_table_from_reader(struct reftable_table *tab,
+ struct reftable_reader *reader);
++
++/* returns the hash ID from a generic reftable_table */
++uint32_t reftable_table_hash_id(struct reftable_table *tab);
++
++/* create a generic table from reftable_merged_table */
+void reftable_table_from_merged_table(struct reftable_table *tab,
+ struct reftable_merged_table *table);
+
++/* returns the max update_index covered by this table. */
++uint64_t reftable_table_max_update_index(struct reftable_table *tab);
++
++/* returns the min update_index covered by this table. */
++uint64_t reftable_table_min_update_index(struct reftable_table *tab);
++
+/* convenience function to read a single ref. Returns < 0 for error, 0
+ for success, and 1 if ref not found. */
+int reftable_table_read_ref(struct reftable_table *tab, const char *name,
@@ reftable/reftable_test.c (new)
+ reftable_new_writer(&strbuf_add_void, &buf, &opts);
+
+ struct reftable_ref_record rec = {
-+ .ref_name = "master",
++ .refname = "master",
+ .update_index = 1,
+ };
+ int err;
+ struct reftable_block_source source = { 0 };
-+ struct reftable_reader **readers = malloc(sizeof(*readers) * 1);
++ struct reftable_reader **readers =
++ reftable_calloc(sizeof(*readers) * 1);
++ struct reftable_table *tab = reftable_calloc(sizeof(*tab) * 1);
+ uint32_t hash_id;
+ struct reftable_reader *rd = NULL;
+ struct reftable_merged_table *merged = NULL;
@@ reftable/reftable_test.c (new)
+ hash_id = reftable_reader_hash_id(rd);
+ assert(hash_id == SHA1_ID);
+
-+ readers[0] = rd;
-+
-+ err = reftable_new_merged_table(&merged, readers, 1, SHA1_ID);
++ reftable_table_from_reader(&tab[0], rd);
++ err = reftable_new_merged_table(&merged, tab, 1, SHA1_ID);
+ assert_err(err);
+
-+ reftable_merged_table_close(merged);
++ reader_close(rd);
+ reftable_merged_table_free(merged);
+ strbuf_release(&buf);
+}
@@ reftable/reftable_test.c (new)
+
+ snprintf(name, sizeof(name), "refs/heads/branch%02d", i);
+
-+ ref.ref_name = name;
++ ref.refname = name;
+ ref.value = hash;
+ ref.update_index = update_index;
+ (*names)[i] = xstrdup(name);
@@ reftable/reftable_test.c (new)
+
+ snprintf(name, sizeof(name), "refs/heads/branch%02d", i);
+
-+ log.ref_name = name;
++ log.refname = name;
+ log.new_hash = hash;
+ log.update_index = update_index;
+ log.message = "message";
@@ reftable/reftable_test.c (new)
+ };
+ int err;
+ struct reftable_log_record log = {
-+ .ref_name = "refs/heads/master",
++ .refname = "refs/heads/master",
+ .name = "Han-Wen Nienhuys",
+ .email = "[email protected]",
+ .tz_offset = 100,
@@ reftable/reftable_test.c (new)
+ snprintf(name, sizeof(name), "b%02d%0*d", i, 130, 7);
+ names[i] = xstrdup(name);
+ puts(name);
-+ ref.ref_name = name;
++ ref.refname = name;
+ ref.update_index = i;
+
+ err = reftable_writer_add_ref(w, &ref);
@@ reftable/reftable_test.c (new)
+ set_test_hash(hash1, i);
+ set_test_hash(hash2, i + 1);
+
-+ log.ref_name = names[i];
++ log.refname = names[i];
+ log.update_index = i;
+ log.old_hash = hash1;
+ log.new_hash = hash2;
@@ reftable/reftable_test.c (new)
+ }
+
+ assert_err(err);
-+ assert_streq(names[i], log.ref_name);
++ assert_streq(names[i], log.refname);
+ assert(i == log.update_index);
+ i++;
+ reftable_log_record_clear(&log);
@@ reftable/reftable_test.c (new)
+ if (r > 0) {
+ break;
+ }
-+ assert(0 == strcmp(names[j], ref.ref_name));
++ assert(0 == strcmp(names[j], ref.refname));
+ assert(update_index == ref.update_index);
+
+ j++;
@@ reftable/reftable_test.c (new)
+ assert_err(err);
+ err = reftable_iterator_next_ref(&it, &ref);
+ assert_err(err);
-+ assert(0 == strcmp(names[i], ref.ref_name));
++ assert(0 == strcmp(names[i], ref.refname));
+ assert(i == ref.value[0]);
+
+ reftable_ref_record_clear(&ref);
@@ reftable/reftable_test.c (new)
+ /* Put the variable part in the start */
+ snprintf(name, sizeof(name), "br%02d%s", i, fill);
+ name[40] = 0;
-+ ref.ref_name = name;
++ ref.refname = name;
+
+ set_test_hash(hash1, i / 4);
+ set_test_hash(hash2, 3 + i / 4);
@@ reftable/reftable_test.c (new)
+ }
+
+ assert(j < want_names_len);
-+ assert(0 == strcmp(ref.ref_name, want_names[j]));
++ assert(0 == strcmp(ref.refname, want_names[j]));
+ j++;
+ reftable_ref_record_clear(&ref);
+ }
@@ reftable/stack.c (new)
+void reftable_stack_destroy(struct reftable_stack *st)
+{
+ if (st->merged != NULL) {
-+ reftable_merged_table_close(st->merged);
+ reftable_merged_table_free(st->merged);
+ st->merged = NULL;
+ }
++
++ if (st->readers != NULL) {
++ int i = 0;
++ for (i = 0; i < st->readers_len; i++) {
++ reftable_reader_free(st->readers[i]);
++ }
++ st->readers_len = 0;
++ FREE_AND_NULL(st->readers);
++ }
+ FREE_AND_NULL(st->list_file);
+ FREE_AND_NULL(st->reftable_dir);
+ reftable_free(st);
@@ reftable/stack.c (new)
+ reftable_calloc(sizeof(struct reftable_reader *) * cur_len);
+ int i = 0;
+ for (i = 0; i < cur_len; i++) {
-+ cur[i] = st->merged->stack[i];
++ cur[i] = st->readers[i];
+ }
+ return cur;
+}
@@ reftable/stack.c (new)
+ struct reftable_reader **cur = stack_copy_readers(st, cur_len);
+ int err = 0;
+ int names_len = names_length(names);
-+ struct reftable_reader **new_tables =
-+ reftable_malloc(sizeof(struct reftable_reader *) * names_len);
-+ int new_tables_len = 0;
++ struct reftable_reader **new_readers =
++ reftable_calloc(sizeof(struct reftable_reader *) * names_len);
++ struct reftable_table *new_tables =
++ reftable_calloc(sizeof(struct reftable_table) * names_len);
++ int new_readers_len = 0;
+ struct reftable_merged_table *new_merged = NULL;
+ int i;
+
@@ reftable/stack.c (new)
+ goto done;
+ }
+
-+ new_tables[new_tables_len++] = rd;
++ new_readers[new_readers_len] = rd;
++ reftable_table_from_reader(&new_tables[new_readers_len], rd);
++ new_readers_len++;
+ }
+
+ /* success! */
-+ err = reftable_new_merged_table(&new_merged, new_tables, new_tables_len,
-+ st->config.hash_id);
++ err = reftable_new_merged_table(&new_merged, new_tables,
++ new_readers_len, st->config.hash_id);
+ if (err < 0)
+ goto done;
+
+ new_tables = NULL;
-+ new_tables_len = 0;
++ st->readers_len = new_readers_len;
+ if (st->merged != NULL) {
+ merged_table_clear(st->merged);
+ reftable_merged_table_free(st->merged);
+ }
++ if (st->readers != NULL) {
++ reftable_free(st->readers);
++ }
++ st->readers = new_readers;
++ new_readers = NULL;
++ new_readers_len = 0;
++
+ new_merged->suppress_deletions = true;
+ st->merged = new_merged;
-+
+ for (i = 0; i < cur_len; i++) {
+ if (cur[i] != NULL) {
+ reader_close(cur[i]);
@@ reftable/stack.c (new)
+ }
+
+done:
-+ for (i = 0; i < new_tables_len; i++) {
-+ reader_close(new_tables[i]);
-+ reftable_reader_free(new_tables[i]);
++ for (i = 0; i < new_readers_len; i++) {
++ reader_close(new_readers[i]);
++ reftable_reader_free(new_readers[i]);
+ }
++ reftable_free(new_readers);
+ reftable_free(new_tables);
+ reftable_free(cur);
+ return err;
@@ reftable/stack.c (new)
+ if (err < 0)
+ return err;
+
-+ for (i = 0; i < st->merged->stack_len; i++) {
++ for (i = 0; i < st->readers_len; i++) {
+ if (names[i] == NULL) {
+ err = 1;
+ goto done;
+ }
+
-+ if (strcmp(st->merged->stack[i]->name, names[i])) {
++ if (strcmp(st->readers[i]->name, names[i])) {
+ err = 1;
+ goto done;
+ }
@@ reftable/stack.c (new)
+ goto done;
+
+ for (i = 0; i < add->stack->merged->stack_len; i++) {
-+ strbuf_addstr(&table_list, add->stack->merged->stack[i]->name);
++ strbuf_addstr(&table_list, add->stack->readers[i]->name);
+ strbuf_addstr(&table_list, "\n");
+ }
+ for (i = 0; i < add->new_tables_len; i++) {
@@ reftable/stack.c (new)
+{
+ int sz = st->merged->stack_len;
+ if (sz > 0)
-+ return reftable_reader_max_update_index(
-+ st->merged->stack[sz - 1]) +
++ return reftable_reader_max_update_index(st->readers[sz - 1]) +
+ 1;
+ return 1;
+}
@@ reftable/stack.c (new)
+ int err = 0;
+
+ format_name(&next_name,
-+ reftable_reader_min_update_index(st->merged->stack[first]),
-+ reftable_reader_max_update_index(st->merged->stack[first]));
++ reftable_reader_min_update_index(st->readers[first]),
++ reftable_reader_max_update_index(st->readers[first]));
+
+ strbuf_reset(temp_tab);
+ strbuf_addstr(temp_tab, st->reftable_dir);
@@ reftable/stack.c (new)
+ struct reftable_log_expiry_config *config)
+{
+ int subtabs_len = last - first + 1;
-+ struct reftable_reader **subtabs = reftable_calloc(
-+ sizeof(struct reftable_reader *) * (last - first + 1));
++ struct reftable_table *subtabs = reftable_calloc(
++ sizeof(struct reftable_table) * (last - first + 1));
+ struct reftable_merged_table *mt = NULL;
+ int err = 0;
+ struct reftable_iterator it = { 0 };
@@ reftable/stack.c (new)
+
+ int i = 0, j = 0;
+ for (i = first, j = 0; i <= last; i++) {
-+ struct reftable_reader *t = st->merged->stack[i];
-+ subtabs[j++] = t;
++ struct reftable_reader *t = st->readers[i];
++ reftable_table_from_reader(&subtabs[j++], t);
+ st->stats.bytes += t->size;
+ }
-+ reftable_writer_set_limits(wr,
-+ st->merged->stack[first]->min_update_index,
-+ st->merged->stack[last]->max_update_index);
++ reftable_writer_set_limits(wr, st->readers[first]->min_update_index,
++ st->readers[last]->max_update_index);
+
+ err = reftable_new_merged_table(&mt, subtabs, subtabs_len,
+ st->config.hash_id);
@@ reftable/stack.c (new)
+
+ strbuf_addstr(&subtab_file_name, st->reftable_dir);
+ strbuf_addstr(&subtab_file_name, "/");
-+ strbuf_addstr(&subtab_file_name,
-+ reader_name(st->merged->stack[i]));
++ strbuf_addstr(&subtab_file_name, reader_name(st->readers[i]));
+
+ strbuf_reset(&subtab_lock);
+ strbuf_addbuf(&subtab_lock, &subtab_file_name);
@@ reftable/stack.c (new)
+ }
+ have_lock = true;
+
-+ format_name(&new_table_name, st->merged->stack[first]->min_update_index,
-+ st->merged->stack[last]->max_update_index);
++ format_name(&new_table_name, st->readers[first]->min_update_index,
++ st->readers[last]->max_update_index);
+ strbuf_addstr(&new_table_name, ".ref");
+
+ strbuf_reset(&new_table_path);
@@ reftable/stack.c (new)
+ }
+
+ for (i = 0; i < first; i++) {
-+ strbuf_addstr(&ref_list_contents, st->merged->stack[i]->name);
++ strbuf_addstr(&ref_list_contents, st->readers[i]->name);
+ strbuf_addstr(&ref_list_contents, "\n");
+ }
+ if (!is_empty_table) {
@@ reftable/stack.c (new)
+ strbuf_addstr(&ref_list_contents, "\n");
+ }
+ for (i = last + 1; i < st->merged->stack_len; i++) {
-+ strbuf_addstr(&ref_list_contents, st->merged->stack[i]->name);
++ strbuf_addstr(&ref_list_contents, st->readers[i]->name);
+ strbuf_addstr(&ref_list_contents, "\n");
+ }
+
@@ reftable/stack.c (new)
+ int overhead = header_size(version) - 1;
+ int i = 0;
+ for (i = 0; i < st->merged->stack_len; i++) {
-+ sizes[i] = st->merged->stack[i]->size - overhead;
++ sizes[i] = st->readers[i]->size - overhead;
+ }
+ return sizes;
+}
@@ reftable/stack.c (new)
+ if (err)
+ goto done;
+
-+ if (strcmp(log->ref_name, refname) ||
++ if (strcmp(log->refname, refname) ||
+ reftable_log_record_is_deletion(log)) {
+ err = 1;
+ goto done;
@@ reftable/stack.h (new)
+
+ struct reftable_write_options config;
+
++ struct reftable_reader **readers;
++ size_t readers_len;
+ struct reftable_merged_table *merged;
+ struct reftable_compaction_stats stats;
+};
@@ reftable/stack_test.c (new)
+ struct reftable_stack *st = NULL;
+ int err;
+ struct reftable_ref_record ref = {
-+ .ref_name = "HEAD",
++ .refname = "HEAD",
+ .update_index = 1,
+ .target = "master",
+ };
@@ reftable/stack_test.c (new)
+ err = reftable_stack_add(st, &write_test_ref, &ref);
+ assert_err(err);
+
-+ err = reftable_stack_read_ref(st, ref.ref_name, &dest);
++ err = reftable_stack_read_ref(st, ref.refname, &dest);
+ assert_err(err);
+ assert(0 == strcmp("master", dest.target));
+
@@ reftable/stack_test.c (new)
+ char dir[256] = "/tmp/stack_test.XXXXXX";
+ int err;
+ struct reftable_ref_record ref1 = {
-+ .ref_name = "HEAD",
++ .refname = "HEAD",
+ .update_index = 1,
+ .target = "master",
+ };
+ struct reftable_ref_record ref2 = {
-+ .ref_name = "branch2",
++ .refname = "branch2",
+ .update_index = 2,
+ .target = "master",
+ };
@@ reftable/stack_test.c (new)
+ struct reftable_addition *add = NULL;
+
+ struct reftable_ref_record ref = {
-+ .ref_name = "HEAD",
++ .refname = "HEAD",
+ .update_index = 1,
+ .target = "master",
+ };
@@ reftable/stack_test.c (new)
+
+ reftable_addition_destroy(add);
+
-+ err = reftable_stack_read_ref(st, ref.ref_name, &dest);
++ err = reftable_stack_read_ref(st, ref.refname, &dest);
+ assert_err(err);
+ assert(0 == strcmp("master", dest.target));
+
@@ reftable/stack_test.c (new)
+ char dir[256] = "/tmp/stack_test.XXXXXX";
+ int i;
+ struct reftable_ref_record ref = {
-+ .ref_name = "a/b",
++ .refname = "a/b",
+ .update_index = 1,
+ .target = "master",
+ };
@@ reftable/stack_test.c (new)
+
+ for (i = 0; i < ARRAY_SIZE(additions); i++) {
+ struct reftable_ref_record ref = {
-+ .ref_name = additions[i],
++ .refname = additions[i],
+ .update_index = 1,
+ .target = "master",
+ };
@@ reftable/stack_test.c (new)
+ struct reftable_stack *st = NULL;
+ int err;
+ struct reftable_ref_record ref1 = {
-+ .ref_name = "name1",
++ .refname = "name1",
+ .update_index = 1,
+ .target = "master",
+ };
+ struct reftable_ref_record ref2 = {
-+ .ref_name = "name2",
++ .refname = "name2",
+ .update_index = 1,
+ .target = "master",
+ };
@@ reftable/stack_test.c (new)
+ for (i = 0; i < N; i++) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "branch%02d", i);
-+ refs[i].ref_name = xstrdup(buf);
++ refs[i].refname = xstrdup(buf);
+ refs[i].value = reftable_malloc(SHA1_SIZE);
+ refs[i].update_index = i + 1;
+ set_test_hash(refs[i].value, i);
+
-+ logs[i].ref_name = xstrdup(buf);
++ logs[i].refname = xstrdup(buf);
+ logs[i].update_index = N + i + 1;
+ logs[i].new_hash = reftable_malloc(SHA1_SIZE);
+ logs[i].email = xstrdup("identity@invalid");
@@ reftable/stack_test.c (new)
+
+ for (i = 0; i < N; i++) {
+ struct reftable_ref_record dest = { 0 };
-+ int err = reftable_stack_read_ref(st, refs[i].ref_name, &dest);
++ int err = reftable_stack_read_ref(st, refs[i].refname, &dest);
+ assert_err(err);
+ assert(reftable_ref_record_equal(&dest, refs + i, SHA1_SIZE));
+ reftable_ref_record_clear(&dest);
@@ reftable/stack_test.c (new)
+
+ for (i = 0; i < N; i++) {
+ struct reftable_log_record dest = { 0 };
-+ int err = reftable_stack_read_log(st, refs[i].ref_name, &dest);
++ int err = reftable_stack_read_log(st, refs[i].refname, &dest);
+ assert_err(err);
+ assert(reftable_log_record_equal(&dest, logs + i, SHA1_SIZE));
+ reftable_log_record_clear(&dest);
@@ reftable/stack_test.c (new)
+ uint8_t h1[SHA1_SIZE] = { 0x01 }, h2[SHA1_SIZE] = { 0x02 };
+
+ struct reftable_log_record input = {
-+ .ref_name = "branch",
++ .refname = "branch",
+ .update_index = 1,
+ .new_hash = h1,
+ .old_hash = h2,
@@ reftable/stack_test.c (new)
+ err = reftable_stack_add(st, &write_test_log, &arg);
+ assert_err(err);
+
-+ err = reftable_stack_read_log(st, input.ref_name, &dest);
++ err = reftable_stack_read_log(st, input.refname, &dest);
+ assert_err(err);
+ assert(0 == strcmp(dest.message, "one\n"));
+
@@ reftable/stack_test.c (new)
+ arg.update_index = 2;
+ err = reftable_stack_add(st, &write_test_log, &arg);
+ assert_err(err);
-+ err = reftable_stack_read_log(st, input.ref_name, &dest);
++ err = reftable_stack_read_log(st, input.refname, &dest);
+ assert_err(err);
+ assert(0 == strcmp(dest.message, "two\n"));
+
@@ reftable/stack_test.c (new)
+
+ for (i = 0; i < N; i++) {
+ const char *buf = "branch";
-+ refs[i].ref_name = xstrdup(buf);
++ refs[i].refname = xstrdup(buf);
+ refs[i].update_index = i + 1;
+ if (i % 2 == 0) {
+ refs[i].value = reftable_malloc(SHA1_SIZE);
+ set_test_hash(refs[i].value, i);
+ }
-+ logs[i].ref_name = xstrdup(buf);
++ logs[i].refname = xstrdup(buf);
+ /* update_index is part of the key. */
+ logs[i].update_index = 42;
+ if (i % 2 == 0) {
@@ reftable/stack_test.c (new)
+ int err;
+
+ struct reftable_ref_record ref = {
-+ .ref_name = "master",
++ .refname = "master",
+ .target = "target",
+ .update_index = 1,
+ };
@@ reftable/stack_test.c (new)
+ char buf[256];
+ snprintf(buf, sizeof(buf), "branch%02d", i);
+
-+ logs[i].ref_name = xstrdup(buf);
++ logs[i].refname = xstrdup(buf);
+ logs[i].update_index = i;
+ logs[i].time = i;
+ logs[i].new_hash = reftable_malloc(SHA1_SIZE);
@@ reftable/stack_test.c (new)
+ err = reftable_stack_compact_all(st, &expiry);
+ assert_err(err);
+
-+ err = reftable_stack_read_log(st, logs[9].ref_name, &log);
++ err = reftable_stack_read_log(st, logs[9].refname, &log);
+ assert(err == 1);
+
-+ err = reftable_stack_read_log(st, logs[11].ref_name, &log);
++ err = reftable_stack_read_log(st, logs[11].refname, &log);
+ assert_err(err);
+
+ expiry.min_update_index = 15;
+ err = reftable_stack_compact_all(st, &expiry);
+ assert_err(err);
+
-+ err = reftable_stack_read_log(st, logs[14].ref_name, &log);
++ err = reftable_stack_read_log(st, logs[14].refname, &log);
+ assert(err == 1);
+
-+ err = reftable_stack_read_log(st, logs[16].ref_name, &log);
++ err = reftable_stack_read_log(st, logs[16].refname, &log);
+ assert_err(err);
+
+ /* cleanup */
@@ reftable/stack_test.c (new)
+ for (i = 0; i < N; i++) {
+ char name[100];
+ struct reftable_ref_record ref = {
-+ .ref_name = name,
++ .refname = name,
+ .update_index = reftable_stack_next_update_index(st),
+ .target = "master",
+ };
@@ reftable/strbuf.h (new)
+ x = STRBUF_INIT;"
+ */
+struct strbuf {
-+ int len;
-+ int cap;
++ size_t len;
++ size_t cap;
+ char *buf;
+
+ /* Used to enforce initialization with STRBUF_INIT */
@@ reftable/writer.c (new)
+struct obj_index_tree_node {
+ struct strbuf hash;
+ uint64_t *offsets;
-+ int offset_len;
-+ int offset_cap;
++ size_t offset_len;
++ size_t offset_cap;
+};
++
+#define OBJ_INDEX_TREE_NODE_INIT \
+ { \
+ .hash = STRBUF_INIT \
@@ reftable/writer.c (new)
+ struct reftable_ref_record copy = *ref;
+ int err = 0;
+
-+ if (ref->ref_name == NULL)
++ if (ref->refname == NULL)
+ return REFTABLE_API_ERROR;
+ if (ref->update_index < w->min_update_index ||
+ ref->update_index > w->max_update_index)
@@ reftable/writer.c (new)
+ char *input_log_message = log->message;
+ struct strbuf cleaned_message = STRBUF_INIT;
+ int err;
-+ if (log->ref_name == NULL)
++ if (log->refname == NULL)
+ return REFTABLE_API_ERROR;
+
+ if (w->block_writer != NULL &&
@@ reftable/writer.h (new)
+
+ /* pending index records for the current section */
+ struct reftable_index_record *index;
-+ int index_len;
-+ int index_cap;
++ size_t index_len;
++ size_t index_cap;
+
+ /*
+ tree for use with tsearch; used to populate the 'o' inverse OID
12: c92b8d12ec = 13: d155240b16 Add standalone build infrastructure for reftable
13: 479fe884e9 ! 14: 073bff7279 Reftable support for git-core
@@ Commit message
For background, see the previous commit introducing the library.
- This introduces the refs/reftable-backend.c containing reftable powered ref
- storage backend.
+ This introduces the file refs/reftable-backend.c containing a reftable-powered
+ ref storage backend.
- It can be activated by passing --ref-storage=reftable to "git init".
+ It can be activated by passing --ref-storage=reftable to "git init", or setting
+ GIT_TEST_REFTABLE in the environment.
Example use: see t/t0031-reftable.sh
@@ Makefile: cocciclean:
## builtin/clone.c ##
@@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)
- }
}
-- init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, INIT_DB_QUIET);
-+ init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN,
+ 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;
+@@ builtin/clone.c: 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);
++ initialize_repository_version(hash_algo, default_ref_storage());
+ repo_set_hash_algo(the_repository, hash_algo);
+
+ mapped_refs = wanted_peer_refs(refs, &remote->fetch);
## builtin/init-db.c ##
@@ builtin/init-db.c: static int needs_work_tree_config(const char *git_dir, const char *work_tree)
@@ builtin/init-db.c: static int create_default_files(const char *template_path,
* We need to create a "refs" dir in any case so that older
* versions of git can tell that this is a repository.
@@ builtin/init-db.c: static int create_default_files(const char *template_path,
- * Create the default symlink from ".git/HEAD" to the "master"
- * branch, if it does not exist yet.
+ * 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) {
- if (create_symref("HEAD", "refs/heads/master", NULL) < 0)
- exit(1);
+ char *ref;
+
+@@ builtin/init-db.c: static int create_default_files(const char *template_path,
+ free(ref);
}
- initialize_repository_version(fmt->hash_algo);
@@ builtin/init-db.c: static int create_default_files(const char *template_path,
/* Check filemode trustability */
path = git_path_buf(&buf, "config");
@@ builtin/init-db.c: 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, unsigned int flags)
-+ const char *template_dir, int hash, const char *ref_storage_format,
-+ unsigned int flags)
+ 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;
@@ builtin/init-db.c: static const char *const init_db_usage[] = {
const char *work_tree;
const char *template_dir = NULL;
@@ builtin/init-db.c: int cmd_init_db(int argc, const char **argv, const char *prefix)
- const char *object_format = NULL;
+ 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"),
@@ builtin/init-db.c: int cmd_init_db(int argc, const char **argv, const char *pref
+ 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(0, "object-format", &object_format, N_("hash"),
+ OPT_STRING('b', "initial-branch", &initial_branch, N_("name"),
@@ builtin/init-db.c: int cmd_init_db(int argc, const char **argv, const char *prefix)
}
@@ builtin/init-db.c: int cmd_init_db(int argc, const char **argv, const char *pref
UNLEAK(work_tree);
flags |= INIT_DB_EXIST_OK;
-- return init_db(git_dir, real_git_dir, template_dir, hash_algo, flags);
-+ return init_db(git_dir, real_git_dir, template_dir, hash_algo,
-+ ref_storage_format, flags);
+ return init_db(git_dir, real_git_dir, template_dir, hash_algo,
+- initial_branch, flags);
++ initial_branch, ref_storage_format, flags);
}
+ ## builtin/worktree.c ##
+@@
+ #include "submodule.h"
+ #include "utf8.h"
+ #include "worktree.h"
++#include "../refs/refs-internal.h"
+
+ static const char * const worktree_usage[] = {
+ N_("git worktree add [<options>] <path> [<commit-ish>]"),
+@@ builtin/worktree.c: 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, "../..");
+
+
## cache.h ##
@@ cache.h: int path_inside_repo(const char *prefix, const char *path);
+ #define INIT_DB_EXIST_OK 0x0002
int init_db(const char *git_dir, const char *real_git_dir,
- const char *template_dir, int hash_algo,
-- unsigned int flags);
+- const char *template_dir, int hash_algo,
+- const char *initial_branch, unsigned int flags);
-void initialize_repository_version(int hash_algo);
++ const char *template_dir, int hash_algo, const char *initial_branch,
+ const char *ref_storage_format, unsigned int flags);
+void initialize_repository_version(int hash_algo,
+ const char *ref_storage_format);
@@ cache.h: struct repository_format {
## refs.c ##
@@
- #include "argv-array.h"
#include "repository.h"
+ #include "sigchain.h"
+const char *default_ref_storage(void)
+{
@@ refs.c: struct ref_store *get_main_ref_store(struct repository *r)
- r->refs_private = ref_store_init(r->gitdir, REF_STORE_ALL_CAPS);
+ r->refs_private = ref_store_init(r->gitdir,
+ r->ref_storage_format ?
-+ r->ref_storage_format :
-+ default_ref_storage(),
++ r->ref_storage_format :
++ default_ref_storage(),
+ REF_STORE_ALL_CAPS);
return r->refs_private;
}
@@ refs/refs-internal.h: struct ref_storage_be {
## refs/reftable-backend.c (new) ##
@@
+#include "../cache.h"
++#include "../chdir-notify.h"
+#include "../config.h"
-+#include "../refs.h"
-+#include "refs-internal.h"
+#include "../iterator.h"
+#include "../lockfile.h"
-+#include "../chdir-notify.h"
-+
++#include "../refs.h"
+#include "../reftable/reftable.h"
++#include "../worktree.h"
++#include "refs-internal.h"
+
+extern struct ref_storage_be refs_be_reftable;
+
@@ refs/reftable-backend.c (new)
+
+ int err;
+ char *repo_dir;
++
+ char *reftable_dir;
-+ struct reftable_stack *stack;
++ char *worktree_reftable_dir;
++
++ struct reftable_stack *main_stack;
++ struct reftable_stack *worktree_stack;
+};
+
-+static int reftable_read_raw_ref(struct ref_store *ref_store,
-+ const char *refname, struct object_id *oid,
-+ struct strbuf *referent, unsigned int *type);
++static struct reftable_stack *stack_for(struct git_reftable_ref_store *store,
++ const char *refname)
++{
++ if (store->worktree_stack == NULL)
++ return store->main_stack;
++
++ switch (ref_type(refname)) {
++ case REF_TYPE_PER_WORKTREE:
++ case REF_TYPE_PSEUDOREF:
++ case REF_TYPE_OTHER_PSEUDOREF:
++ return store->worktree_stack;
++ default:
++ case REF_TYPE_MAIN_PSEUDOREF:
++ case REF_TYPE_NORMAL:
++ return store->main_stack;
++ }
++}
++
++static int git_reftable_read_raw_ref(struct ref_store *ref_store,
++ const char *refname, struct object_id *oid,
++ struct strbuf *referent,
++ unsigned int *type);
+
+static void clear_reftable_log_record(struct reftable_log_record *log)
+{
+ log->old_hash = NULL;
+ log->new_hash = NULL;
+ log->message = NULL;
-+ log->ref_name = NULL;
++ log->refname = NULL;
+ reftable_log_record_clear(log);
+}
+
@@ refs/reftable-backend.c (new)
+ log->tz_offset = sign * atoi(split.tz_begin);
+}
+
++static int has_suffix(struct strbuf *b, const char *suffix)
++{
++ size_t len = strlen(suffix);
++
++ if (len > b->len) {
++ return 0;
++ }
++
++ return 0 == strncmp(b->buf + b->len - len, suffix, len);
++}
++
++static int trim_component(struct strbuf *b)
++{
++ char *last;
++ last = strrchr(b->buf, '/');
++ if (!last)
++ return -1;
++ strbuf_setlen(b, last - b->buf);
++ return 0;
++}
++
++static int is_worktree(struct strbuf *b)
++{
++ if (trim_component(b) < 0) {
++ return 0;
++ }
++ if (!has_suffix(b, "/worktrees")) {
++ return 0;
++ }
++ trim_component(b);
++ return 1;
++}
++
+static struct ref_store *git_reftable_ref_store_create(const char *path,
+ unsigned int store_flags)
+{
@@ refs/reftable-backend.c (new)
+ .hash_id = the_hash_algo->format_id,
+ };
+ struct strbuf sb = STRBUF_INIT;
++ const char *gitdir = path;
++ struct strbuf wt_buf = STRBUF_INIT;
++ int wt = 0;
++
++ strbuf_addstr(&wt_buf, path);
++
++ /* this is clumsy, but the official worktree functions (eg.
++ * get_worktrees()) function will try to initialize a ref storage
++ * backend, leading to infinite recursion. */
++ wt = is_worktree(&wt_buf);
++ if (wt) {
++ gitdir = wt_buf.buf;
++ }
+
+ base_ref_store_init(ref_store, &refs_be_reftable);
+ refs->store_flags = store_flags;
-+ refs->repo_dir = xstrdup(path);
-+ strbuf_addf(&sb, "%s/reftable", path);
++ refs->repo_dir = xstrdup(gitdir);
++ strbuf_addf(&sb, "%s/reftable", gitdir);
+ refs->reftable_dir = xstrdup(sb.buf);
+ strbuf_reset(&sb);
+
-+ refs->err = reftable_new_stack(&refs->stack, refs->reftable_dir, cfg);
++ refs->err =
++ reftable_new_stack(&refs->main_stack, refs->reftable_dir, cfg);
+ assert(refs->err != REFTABLE_API_ERROR);
++
++ if (refs->err == 0 && wt) {
++ strbuf_addf(&sb, "%s/reftable", path);
++ refs->worktree_reftable_dir = xstrdup(sb.buf);
++
++ refs->err = reftable_new_stack(&refs->worktree_stack,
++ refs->worktree_reftable_dir,
++ cfg);
++ assert(refs->err != REFTABLE_API_ERROR);
++ }
++
+ strbuf_release(&sb);
++ strbuf_release(&wt_buf);
+ return ref_store;
+}
+
-+static int reftable_init_db(struct ref_store *ref_store, struct strbuf *err)
++static int git_reftable_init_db(struct ref_store *ref_store, struct strbuf *err)
+{
+ struct git_reftable_ref_store *refs =
+ (struct git_reftable_ref_store *)ref_store;
+ struct strbuf sb = STRBUF_INIT;
+
+ safe_create_dir(refs->reftable_dir, 1);
++ assert(refs->worktree_reftable_dir == NULL);
+
+ strbuf_addf(&sb, "%s/HEAD", refs->repo_dir);
+ write_file(sb.buf, "ref: refs/heads/.invalid");
@@ refs/reftable-backend.c (new)
+ struct reftable_ref_record ref;
+ struct object_id oid;
+ struct ref_store *ref_store;
++
++ /* In case we must iterate over 2 stacks, this is non-null. */
++ struct reftable_merged_table *merged;
+ unsigned int flags;
+ int err;
+ const char *prefix;
@@ refs/reftable-backend.c (new)
+ a PSEUDOREF, but a PER_WORKTREE, b/c each worktree can have
+ its own HEAD.
+ */
-+ ri->base.refname = ri->ref.ref_name;
++ ri->base.refname = ri->ref.refname;
+ if (ri->prefix != NULL &&
-+ strncmp(ri->prefix, ri->ref.ref_name, strlen(ri->prefix))) {
++ strncmp(ri->prefix, ri->ref.refname, strlen(ri->prefix))) {
+ ri->err = 1;
+ break;
+ }
@@ refs/reftable-backend.c (new)
+ } else if (ri->ref.target != NULL) {
+ int out_flags = 0;
+ const char *resolved = refs_resolve_ref_unsafe(
-+ ri->ref_store, ri->ref.ref_name,
++ ri->ref_store, ri->ref.refname,
+ RESOLVE_REF_READING, &ri->oid, &out_flags);
+ ri->base.flags = out_flags;
+ if (resolved == NULL &&
@@ refs/reftable-backend.c (new)
+ (struct git_reftable_iterator *)ref_iterator;
+ reftable_ref_record_clear(&ri->ref);
+ reftable_iterator_destroy(&ri->iter);
++ if (ri->merged) {
++ reftable_merged_table_free(ri->merged);
++ }
+ return 0;
+}
+
@@ refs/reftable-backend.c (new)
+};
+
+static struct ref_iterator *
-+reftable_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
-+ unsigned int flags)
++git_reftable_ref_iterator_begin(struct ref_store *ref_store, const char *prefix,
++ unsigned int flags)
+{
+ struct git_reftable_ref_store *refs =
+ (struct git_reftable_ref_store *)ref_store;
+ struct git_reftable_iterator *ri = xcalloc(1, sizeof(*ri));
-+ struct reftable_merged_table *mt = NULL;
+
+ if (refs->err < 0) {
+ ri->err = refs->err;
-+ } else {
-+ mt = reftable_stack_merged_table(refs->stack);
++ } else if (refs->worktree_stack == NULL) {
++ struct reftable_merged_table *mt =
++ reftable_stack_merged_table(refs->main_stack);
+ ri->err = reftable_merged_table_seek_ref(mt, &ri->iter, prefix);
++ } else {
++ struct reftable_merged_table *mt1 =
++ reftable_stack_merged_table(refs->main_stack);
++ struct reftable_merged_table *mt2 =
++ reftable_stack_merged_table(refs->worktree_stack);
++ struct reftable_table *tabs =
++ xcalloc(2, sizeof(struct reftable_table));
++ reftable_table_from_merged_table(&tabs[0], mt1);
++ reftable_table_from_merged_table(&tabs[1], mt2);
++ ri->err = reftable_new_merged_table(&ri->merged, tabs, 2,
++ the_hash_algo->format_id);
++ if (ri->err == 0)
++ ri->err = reftable_merged_table_seek_ref(
++ ri->merged, &ri->iter, prefix);
+ }
+
+ base_ref_iterator_init(&ri->base, &reftable_ref_iterator_vtable, 1);
@@ refs/reftable-backend.c (new)
+ struct ref_update *update = transaction->updates[i];
+ struct object_id old_oid;
+
-+ err = reftable_read_raw_ref(ref_store, update->refname,
-+ &old_oid, &referent,
-+ /* mutate input, like
-+ files-backend.c */
-+ &update->type);
++ err = git_reftable_read_raw_ref(ref_store, update->refname,
++ &old_oid, &referent,
++ /* mutate input, like
++ files-backend.c */
++ &update->type);
+ if (err < 0 && errno == ENOENT &&
+ is_null_oid(&update->old_oid)) {
+ err = 0;
@@ refs/reftable-backend.c (new)
+ return err;
+}
+
-+static int reftable_transaction_prepare(struct ref_store *ref_store,
-+ struct ref_transaction *transaction,
-+ struct strbuf *errbuf)
++static int git_reftable_transaction_prepare(struct ref_store *ref_store,
++ struct ref_transaction *transaction,
++ struct strbuf *errbuf)
+{
+ struct git_reftable_ref_store *refs =
+ (struct git_reftable_ref_store *)ref_store;
+ struct reftable_addition *add = NULL;
++ struct reftable_stack *stack =
++ transaction->nr ?
++ stack_for(refs, transaction->updates[0]->refname) :
++ refs->main_stack;
+ int err = refs->err;
+ if (err < 0) {
+ goto done;
+ }
+
-+ err = reftable_stack_reload(refs->stack);
++ err = reftable_stack_reload(stack);
+ if (err) {
+ goto done;
+ }
+
-+ err = reftable_stack_new_addition(&add, refs->stack);
++ err = reftable_stack_new_addition(&add, stack);
+ if (err) {
+ goto done;
+ }
@@ refs/reftable-backend.c (new)
+ return err;
+}
+
-+static int reftable_transaction_abort(struct ref_store *ref_store,
-+ struct ref_transaction *transaction,
-+ struct strbuf *err)
++static int git_reftable_transaction_abort(struct ref_store *ref_store,
++ struct ref_transaction *transaction,
++ struct strbuf *err)
+{
+ struct reftable_addition *add =
+ (struct reftable_addition *)transaction->backend_data;
@@ refs/reftable-backend.c (new)
+ struct ref_transaction *transaction = (struct ref_transaction *)arg;
+ struct git_reftable_ref_store *refs =
+ (struct git_reftable_ref_store *)transaction->ref_store;
-+ uint64_t ts = reftable_stack_next_update_index(refs->stack);
++ struct reftable_stack *stack =
++ stack_for(refs, transaction->updates[0]->refname);
++ uint64_t ts = reftable_stack_next_update_index(stack);
+ int err = 0;
+ int i = 0;
+ struct reftable_log_record *logs =
@@ refs/reftable-backend.c (new)
+ struct ref_update *u = sorted[i];
+ struct reftable_log_record *log = &logs[i];
+ fill_reftable_log_record(log);
-+ log->ref_name = (char *)u->refname;
++ log->refname = (char *)u->refname;
+ log->old_hash = u->old_oid.hash;
+ log->new_hash = u->new_oid.hash;
+ log->update_index = ts;
@@ refs/reftable-backend.c (new)
+ struct object_id peeled;
+
+ int peel_error = peel_object(&u->new_oid, &peeled);
-+ ref.ref_name = (char *)u->refname;
++ ref.refname = (char *)u->refname;
+
+ if (!is_null_oid(&u->new_oid)) {
+ ref.value = u->new_oid.hash;
@@ refs/reftable-backend.c (new)
+ return err;
+}
+
-+static int reftable_transaction_finish(struct ref_store *ref_store,
-+ struct ref_transaction *transaction,
-+ struct strbuf *errmsg)
++static int git_reftable_transaction_finish(struct ref_store *ref_store,
++ struct ref_transaction *transaction,
++ struct strbuf *errmsg)
+{
+ struct reftable_addition *add =
+ (struct reftable_addition *)transaction->backend_data;
@@ refs/reftable-backend.c (new)
+ }
+ }
+ }
-+
-+ err = reftable_addition_add(add, &write_transaction_table, transaction);
-+ if (err < 0) {
-+ goto done;
++ if (transaction->nr) {
++ err = reftable_addition_add(add, &write_transaction_table,
++ transaction);
++ if (err < 0) {
++ goto done;
++ }
+ }
+
+ err = reftable_addition_commit(add);
@@ refs/reftable-backend.c (new)
+}
+
+static int
-+reftable_transaction_initial_commit(struct ref_store *ref_store,
-+ struct ref_transaction *transaction,
-+ struct strbuf *errmsg)
++git_reftable_transaction_initial_commit(struct ref_store *ref_store,
++ struct ref_transaction *transaction,
++ struct strbuf *errmsg)
+{
-+ int err = reftable_transaction_prepare(ref_store, transaction, errmsg);
++ int err = git_reftable_transaction_prepare(ref_store, transaction,
++ errmsg);
+ if (err)
+ return err;
+
-+ return reftable_transaction_finish(ref_store, transaction, errmsg);
++ return git_reftable_transaction_finish(ref_store, transaction, errmsg);
+}
+
+struct write_pseudoref_arg {
@@ refs/reftable-backend.c (new)
+ }
+ }
+
-+ write_ref.ref_name = (char *)arg->pseudoref;
++ write_ref.refname = (char *)arg->pseudoref;
+ write_ref.update_index = ts;
+ if (!is_null_oid(arg->new_oid))
+ write_ref.value = (uint8_t *)arg->new_oid->hash;
@@ refs/reftable-backend.c (new)
+ return err;
+}
+
-+static int reftable_write_pseudoref(struct ref_store *ref_store,
-+ const char *pseudoref,
-+ const struct object_id *oid,
-+ const struct object_id *old_oid,
-+ struct strbuf *errbuf)
++static int git_reftable_write_pseudoref(struct ref_store *ref_store,
++ const char *pseudoref,
++ const struct object_id *oid,
++ const struct object_id *old_oid,
++ struct strbuf *errbuf)
+{
+ struct git_reftable_ref_store *refs =
+ (struct git_reftable_ref_store *)ref_store;
++ struct reftable_stack *stack = stack_for(refs, pseudoref);
+ struct write_pseudoref_arg arg = {
-+ .stack = refs->stack,
++ .stack = stack,
+ .pseudoref = pseudoref,
+ .new_oid = oid,
+ };
@@ refs/reftable-backend.c (new)
+ goto done;
+ }
+
-+ err = reftable_stack_reload(refs->stack);
++ err = reftable_stack_reload(stack);
+ if (err) {
+ goto done;
+ }
-+ err = reftable_stack_new_addition(&add, refs->stack);
++ err = reftable_stack_new_addition(&add, stack);
+ if (err) {
+ goto done;
+ }
@@ refs/reftable-backend.c (new)
+ const struct object_id *old_oid)
+{
+ struct strbuf errbuf = STRBUF_INIT;
-+ int ret = reftable_write_pseudoref(ref_store, pseudoref, &null_oid,
-+ old_oid, &errbuf);
++ int ret = git_reftable_write_pseudoref(ref_store, pseudoref, &null_oid,
++ old_oid, &errbuf);
+ /* XXX what to do with the error message? */
+ strbuf_release(&errbuf);
+ return ret;
@@ refs/reftable-backend.c (new)
+ reftable_writer_set_limits(writer, ts, ts);
+ for (i = 0; i < arg->refnames->nr; i++) {
+ struct reftable_ref_record ref = {
-+ .ref_name = (char *)arg->refnames->items[i].string,
++ .refname = (char *)arg->refnames->items[i].string,
+ .update_index = ts,
+ };
+ err = reftable_writer_add_ref(writer, &ref);
@@ refs/reftable-backend.c (new)
+ log.new_hash = NULL;
+ log.old_hash = NULL;
+ log.update_index = ts;
-+ log.ref_name = (char *)arg->refnames->items[i].string;
++ log.refname = (char *)arg->refnames->items[i].string;
+
-+ if (reftable_stack_read_ref(arg->stack, log.ref_name,
++ if (reftable_stack_read_ref(arg->stack, log.refname,
+ ¤t) == 0) {
+ log.old_hash = current.value;
+ }
@@ refs/reftable-backend.c (new)
+ return 0;
+}
+
-+static int reftable_delete_refs(struct ref_store *ref_store, const char *msg,
-+ struct string_list *refnames,
-+ unsigned int flags)
++static int git_reftable_delete_refs(struct ref_store *ref_store,
++ const char *msg,
++ struct string_list *refnames,
++ unsigned int flags)
+{
+ struct git_reftable_ref_store *refs =
+ (struct git_reftable_ref_store *)ref_store;
++ struct reftable_stack *stack =
++ stack_for(refs, refnames->items[0].string);
+ struct write_delete_refs_arg arg = {
-+ .stack = refs->stack,
++ .stack = stack,
+ .refnames = refnames,
+ .logmsg = msg,
+ .flags = flags,
@@ refs/reftable-backend.c (new)
+ }
+
+ string_list_sort(refnames);
-+ err = reftable_stack_reload(refs->stack);
++ err = reftable_stack_reload(stack);
+ if (err) {
+ goto done;
+ }
-+ err = reftable_stack_add(refs->stack, &write_delete_refs_table, &arg);
++ err = reftable_stack_add(stack, &write_delete_refs_table, &arg);
+done:
+ assert(err != REFTABLE_API_ERROR);
+ return err;
+}
+
-+static int reftable_pack_refs(struct ref_store *ref_store, unsigned int flags)
++static int git_reftable_pack_refs(struct ref_store *ref_store,
++ unsigned int flags)
+{
+ struct git_reftable_ref_store *refs =
+ (struct git_reftable_ref_store *)ref_store;
-+ if (refs->err < 0) {
-+ return refs->err;
++ int err = refs->err;
++ if (err < 0) {
++ return err;
+ }
-+ return reftable_stack_compact_all(refs->stack, NULL);
++ err = reftable_stack_compact_all(refs->main_stack, NULL);
++ if (err == 0 && refs->worktree_stack != NULL)
++ err = reftable_stack_compact_all(refs->worktree_stack, NULL);
++ return err;
+}
+
+struct write_create_symref_arg {
+ struct git_reftable_ref_store *refs;
++ struct reftable_stack *stack;
+ const char *refname;
+ const char *target;
+ const char *logmsg;
@@ refs/reftable-backend.c (new)
+{
+ struct write_create_symref_arg *create =
+ (struct write_create_symref_arg *)arg;
-+ uint64_t ts = reftable_stack_next_update_index(create->refs->stack);
++ uint64_t ts = reftable_stack_next_update_index(create->stack);
+ int err = 0;
+
+ struct reftable_ref_record ref = {
-+ .ref_name = (char *)create->refname,
++ .refname = (char *)create->refname,
+ .target = (char *)create->target,
+ .update_index = ts,
+ };
@@ refs/reftable-backend.c (new)
+ struct object_id old_oid;
+
+ fill_reftable_log_record(&log);
-+ log.ref_name = (char *)create->refname;
++ log.refname = (char *)create->refname;
+ log.message = (char *)create->logmsg;
+ log.update_index = ts;
+ if (refs_resolve_ref_unsafe(
@@ refs/reftable-backend.c (new)
+ if (log.old_hash != NULL || log.new_hash != NULL) {
+ err = reftable_writer_add_log(writer, &log);
+ }
-+ log.ref_name = NULL;
++ log.refname = NULL;
+ log.message = NULL;
+ log.old_hash = NULL;
+ log.new_hash = NULL;
@@ refs/reftable-backend.c (new)
+ return err;
+}
+
-+static int reftable_create_symref(struct ref_store *ref_store,
-+ const char *refname, const char *target,
-+ const char *logmsg)
++static int git_reftable_create_symref(struct ref_store *ref_store,
++ const char *refname, const char *target,
++ const char *logmsg)
+{
+ struct git_reftable_ref_store *refs =
+ (struct git_reftable_ref_store *)ref_store;
++ struct reftable_stack *stack = stack_for(refs, refname);
+ struct write_create_symref_arg arg = { .refs = refs,
++ .stack = stack,
+ .refname = refname,
+ .target = target,
+ .logmsg = logmsg };
@@ refs/reftable-backend.c (new)
+ if (err < 0) {
+ goto done;
+ }
-+ err = reftable_stack_reload(refs->stack);
++ err = reftable_stack_reload(stack);
+ if (err) {
+ goto done;
+ }
-+ err = reftable_stack_add(refs->stack, &write_create_symref_table, &arg);
++ err = reftable_stack_add(stack, &write_create_symref_table, &arg);
+done:
+ assert(err != REFTABLE_API_ERROR);
+ return err;
@@ refs/reftable-backend.c (new)
+ goto done;
+ }
+
-+ free(ref.ref_name);
-+ ref.ref_name = strdup(arg->newname);
++ free(ref.refname);
++ ref.refname = strdup(arg->newname);
+ reftable_writer_set_limits(writer, ts, ts);
+ ref.update_index = ts;
+
+ {
+ struct reftable_ref_record todo[2] = { { NULL } };
-+ todo[0].ref_name = (char *)arg->oldname;
++ todo[0].refname = (char *)arg->oldname;
+ todo[0].update_index = ts;
+ /* leave todo[0] empty */
+ todo[1] = ref;
@@ refs/reftable-backend.c (new)
+ fill_reftable_log_record(&todo[0]);
+ fill_reftable_log_record(&todo[1]);
+
-+ todo[0].ref_name = (char *)arg->oldname;
++ todo[0].refname = (char *)arg->oldname;
+ todo[0].update_index = ts;
+ todo[0].message = (char *)arg->logmsg;
+ todo[0].old_hash = ref.value;
+ todo[0].new_hash = NULL;
+
-+ todo[1].ref_name = (char *)arg->newname;
++ todo[1].refname = (char *)arg->newname;
+ todo[1].update_index = ts;
+ todo[1].old_hash = NULL;
+ todo[1].new_hash = ref.value;
@@ refs/reftable-backend.c (new)
+ return err;
+}
+
-+static int reftable_rename_ref(struct ref_store *ref_store,
-+ const char *oldrefname, const char *newrefname,
-+ const char *logmsg)
++static int git_reftable_rename_ref(struct ref_store *ref_store,
++ const char *oldrefname,
++ const char *newrefname, const char *logmsg)
+{
+ struct git_reftable_ref_store *refs =
+ (struct git_reftable_ref_store *)ref_store;
++ struct reftable_stack *stack = stack_for(refs, newrefname);
+ struct write_rename_arg arg = {
-+ .stack = refs->stack,
++ .stack = stack,
+ .oldname = oldrefname,
+ .newname = newrefname,
+ .logmsg = logmsg,
@@ refs/reftable-backend.c (new)
+ if (err < 0) {
+ goto done;
+ }
-+ err = reftable_stack_reload(refs->stack);
++ err = reftable_stack_reload(stack);
+ if (err) {
+ goto done;
+ }
+
-+ err = reftable_stack_add(refs->stack, &write_rename_table, &arg);
++ err = reftable_stack_add(stack, &write_rename_table, &arg);
+done:
+ assert(err != REFTABLE_API_ERROR);
+ return err;
+}
+
-+static int reftable_copy_ref(struct ref_store *ref_store,
-+ const char *oldrefname, const char *newrefname,
-+ const char *logmsg)
++static int git_reftable_copy_ref(struct ref_store *ref_store,
++ const char *oldrefname, const char *newrefname,
++ const char *logmsg)
+{
+ BUG("reftable reference store does not support copying references");
+}
+
-+struct reftable_reflog_ref_iterator {
++struct git_reftable_reflog_ref_iterator {
+ struct ref_iterator base;
+ struct reftable_iterator iter;
+ struct reftable_log_record log;
+ struct object_id oid;
++
++ /* Used when iterating over worktree & main */
++ struct reftable_merged_table *merged;
+ char *last_name;
+};
+
+static int
-+reftable_reflog_ref_iterator_advance(struct ref_iterator *ref_iterator)
++git_reftable_reflog_ref_iterator_advance(struct ref_iterator *ref_iterator)
+{
-+ struct reftable_reflog_ref_iterator *ri =
-+ (struct reftable_reflog_ref_iterator *)ref_iterator;
++ struct git_reftable_reflog_ref_iterator *ri =
++ (struct git_reftable_reflog_ref_iterator *)ref_iterator;
+
+ while (1) {
+ int err = reftable_iterator_next_log(&ri->iter, &ri->log);
@@ refs/reftable-backend.c (new)
+ return ITER_ERROR;
+ }
+
-+ ri->base.refname = ri->log.ref_name;
++ ri->base.refname = ri->log.refname;
+ if (ri->last_name != NULL &&
-+ !strcmp(ri->log.ref_name, ri->last_name)) {
++ !strcmp(ri->log.refname, ri->last_name)) {
+ /* we want the refnames that we have reflogs for, so we
+ * skip if we've already produced this name. This could
+ * be faster by seeking directly to
@@ refs/reftable-backend.c (new)
+ }
+
+ free(ri->last_name);
-+ ri->last_name = xstrdup(ri->log.ref_name);
++ ri->last_name = xstrdup(ri->log.refname);
+ hashcpy(ri->oid.hash, ri->log.new_hash);
+ return ITER_OK;
+ }
+}
+
-+static int reftable_reflog_ref_iterator_peel(struct ref_iterator *ref_iterator,
-+ struct object_id *peeled)
++static int
++git_reftable_reflog_ref_iterator_peel(struct ref_iterator *ref_iterator,
++ struct object_id *peeled)
+{
+ BUG("not supported.");
+ return -1;
+}
+
-+static int reftable_reflog_ref_iterator_abort(struct ref_iterator *ref_iterator)
++static int
++git_reftable_reflog_ref_iterator_abort(struct ref_iterator *ref_iterator)
+{
-+ struct reftable_reflog_ref_iterator *ri =
-+ (struct reftable_reflog_ref_iterator *)ref_iterator;
++ struct git_reftable_reflog_ref_iterator *ri =
++ (struct git_reftable_reflog_ref_iterator *)ref_iterator;
+ reftable_log_record_clear(&ri->log);
+ reftable_iterator_destroy(&ri->iter);
++ if (ri->merged)
++ reftable_merged_table_free(ri->merged);
+ return 0;
+}
+
-+static struct ref_iterator_vtable reftable_reflog_ref_iterator_vtable = {
-+ reftable_reflog_ref_iterator_advance, reftable_reflog_ref_iterator_peel,
-+ reftable_reflog_ref_iterator_abort
++static struct ref_iterator_vtable git_reftable_reflog_ref_iterator_vtable = {
++ git_reftable_reflog_ref_iterator_advance,
++ git_reftable_reflog_ref_iterator_peel,
++ git_reftable_reflog_ref_iterator_abort
+};
+
+static struct ref_iterator *
-+reftable_reflog_iterator_begin(struct ref_store *ref_store)
++git_reftable_reflog_iterator_begin(struct ref_store *ref_store)
+{
-+ struct reftable_reflog_ref_iterator *ri = xcalloc(sizeof(*ri), 1);
++ struct git_reftable_reflog_ref_iterator *ri = xcalloc(sizeof(*ri), 1);
+ struct git_reftable_ref_store *refs =
+ (struct git_reftable_ref_store *)ref_store;
+
-+ struct reftable_merged_table *mt =
-+ reftable_stack_merged_table(refs->stack);
-+ int err = reftable_merged_table_seek_log(mt, &ri->iter, "");
-+ if (err < 0) {
-+ free(ri);
-+ return NULL;
++ if (refs->worktree_stack == NULL) {
++ struct reftable_stack *stack = refs->main_stack;
++ struct reftable_merged_table *mt =
++ reftable_stack_merged_table(stack);
++ int err = reftable_merged_table_seek_log(mt, &ri->iter, "");
++ if (err < 0) {
++ free(ri);
++ /* XXX is this allowed? */
++ return NULL;
++ }
++ } else {
++ struct reftable_merged_table *mt1 =
++ reftable_stack_merged_table(refs->main_stack);
++ struct reftable_merged_table *mt2 =
++ reftable_stack_merged_table(refs->worktree_stack);
++ struct reftable_table *tabs =
++ xcalloc(2, sizeof(struct reftable_table));
++ int err = 0;
++ reftable_table_from_merged_table(&tabs[0], mt1);
++ reftable_table_from_merged_table(&tabs[1], mt2);
++ err = reftable_new_merged_table(&ri->merged, tabs, 2,
++ the_hash_algo->format_id);
++ if (err < 0) {
++ free(tabs);
++ /* XXX see above */
++ return NULL;
++ }
++ err = reftable_merged_table_seek_ref(ri->merged, &ri->iter, "");
++ if (err < 0) {
++ return NULL;
++ }
+ }
-+
-+ base_ref_iterator_init(&ri->base, &reftable_reflog_ref_iterator_vtable,
-+ 1);
++ base_ref_iterator_init(&ri->base,
++ &git_reftable_reflog_ref_iterator_vtable, 1);
+ ri->base.oid = &ri->oid;
+
+ return (struct ref_iterator *)ri;
+}
+
-+static int
-+reftable_for_each_reflog_ent_newest_first(struct ref_store *ref_store,
-+ const char *refname,
-+ each_reflog_ent_fn fn, void *cb_data)
++static int git_reftable_for_each_reflog_ent_newest_first(
++ struct ref_store *ref_store, const char *refname, each_reflog_ent_fn fn,
++ void *cb_data)
+{
+ struct reftable_iterator it = { NULL };
+ struct git_reftable_ref_store *refs =
+ (struct git_reftable_ref_store *)ref_store;
++ struct reftable_stack *stack = stack_for(refs, refname);
+ struct reftable_merged_table *mt = NULL;
+ int err = 0;
+ struct reftable_log_record log = { NULL };
@@ refs/reftable-backend.c (new)
+ return refs->err;
+ }
+
-+ mt = reftable_stack_merged_table(refs->stack);
++ mt = reftable_stack_merged_table(stack);
+ err = reftable_merged_table_seek_log(mt, &it, refname);
+ while (err == 0) {
+ struct object_id old_oid;
@@ refs/reftable-backend.c (new)
+ break;
+ }
+
-+ if (strcmp(log.ref_name, refname)) {
++ if (strcmp(log.refname, refname)) {
+ break;
+ }
+
@@ refs/reftable-backend.c (new)
+ return err;
+}
+
-+static int
-+reftable_for_each_reflog_ent_oldest_first(struct ref_store *ref_store,
-+ const char *refname,
-+ each_reflog_ent_fn fn, void *cb_data)
++static int git_reftable_for_each_reflog_ent_oldest_first(
++ struct ref_store *ref_store, const char *refname, each_reflog_ent_fn fn,
++ void *cb_data)
+{
+ struct reftable_iterator it = { NULL };
+ struct git_reftable_ref_store *refs =
+ (struct git_reftable_ref_store *)ref_store;
++ struct reftable_stack *stack = stack_for(refs, refname);
+ struct reftable_merged_table *mt = NULL;
+ struct reftable_log_record *logs = NULL;
+ int cap = 0;
@@ refs/reftable-backend.c (new)
+ if (refs->err < 0) {
+ return refs->err;
+ }
-+ mt = reftable_stack_merged_table(refs->stack);
++ mt = reftable_stack_merged_table(stack);
+ err = reftable_merged_table_seek_log(mt, &it, refname);
+
+ while (err == 0) {
@@ refs/reftable-backend.c (new)
+ break;
+ }
+
-+ if (strcmp(log.ref_name, refname)) {
++ if (strcmp(log.refname, refname)) {
+ break;
+ }
+
@@ refs/reftable-backend.c (new)
+ return err;
+}
+
-+static int reftable_reflog_exists(struct ref_store *ref_store,
-+ const char *refname)
++static int git_reftable_reflog_exists(struct ref_store *ref_store,
++ const char *refname)
+{
+ /* always exists. */
+ return 1;
+}
+
-+static int reftable_create_reflog(struct ref_store *ref_store,
-+ const char *refname, int force_create,
-+ struct strbuf *err)
++static int git_reftable_create_reflog(struct ref_store *ref_store,
++ const char *refname, int force_create,
++ struct strbuf *err)
+{
+ return 0;
+}
+
-+static int reftable_delete_reflog(struct ref_store *ref_store,
-+ const char *refname)
++static int git_reftable_delete_reflog(struct ref_store *ref_store,
++ const char *refname)
+{
+ return 0;
+}
+
+struct reflog_expiry_arg {
+ struct git_reftable_ref_store *refs;
++ struct reftable_stack *stack;
+ struct reftable_log_record *tombstones;
+ int len;
+ int cap;
@@ refs/reftable-backend.c (new)
+ const char *refname, uint64_t ts)
+{
+ struct reftable_log_record tombstone = {
-+ .ref_name = xstrdup(refname),
++ .refname = xstrdup(refname),
+ .update_index = ts,
+ };
+ if (arg->len == arg->cap) {
@@ refs/reftable-backend.c (new)
+static int write_reflog_expiry_table(struct reftable_writer *writer, void *argv)
+{
+ struct reflog_expiry_arg *arg = (struct reflog_expiry_arg *)argv;
-+ uint64_t ts = reftable_stack_next_update_index(arg->refs->stack);
++ uint64_t ts = reftable_stack_next_update_index(arg->stack);
+ int i = 0;
+ reftable_writer_set_limits(writer, ts, ts);
+ for (i = 0; i < arg->len; i++) {
@@ refs/reftable-backend.c (new)
+ return 0;
+}
+
-+static int reftable_reflog_expire(struct ref_store *ref_store,
-+ const char *refname,
-+ const struct object_id *oid,
-+ unsigned int flags,
-+ reflog_expiry_prepare_fn prepare_fn,
-+ reflog_expiry_should_prune_fn should_prune_fn,
-+ reflog_expiry_cleanup_fn cleanup_fn,
-+ void *policy_cb_data)
++static int
++git_reftable_reflog_expire(struct ref_store *ref_store, const char *refname,
++ const struct object_id *oid, unsigned int flags,
++ reflog_expiry_prepare_fn prepare_fn,
++ reflog_expiry_should_prune_fn should_prune_fn,
++ reflog_expiry_cleanup_fn cleanup_fn,
++ void *policy_cb_data)
+{
+ /*
+ For log expiry, we write tombstones in place of the expired entries,
@@ refs/reftable-backend.c (new)
+ stack, and expiring entries paradoxically takes extra memory.
+
+ This memory is only reclaimed when some operation issues a
-+ reftable_pack_refs(), which will compact the entire stack and get rid
-+ of deletion entries.
++ git_reftable_pack_refs(), which will compact the entire stack and get
++ rid of deletion entries.
+
+ It would be better if the refs backend supported an API that sets a
+ criterion for all refs, passing the criterion to pack_refs().
+ */
+ struct git_reftable_ref_store *refs =
+ (struct git_reftable_ref_store *)ref_store;
++ struct reftable_stack *stack = stack_for(refs, refname);
+ struct reftable_merged_table *mt = NULL;
+ struct reflog_expiry_arg arg = {
++ .stack = stack,
+ .refs = refs,
+ };
+ struct reftable_log_record log = { NULL };
@@ refs/reftable-backend.c (new)
+ if (refs->err < 0) {
+ return refs->err;
+ }
-+ err = reftable_stack_reload(refs->stack);
++ err = reftable_stack_reload(stack);
+ if (err) {
+ goto done;
+ }
+
-+ mt = reftable_stack_merged_table(refs->stack);
++ mt = reftable_stack_merged_table(stack);
+ err = reftable_merged_table_seek_log(mt, &it, refname);
+ if (err < 0) {
+ goto done;
@@ refs/reftable-backend.c (new)
+ goto done;
+ }
+
-+ if (err > 0 || strcmp(log.ref_name, refname)) {
++ if (err > 0 || strcmp(log.refname, refname)) {
+ break;
+ }
+ hashcpy(ooid.hash, log.old_hash);
@@ refs/reftable-backend.c (new)
+ add_log_tombstone(&arg, refname, log.update_index);
+ }
+ }
-+ err = reftable_stack_add(refs->stack, &write_reflog_expiry_table, &arg);
++ err = reftable_stack_add(stack, &write_reflog_expiry_table, &arg);
+
+done:
+ assert(err != REFTABLE_API_ERROR);
@@ refs/reftable-backend.c (new)
+ return err;
+}
+
-+static int reftable_read_raw_ref(struct ref_store *ref_store,
-+ const char *refname, struct object_id *oid,
-+ struct strbuf *referent, unsigned int *type)
++static int git_reftable_read_raw_ref(struct ref_store *ref_store,
++ const char *refname, struct object_id *oid,
++ struct strbuf *referent,
++ unsigned int *type)
+{
+ struct git_reftable_ref_store *refs =
+ (struct git_reftable_ref_store *)ref_store;
++ struct reftable_stack *stack = stack_for(refs, refname);
++
+ struct reftable_ref_record ref = { NULL };
+ int err = 0;
+ if (refs->err < 0) {
@@ refs/reftable-backend.c (new)
+ /* This is usually not needed, but Git doesn't signal to ref backend if
+ a subprocess updated the ref DB. So we always check.
+ */
-+ err = reftable_stack_reload(refs->stack);
++ err = reftable_stack_reload(stack);
+ if (err) {
+ goto done;
+ }
+
-+ err = reftable_stack_read_ref(refs->stack, refname, &ref);
++ err = reftable_stack_read_ref(stack, refname, &ref);
+ if (err > 0) {
+ errno = ENOENT;
+ err = -1;
@@ refs/reftable-backend.c (new)
+ &refs_be_files,
+ "reftable",
+ git_reftable_ref_store_create,
-+ reftable_init_db,
-+ reftable_transaction_prepare,
-+ reftable_transaction_finish,
-+ reftable_transaction_abort,
-+ reftable_transaction_initial_commit,
-+
-+ reftable_pack_refs,
-+ reftable_create_symref,
-+ reftable_delete_refs,
-+ reftable_rename_ref,
-+ reftable_copy_ref,
-+
-+ reftable_write_pseudoref,
++ git_reftable_init_db,
++ git_reftable_transaction_prepare,
++ git_reftable_transaction_finish,
++ git_reftable_transaction_abort,
++ git_reftable_transaction_initial_commit,
++
++ git_reftable_pack_refs,
++ git_reftable_create_symref,
++ git_reftable_delete_refs,
++ git_reftable_rename_ref,
++ git_reftable_copy_ref,
++
++ git_reftable_write_pseudoref,
+ reftable_delete_pseudoref,
+
-+ reftable_ref_iterator_begin,
-+ reftable_read_raw_ref,
++ git_reftable_ref_iterator_begin,
++ git_reftable_read_raw_ref,
+
-+ reftable_reflog_iterator_begin,
-+ reftable_for_each_reflog_ent_oldest_first,
-+ reftable_for_each_reflog_ent_newest_first,
-+ reftable_reflog_exists,
-+ reftable_create_reflog,
-+ reftable_delete_reflog,
-+ reftable_reflog_expire,
++ git_reftable_reflog_iterator_begin,
++ git_reftable_for_each_reflog_ent_oldest_first,
++ git_reftable_for_each_reflog_ent_newest_first,
++ git_reftable_reflog_exists,
++ git_reftable_create_reflog,
++ git_reftable_delete_reflog,
++ git_reftable_reflog_expire,
+};
## reftable/update.sh ##
@@ t/t0031-reftable.sh (new)
+ test -f file2
+'
+
++test_expect_success 'worktrees' '
++ git init --ref-storage=reftable start &&
++ (cd start && test_commit file1 && git checkout -b branch1 &&
++ git checkout -b branch2 &&
++ git worktree add ../wt
++ ) &&
++ cd wt &&
++ git checkout branch1 &&
++ git branch
++'
++
++test_expect_success 'worktrees 2' '
++ initialize &&
++ test_commit file1 &&
++ mkdir existing_empty &&
++ git worktree add --detach existing_empty master
++'
++
+test_done
+
14: eafd8eeefc = 15: 04c86e7395 Hookup unittests for the reftable library.
15: 46af142f33 ! 16: c751265705 Add GIT_DEBUG_REFS debugging mechanism
@@ Makefile: LIB_OBJS += rebase.o
LIB_OBJS += refs/reftable-backend.o
LIB_OBJS += refs/iterator.o
+ ## builtin/worktree.c ##
+@@ builtin/worktree.c: static int add_worktree(const char *path, const char *refname,
+ * worktree.
+ */
+ strbuf_reset(&sb);
+- if (get_main_ref_store(the_repository)->be == &refs_be_reftable) {
++
++ /* XXX: check GIT_TEST_REFTABLE because GIT_DEBUG_REFS obscures the
++ * instance type. */
++ if (get_main_ref_store(the_repository)->be == &refs_be_reftable ||
++ getenv("GIT_TEST_REFTABLE") != NULL) {
+ /* 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");
+
## refs.c ##
@@ refs.c: struct ref_store *get_main_ref_store(struct repository *r)
- r->ref_storage_format :
- default_ref_storage(),
+ r->ref_storage_format :
+ default_ref_storage(),
REF_STORE_ALL_CAPS);
+ if (getenv("GIT_DEBUG_REFS")) {
-+ r->refs_private = debug_wrap(r->refs_private);
++ r->refs_private = debug_wrap(r->gitdir, r->refs_private);
+ }
return r->refs_private;
}
@@ refs/debug.c (new)
+};
+
+extern struct ref_storage_be refs_be_debug;
-+struct ref_store *debug_wrap(struct ref_store *store);
++struct ref_store *debug_wrap(const char *gitdir, struct ref_store *store);
+
-+struct ref_store *debug_wrap(struct ref_store *store)
++struct ref_store *debug_wrap(const char *gitdir, struct ref_store *store)
+{
+ struct debug_ref_store *res = malloc(sizeof(struct debug_ref_store));
++ fprintf(stderr, "ref_store for %s\n", gitdir);
+ res->refs = store;
+ base_ref_store_init((struct ref_store *)res, &refs_be_debug);
+ return (struct ref_store *)res;
@@ refs/debug.c (new)
+ struct debug_ref_iterator *diter =
+ (struct debug_ref_iterator *)ref_iterator;
+ int res = diter->iter->vtable->advance(diter->iter);
-+ fprintf(stderr, "iterator_advance: %s: %d\n", diter->iter->refname,
-+ res);
++ if (res)
++ fprintf(stderr, "iterator_advance: %d\n", res);
++ else
++ fprintf(stderr, "iterator_advance before: %s\n",
++ diter->iter->refname);
++
+ diter->base.ordered = diter->iter->ordered;
+ diter->base.refname = diter->iter->refname;
+ diter->base.oid = diter->iter->oid;
@@ refs/refs-internal.h: struct ref_store {
+ * Print out ref operations as they occur. Useful for debugging alternate ref
+ * backends.
+ */
-+struct ref_store *debug_wrap(struct ref_store *store);
++struct ref_store *debug_wrap(const char *gitdir, struct ref_store *store);
+
#endif /* REFS_REFS_INTERNAL_H */16: 5211c64310 = 17: ef0dd45f07 vcxproj: adjust for the reftable changes 17: 9724854088 = 18: d4dc1deae5 git-prompt: prepare for reftable refs backend 18: ece1fa1f62 = 19: 5a85abf9c4 Add reftable testing infrastructure 19: 991abf9e1b = 20: 0ebf7beb95 Add "test-tool dump-reftable" command.
Signature · 1 line
gitgitgadget
PATCH v19 1/20 lib-t6000.sh: write tag using git-update-ref
From: Han-Wen Nienhuys <[email protected]>
--- t/lib-t6000.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
t/lib-t6000.sh +2 -3
@@ -1,7 +1,5 @@ : included from 6002 and others -mkdir -p .git/refs/tags - >sed.script # Answer the sha1 has associated with the tag. The tag must exist under refs/tags
@@ -26,7 +24,8 @@ save_tag () { _tag=$1 test -n "$_tag" || error "usage: save_tag tag commit-args ..." shift 1 - "$@" >".git/refs/tags/$_tag" + + git update-ref "refs/tags/$_tag" $("$@") echo "s/$(tag $_tag)/$_tag/g" >sed.script.tmp cat sed.script >>sed.script.tmp
Signature · 1 line
gitgitgadget
PATCH v19 2/20 t3432: use git-reflog to inspect the reflog for HEAD
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- t/t3432-rebase-fast-forward.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
t/t3432-rebase-fast-forward.sh +4 -3
@@ -60,15 +60,16 @@ test_rebase_same_head_ () { fi && oldhead=\$(git rev-parse HEAD) && test_when_finished 'git reset --hard \$oldhead' && - cp .git/logs/HEAD expect && + git reflog HEAD > expect && git rebase$flag $* >stdout && + git reflog HEAD > actual && if test $what = work then old=\$(wc -l <expect) && - test_line_count '-gt' \$old .git/logs/HEAD + test_line_count '-gt' \$old actual elif test $what = noop then - test_cmp expect .git/logs/HEAD + test_cmp expect actual fi && newhead=\$(git rev-parse HEAD) && if test $cmp = same
Signature · 1 line
gitgitgadget
Re: [PATCH v19 02/20] t3432: use git-reflog to inspect the reflog for HEAD
Hi Han-Wen,
Quoted 12 lines · Han-Wen Nienhuys via GitGitGadget
From: Han-Wen Nienhuys <[email protected]>
Signed-off-by Han-Wen Nienhuys <[email protected]>--- t/t3432-rebase-fast-forward.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)t/t3432-rebase-fast-forward.sh +0
-0
t/t3432-rebase-fast-forward.sh test_rebase_same_head_ () { · line 63
Quoted 5 lines
t/t3432-rebase-fast-forward.sh +1
-1@@ -60,15 +60,16 @@ test_rebase_same_head_ () { fi && oldhead=\$(git rev-parse HEAD) && test_when_finished 'git reset --hard \$oldhead' && - cp .git/logs/HEAD expect && + git reflog HEAD > expect &&
Tiny nit: there should be no space present after the redirect operator, so it should be written like this
git reflog HEAD >expect
t/t3432-rebase-fast-forward.sh test_rebase_same_head_ () { · line 65
Quoted 2 lines
t/t3432-rebase-fast-forward.sh +1
-0git rebase$flag $* >stdout && + git reflog HEAD > actual &&
Same here.
Quoted 15 lines
t/t3432-rebase-fast-forward.sh +2
-2if test $what = work then old=\$(wc -l <expect) && - test_line_count '-gt' \$old .git/logs/HEAD + test_line_count '-gt' \$old actual elif test $what = noop then - test_cmp expect .git/logs/HEAD + test_cmp expect actual fi && newhead=\$(git rev-parse HEAD) && if test $cmp = sameSignature · 1 line
gitgitgadget
PATCH v19 3/20 checkout: add '\n' to reflog message
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
Reftable precisely reproduces the given message. This leads to differences, because the files backend implicitly adds a trailing '\n' to all messages.
--- builtin/checkout.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
builtin/checkout.c +3 -2
@@ -884,8 +884,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts, reflog_msg =getenv ("GIT_REFLOG_ACTION" );if (!reflog_msg) -strbuf_addf (&msg,"checkout: moving from %s to %s" , - old_desc ? old_desc :"(invalid)" , new_branch_info->name); +strbuf_addf (&msg,"checkout: moving from %s to %s\n" , + old_desc ? old_desc :"(invalid)" , + new_branch_info->name);else strbuf_insertstr (&msg,0 , reflog_msg);
Signature · 1 line
gitgitgadget
Re: [PATCH v19 03/20] checkout: add '\n' to reflog message
Quoted 4 lines · Han-Wen Nienhuys via GitGitGadget
From: Han-Wen Nienhuys <[email protected]>
Reftable precisely reproduces the given message. This leads to differences, because the files backend implicitly adds a trailing '\n' to all messages.
What does this mean? With the files backend we'll now see a redundant two LFs in a row? I think you are careful enough not to introduce unnecessary compatibility breakage like that, so perhaps "implicitly adds" is a wrong way to characterize what happens in the files backend, and it only adds LF when the message does not end with one, but does not add an extra one if not necessary?
If so, then the change in the patch does not break compatibility, but the above description does not give readers confidence that it is indeed the case. IOW it is unclear how this change manages to avoid breaking existing code.
Sorry, but I am left puzzled.
Quoted 21 lines
Signed-off-by Han-Wen Nienhuys <[email protected]>--- builtin/checkout.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)builtin/checkout.c +3
-2@@ -884,8 +884,9 @@ static void update_refs_for_switch(const struct checkout_opts *opts, reflog_msg =getenv ("GIT_REFLOG_ACTION" );if (!reflog_msg) -strbuf_addf (&msg,"checkout: moving from %s to %s" , - old_desc ? old_desc :"(invalid)" , new_branch_info->name); +strbuf_addf (&msg,"checkout: moving from %s to %s\n" , + old_desc ? old_desc :"(invalid)" , + new_branch_info->name);else strbuf_insertstr (&msg,0 , reflog_msg);
Re: [PATCH v19 03/20] checkout: add '\n' to reflog message
Quoted 15 lines · Junio C Hamano
Quoted 4 lines · Han-Wen Nienhuys via GitGitGadget
From: Han-Wen Nienhuys <[email protected]>
Reftable precisely reproduces the given message. This leads to differences, because the files backend implicitly adds a trailing '\n' to all messages.
What does this mean? With the files backend we'll now see a redundant two LFs in a row? I think you are careful enough not to introduce unnecessary compatibility breakage like that, so perhaps "implicitly adds" is a wrong way to characterize what happens in the files backend, and it only adds LF when the message does not end with one, but does not add an extra one if not necessary?
Quoted 6 lines
If so, then the change in the patch does not break compatibility, but the above description does not give readers confidence that it is indeed the case. IOW it is unclear how this change manages to avoid breaking existing code.
Sorry, but I am left puzzled.
Most places that write a reflog message use a message that ends with '\n'.
Some places (the one mentioned here) do not append a '\n'. When it is read back, the message does have a '\n', leading to discrepancies with reftable (which faithfully reproduced the message without '\n')
I initially thought we could or should fix this across git-core, but I think it will be a lot of work to find all occurrences, so I implemented
https://github.com/google/reftable/commit/785f745daf567aeabe47cb01024ea879b9e15c2f
which does extra validation on reflog messages, and normalizes the '\n' at the end. Maybe this is also worth discussing in the reftable
spec: in reftable, you can use multi-line reflog messages, but that seems fundamentally impossible in the traditional file storage.
I can drop this commit from the series, but I think it would be a useful cleanup anyways.
--
Han-Wen Nienhuys - Google Munich
I work 80%. Don't expect answers from me on Fridays. --
Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
Geschäftsführer: Paul Manicle, Halimah DeLaine Prado --
Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich
Registergericht und -nummer: Hamburg, HRB 86891
Sitz der Gesellschaft: Hamburg
Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Re: [PATCH v19 03/20] checkout: add '\n' to reflog message
Quoted 3 lines · Han-Wen Nienhuys
Most places that write a reflog message use a message that ends with '\n'.
Some places (the one mentioned here) do not append a '\n'.
Just to make sure I am following, I see
if (update_ref("initial pull", "HEAD", merge_head, ...in bultin/pull.c; this is supposed to be written with "\n" under the new world order?
As reflog messages are supposed to be a single liner (due to the nature of the original storage format), I have a feeling that it is cumbersome to the programmers to requre "\n" at the end. It would not add much value to the readability of the resulting code, either.
If the places that have hardcoded messages like the above have to be written like so:
if (update_ref("initial pull\n", "HEAD", merge_head, ...it makes the code worse.
Let's see if I can come up with a coherent "rule" that we can follow to improve the current situation that is an undisciplined mixture as you found out.
... goes and looks the current code ...
So, what happens is that update->msg in each ref transaction (in refs.c::ref_transaction_add_update()) gets verbatim copy of what the caller gives the refs API, but when it is written out as an reflog entry, the message is cleansed by calling refs.c::copy_reflog_msg() that squashes a run of whitespaces (not just SP but including LF and HT) into a single SP and then trims whitespaces at the end.
Then to the result, a LF is unconditionally added.
I would consider the massaging of the application-supplied message done in copy_reflog_msg() as normalization, which turns possibly invalid data (like a message that has LF anywhere in it) into an acceptable form. Which defines the kosher form of a reflog message as not having any LF (or excess SP) in it, and the terminating LF is not part of the message. From that point of view, if we were to do something, I would rather standardize on not having the final (and only) LF.
But some callers that prepare a message in a string buffer may find it easier not having to strip the trailing LF, or even depend on the fact that they can feed a string split into multiple lines and the result becomes a single line. So I do not see a point in "fixing" the existing callers that gives an extra LF at the end or other not-so-kosher form of data---these will be normalized by calling copy_reflog_msg() anyway.
If they are easy to fix, like sending a constant string with LF at the end, we would want to fix them to improve the readability of the resulting code, though. If the update_ref() call in builtin/pull.c were sending "initial pull\n" as its first parameter, it would be worth fixing by removing the LF, as the result would be easier to read and it is not too much work. On the other hand, if the message that eventually becomes a reflog entry comes from outside and if the existing code relies on the sanitizing done by copy_reflog_msg(), I do not see a point in carefully removing the LF at the end on the caller's side, either.
Quoted 2 lines
I initially thought we could or should fix this across git-core, but I think it will be a lot of work to find all occurrences.
I think we are on the same page, even if the definition of which is correct might be different between us. As long as all the refs backend does the same sanitizing (which is probably easy---just call copy_reflog_msg() from them, or perhaps we may want to move the call to where update->msg are queued for each step in a transaction, which would relieve individual backends from having to worry about this issue at all), they would all behave the same for the same reflog data.
Thanks.
Re: [PATCH v19 03/20] checkout: add '\n' to reflog message
Quoted 5 lines · Junio C Hamano
Quoted 2 lines
I initially thought we could or should fix this across git-core, but I think it will be a lot of work to find all occurrences.
I think we are on the same page, even if the definition of which is correct might be different between us. As long as all the refs
Right. The practical upshot of this is that I should drop this patch, though.
Signature · 1 line
Han-Wen Nienhuys - [email protected] - http://www.xs4all.nl/~hanwen
Re* [PATCH v19 03/20] checkout: add '\n' to reflog message
Quoted 8 lines · Han-Wen Nienhuys
Quoted 5 lines · Junio C Hamano
Quoted 2 lines
I initially thought we could or should fix this across git-core, but I think it will be a lot of work to find all occurrences.
I think we are on the same page, even if the definition of which is correct might be different between us. As long as all the refs
Right. The practical upshot of this is that I should drop this patch, though.
I guess we are on the same page on that one, too, even if our definition of which side is up and which side is down may be different ;-)
I think this can be dropped, as it does not even try to enforce "all reflog message arguments must end with LF".
At the same time, we would need to make sure that reftable backend cleanses the reflog message the callers of refs API supplies the same way as the files backend. In short, if a series of Git operations that accumulate records in a reflog are performed with either the files or the reftable backend, "git log -g" and other forms to retrieve these messages should produce identical results from both backends.
What I observed during this review was:
- We expect that a reflog message consists of a single line. The file format used by the files backend may add a LF after the message as a delimiter, and output by commands like "git log -g" may complete such an incomplete line by adding a LF at the end, but philosophically, the terminating LF is not a part of the message.
- We however allow callers of refs API to supply a random sequence of NUL terminated bytes. We cleanse caller-supplied message by squasing a run of whitespaces into a SP, and by trimming trailing whitespace, before storing the message. This is how we tolerate, instead of erring out, a message with LF in it (be it at the end, in the middle, or both).
Currently, the cleansing of the reflog message is done by the files backend, before the log is written out. This is sufficient with the current code, as that is the only backend that writes reflogs. But new backends can be added that write reflogs, and we'd want the resulting log message we would read out of "log -g" the same no matter what backend is used.
For that, we could force each new ref backend implementation to call the same cleansing helper, but instead why not do the cleansing at the backend agnostic way in refs.c layer? That would be less error prone.
An added benefit is that the "cleansing" function could be updated later, independent from individual backends, to e.g. allow multi-line log messages if we wanted to, and when that happens, it would help a lot to ensure we covered all bases if the cleansing function (which would be updated) is called from the generic layer.
Side note: I am not interested in supporting multi-line reflog messages right at the moment (nobody is asking for it), but I envision that instead of the "squash a run of whitespaces into a SP and rtrim" cleansing, we can %urlencode problematic bytes in the message *AND* append a SP at the end, when a new version of Git that supports multi-line and/or verbatim reflog messages writes a reflog record. The reading side can detect the presense of SP at the end (which should have been rtrimmed out if it were written by existing versions of Git) as a signal that decoding %urlencode recovers the original reflog message.
In any case, a patch that moves the existing "squash SPs and rtrim" cleansing from the files backend to the generic layer may look like the attached patch. We can add reftable backend on top of a change like this one and then we do not have to worry about each backend cleansing the incoming reflog messages the same way. Nice?
Thanks.
refs.c | 50 +++++++++++++++++++++++++++++++++++++++++--------- refs/files-backend.c | 2 +- refs/refs-internal.h | 6 ------ 3 files changed, 42 insertions(+), 16 deletions(-)
refs.c +41 -9
@@ -870,7 +870,7 @@ int delete_ref(const char *msg, const char *refname, old_oid, flags); } -void copy_reflog_msg (struct strbuf *sb,const char *msg) +static void copy_reflog_msg (struct strbuf *sb,const char *msg) {char c;int wasspace =1 ;
@@ -887,6 +887,15 @@ void copy_reflog_msg(struct strbuf *sb, const char *msg)strbuf_rtrim (sb); } +static char *normalize_reflog_message (const char *msg) +{ +struct strbuf sb =STRBUF_INIT ; + +if (msg && *msg) +copy_reflog_msg (&sb, msg); +return strbuf_detach (&sb,NULL ); +} +int should_autocreate_reflog (const char *refname) {switch (log_all_ref_updates) {
@@ -1092,7 +1101,7 @@ struct ref_update *ref_transaction_add_update(oidcpy (&update->new_oid, new_oid);if (flags &REF_HAVE_OLD )oidcpy (&update->old_oid, old_oid); - update->msg =xstrdup_or_null (msg); + update->msg =normalize_reflog_message (msg);return update; }
@@ -1951,9 +1960,14 @@ int refs_create_symref(struct ref_store *refs,const char *refs_heads_master, const char *logmsg) { - return refs->be->create_symref(refs, ref_target, -refs_heads_master , -logmsg ); + char *msg; +int retval; + + msg =normalize_reflog_message (logmsg); + retval = refs->be->create_symref (refs, ref_target, + refs_heads_master, msg); +free (msg); +return retval; } int create_symref(const char *ref_target, const char *refs_heads_master,
@@ -2268,10 +2282,16 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,return refs->be->initial_transaction_commit (refs, transaction, err); } -int refs_delete_refs (struct ref_store *refs,const char *msg, +int refs_delete_refs (struct ref_store *refs,const char *logmsg,struct string_list *refnames,unsigned int flags) { -return refs->be->delete_refs (refs, msg, refnames, flags); +char *msg; +int retval; + + msg =normalize_reflog_message (logmsg); + retval = refs->be->delete_refs (refs, msg, refnames, flags); +free (msg); +return retval; } int delete_refs(const char *msg, struct string_list *refnames,
@@ -2283,7 +2303,13 @@ int delete_refs(const char *msg, struct string_list *refnames,int refs_rename_ref (struct ref_store *refs,const char *oldref,const char *newref,const char *logmsg) { -return refs->be->rename_ref (refs, oldref, newref, logmsg); +char *msg; +int retval; + + msg =normalize_reflog_message (logmsg); + retval = refs->be->rename_ref (refs, oldref, newref, msg); +free (msg); +return retval; }int rename_ref (const char *oldref,const char *newref,const char *logmsg)
@@ -2294,7 +2320,13 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)int refs_copy_existing_ref (struct ref_store *refs,const char *oldref,const char *newref,const char *logmsg) { -return refs->be->copy_ref (refs, oldref, newref, logmsg); +char *msg; +int retval; + + msg =normalize_reflog_message (logmsg); + retval = refs->be->copy_ref (refs, oldref, newref, msg); +free (msg); +return retval; }int copy_existing_ref (const char *oldref,const char *newref,const char *logmsg)
refs/files-backend.c +1 -1
@@ -1629,7 +1629,7 @@ static int log_ref_write_fd(int fd, const struct object_id *old_oid,strbuf_addf (&sb,"%s %s %s" ,oid_to_hex (old_oid),oid_to_hex (new_oid), committer);if (msg && *msg) -copy_reflog_msg (&sb, msg); +strbuf_addstr (&sb, msg);strbuf_addch (&sb,'\n' );if (write_in_full (fd, sb.buf, sb.len) <0 ) ret =-1 ;
refs/refs-internal.h +0 -6
@@ -96,12 +96,6 @@ enum peel_status {*/ enum peel_status peel_object (const struct object_id *name,struct object_id *oid); -/* -* Copy the reflog message msg to sb while cleaning up the whitespaces. -* Especially, convert LF to space, because reflog file is one line per entry. -*/ -void copy_reflog_msg (struct strbuf *sb,const char *msg); -/** * Information needed for a single ref update. Set new_oid to the new * value or to null_oid to delete the ref. To check the old value
Re: Re* [PATCH v19 03/20] checkout: add '\n' to reflog message
Quoted 5 lines · Junio C Hamano
In any case, a patch that moves the existing "squash SPs and rtrim" cleansing from the files backend to the generic layer may look like the attached patch. We can add reftable backend on top of a change like this one and then we do not have to worry about each backend cleansing the incoming reflog messages the same way. Nice?
Yes, very nice! Will you merge this, or should I make this part of the reftable series?
The reftable code already has normalization for reflog messages, so it doesn't really make a difference; either way is fine.
Signature · 7 lines
Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Re: Re* [PATCH v19 03/20] checkout: add '\n' to reflog message
Quoted 11 lines · Han-Wen Nienhuys
Quoted 5 lines · Junio C Hamano
In any case, a patch that moves the existing "squash SPs and rtrim" cleansing from the files backend to the generic layer may look like the attached patch. We can add reftable backend on top of a change like this one and then we do not have to worry about each backend cleansing the incoming reflog messages the same way. Nice?
Yes, very nice! Will you merge this, or should I make this part of the reftable series?
The reftable code already has normalization for reflog messages, so it doesn't really make a difference; either way is fine.
It probably fits well in the "to prepare the existing code to support any new backend" series you have split out of the reftable series and sent separately earlier, not even "part of the reftable series", I think. With something like that, you may even be able to drop the custom reflog message munging from reftable proper, just like the whole point of the patch you are responding was to drop the custom munging from the files backend.
Thanks.
PATCH v19 4/20 Write pseudorefs through ref backends.
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
Pseudorefs store transient data in in the repository. Examples are HEAD, CHERRY_PICK_HEAD, etc.
These refs have always been read through the ref backends, but they were written in a one-off routine that wrote an object ID or symref directly into .git/<pseudo_ref_name>.
This causes problems when introducing a new ref storage backend. To remedy this, extend the ref backend implementation with a write_pseudoref_fn and update_pseudoref_fn.
--- refs.c | 119 ++++++++---------------------------------- refs.h | 11 ++++ refs/files-backend.c | 114 +++++++++++++++++++++++++++++++++++++++- refs/packed-backend.c | 21 +++++++- refs/refs-internal.h | 18 +++++++ 5 files changed, 184 insertions(+), 99 deletions(-)
refs.c +22 -97
@@ -323,6 +323,12 @@ int ref_exists(const char *refname)return refs_ref_exists (get_main_ref_store (the_repository), refname); } +int delete_pseudoref (const char *pseudoref,const struct object_id *old_oid) +{ +return refs_delete_pseudoref (get_main_ref_store (the_repository), + pseudoref, old_oid); +} +static int filter_refs (const char *refname,const struct object_id *oid,int flags,void *data) {
@@ -771,101 +777,6 @@ long get_files_ref_lock_timeout_ms(void)return timeout_ms; } -static int write_pseudoref (const char *pseudoref,const struct object_id *oid, -const struct object_id *old_oid,struct strbuf *err) -{ -const char *filename; -int fd; -struct lock_file lock =LOCK_INIT ; -struct strbuf buf =STRBUF_INIT ; -int ret =-1 ; - -if (!oid) -return 0 ; - -strbuf_addf (&buf,"%s\n" ,oid_to_hex (oid)); - - filename =git_path ("%s" , pseudoref); - fd =hold_lock_file_for_update_timeout (&lock, filename,0 , -get_files_ref_lock_timeout_ms ()); -if (fd <0 ) { -strbuf_addf (err,_ ("could not open '%s' for writing: %s" ), - filename,strerror (errno)); - goto done; - } - -if (old_oid) { -struct object_id actual_old_oid; - -if (read_ref (pseudoref, &actual_old_oid)) { -if (!is_null_oid (old_oid)) { -strbuf_addf (err,_ ("could not read ref '%s'" ), - pseudoref); -rollback_lock_file (&lock); - goto done; - } - }else if (is_null_oid (old_oid)) { -strbuf_addf (err,_ ("ref '%s' already exists" ), - pseudoref); -rollback_lock_file (&lock); - goto done; - }else if (!oideq (&actual_old_oid, old_oid)) { -strbuf_addf (err,_ ("unexpected object ID when writing '%s'" ), - pseudoref); -rollback_lock_file (&lock); - goto done; - } - } - -if (write_in_full (fd, buf.buf, buf.len) <0 ) { -strbuf_addf (err,_ ("could not write to '%s'" ), filename); -rollback_lock_file (&lock); - goto done; - } - -commit_lock_file (&lock); - ret =0 ; -done: -strbuf_release (&buf); -return ret; -} - -static int delete_pseudoref (const char *pseudoref,const struct object_id *old_oid) -{ -const char *filename; - - filename =git_path ("%s" , pseudoref); - -if (old_oid && !is_null_oid (old_oid)) { -struct lock_file lock =LOCK_INIT ; -int fd; -struct object_id actual_old_oid; - - fd =hold_lock_file_for_update_timeout ( - &lock, filename,0 , -get_files_ref_lock_timeout_ms ()); -if (fd <0 ) { -error_errno (_ ("could not open '%s' for writing" ), - filename); -return -1 ; - } -if (read_ref (pseudoref, &actual_old_oid)) -die (_ ("could not read ref '%s'" ), pseudoref); -if (!oideq (&actual_old_oid, old_oid)) { -error (_ ("unexpected object ID when deleting '%s'" ), - pseudoref); -rollback_lock_file (&lock); -return -1 ; - } - -unlink (filename); -rollback_lock_file (&lock); - }else { -unlink (filename); - } - -return 0 ; -} int refs_delete_ref(struct ref_store *refs, const char *msg, const char *refname,
@@ -877,7 +788,7 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,if (ref_type (refname) ==REF_TYPE_PSEUDOREF ) {assert (refs ==get_main_ref_store (the_repository)); -return delete_pseudoref (refname, old_oid); +return refs_delete_pseudoref (refs, refname, old_oid); } transaction =ref_store_transaction_begin (refs, &err);
@@ -1204,7 +1115,8 @@ int refs_update_ref(struct ref_store *refs, const char *msg,if (ref_type (refname) ==REF_TYPE_PSEUDOREF ) {assert (refs ==get_main_ref_store (the_repository)); - ret =write_pseudoref (refname, new_oid, old_oid, &err); + ret =refs_write_pseudoref (refs, refname, new_oid, old_oid, + &err); }else { t =ref_store_transaction_begin (refs, &err); if (!t ||
@@ -1473,6 +1385,19 @@ int head_ref(each_ref_fn fn, void *cb_data)return refs_head_ref (get_main_ref_store (the_repository), fn, cb_data); } +int refs_write_pseudoref (struct ref_store *refs,const char *pseudoref, +const struct object_id *oid, +const struct object_id *old_oid,struct strbuf *err) +{ +return refs->be->write_pseudoref (refs, pseudoref, oid, old_oid, err); +} + +int refs_delete_pseudoref (struct ref_store *refs,const char *pseudoref, +const struct object_id *old_oid) +{ +return refs->be->delete_pseudoref (refs, pseudoref, old_oid); +} +struct ref_iterator *refs_ref_iterator_begin (struct ref_store *refs,const char *prefix,int trim,int flags)
refs.h +11 -0
@@ -741,6 +741,17 @@ int update_ref(const char *msg, const char *refname, const struct object_id *new_oid, const struct object_id *old_oid, unsigned int flags, enum action_on_err onerr); +/* Pseudorefs (eg. HEAD, CHERRY_PICK_HEAD) have a separate routines for updating +and deletion as they cannot take part in normal transactional updates. +Pseudorefs should only be written for the main repository. +*/ +int refs_write_pseudoref (struct ref_store *refs,const char *pseudoref, +const struct object_id *oid, +const struct object_id *old_oid,struct strbuf *err); +int refs_delete_pseudoref (struct ref_store *refs,const char *pseudoref, +const struct object_id *old_oid); +int delete_pseudoref (const char *pseudoref,const struct object_id *old_oid); +int parse_hide_refs_config (const char *var,const char *value,const char *);/*
refs/files-backend.c +113 -1
130 lines not on this page · raw message
refs/packed-backend.c +20 -1
@@ -1590,6 +1590,22 @@ static int packed_copy_ref(struct ref_store *ref_store,BUG ("packed reference store does not support copying references" ); } +static int packed_write_pseudoref (struct ref_store *ref_store, +const char *pseudoref, +const struct object_id *oid, +const struct object_id *old_oid, +struct strbuf *err) +{ +BUG ("packed reference store does not support writing pseudo-references" ); +} + +static int packed_delete_pseudoref (struct ref_store *ref_store, +const char *pseudoref, +const struct object_id *old_oid) +{ +BUG ("packed reference store does not support deleting pseudo-references" ); +} +static struct ref_iterator *packed_reflog_iterator_begin (struct ref_store *ref_store) {return empty_ref_iterator_begin ();
@@ -1656,6 +1672,9 @@ struct ref_storage_be refs_be_packed = { packed_rename_ref, packed_copy_ref, + packed_write_pseudoref, + packed_delete_pseudoref, + packed_ref_iterator_begin, packed_read_raw_ref,
@@ -1665,5 +1684,5 @@ struct ref_storage_be refs_be_packed = { packed_reflog_exists, packed_create_reflog, packed_delete_reflog, - packed_reflog_expire + packed_reflog_expire, };
refs/refs-internal.h +18 -0
@@ -556,6 +556,21 @@ typedef int copy_ref_fn(struct ref_store *ref_store, const char *oldref, const char *newref, const char *logmsg); +typedef int write_pseudoref_fn (struct ref_store *ref_store, +const char *pseudoref, +const struct object_id *oid, +const struct object_id *old_oid, +struct strbuf *err); + +/* +* Deletes a pseudoref. Deletion always succeeds (even if the pseudoref doesn't +* exist.), except if old_oid is specified. If it is, it can fail due to lock +* failure, failure reading the old OID, or an OID mismatch +*/ +typedef int delete_pseudoref_fn (struct ref_store *ref_store, +const char *pseudoref, +const struct object_id *old_oid); +/* * Iterate over the references in `ref_store` whose names start with * `prefix`. `prefix` is matched as a literal string, without regard
@@ -655,6 +670,9 @@ struct ref_storage_be {rename_ref_fn *rename_ref;copy_ref_fn *copy_ref; +write_pseudoref_fn *write_pseudoref; +delete_pseudoref_fn *delete_pseudoref; +ref_iterator_begin_fn *iterator_begin;read_raw_ref_fn *read_raw_ref;
Signature · 1 line
gitgitgadget
PATCH v19 5/20 Make refs_ref_exists public
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- refs.c | 2 +- refs.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-)
refs.c +1 -1
@@ -313,7 +313,7 @@ int read_ref(const char *refname, struct object_id *oid)return read_ref_full (refname,RESOLVE_REF_READING , oid,NULL ); } -static int refs_ref_exists (struct ref_store *refs,const char *refname) +int refs_ref_exists (struct ref_store *refs,const char *refname) {return !!refs_resolve_ref_unsafe (refs, refname,RESOLVE_REF_READING ,NULL ,NULL ); }
refs.h +2 -0
@@ -105,6 +105,8 @@ int refs_verify_refname_available(struct ref_store *refs,const struct string_list *skip, struct strbuf *err); +int refs_ref_exists (struct ref_store *refs,const char *refname); +int ref_exists (const char *refname);int should_autocreate_reflog (const char *refname);
Signature · 1 line
gitgitgadget
PATCH v19 6/20 Treat BISECT_HEAD as a pseudo ref
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
Both the git-bisect.sh as bisect--helper inspected the file system directly.
--- builtin/bisect--helper.c | 3 +-- git-bisect.sh | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-)
builtin/bisect--helper.c +1 -2
@@ -13,7 +13,6 @@ static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS") static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV") static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK") static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START") -static GIT_PATH_FUNC(git_path_bisect_head, "BISECT_HEAD") static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG") static GIT_PATH_FUNC(git_path_head_name, "head-name") static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
@@ -164,7 +163,7 @@ static int bisect_reset(const char *commit)strbuf_addstr (&branch, commit); } -if (!file_exists (git_path_bisect_head ())) { +if (!ref_exists ("BISECT_HEAD" )) {struct argv_array argv =ARGV_ARRAY_INIT ;argv_array_pushl (&argv,"checkout" , branch.buf,"--" ,NULL );
git-bisect.sh +2 -2
@@ -41,7 +41,7 @@ TERM_GOOD=good bisect_head() { - if test -f "$GIT_DIR/BISECT_HEAD" + if git rev-parse --verify -q BISECT_HEAD > /dev/null then echo BISECT_HEAD else
@@ -153,7 +153,7 @@ bisect_next() { git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD $TERM_GOOD|| exit # Perform all bisection computation, display and checkout - git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout) + git bisect--helper --next-all $(git rev-parse --verify -q BISECT_HEAD > /dev/null && echo --no-checkout) res=$? # Check if we should exit because bisection is finished
Signature · 1 line
gitgitgadget
PATCH v19 7/20 Treat CHERRY_PICK_HEAD as a pseudo ref
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
Check for existence and delete CHERRY_PICK_HEAD through pseudo ref functions. This will help cherry-pick work with alternate ref storage backends.
--- builtin/commit.c | 34 +++++++++++++++++++--------------- builtin/merge.c | 2 +- path.c | 1 - path.h | 7 ++++--- sequencer.c | 42 ++++++++++++++++++++++++++---------------- wt-status.c | 4 ++-- 6 files changed, 52 insertions(+), 38 deletions(-)
builtin/commit.c +19 -15
@@ -847,21 +847,25 @@ static int prepare_to_commit(const char *index_file, const char *prefix,if (cleanup_mode ==COMMIT_MSG_CLEANUP_SCISSORS && !merge_contains_scissors)wt_status_add_cut_line (s->fp); -status_printf_ln (s,GIT_COLOR_NORMAL , - whence ==FROM_MERGE - ?_ ("\n" -"It looks like you may be committing a merge.\n" -"If this is not correct, please remove the file\n" -" %s\n" -"and try again.\n" ) - :_ ("\n" -"It looks like you may be committing a cherry-pick.\n" -"If this is not correct, please remove the file\n" -" %s\n" -"and try again.\n" ), - whence ==FROM_MERGE ? -git_path_merge_head (the_repository) : -git_path_cherry_pick_head (the_repository)); +if (whence ==FROM_MERGE ) +status_printf_ln ( + s,GIT_COLOR_NORMAL , + +_ ("\n" +"It looks like you may be committing a merge.\n" +"If this is not correct, please remove the file\n" +" %s\n" +"and try again.\n" ), +git_path_merge_head (the_repository)); +else +status_printf_ln ( + s,GIT_COLOR_NORMAL , + +_ ("\n" +"It looks like you may be committing a cherry-pick.\n" +"If this is not correct, please run\n" +" git cherry-pick --abort\n" +"and try again.\n" )); }fprintf (s->fp,"\n" );
builtin/merge.c +1 -1
@@ -1352,7 +1352,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)else die (_ ("You have not concluded your merge (MERGE_HEAD exists)." )); } -if (file_exists (git_path_cherry_pick_head (the_repository))) { +if (ref_exists ("CHERRY_PICK_HEAD" )) {if (advice_resolve_conflict)die (_ ("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n" "Please, commit your changes before you merge." ));
path.c +0 -1
@@ -1528,7 +1528,6 @@ char *xdg_cache_home(const char *filename)return NULL ; } -REPO_GIT_PATH_FUNC(cherry_pick_head, "CHERRY_PICK_HEAD") REPO_GIT_PATH_FUNC(revert_head, "REVERT_HEAD") REPO_GIT_PATH_FUNC(squash_msg, "SQUASH_MSG")REPO_GIT_PATH_FUNC (merge_msg,"MERGE_MSG" )
path.h +4 -3
@@ -170,7 +170,6 @@ void report_linked_checkout_garbage(void); }struct path_cache { -const char *cherry_pick_head;const char *revert_head;const char *squash_msg;const char *merge_msg;
@@ -182,9 +181,11 @@ struct path_cache {const char *shallow; }; -#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +#define PATH_CACHE_INIT \ + { \ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL \ + } -const char *git_path_cherry_pick_head (struct repository *r);const char *git_path_revert_head (struct repository *r);const char *git_path_squash_msg (struct repository *r);const char *git_path_merge_msg (struct repository *r);
sequencer.c +26 -16
114 lines not on this page · raw message
wt-status.c +2 -2
@@ -1673,8 +1673,8 @@ void wt_status_get_state(struct repository *r, state->merge_in_progress =1 ; }else if (wt_status_check_rebase (NULL , state)) { ;/* all set */ - }else if (!stat (git_path_cherry_pick_head (r), &st) && - !get_oid ("CHERRY_PICK_HEAD" , &oid)) { + }else if (refs_ref_exists (get_main_ref_store (r),"CHERRY_PICK_HEAD" ) && + !get_oid ("CHERRY_PICK_HEAD" , &oid)) { state->cherry_pick_in_progress =1 ;oidcpy (&state->cherry_pick_head_oid, &oid); }
Signature · 1 line
gitgitgadget
PATCH v19 8/20 Treat REVERT_HEAD as a pseudo ref
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- path.c | 1 - path.h | 8 +++----- sequencer.c | 16 +++++++++------- wt-status.c | 2 +- 4 files changed, 13 insertions(+), 14 deletions(-)
path.c +0 -1
@@ -1528,7 +1528,6 @@ char *xdg_cache_home(const char *filename)return NULL ; } -REPO_GIT_PATH_FUNC(revert_head, "REVERT_HEAD") REPO_GIT_PATH_FUNC(squash_msg, "SQUASH_MSG") REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")REPO_GIT_PATH_FUNC (merge_rr,"MERGE_RR" )
path.h +3 -5
@@ -170,7 +170,6 @@ void report_linked_checkout_garbage(void); }struct path_cache { -const char *revert_head;const char *squash_msg;const char *merge_msg;const char *merge_rr;
@@ -181,12 +180,11 @@ struct path_cache {const char *shallow; }; -#define PATH_CACHE_INIT \ - { \ - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL \ +#define PATH_CACHE_INIT \ + { \ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL \ } -const char *git_path_revert_head (struct repository *r);const char *git_path_squash_msg (struct repository *r);const char *git_path_merge_msg (struct repository *r);const char *git_path_merge_rr (struct repository *r);
sequencer.c +9 -7
@@ -2317,8 +2317,10 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose) need_cleanup =1 ; } -if (file_exists (git_path_revert_head (r))) { -if (!unlink (git_path_revert_head (r)) && verbose) +if (refs_ref_exists (get_main_ref_store (r),"REVERT_HEAD" )) { +if (!refs_delete_pseudoref (get_main_ref_store (r),"REVERT_HEAD" , +NULL ) && + verbose)warning (_ ("cancelling a revert in progress" )); opts.action =REPLAY_REVERT ; need_cleanup =1 ;
@@ -2677,7 +2679,7 @@ static int create_seq_dir(struct repository *r)const char *in_progress_error =NULL ;const char *in_progress_advice =NULL ;unsigned int advise_skip = -file_exists (git_path_revert_head (r)) || +refs_ref_exists (get_main_ref_store (r),"REVERT_HEAD" ) ||refs_ref_exists (get_main_ref_store (r),"CHERRY_PICK_HEAD" );if (!sequencer_get_last_command (r, &action)) {
@@ -2778,7 +2780,7 @@ static int rollback_single_pick(struct repository *r)struct object_id head_oid;if (!refs_ref_exists (get_main_ref_store (r),"CHERRY_PICK_HEAD" ) && - !file_exists (git_path_revert_head (r))) + !refs_ref_exists (get_main_ref_store (r),"REVERT_HEAD" ))return error (_ ("no cherry-pick or revert in progress" ));if (read_ref_full ("HEAD" ,0 , &head_oid,NULL ))return error (_ ("cannot resolve HEAD" ));
@@ -2872,7 +2874,7 @@ int sequencer_skip(struct repository *r, struct replay_opts *opts)*/ switch (opts->action) {case REPLAY_REVERT : -if (!file_exists (git_path_revert_head (r))) { +if (!refs_ref_exists (get_main_ref_store (r),"REVERT_HEAD" )) {if (action !=REPLAY_REVERT )return error (_ ("no revert in progress" ));if (!rollback_is_safe ())
@@ -4210,7 +4212,7 @@ static int continue_single_pick(struct repository *r)const char *argv[] = {"commit" ,NULL };if (!refs_ref_exists (get_main_ref_store (r),"CHERRY_PICK_HEAD" ) && - !file_exists (git_path_revert_head (r))) + !refs_ref_exists (get_main_ref_store (r),"REVERT_HEAD" ))return error (_ ("no cherry-pick or revert in progress" ));return run_command_v_opt (argv,RUN_GIT_CMD ); }
@@ -4390,7 +4392,7 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)/* Verify that the conflict has been resolved */ if (refs_ref_exists (get_main_ref_store (r),"CHERRY_PICK_HEAD" ) || -file_exists (git_path_revert_head (r))) { +refs_ref_exists (get_main_ref_store (r),"REVERT_HEAD" )) { res =continue_single_pick (r);if (res) goto release_todo_list;
wt-status.c +1 -1
@@ -1679,7 +1679,7 @@ void wt_status_get_state(struct repository *r,oidcpy (&state->cherry_pick_head_oid, &oid); }wt_status_check_bisect (NULL , state); -if (!stat (git_path_revert_head (r), &st) && +if (refs_ref_exists (get_main_ref_store (r),"REVERT_HEAD" ) && !get_oid ("REVERT_HEAD" , &oid)) { state->revert_in_progress =1 ;oidcpy (&state->revert_head_oid, &oid);
Signature · 1 line
gitgitgadget
PATCH v19 9/20 Move REF_LOG_ONLY to refs-internal.h
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
REF_LOG_ONLY is used in the transaction preparation: if a symref is involved in a transaction, the referent of the symref should be updated, and the symref itself should only be updated in the reflog. Other ref backends will need to duplicate this logic.
--- refs/files-backend.c | 7 ------- refs/refs-internal.h | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-)
refs/files-backend.c +0 -7
@@ -38,13 +38,6 @@*/ #define REF_NEEDS_COMMIT (1 << 6) -/* -* Used as a flag in ref_update::flags when we want to log a ref -* update but not actually perform it. This is used when a symbolic -* ref update is split up. -*/ -#define REF_LOG_ONLY (1 << 7) -/* * Used as a flag in ref_update::flags when the ref_update was via an * update to HEAD.
refs/refs-internal.h +7 -0
@@ -31,6 +31,13 @@ struct ref_transaction;*/ #define REF_HAVE_OLD (1 << 3) +/* +* Used as a flag in ref_update::flags when we want to log a ref +* update but not actually perform it. This is used when a symbolic +* ref update is split up. +*/ +#define REF_LOG_ONLY (1 << 7) +/* * Return the length of time to retry acquiring a loose reference lock * before giving up, in milliseconds:
Signature · 1 line
gitgitgadget
PATCH v19 10/20 Iterate over the "refs/" namespace in for_each_[raw]ref
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This happens implicitly in the files/packed ref backend; making it explicit simplifies adding alternate ref storage backends, such as reftable.
--- refs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
refs.c +3 -3
@@ -1482,7 +1482,7 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix,int refs_for_each_ref (struct ref_store *refs,each_ref_fn fn,void *cb_data) { -return do_for_each_ref (refs,"" , fn,0 ,0 , cb_data); +return do_for_each_ref (refs,"refs/" , fn,0 ,0 , cb_data); }int for_each_ref (each_ref_fn fn,void *cb_data)
@@ -1542,8 +1542,8 @@ int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)int refs_for_each_rawref (struct ref_store *refs,each_ref_fn fn,void *cb_data) { -return do_for_each_ref (refs,"" , fn,0 , -DO_FOR_EACH_INCLUDE_BROKEN , cb_data); +return do_for_each_ref (refs,"refs/" , fn,0 ,DO_FOR_EACH_INCLUDE_BROKEN , + cb_data); }int for_each_rawref (each_ref_fn fn,void *cb_data)
Signature · 1 line
gitgitgadget
PATCH v19 11/20 Add .gitattributes for the reftable/ directory
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- reftable/.gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 reftable/.gitattributes
reftable/.gitattributes new file +1 -0
@@ -0,0 +1 @@ +/zlib-compat.c whitespace=-indent-with-non-tab,-trailing-space
Signature · 1 line
gitgitgadget
PATCH v19 12/20 Add reftable library
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
Reftable is a format for storing the ref database. The rationale and format layout is detailed in commit 35e6c47404 ("reftable: file format documentation", 2020-05-20).
This commits imports a fully functioning library for reading and writing reftables, along with unittests.
It is suggested to read the code in the following order:
* The specification under Documentation/technical/reftable.txt
* reftable.h - the public API
* record.{c,h} - reading and writing records
* block.{c,h} - reading and writing blocks.
* reader.{c,h} - reading a complete reftable file.
* writer.{c,h} - writing a complete reftable file.
* merged.{c,h} and pq.{c,h} - reading a stack of reftables
* stack.{c,h} - writing and compacting stacks of reftable on the filesystem.
--- reftable/LICENSE | 31 + reftable/VERSION | 1 + reftable/basics.c | 215 +++++++ reftable/basics.h | 53 ++ reftable/block.c | 432 +++++++++++++ reftable/block.h | 129 ++++ reftable/block_test.c | 157 +++++ reftable/compat.c | 98 +++ reftable/compat.h | 48 ++ reftable/constants.h | 21 + reftable/dump.c | 212 +++++++ reftable/file.c | 95 +++ reftable/iter.c | 242 ++++++++ reftable/iter.h | 72 +++ reftable/merged.c | 317 ++++++++++ reftable/merged.h | 43 ++ reftable/merged_test.c | 286 +++++++++ reftable/pq.c | 115 ++++ reftable/pq.h | 34 + reftable/reader.c | 744 ++++++++++++++++++++++ reftable/reader.h | 65 ++ reftable/record.c | 1126 ++++++++++++++++++++++++++++++++++ reftable/record.h | 143 +++++ reftable/record_test.c | 410 +++++++++++++ reftable/refname.c | 209 +++++++ reftable/refname.h | 38 ++ reftable/refname_test.c | 99 +++ reftable/reftable-tests.h | 22 + reftable/reftable.c | 154 +++++ reftable/reftable.h | 585 ++++++++++++++++++ reftable/reftable_test.c | 632 +++++++++++++++++++ reftable/stack.c | 1225 +++++++++++++++++++++++++++++++++++++ reftable/stack.h | 50 ++ reftable/stack_test.c | 787 ++++++++++++++++++++++++ reftable/strbuf.c | 206 +++++++ reftable/strbuf.h | 88 +++ reftable/strbuf_test.c | 39 ++ reftable/system.h | 53 ++ reftable/test_framework.c | 69 +++ reftable/test_framework.h | 64 ++ reftable/tree.c | 63 ++ reftable/tree.h | 34 + reftable/tree_test.c | 62 ++ reftable/update.sh | 22 + reftable/writer.c | 663 ++++++++++++++++++++ reftable/writer.h | 60 ++ reftable/zlib-compat.c | 92 +++ 47 files changed, 10405 insertions(+) create mode 100644 reftable/LICENSE create mode 100644 reftable/VERSION create mode 100644 reftable/basics.c create mode 100644 reftable/basics.h create mode 100644 reftable/block.c create mode 100644 reftable/block.h create mode 100644 reftable/block_test.c create mode 100644 reftable/compat.c create mode 100644 reftable/compat.h create mode 100644 reftable/constants.h create mode 100644 reftable/dump.c create mode 100644 reftable/file.c create mode 100644 reftable/iter.c create mode 100644 reftable/iter.h create mode 100644 reftable/merged.c create mode 100644 reftable/merged.h create mode 100644 reftable/merged_test.c create mode 100644 reftable/pq.c create mode 100644 reftable/pq.h create mode 100644 reftable/reader.c create mode 100644 reftable/reader.h create mode 100644 reftable/record.c create mode 100644 reftable/record.h create mode 100644 reftable/record_test.c create mode 100644 reftable/refname.c create mode 100644 reftable/refname.h create mode 100644 reftable/refname_test.c create mode 100644 reftable/reftable-tests.h create mode 100644 reftable/reftable.c create mode 100644 reftable/reftable.h create mode 100644 reftable/reftable_test.c create mode 100644 reftable/stack.c create mode 100644 reftable/stack.h create mode 100644 reftable/stack_test.c create mode 100644 reftable/strbuf.c create mode 100644 reftable/strbuf.h create mode 100644 reftable/strbuf_test.c create mode 100644 reftable/system.h create mode 100644 reftable/test_framework.c create mode 100644 reftable/test_framework.h create mode 100644 reftable/tree.c create mode 100644 reftable/tree.h create mode 100644 reftable/tree_test.c create mode 100755 reftable/update.sh create mode 100644 reftable/writer.c create mode 100644 reftable/writer.h create mode 100644 reftable/zlib-compat.c
reftable/LICENSE new file +31 -0
@@ -0,0 +1,31 @@ +BSD License + +Copyright (c) 2020, Google LLC +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +* Neither the name of Google LLC nor the names of its contributors may +be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
reftable/VERSION new file +1 -0
@@ -0,0 +1 @@ +994cc2f626ff626ff04be5240413775f832110fb C: formatting tweaks
reftable/basics.c new file +215 -0
215 lines not on this page · raw message
reftable/basics.h new file +53 -0
53 lines not on this page · raw message
reftable/block.c new file +432 -0
432 lines not on this page · raw message
reftable/block.h new file +129 -0
129 lines not on this page · raw message
reftable/block_test.c new file +157 -0
157 lines not on this page · raw message
reftable/compat.c new file +98 -0
98 lines not on this page · raw message
reftable/compat.h new file +48 -0
48 lines not on this page · raw message
reftable/constants.h new file +21 -0
@@ -0,0 +1,21 @@ +/* +Copyright 2020 Google LLC + +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file or at +https://developers.google.com/open-source/licenses/bsd +*/ + +#ifndef CONSTANTS_H +#define CONSTANTS_H + +#define BLOCK_TYPE_LOG 'g' +#define BLOCK_TYPE_INDEX 'i' +#define BLOCK_TYPE_REF 'r' +#define BLOCK_TYPE_OBJ 'o' +#define BLOCK_TYPE_ANY 0 + +#define MAX_RESTARTS ((1 << 16) - 1) +#define DEFAULT_BLOCK_SIZE 4096 + +#endif
reftable/dump.c new file +212 -0
212 lines not on this page · raw message
reftable/file.c new file +95 -0
95 lines not on this page · raw message
reftable/iter.c new file +242 -0
242 lines not on this page · raw message
reftable/iter.h new file +72 -0
72 lines not on this page · raw message
reftable/merged.c new file +317 -0
317 lines not on this page · raw message
reftable/merged.h new file +43 -0
43 lines not on this page · raw message
reftable/merged_test.c new file +286 -0
286 lines not on this page · raw message
reftable/pq.c new file +115 -0
115 lines not on this page · raw message
reftable/pq.h new file +34 -0
@@ -0,0 +1,34 @@ +/* +Copyright 2020 Google LLC + +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file or at +https://developers.google.com/open-source/licenses/bsd +*/ + +#ifndef PQ_H +#define PQ_H + +#include "record.h" + +struct pq_entry { +int index; +struct reftable_record rec; +}; + +int pq_less (struct pq_entry a,struct pq_entry b); + +struct merged_iter_pqueue { +struct pq_entry *heap; +size_t len; +size_t cap; +}; + +struct pq_entry merged_iter_pqueue_top (struct merged_iter_pqueue pq); +bool merged_iter_pqueue_is_empty (struct merged_iter_pqueue pq); +void merged_iter_pqueue_check (struct merged_iter_pqueue pq); +struct pq_entry merged_iter_pqueue_remove (struct merged_iter_pqueue *pq); +void merged_iter_pqueue_add (struct merged_iter_pqueue *pq,struct pq_entry e); +void merged_iter_pqueue_clear (struct merged_iter_pqueue *pq); + +#endif
reftable/reader.c new file +744 -0
399 lines not on this page · raw message
reftable/reader.h new file +65 -0
65 lines not on this page · raw message
reftable/record.c new file +1126 -0
1,126 lines not on this page · raw message
reftable/record.h new file +143 -0
143 lines not on this page · raw message
reftable/record_test.c new file +410 -0
410 lines not on this page · raw message
reftable/refname.c new file +209 -0
209 lines not on this page · raw message
reftable/refname_test.c new file +99 -0
99 lines not on this page · raw message
reftable/reftable.c new file +154 -0
154 lines not on this page · raw message
reftable/reftable.h new file +585 -0
585 lines not on this page · raw message
reftable/reftable_test.c new file +632 -0
632 lines not on this page · raw message
reftable/stack.c new file +1225 -0
1,225 lines not on this page · raw message
reftable/stack.h new file +50 -0
50 lines not on this page · raw message
reftable/stack_test.c new file +787 -0
787 lines not on this page · raw message
reftable/strbuf.c new file +206 -0
206 lines not on this page · raw message
reftable/strbuf.h new file +88 -0
88 lines not on this page · raw message
reftable/system.h new file +53 -0
53 lines not on this page · raw message
reftable/test_framework.c new file +69 -0
69 lines not on this page · raw message
reftable/test_framework.h new file +64 -0
64 lines not on this page · raw message
reftable/tree.c new file +63 -0
63 lines not on this page · raw message
reftable/tree_test.c new file +62 -0
62 lines not on this page · raw message
reftable/writer.c new file +663 -0
663 lines not on this page · raw message
reftable/writer.h new file +60 -0
60 lines not on this page · raw message
reftable/zlib-compat.c new file +92 -0
92 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
gitgitgadget
Part of the diff is not shown here. View the raw message.
PATCH v19 13/20 Add standalone build infrastructure for reftable
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
The reftable library is integrates with Git, so it become a first class supported storage mechanism, but is sufficiently complex that other projects (e.g. libgit2) may want to consume the same source code.
To make this possible, the library also compiles completely standalone, which is demonstrated with the Bazel BUILD/WORKSPACE files that this commit adds.
--- reftable/BUILD | 203 ++++++++++++++++++++++++++++++++++++++++++++ reftable/README.md | 33 +++++++ reftable/WORKSPACE | 14 +++ reftable/zlib.BUILD | 36 ++++++++ 4 files changed, 286 insertions(+) create mode 100644 reftable/BUILD create mode 100644 reftable/README.md create mode 100644 reftable/WORKSPACE create mode 100644 reftable/zlib.BUILD
reftable/BUILD new file +203 -0
@@ -0,0 +1,203 @@ +# Mirror core-git COPTS so reftable compiles without warning in CGit. +GIT_COPTS = [ + "-DREFTABLE_STANDALONE", + "-Wall", + "-Werror", + "-Wdeclaration-after-statement", + "-Wstrict-prototypes", + "-Wformat-security", + "-Wno-format-zero-length", + "-Wold-style-definition", + "-Woverflow", + "-Wpointer-arith", + "-Wstrict-prototypes", + "-Wunused", + "-Wvla", + "-Wextra", + "-Wmissing-prototypes", + "-Wno-empty-body", + "-Wno-missing-field-initializers", + "-Wno-sign-compare", + "-Werror=strict-aliasing", + "-Wno-unused-parameter", +] + +cc_library( + name = "reftable", + srcs = [ + "basics.c", + "block.c", + "compat.c", + "file.c", + "iter.c", + "merged.c", + "pq.c", + "reader.c", + "record.c", + "refname.c", + "reftable.c", + "strbuf.c", + "stack.c", + "tree.c", + "writer.c", + "zlib-compat.c", + "basics.h", + "block.h", + "compat.h", + "constants.h", + "iter.h", + "merged.h", + "pq.h", + "reader.h", + "refname.h", + "record.h", + "strbuf.h", + "stack.h", + "system.h", + "tree.h", + "writer.h", + ], + hdrs = [ + "reftable.h", + ], + includes = [ + "include", + ], + copts = [ + "-fvisibility=protected", + ] + GIT_COPTS, + deps = ["@zlib"], + visibility = ["//visibility:public"] +) + +cc_library( + name = "testlib", + srcs = [ + "test_framework.c", + "dump.c", + "test_framework.h", + ], + hdrs = [ + "reftable-tests.h", + ], + copts = GIT_COPTS, + deps = [":reftable"], + visibility = ["//visibility:public"] +) + +cc_test( + name = "record_test", + srcs = ["record_test.c"], + deps = [ + ":reftable", + ":testlib", + ], + copts = [ + "-Drecord_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +cc_test( + name = "reftable_test", + srcs = ["reftable_test.c"], + deps = [ + ":reftable", + ":testlib", + ], + copts = [ + "-Dreftable_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +cc_test( + name = "strbuf_test", + srcs = ["strbuf_test.c"], + deps = [ + ":reftable", + ":testlib", + ] , + copts = [ + "-Dstrbuf_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +cc_test( + name = "stack_test", + srcs = ["stack_test.c"], + deps = [ + ":reftable", + ":testlib", + ], + copts = [ + "-Dstack_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +cc_test( + name = "tree_test", + srcs = ["tree_test.c"], + deps = [ + ":reftable", + ":testlib", + ], + copts = [ + "-Dtree_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +cc_test( + name = "block_test", + srcs = ["block_test.c"], + deps = [ + ":reftable", + ":testlib", + ], + copts = [ + "-Dblock_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +cc_test( + name = "refname_test", + srcs = ["refname_test.c"], + deps = [ + ":reftable", + ":testlib", + ], + copts = [ + "-Drefname_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +cc_test( + name = "merged_test", + srcs = ["merged_test.c"], + deps = [ + ":reftable", + ":testlib", + ], + copts = [ + "-Dmerged_test_main=main", + "-fvisibility=protected", + ] + GIT_COPTS, +) + +[sh_test( + name = "%s_valgrind_test" % t, + srcs = [ "valgrind_test.sh" ], + args = [ t ], + data = [ t ]) + for t in ["record_test", + "merged_test", + "refname_test", + "tree_test", + "block_test", + "strbuf_test", + "stack_test"]]
reftable/README.md new file +33 -0
@@ -0,0 +1,33 @@ +This directory contains an implementation of the reftable library. + +The library is integrated into the git-core project, but can also be built +standalone, by compiling with -DREFTABLE_STANDALONE. The standalone build is +exercised by the accompanying BUILD/WORKSPACE files, and can be run as + + bazel test :all + +It includes a fragment of the zlib library to provide uncompress2(), which is a +recent addition to the API. zlib is licensed as follows: + +``` + (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + [email protected] [email protected] +```
reftable/WORKSPACE new file +14 -0
@@ -0,0 +1,14 @@ +workspace(name = "reftable") + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "zlib", + build_file = "@//:zlib.BUILD", + sha256 = "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1", + strip_prefix = "zlib-1.2.11", + urls = [ + "https://mirror.bazel.build/zlib.net/zlib-1.2.11.tar.gz", + "https://zlib.net/zlib-1.2.11.tar.gz", + ], +)
reftable/zlib.BUILD new file +36 -0
@@ -0,0 +1,36 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) # BSD/MIT-like license (for zlib) + +cc_library( + name = "zlib", + srcs = [ + "adler32.c", + "compress.c", + "crc32.c", + "crc32.h", + "deflate.c", + "deflate.h", + "gzclose.c", + "gzguts.h", + "gzlib.c", + "gzread.c", + "gzwrite.c", + "infback.c", + "inffast.c", + "inffast.h", + "inffixed.h", + "inflate.c", + "inflate.h", + "inftrees.c", + "inftrees.h", + "trees.c", + "trees.h", + "uncompr.c", + "zconf.h", + "zutil.c", + "zutil.h", + ], + hdrs = ["zlib.h"], + includes = ["."], +)
Signature · 1 line
gitgitgadget
PATCH v19 14/20 Reftable support for git-core
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
For background, see the previous commit introducing the library.
This introduces the file refs/reftable-backend.c containing a reftable-powered ref storage backend.
It can be activated by passing --ref-storage=reftable to "git init", or setting GIT_TEST_REFTABLE in the environment.
Example use: see t/t0031-reftable.sh
--- .../technical/repository-version.txt | 7 + Makefile | 28 +- builtin/clone.c | 4 +- builtin/init-db.c | 54 +- builtin/worktree.c | 27 +- cache.h | 8 +- refs.c | 27 +- refs.h | 3 + refs/refs-internal.h | 1 + refs/reftable-backend.c | 1497 +++++++++++++++++ reftable/update.sh | 3 - repository.c | 2 + repository.h | 3 + setup.c | 12 +- t/t0031-reftable.sh | 173 ++ 15 files changed, 1811 insertions(+), 38 deletions(-) create mode 100644 refs/reftable-backend.c create mode 100755 t/t0031-reftable.sh
Documentation/technical/repository-version.txt +7 -0
@@ -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.
Makefile +26 -2
@@ -807,6 +807,7 @@ TEST_SHELL_PATH = $(SHELL_PATH) LIB_FILE = libgit.a XDIFF_LIB = xdiff/lib.a VCSSVN_LIB = vcs-svn/lib.a +REFTABLE_LIB = reftable/libreftable.a GENERATED_H += config-list.h GENERATED_H += command-list.h
@@ -959,6 +960,7 @@ LIB_OBJS += ref-filter.o LIB_OBJS += reflog-walk.o LIB_OBJS += refs.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
@@ -1162,7 +1164,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)
@@ -2352,11 +2354,30 @@ VCSSVN_OBJS += vcs-svn/sliding_window.o VCSSVN_OBJS += vcs-svn/svndiff.o VCSSVN_OBJS += vcs-svn/svndump.o +REFTABLE_OBJS += reftable/basics.o +REFTABLE_OBJS += reftable/block.o +REFTABLE_OBJS += reftable/compat.o +REFTABLE_OBJS += reftable/file.o +REFTABLE_OBJS += reftable/iter.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/reftable.o +REFTABLE_OBJS += reftable/strbuf.o +REFTABLE_OBJS += reftable/stack.o +REFTABLE_OBJS += reftable/tree.o +REFTABLE_OBJS += reftable/writer.o +REFTABLE_OBJS += reftable/zlib-compat.o + + TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \ $(XDIFF_OBJS) \ $(VCSSVN_OBJS) \ $(FUZZ_OBJS) \ + $(REFTABLE_OBJS) \ common-main.o \ git.o ifndef NO_CURL
@@ -2497,6 +2518,9 @@ $(XDIFF_LIB): $(XDIFF_OBJS) $(VCSSVN_LIB): $(VCSSVN_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ +$(REFTABLE_LIB): $(REFTABLE_OBJS) + $(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^ + export DEFAULT_EDITOR DEFAULT_PAGER Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
@@ -3112,7 +3136,7 @@ cocciclean: clean: profile-clean coverage-clean cocciclean $(RM) *.res $(RM) $(OBJECTS) - $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) + $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) $(REFTABLE_LIB) $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X $(RM) $(TEST_PROGRAMS) $(RM) $(FUZZ_PROGRAMS)
builtin/clone.c +2 -2
@@ -1112,7 +1112,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;
@@ -1227,7 +1227,7 @@ 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); +initialize_repository_version (hash_algo,default_ref_storage ());repo_set_hash_algo (the_repository, hash_algo); mapped_refs =wanted_peer_refs (refs, &remote->fetch);
builtin/init-db.c +40 -14
132 lines not on this page · raw message
builtin/worktree.c +24 -3
@@ -12,6 +12,7 @@#include "submodule.h" #include "utf8.h" #include "worktree.h" +#include "../refs/refs-internal.h" static const char *const worktree_usage[] = {N_ ("git worktree add [<options>] <path> [<commit-ish>]" ),
@@ -402,9 +403,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,"../.." );
cache.h +5 -3
@@ -627,9 +627,10 @@ int path_inside_repo(const char *prefix, const char *path);#define INIT_DB_EXIST_OK 0x0002int init_db (const char *git_dir,const char *real_git_dir, -const char *template_dir,int hash_algo, -const char *initial_branch,unsigned int flags); -void initialize_repository_version (int hash_algo); +const char *template_dir,int hash_algo,const char *initial_branch, +const char *ref_storage_format,unsigned int flags); +void initialize_repository_version (int hash_algo, +const char *ref_storage_format);void sanitize_stdfds (void );int daemonize (void );
@@ -1044,6 +1045,7 @@ struct repository_format {int hash_algo;int has_extensions;char *work_tree; +char *ref_storage;struct string_list unknown_extensions; };
refs.c +19 -8
@@ -19,10 +19,16 @@#include "repository.h" #include "sigchain.h" +const char *default_ref_storage (void ) +{ +const char *test =getenv ("GIT_TEST_REFTABLE" ); +return test ?"reftable" :"files" ; +} +/* * List of all available backends */ -static struct ref_storage_be *refs_backends = &refs_be_files; +static struct ref_storage_be *refs_backends = &refs_be_reftable;static struct ref_storage_be *find_ref_storage_backend (const char *name) {
@@ -1749,13 +1755,13 @@ static struct ref_store *lookup_ref_store_map(struct hashmap *map,* Create, record, and return a ref_store instance for the specified * gitdir. */ -static struct ref_store *ref_store_init (const char *gitdir, +static struct ref_store *ref_store_init (const char *gitdir,const char *be_name,unsigned int flags) { -const char *be_name ="files" ; -struct ref_storage_be *be =find_ref_storage_backend (be_name); +struct ref_storage_be *be;struct ref_store *refs; + be =find_ref_storage_backend (be_name);if (!be)BUG ("reference backend %s is unknown" , be_name);
@@ -1771,7 +1777,11 @@ struct ref_store *get_main_ref_store(struct repository *r)if (!r->gitdir)BUG ("attempting to get main_ref_store outside of repository" ); - r->refs_private =ref_store_init (r->gitdir,REF_STORE_ALL_CAPS ); + r->refs_private =ref_store_init (r->gitdir, + r->ref_storage_format ? + r->ref_storage_format : +default_ref_storage (), +REF_STORE_ALL_CAPS );return r->refs_private; }
@@ -1826,7 +1836,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule) goto done;/* assume that add_submodule_odb() has been called */ - refs =ref_store_init (submodule_sb.buf, + refs =ref_store_init (submodule_sb.buf,default_ref_storage (),REF_STORE_READ |REF_STORE_ODB );register_ref_store_map (&submodule_ref_stores,"submodule" , refs, submodule);
@@ -1840,6 +1850,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule)struct ref_store *get_worktree_ref_store (const struct worktree *wt) { +const char *format =default_ref_storage ();struct ref_store *refs;const char *id;
@@ -1853,9 +1864,9 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)if (wt->id) refs =ref_store_init (git_common_path ("worktrees/%s" , wt->id), -REF_STORE_ALL_CAPS ); + format,REF_STORE_ALL_CAPS );else - refs =ref_store_init (get_git_common_dir (), + refs =ref_store_init (get_git_common_dir (), format,REF_STORE_ALL_CAPS );if (refs)
refs.h +3 -0
@@ -9,6 +9,9 @@ struct string_list;struct string_list_item ;struct worktree ; +/* Returns the ref storage backend to use by default. */ +const char *default_ref_storage (void ); +/* * Resolve a reference, recursively following symbolic refererences. *
refs/refs-internal.h +1 -0
@@ -693,6 +693,7 @@ struct ref_storage_be { };extern struct ref_storage_be refs_be_files; +extern struct ref_storage_be refs_be_reftable;extern struct ref_storage_be refs_be_packed;/*
refs/reftable-backend.c new file +1497 -0
1,497 lines not on this page · raw message
reftable/update.sh +0 -3
@@ -16,7 +16,4 @@ cp reftable-repo/LICENSE reftable/ git --git-dir reftable-repo/.git show --no-patch --format=oneline HEAD \ > reftable/VERSION -mv reftable/system.h reftable/system.h~ -sed 's|if REFTABLE_IN_GITCORE|if 1 /* REFTABLE_IN_GITCORE */|' < reftable/system.h~ > reftable/system.h - git add reftable/*.[ch] reftable/LICENSE reftable/VERSION
repository.c +2 -0
@@ -178,6 +178,8 @@ int repo_init(struct repository *repo,if (worktree)repo_set_worktree (repo, worktree); + repo->ref_storage_format =xstrdup_or_null (format.ref_storage); +clear_repository_format (&format);return 0 ;
repository.h +3 -0
@@ -74,6 +74,9 @@ struct repository {*/ struct ref_store *refs_private; +/* The format to use for the ref database. */ +char *ref_storage_format; +/* * Contains path to often used file names. */
setup.c +9 -3
@@ -469,9 +469,11 @@ static int check_repo_format(const char *var, const char *value, void *vdata)if (!value)return config_error_nonbool (var); data->partial_clone =xstrdup (value); - }else if (!strcmp (ext,"worktreeconfig" )) + }else if (!strcmp (ext,"worktreeconfig" )) { data->worktree_config =git_config_bool (var, value); -else + }else if (!strcmp (ext,"refstorage" )) { + data->ref_storage =xstrdup (value); + }else string_list_append (&data->unknown_extensions, ext); }
@@ -594,6 +596,7 @@ void clear_repository_format(struct repository_format *format)string_list_clear (&format->unknown_extensions,0 );free (format->work_tree);free (format->partial_clone); +free (format->ref_storage);init_repository_format (format); }
@@ -1239,8 +1242,11 @@ const char *setup_git_directory_gently(int *nongit_ok) gitdir =DEFAULT_GIT_DIR_ENVIRONMENT ;setup_git_env (gitdir); } -if (startup_info->have_repository) +if (startup_info->have_repository) {repo_set_hash_algo (the_repository, repo_fmt.hash_algo); + the_repository->ref_storage_format = +xstrdup_or_null (repo_fmt.ref_storage); + } }strbuf_release (&dir);
t/t0031-reftable.sh new file +173 -0
173 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v19 15/20 Hookup unittests for the reftable library.
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
The unittests are under reftable/*_test.c, so all of the reftable code stays in one directory. They are called from t/helpers/test-reftable.c in t0031-reftable.sh
--- Makefile | 17 ++++++++++++++++- t/helper/test-reftable.c | 15 +++++++++++++++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + t/t0031-reftable.sh | 5 +++++ 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 t/helper/test-reftable.c
Makefile +16 -1
@@ -725,6 +725,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
@@ -808,6 +809,7 @@ LIB_FILE = libgit.a XDIFF_LIB = xdiff/lib.a VCSSVN_LIB = vcs-svn/lib.a REFTABLE_LIB = reftable/libreftable.a +REFTABLE_TEST_LIB = reftable/libreftable_test.a GENERATED_H += config-list.h GENERATED_H += command-list.h
@@ -2371,6 +2373,15 @@ REFTABLE_OBJS += reftable/tree.o REFTABLE_OBJS += reftable/writer.o REFTABLE_OBJS += reftable/zlib-compat.o +REFTABLE_TEST_OBJS += reftable/block_test.o +REFTABLE_TEST_OBJS += reftable/merged_test.o +REFTABLE_TEST_OBJS += reftable/record_test.o +REFTABLE_TEST_OBJS += reftable/refname_test.o +REFTABLE_TEST_OBJS += reftable/reftable_test.o +REFTABLE_TEST_OBJS += reftable/strbuf_test.o +REFTABLE_TEST_OBJS += reftable/stack_test.o +REFTABLE_TEST_OBJS += reftable/tree_test.o +REFTABLE_TEST_OBJS += reftable/test_framework.o TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS)) OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
@@ -2378,6 +2389,7 @@ OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \ $(VCSSVN_OBJS) \ $(FUZZ_OBJS) \ $(REFTABLE_OBJS) \ + $(REFTABLE_TEST_OBJS) \ common-main.o \ git.o ifndef NO_CURL
@@ -2521,6 +2533,9 @@ $(VCSSVN_LIB): $(VCSSVN_OBJS) $(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
@@ -2803,7 +2818,7 @@ t/helper/test-svn-fe$X: $(VCSSVN_LIB) 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
t/helper/test-reftable.c new file +15 -0
@@ -0,0 +1,15 @@ +#include "reftable/reftable-tests.h" +#include "test-tool.h" + +int cmd__reftable (int argc,const char **argv) +{ +block_test_main (argc, argv); +merged_test_main (argc, argv); +record_test_main (argc, argv); +refname_test_main (argc, argv); +reftable_test_main (argc, argv); +strbuf_test_main (argc, argv); +stack_test_main (argc, argv); +tree_test_main (argc, argv); +return 0 ; +}
t/helper/test-tool.c +1 -0
@@ -52,6 +52,7 @@ static struct test_cmd cmds[] = { { "read-graph", cmd__read_graph }, { "read-midx", cmd__read_midx }, { "ref-store", cmd__ref_store }, + { "reftable", cmd__reftable }, { "regex", cmd__regex }, { "repository", cmd__repository }, { "revision-walking", cmd__revision_walking },
t/helper/test-tool.h +1 -0
@@ -41,6 +41,7 @@ int cmd__read_cache(int argc, const char **argv);int cmd__read_graph (int argc,const char **argv);int cmd__read_midx (int argc,const char **argv);int cmd__ref_store (int argc,const char **argv); +int cmd__reftable (int argc,const char **argv);int cmd__regex (int argc,const char **argv);int cmd__repository (int argc,const char **argv);int cmd__revision_walking (int argc,const char **argv);
t/t0031-reftable.sh +5 -0
@@ -15,6 +15,11 @@ initialize () { mv .git/hooks .git/hooks-disabled } +test_expect_success 'unittests' ' + test-tool reftable +' + + test_expect_success 'delete ref' ' initialize && test_commit file &&
Signature · 1 line
gitgitgadget
PATCH v19 16/20 Add GIT_DEBUG_REFS debugging mechanism
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
When set in the environment, GIT_DEBUG_REFS makes git print operations and results as they flow through the ref storage backend. This helps debug discrepancies between different ref backends.
--- Makefile | 1 + builtin/worktree.c | 6 +- refs.c | 3 + refs/debug.c | 416 ++++++++++++++++++++++++++++++++++++++++++ refs/refs-internal.h | 6 + t/t0033-debug-refs.sh | 18 ++ 6 files changed, 449 insertions(+), 1 deletion(-) create mode 100644 refs/debug.c create mode 100755 t/t0033-debug-refs.sh
Makefile +1 -0
@@ -961,6 +961,7 @@ LIB_OBJS += rebase.o LIB_OBJS += ref-filter.o 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
builtin/worktree.c +5 -1
@@ -403,7 +403,11 @@ static int add_worktree(const char *path, const char *refname,* worktree. */ strbuf_reset (&sb); -if (get_main_ref_store (the_repository)->be == &refs_be_reftable) { + +/* XXX: check GIT_TEST_REFTABLE because GIT_DEBUG_REFS obscures the +* instance type. */ +if (get_main_ref_store (the_repository)->be == &refs_be_reftable || +getenv ("GIT_TEST_REFTABLE" ) !=NULL ) {/* 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" );
refs.c +3 -0
@@ -1782,6 +1782,9 @@ struct ref_store *get_main_ref_store(struct repository *r) r->ref_storage_format :default_ref_storage (), REF_STORE_ALL_CAPS); +if (getenv ("GIT_DEBUG_REFS" )) { + r->refs_private =debug_wrap (r->gitdir, r->refs_private); + }return r->refs_private; }
refs/debug.c new file +416 -0
416 lines not on this page · raw message
refs/refs-internal.h +6 -0
@@ -713,4 +713,10 @@ struct ref_store {void base_ref_store_init (struct ref_store *refs,const struct ref_storage_be *be); +/* +* Print out ref operations as they occur. Useful for debugging alternate ref +* backends. +*/ +struct ref_store *debug_wrap (const char *gitdir,struct ref_store *store); +#endif /* REFS_REFS_INTERNAL_H */
t/t0033-debug-refs.sh new file +18 -0
@@ -0,0 +1,18 @@ +#!/bin/sh +# +# Copyright (c) 2020 Google LLC +# + +test_description='cross-check reftable with files, using GIT_DEBUG_REFS output' + +. ./test-lib.sh + +test_expect_success 'GIT_DEBUG_REFS' ' + git init --ref-storage=files files && + git init --ref-storage=reftable reftable && + (cd files && GIT_DEBUG_REFS=1 test_commit message file) > files.txt && + (cd reftable && GIT_DEBUG_REFS=1 test_commit message file) > reftable.txt && + test_cmp files.txt reftable.txt +' + +test_done
Signature · 1 line
gitgitgadget
PATCH v19 17/20 vcxproj: adjust for the reftable changes
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
This allows Git to be compiled via Visual Studio again after integrating the hn/reftable branch.
--- config.mak.uname | 2 +- contrib/buildsystems/Generators/Vcxproj.pm | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-)
config.mak.uname +1 -1
@@ -709,7 +709,7 @@ vcxproj: # Make .vcxproj files and add them unset QUIET_GEN QUIET_BUILT_IN; \ perl contrib/buildsystems/generate -g Vcxproj - git add -f git.sln {*,*/lib,t/helper/*}/*.vcxproj + git add -f git.sln {*,*/lib,*/libreftable,t/helper/*}/*.vcxproj # Generate the LinkOrCopyBuiltins.targets and LinkOrCopyRemoteHttp.targets file (echo '<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">' && \
contrib/buildsystems/Generators/Vcxproj.pm +10 -1
@@ -77,7 +77,7 @@ sub createProject { my $libs_release = "\n "; my $libs_debug = "\n "; if (!$static_library) { - $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}})); + $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib|reftable\/libreftable\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}})); $libs_debug = $libs_release; $libs_debug =~ s/zlib\.lib/zlibd\.lib/g; $libs_debug =~ s/libcurl\.lib/libcurl-d\.lib/g;
@@ -231,6 +231,7 @@ sub createProject { EOM if (!$static_library || $target =~ 'vcs-svn' || $target =~ 'xdiff') { my $uuid_libgit = $$build_structure{"LIBS_libgit_GUID"}; + my $uuid_libreftable = $$build_structure{"LIBS_reftable/libreftable_GUID"}; my $uuid_xdiff_lib = $$build_structure{"LIBS_xdiff/lib_GUID"}; print F << "EOM";
@@ -240,6 +241,14 @@ sub createProject { <ReferenceOutputAssembly>false</ReferenceOutputAssembly> </ProjectReference> EOM + if (!($name =~ /xdiff|libreftable/)) { + print F << "EOM"; + <ProjectReference Include="$cdup\\reftable\\libreftable\\libreftable.vcxproj"> + <Project>$uuid_libreftable</Project> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + </ProjectReference> +EOM + } if (!($name =~ 'xdiff')) { print F << "EOM"; <ProjectReference Include="$cdup\\xdiff\\lib\\xdiff_lib.vcxproj">
Signature · 1 line
gitgitgadget
PATCH v19 18/20 git-prompt: prepare for reftable refs backend
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: =?UTF-8?q?SZEDER=20G=C3=A1bor?= <[email protected]>
In our git-prompt script we strive to use Bash builtins wherever possible, because fork()-ing subshells for command substitutions and fork()+exec()-ing Git commands are expensive on some platforms. We even read and parse '.git/HEAD' using Bash builtins to get the name of the current branch [1]. However, the upcoming reftable refs backend won't use '.git/HEAD' at all, but will write an invalid refname as placeholder for backwards compatibility instead, which will break our git-prompt script.
Update the git-prompt script to recognize the placeholder '.git/HEAD' written by the reftable backend (its content is specified in the reftable specs), and then fall back to use 'git symbolic-ref' to get the name of the current branch.
[1] 3a43c4b5bd (bash prompt: use bash builtins to find out current
branch, 2011-03-31)
--- contrib/completion/git-prompt.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
contrib/completion/git-prompt.sh +6 -1
@@ -476,10 +476,15 @@ __git_ps1 () if ! __git_eread "$g/HEAD" head; then return $exit fi - # is it a symbolic ref? b="${head#ref: }" if [ "$head" = "$b" ]; then detached=yes + elif [ "$b" = "refs/heads/.invalid" ]; then + # Reftable + b="$(git symbolic-ref HEAD 2>/dev/null)" || + detached=yes + fi + if [ "$detached" = yes ]; then b="$( case "${GIT_PS1_DESCRIBE_STYLE-}" in (contains)
Signature · 1 line
gitgitgadget
PATCH v19 19/20 Add reftable testing infrastructure
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
* Add GIT_TEST_REFTABLE environment var to control default ref storage
* Add test_prerequisite REFTABLE.
* Skip some tests that are incompatible:
* t3210-pack-refs.sh - does not apply
* t1450-fsck.sh - manipulates .git/ directly to create invalid state
Major test failures:
* t1400-update-ref.sh - Reads from .git/{refs,logs} directly
* t1404-update-ref-errors.sh - Manipulates .git/refs/ directly
* t1405 - inspecs .git/ directly.
--- t/t1409-avoid-packing-refs.sh | 6 ++++++ t/t1450-fsck.sh | 6 ++++++ t/t3210-pack-refs.sh | 6 ++++++ t/test-lib.sh | 5 +++++ 4 files changed, 23 insertions(+)
t/t1409-avoid-packing-refs.sh +6 -0
@@ -4,6 +4,12 @@ test_description='avoid rewriting packed-refs unnecessarily' . ./test-lib.sh +if test_have_prereq REFTABLE +then + skip_all='skipping pack-refs tests; incompatible with reftable' + test_done +fi + # Add an identifying mark to the packed-refs file header line. This # shouldn't upset readers, and it should be omitted if the file is # ever rewritten.
t/t1450-fsck.sh +6 -0
@@ -8,6 +8,12 @@ test_description='git fsck random collection of tests . ./test-lib.sh +if test_have_prereq REFTABLE +then + skip_all='skipping tests; incompatible with reftable' + test_done +fi + test_expect_success setup ' test_oid_init && git config gc.auto 0 &&
t/t3210-pack-refs.sh +6 -0
@@ -11,6 +11,12 @@ semantic is still the same. ' . ./test-lib.sh +if test_have_prereq REFTABLE +then + skip_all='skipping pack-refs tests; incompatible with reftable' + test_done +fi + test_expect_success 'enable reflogs' ' git config core.logallrefupdates true '
t/test-lib.sh +5 -0
@@ -1505,6 +1505,11 @@ parisc* | hppa*) ;; esac +if test -n "$GIT_TEST_REFTABLE" +then + test_set_prereq REFTABLE +fi + ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1 test -z "$NO_PERL" && test_set_prereq PERL test -z "$NO_PTHREADS" && test_set_prereq PTHREADS
Signature · 1 line
gitgitgadget
PATCH v19 20/20 Add "test-tool dump-reftable" command.
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This command dumps individual tables or a stack of of tables.
--- Makefile | 1 + t/helper/test-reftable.c | 5 +++++ t/helper/test-tool.c | 1 + t/helper/test-tool.h | 1 + 4 files changed, 8 insertions(+)
Makefile +1 -0
@@ -2375,6 +2375,7 @@ REFTABLE_OBJS += reftable/writer.o REFTABLE_OBJS += reftable/zlib-compat.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/record_test.o REFTABLE_TEST_OBJS += reftable/refname_test.o
t/helper/test-reftable.c +5 -0
@@ -13,3 +13,8 @@ int cmd__reftable(int argc, const char **argv)tree_test_main (argc, argv);return 0 ; } + +int cmd__dump_reftable (int argc,const char **argv) +{ +return reftable_dump_main (argc, (char *const *)argv); +}
t/helper/test-tool.c +1 -0
@@ -53,6 +53,7 @@ static struct test_cmd cmds[] = { { "read-midx", cmd__read_midx }, { "ref-store", cmd__ref_store }, { "reftable", cmd__reftable }, + { "dump-reftable", cmd__dump_reftable }, { "regex", cmd__regex }, { "repository", cmd__repository }, { "revision-walking", cmd__revision_walking },
t/helper/test-tool.h +1 -0
@@ -17,6 +17,7 @@ int cmd__dump_cache_tree(int argc, const char **argv);int cmd__dump_fsmonitor (int argc,const char **argv);int cmd__dump_split_index (int argc,const char **argv);int cmd__dump_untracked_cache (int argc,const char **argv); +int cmd__dump_reftable (int argc,const char **argv);int cmd__example_decorate (int argc,const char **argv);int cmd__genrandom (int argc,const char **argv);int cmd__genzeros (int argc,const char **argv);
Signature · 1 line
gitgitgadget
Quoted 1 line · Han-Wen Nienhuys via GitGitGadget
base-commit:
b9a2d1a0207fb9ded3fa524f54db3bc322a12cc4
This is based on 'next', which usually is a sure way for a topic to stay forever out of 'next', but we have impactful dependence only on two topics, I think, and a good news is that both of them are in pretty good shape. I think Brian's part 2 of SHA-256 work should be on the 'master' branch soon, and Dscho's "customizable default branch" is also ready---it just would, like all other topics, want to spend at least one week on 'next' to be safe. And after that, this topic can be directly on 'master' (there is another trivial conflict around bisect--helper, but I am not worried about it), which looks quite good.
Tentatively I've prepared a merge of bc/sha-256-part-2 and js/default-branch-name on top of today's 'master' and these patches applied cleanly on the result. As you mentioned, we may want to flip the orders of patches a bit to split uncontroversial ones out to a separate preparatory topic and get them merged to 'next' and then to 'master' earlier than the remainder.
Thanks.
-- >8 --
fixup! Reftable support for git-core
Noticed by the "make && make distclean && git clean -n -x" test.
Makefile +1 -1
@@ -3153,7 +3153,7 @@ cocciclean: clean: profile-clean coverage-clean cocciclean $(RM) *.res $(RM) $(OBJECTS) - $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) $(REFTABLE_LIB) + $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB) $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X $(RM) $(TEST_PROGRAMS) $(RM) $(FUZZ_PROGRAMS)
Signature · 1 line
2.27.0-221-ga08a83db2b
Quoted 5 lines · Junio C Hamano
Quoted 1 line · Han-Wen Nienhuys via GitGitGadget
base-commit:
b9a2d1a0207fb9ded3fa524f54db3bc322a12cc4
Quoted 10 lines
This is based on 'next', which usually is a sure way for a topic to stay forever out of 'next', but we have impactful dependence only on two topics, I think, and a good news is that both of them are in pretty good shape. I think Brian's part 2 of SHA-256 work should be on the 'master' branch soon, and Dscho's "customizable default branch" is also ready---it just would, like all other topics, want to spend at least one week on 'next' to be safe. And after that, this topic can be directly on 'master' (there is another trivial conflict around bisect--helper, but I am not worried about it), which looks quite good.
ok. For the next time, I should keep basing myself on master, even if I know there are conflicts?
Do you have an opinion on
https://public-inbox.org/git/[email protected]/
?
There is some overlap with in sequencer.c, and Phillip's approach is likely more principled, so I'd like to base reftable on that.
Quoted 1 line
fixup! Reftable support for git-core
thanks, folded in!
Signature · 11 lines
Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Quoted 17 lines · Han-Wen Nienhuys
Quoted 5 lines · Junio C Hamano
Quoted 1 line · Han-Wen Nienhuys via GitGitGadget
base-commit:
b9a2d1a0207fb9ded3fa524f54db3bc322a12cc4Quoted 10 lines
This is based on 'next', which usually is a sure way for a topic to stay forever out of 'next', but we have impactful dependence only on two topics, I think, and a good news is that both of them are in pretty good shape. I think Brian's part 2 of SHA-256 work should be on the 'master' branch soon, and Dscho's "customizable default branch" is also ready---it just would, like all other topics, want to spend at least one week on 'next' to be safe. And after that, this topic can be directly on 'master' (there is another trivial conflict around bisect--helper, but I am not worried about it), which looks quite good.
Quoted 2 lines
ok. For the next time, I should keep basing myself on master, even if I know there are conflicts?
Right now we know there won't be and that is why I said the above. If your next round would change the code drastically, or somebody sends changes that deliberately conflicts with what you are doing to sabotage you, the situation would become different.
Quoted 6 lines
Do you have an opinion on
https://public-inbox.org/git/[email protected]/
?There is some overlap with in
sequencer.c, and Phillip's approach is likely more principled, so I'd like to base reftable on that.
I assumed that these were offered to you as possible improvements to be folded into your series, so I didn't read them very carefully and I didn't queue them myself. I expected that I would see them, possibly modified to fit the context better, as part of your series sent from you, perhaps to become a part of early clean-up portion of your topic.
Thanks.
Quoted 7 lines · Junio C Hamano
Quoted 6 lines
Do you have an opinion on
https://public-inbox.org/git/[email protected]/
?There is some overlap with in
sequencer.c, and Phillip's approach is likely more principled, so I'd like to base reftable on that.
Quoted 6 lines
I assumed that these were offered to you as possible improvements to be folded into your series, so I didn't read them very carefully and I didn't queue them myself. I expected that I would see them, possibly modified to fit the context better, as part of your series sent from you, perhaps to become a part of early clean-up portion of your topic.
They are changing the signature of widely used functions, which is useful for my series but not completely necessary. I would rather that someone else decides on how to go forward with the series.
Signature · 11 lines
Han-Wen Nienhuys - Google Munich I work 80%. Don't expect answers from me on Fridays. -- Google Germany GmbH, Erika-Mann-Strasse 33, 80636 Munich Registergericht und -nummer: Hamburg, HRB 86891 Sitz der Gesellschaft: Hamburg Geschäftsführer: Paul Manicle, Halimah DeLaine Prado
Quoted 15 lines · Han-Wen Nienhuys
Quoted 7 lines · Junio C Hamano
Quoted 6 lines
Do you have an opinion on
https://public-inbox.org/git/[email protected]/
?There is some overlap with in
sequencer.c, and Phillip's approach is likely more principled, so I'd like to base reftable on that.Quoted 6 lines
I assumed that these were offered to you as possible improvements to be folded into your series, so I didn't read them very carefully and I didn't queue them myself. I expected that I would see them, possibly modified to fit the context better, as part of your series sent from you, perhaps to become a part of early clean-up portion of your topic.
Quoted 3 lines
They are changing the signature of widely used functions, which is useful for my series but not completely necessary. I would rather that someone else decides on how to go forward with the series.
I was about to say that having written one ref backend, you are likely to have better context to judge how much better Phillip's patches would make the world be than I do ;-), but having worked on the "cleanse caller-supplied reflog message at generic layer" illustration patch, I think I am familiar enough with these three functions Phillip's patch touched to comment on them.
I am not sure why delete_ref() needs to be modified to take a caller-supplied repository when refs_delete_ref() is already even a better way to do so (it allows the caller to give a ref_store, which is a part of a repository). The same between update_ref() and refs_update_ref().
Introducing a new repo_delete_ref() to extend the API into a three-tuple,
delete_ref(...) {
return repo_delete_ref(the_repository, ...);
}
repo_delete_ref(repo, ...) {
return refs_delete_ref(get_main_ref_store(repo), ...);
}
refs_delete_ref(...) {
/* as we already have it */
}_might_ make sense, but then we should do so for not just delete and update, but for all the operations consistently. I do not know if that is worth it offhand. The issue Phillip's patch addresses looks like a mere "lack of convenience", not a fundamental "without this we cannot write correct code easily", at least to me.
What disturbed me more while I was looking at refs.c was that some operations are not done (perhaps cannot be done) as a part of a transaction. refs_delete_refs() and refs_rename_ref() directly call into the backend layer, for example, and that does not smell right.
v20 cover Reftable support git-core
In reply to Han-Wen Nienhuys via GitGitGadgetThis adds the reftable library, and hooks it up as a ref backend.
This is still based on next, as there are conflicts basing on master.
Includes testing support, to test: make -C t/ GIT_TEST_REFTABLE=1
Summary 455 tests fail
v24
* rebase
* FETCH_HEAD special casing
Han-Wen Nienhuys (19):
refs: add \t to reflog in the files backend
Split off reading loose ref data in separate function
t1400: use git rev-parse for testing PSEUDOREF existence Modify pseudo refs through ref backend storage
Make HEAD a PSEUDOREF rather than PER_WORKTREE.
Make refs_ref_exists public
Treat CHERRY_PICK_HEAD as a pseudo ref
Treat REVERT_HEAD as a pseudo ref
Move REF_LOG_ONLY to refs-internal.h
Iteration over entire ref namespace is iterating over "refs/" Add .gitattributes for the reftable/ directory
Add reftable library
Add standalone build infrastructure for reftable Reftable support for git-core
Read FETCH_HEAD as loose ref
Hookup unittests for the reftable library.
Add GIT_DEBUG_REFS debugging mechanism
Add reftable testing infrastructure
Add "test-tool dump-reftable" command.
Johannes Schindelin (1):
vcxproj: adjust for the reftable changes
SZEDER Gábor (1):
git-prompt: prepare for reftable refs backend
Documentation/git-update-ref.txt | 13 +- .../technical/repository-version.txt | 7 + Makefile | 47 +- builtin/clone.c | 4 +- builtin/commit.c | 34 +- builtin/init-db.c | 55 +- builtin/merge.c | 2 +- builtin/worktree.c | 29 +- cache.h | 8 +- config.mak.uname | 2 +- contrib/buildsystems/Generators/Vcxproj.pm | 11 +- contrib/completion/git-prompt.sh | 7 +- path.c | 2 - path.h | 9 +- ref-filter.c | 7 +- refs.c | 171 +- refs.h | 6 + refs/debug.c | 387 +++++ refs/files-backend.c | 45 +- refs/refs-internal.h | 20 + refs/reftable-backend.c | 1415 +++++++++++++++++ reftable/.gitattributes | 1 + reftable/BUILD | 203 +++ reftable/LICENSE | 31 + reftable/README.md | 33 + reftable/VERSION | 1 + reftable/WORKSPACE | 14 + reftable/basics.c | 215 +++ reftable/basics.h | 53 + reftable/block.c | 432 +++++ reftable/block.h | 129 ++ reftable/block_test.c | 157 ++ reftable/compat.c | 98 ++ reftable/compat.h | 48 + reftable/constants.h | 21 + reftable/dump.c | 212 +++ reftable/file.c | 95 ++ reftable/iter.c | 242 +++ reftable/iter.h | 72 + reftable/merged.c | 317 ++++ reftable/merged.h | 43 + reftable/merged_test.c | 286 ++++ reftable/pq.c | 115 ++ reftable/pq.h | 34 + reftable/reader.c | 744 +++++++++ reftable/reader.h | 65 + reftable/record.c | 1126 +++++++++++++ reftable/record.h | 143 ++ reftable/record_test.c | 410 +++++ reftable/refname.c | 209 +++ reftable/refname.h | 38 + reftable/refname_test.c | 99 ++ reftable/reftable-tests.h | 22 + reftable/reftable.c | 154 ++ reftable/reftable.h | 585 +++++++ reftable/reftable_test.c | 632 ++++++++ reftable/stack.c | 1225 ++++++++++++++ reftable/stack.h | 50 + reftable/stack_test.c | 787 +++++++++ reftable/strbuf.c | 206 +++ reftable/strbuf.h | 88 + reftable/strbuf_test.c | 39 + reftable/system.h | 53 + reftable/test_framework.c | 69 + reftable/test_framework.h | 64 + reftable/tree.c | 63 + reftable/tree.h | 34 + reftable/tree_test.c | 62 + reftable/update.sh | 19 + reftable/writer.c | 663 ++++++++ reftable/writer.h | 60 + reftable/zlib-compat.c | 92 ++ reftable/zlib.BUILD | 36 + repository.c | 2 + repository.h | 3 + sequencer.c | 56 +- setup.c | 10 +- t/helper/test-reftable.c | 20 + t/helper/test-tool.c | 2 + t/helper/test-tool.h | 2 + t/t0031-reftable.sh | 185 +++ t/t0033-debug-refs.sh | 18 + t/t1400-update-ref.sh | 30 +- t/t1405-main-ref-store.sh | 5 +- t/t1409-avoid-packing-refs.sh | 6 + t/t1450-fsck.sh | 6 + t/t3210-pack-refs.sh | 6 + t/test-lib.sh | 5 + wt-status.c | 6 +- 89 files changed, 13076 insertions(+), 256 deletions(-) create mode 100644 refs/debug.c create mode 100644 refs/reftable-backend.c create mode 100644 reftable/.gitattributes create mode 100644 reftable/BUILD create mode 100644 reftable/LICENSE create mode 100644 reftable/README.md create mode 100644 reftable/VERSION create mode 100644 reftable/WORKSPACE create mode 100644 reftable/basics.c create mode 100644 reftable/basics.h create mode 100644 reftable/block.c create mode 100644 reftable/block.h create mode 100644 reftable/block_test.c create mode 100644 reftable/compat.c create mode 100644 reftable/compat.h create mode 100644 reftable/constants.h create mode 100644 reftable/dump.c create mode 100644 reftable/file.c create mode 100644 reftable/iter.c create mode 100644 reftable/iter.h create mode 100644 reftable/merged.c create mode 100644 reftable/merged.h create mode 100644 reftable/merged_test.c create mode 100644 reftable/pq.c create mode 100644 reftable/pq.h create mode 100644 reftable/reader.c create mode 100644 reftable/reader.h create mode 100644 reftable/record.c create mode 100644 reftable/record.h create mode 100644 reftable/record_test.c create mode 100644 reftable/refname.c create mode 100644 reftable/refname.h create mode 100644 reftable/refname_test.c create mode 100644 reftable/reftable-tests.h create mode 100644 reftable/reftable.c create mode 100644 reftable/reftable.h create mode 100644 reftable/reftable_test.c create mode 100644 reftable/stack.c create mode 100644 reftable/stack.h create mode 100644 reftable/stack_test.c create mode 100644 reftable/strbuf.c create mode 100644 reftable/strbuf.h create mode 100644 reftable/strbuf_test.c create mode 100644 reftable/system.h create mode 100644 reftable/test_framework.c create mode 100644 reftable/test_framework.h create mode 100644 reftable/tree.c create mode 100644 reftable/tree.h create mode 100644 reftable/tree_test.c create mode 100755 reftable/update.sh create mode 100644 reftable/writer.c create mode 100644 reftable/writer.h create mode 100644 reftable/zlib-compat.c create mode 100644 reftable/zlib.BUILD create mode 100644 t/helper/test-reftable.c create mode 100755 t/t0031-reftable.sh create mode 100755 t/t0033-debug-refs.sh
base-commit: e8ab941b671da6890181aea5b5755d1d9eea24ec
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-539%2Fhanwen%2Freftable-v20
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-539/hanwen/reftable-v20
Pull-Request: https://github.com/gitgitgadget/git/pull/539
Range-diff vs v19:
1: 596c316da4 < -: ---------- lib-t6000.sh: write tag using git-update-ref
2: 277da0cf7e < -: ---------- t3432: use git-reflog to inspect the reflog for HEAD
3: 125695ce92 < -: ---------- checkout: add '\n' to reflog message
-: ---------- > 1: 05ced51683 refs: add \t to reflog in the files backend
-: ---------- > 2: d0f0680c0e Split off reading loose ref data in separate function
-: ---------- > 3: 716cb30c1b t1400: use git rev-parse for testing PSEUDOREF existence
4: 5715681b3f ! 4: 400ffe531f Write pseudorefs through ref backends.
@@ Metadata
Author: Han-Wen Nienhuys <[email protected]>
## Commit message ##
- Write pseudorefs through ref backends.
+ Modify pseudo refs through ref backend storage
- Pseudorefs store transient data in in the repository. Examples are HEAD,
- CHERRY_PICK_HEAD, etc.
+ The previous behavior was introduced in commit 74ec19d4be
+ ("pseudorefs: create and use pseudoref update and delete functions",
+ Jul 31, 2015), with the justification "alternate ref backends still
+ need to store pseudorefs in GIT_DIR".
- These refs have always been read through the ref backends, but they were written
- in a one-off routine that wrote an object ID or symref directly into
- .git/<pseudo_ref_name>.
+ Refs such as REBASE_HEAD are read through the ref backend. This can
+ only work consistently if they are written through the ref backend as
+ well. Tooling that works directly on files under .git should be
+ updated to use git commands to read refs instead.
- This causes problems when introducing a new ref storage backend. To remedy this,
- extend the ref backend implementation with a write_pseudoref_fn and
- update_pseudoref_fn.
+ The following behaviors change:
+
+ * Updates to pseudorefs (eg. ORIG_HEAD) with
+ core.logAllRefUpdates=always will create reflogs for the pseudoref.
+
+ * non-HEAD pseudoref symrefs are also dereferenced on deletion. Update
+ t1405 accordingly.
Signed-off-by: Han-Wen Nienhuys <[email protected]>
- ## refs.c ##
-@@ refs.c: int ref_exists(const char *refname)
- return refs_ref_exists(get_main_ref_store(the_repository), refname);
- }
+ ## Documentation/git-update-ref.txt ##
+@@ Documentation/git-update-ref.txt: still see a subset of the modifications.
+
+ LOGGING UPDATES
+ ---------------
+-If config parameter "core.logAllRefUpdates" is true and the ref is one under
+-"refs/heads/", "refs/remotes/", "refs/notes/", or the symbolic ref HEAD; or
+-the file "$GIT_DIR/logs/<ref>" exists then `git update-ref` will append
+-a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all
+-symbolic refs before creating the log name) describing the change
+-in ref value. Log lines are formatted as:
++If config parameter "core.logAllRefUpdates" is true and the ref is one
++under "refs/heads/", "refs/remotes/", "refs/notes/", or a pseudoref
++like HEAD or ORIG_HEAD; or the file "$GIT_DIR/logs/<ref>" exists then
++`git update-ref` will append a line to the log file
++"$GIT_DIR/logs/<ref>" (dereferencing all symbolic refs before creating
++the log name) describing the change in ref value. Log lines are
++formatted as:
+
+ oldsha1 SP newsha1 SP committer LF
-+int delete_pseudoref(const char *pseudoref, const struct object_id *old_oid)
-+{
-+ return refs_delete_pseudoref(get_main_ref_store(the_repository),
-+ pseudoref, old_oid);
-+}
-+
- static int filter_refs(const char *refname, const struct object_id *oid,
- int flags, void *data)
- {
+
+ ## refs.c ##
@@ refs.c: long get_files_ref_lock_timeout_ms(void)
return timeout_ms;
}
@@ refs.c: long get_files_ref_lock_timeout_ms(void)
-
- return 0;
-}
-
+-
int refs_delete_ref(struct ref_store *refs, const char *msg,
const char *refname,
+ const struct object_id *old_oid,
@@ refs.c: int refs_delete_ref(struct ref_store *refs, const char *msg,
+ struct ref_transaction *transaction;
+ struct strbuf err = STRBUF_INIT;
- if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
- assert(refs == get_main_ref_store(the_repository));
+- if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
+- assert(refs == get_main_ref_store(the_repository));
- return delete_pseudoref(refname, old_oid);
-+ return refs_delete_pseudoref(refs, refname, old_oid);
- }
-
+- }
+-
transaction = ref_store_transaction_begin(refs, &err);
+ if (!transaction ||
+ ref_transaction_delete(transaction, refname, old_oid,
@@ refs.c: int refs_update_ref(struct ref_store *refs, const char *msg,
+ struct strbuf err = STRBUF_INIT;
+ int ret = 0;
- if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
- assert(refs == get_main_ref_store(the_repository));
+- if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
+- assert(refs == get_main_ref_store(the_repository));
- ret = write_pseudoref(refname, new_oid, old_oid, &err);
-+ ret = refs_write_pseudoref(refs, refname, new_oid, old_oid,
-+ &err);
- } else {
- t = ref_store_transaction_begin(refs, &err);
- if (!t ||
-@@ refs.c: int head_ref(each_ref_fn fn, void *cb_data)
- return refs_head_ref(get_main_ref_store(the_repository), fn, cb_data);
- }
-
-+int refs_write_pseudoref(struct ref_store *refs, const char *pseudoref,
-+ const struct object_id *oid,
-+ const struct object_id *old_oid, struct strbuf *err)
-+{
-+ return refs->be->write_pseudoref(refs, pseudoref, oid, old_oid, err);
-+}
-+
-+int refs_delete_pseudoref(struct ref_store *refs, const char *pseudoref,
-+ const struct object_id *old_oid)
-+{
-+ return refs->be->delete_pseudoref(refs, pseudoref, old_oid);
-+}
-+
- struct ref_iterator *refs_ref_iterator_begin(
- struct ref_store *refs,
- const char *prefix, int trim, int flags)
-
- ## refs.h ##
-@@ refs.h: int update_ref(const char *msg, const char *refname,
- const struct object_id *new_oid, const struct object_id *old_oid,
- unsigned int flags, enum action_on_err onerr);
-
-+/* Pseudorefs (eg. HEAD, CHERRY_PICK_HEAD) have a separate routines for updating
-+ and deletion as they cannot take part in normal transactional updates.
-+ Pseudorefs should only be written for the main repository.
-+*/
-+int refs_write_pseudoref(struct ref_store *refs, const char *pseudoref,
-+ const struct object_id *oid,
-+ const struct object_id *old_oid, struct strbuf *err);
-+int refs_delete_pseudoref(struct ref_store *refs, const char *pseudoref,
-+ const struct object_id *old_oid);
-+int delete_pseudoref(const char *pseudoref, const struct object_id *old_oid);
-+
- int parse_hide_refs_config(const char *var, const char *value, const char *);
-
- /*
-
- ## refs/files-backend.c ##
-@@ refs/files-backend.c: static int lock_raw_ref(struct files_ref_store *refs,
- return ret;
- }
-
-+static int files_write_pseudoref(struct ref_store *ref_store,
-+ const char *pseudoref,
-+ const struct object_id *oid,
-+ const struct object_id *old_oid,
-+ struct strbuf *err)
-+{
-+ struct files_ref_store *refs =
-+ files_downcast(ref_store, REF_STORE_READ, "write_pseudoref");
-+ int fd;
-+ struct lock_file lock = LOCK_INIT;
-+ struct strbuf filename = STRBUF_INIT;
-+ struct strbuf buf = STRBUF_INIT;
-+ int ret = -1;
-+
-+ if (!oid)
-+ return 0;
-+
-+ strbuf_addf(&filename, "%s/%s", refs->gitdir, pseudoref);
-+ fd = hold_lock_file_for_update_timeout(&lock, filename.buf, 0,
-+ get_files_ref_lock_timeout_ms());
-+ if (fd < 0) {
-+ strbuf_addf(err, _("could not open '%s' for writing: %s"),
-+ buf.buf, strerror(errno));
-+ goto done;
-+ }
-+
-+ if (old_oid) {
-+ struct object_id actual_old_oid;
-+
-+ if (read_ref(pseudoref, &actual_old_oid)) {
-+ if (!is_null_oid(old_oid)) {
-+ strbuf_addf(err, _("could not read ref '%s'"),
-+ pseudoref);
-+ rollback_lock_file(&lock);
-+ goto done;
-+ }
-+ } else if (is_null_oid(old_oid)) {
-+ strbuf_addf(err, _("ref '%s' already exists"),
-+ pseudoref);
-+ rollback_lock_file(&lock);
-+ goto done;
-+ } else if (!oideq(&actual_old_oid, old_oid)) {
-+ strbuf_addf(err,
-+ _("unexpected object ID when writing '%s'"),
-+ pseudoref);
-+ rollback_lock_file(&lock);
-+ goto done;
-+ }
-+ }
-+
-+ strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
-+ if (write_in_full(fd, buf.buf, buf.len) < 0) {
-+ strbuf_addf(err, _("could not write to '%s'"), filename.buf);
-+ rollback_lock_file(&lock);
-+ goto done;
-+ }
-+
-+ commit_lock_file(&lock);
-+ ret = 0;
-+done:
-+ strbuf_release(&buf);
-+ strbuf_release(&filename);
-+ return ret;
-+}
-+
-+static int files_delete_pseudoref(struct ref_store *ref_store,
-+ const char *pseudoref,
-+ const struct object_id *old_oid)
-+{
-+ struct files_ref_store *refs =
-+ files_downcast(ref_store, REF_STORE_READ, "delete_pseudoref");
-+ struct strbuf filename = STRBUF_INIT;
-+ int ret = -1;
-+
-+ strbuf_addf(&filename, "%s/%s", refs->gitdir, pseudoref);
-+
-+ if (old_oid && !is_null_oid(old_oid)) {
-+ struct lock_file lock = LOCK_INIT;
-+ int fd;
-+ struct object_id actual_old_oid;
-+
-+ fd = hold_lock_file_for_update_timeout(
-+ &lock, filename.buf, 0,
-+ get_files_ref_lock_timeout_ms());
-+ if (fd < 0) {
-+ error_errno(_("could not open '%s' for writing"),
-+ filename.buf);
-+ goto done;
-+ }
-+ if (read_ref(pseudoref, &actual_old_oid))
-+ die(_("could not read ref '%s'"), pseudoref);
-+ if (!oideq(&actual_old_oid, old_oid)) {
-+ error(_("unexpected object ID when deleting '%s'"),
-+ pseudoref);
-+ rollback_lock_file(&lock);
-+ goto done;
-+ }
-+
-+ unlink(filename.buf);
-+ rollback_lock_file(&lock);
-+ } else {
-+ unlink(filename.buf);
-+ }
-+ ret = 0;
-+done:
-+ strbuf_release(&filename);
-+ return ret;
-+}
-+
- struct files_ref_iterator {
- struct ref_iterator base;
-
-@@ refs/files-backend.c: struct ref_storage_be refs_be_files = {
- files_rename_ref,
- files_copy_ref,
-
-+ files_write_pseudoref,
-+ files_delete_pseudoref,
-+
- files_ref_iterator_begin,
- files_read_raw_ref,
-
-@@ refs/files-backend.c: struct ref_storage_be refs_be_files = {
- files_reflog_exists,
- files_create_reflog,
- files_delete_reflog,
-- files_reflog_expire
-+ files_reflog_expire,
- };
+- } else {
+- t = ref_store_transaction_begin(refs, &err);
+- if (!t ||
+- ref_transaction_update(t, refname, new_oid, old_oid,
+- flags, msg, &err) ||
+- ref_transaction_commit(t, &err)) {
+- ret = 1;
+- ref_transaction_free(t);
+- }
++ t = ref_store_transaction_begin(refs, &err);
++ if (!t ||
++ ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
++ &err) ||
++ ref_transaction_commit(t, &err)) {
++ ret = 1;
++ ref_transaction_free(t);
+ }
+ if (ret) {
+ const char *str = _("update_ref failed for ref '%s': %s");
- ## refs/packed-backend.c ##
-@@ refs/packed-backend.c: static int packed_copy_ref(struct ref_store *ref_store,
- BUG("packed reference store does not support copying references");
- }
-
-+static int packed_write_pseudoref(struct ref_store *ref_store,
-+ const char *pseudoref,
-+ const struct object_id *oid,
-+ const struct object_id *old_oid,
-+ struct strbuf *err)
-+{
-+ BUG("packed reference store does not support writing pseudo-references");
-+}
-+
-+static int packed_delete_pseudoref(struct ref_store *ref_store,
-+ const char *pseudoref,
-+ const struct object_id *old_oid)
-+{
-+ BUG("packed reference store does not support deleting pseudo-references");
-+}
-+
- static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_store)
- {
- return empty_ref_iterator_begin();
-@@ refs/packed-backend.c: struct ref_storage_be refs_be_packed = {
- packed_rename_ref,
- packed_copy_ref,
-
-+ packed_write_pseudoref,
-+ packed_delete_pseudoref,
-+
- packed_ref_iterator_begin,
- packed_read_raw_ref,
-
-@@ refs/packed-backend.c: struct ref_storage_be refs_be_packed = {
- packed_reflog_exists,
- packed_create_reflog,
- packed_delete_reflog,
-- packed_reflog_expire
-+ packed_reflog_expire,
- };
+ ## t/t1400-update-ref.sh ##
+@@ t/t1400-update-ref.sh: test_expect_success 'core.logAllRefUpdates=always creates reflog by default' '
+ git reflog exists $outside
+ '
+
+-test_expect_success 'core.logAllRefUpdates=always creates no reflog for ORIG_HEAD' '
++test_expect_success 'core.logAllRefUpdates=always creates reflog for ORIG_HEAD' '
+ test_config core.logAllRefUpdates always &&
+ git update-ref ORIG_HEAD $A &&
+- test_must_fail git reflog exists ORIG_HEAD
++ git reflog exists ORIG_HEAD
+ '
+
+ test_expect_success '--no-create-reflog overrides core.logAllRefUpdates=always' '
+@@ t/t1400-update-ref.sh: test_expect_success 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER
+ test_expect_success 'given old value for missing pseudoref, do not create' '
+ test_must_fail git update-ref PSEUDOREF $A $B 2>err &&
+ test_must_fail git rev-parse PSEUDOREF &&
+- test_i18ngrep "could not read ref" err
++ test_i18ngrep "unable to resolve reference" err
+ '
+
+ test_expect_success 'create pseudoref' '
+@@ t/t1400-update-ref.sh: test_expect_success 'overwrite pseudoref with correct old value' '
+ test_expect_success 'do not overwrite pseudoref with wrong old value' '
+ test_must_fail git update-ref PSEUDOREF $D $E 2>err &&
+ test $C = $(git rev-parse PSEUDOREF) &&
+- test_i18ngrep "unexpected object ID" err
++ test_i18ngrep "cannot lock ref.*expected" err
+ '
+
+ test_expect_success 'delete pseudoref' '
+@@ t/t1400-update-ref.sh: test_expect_success 'do not delete pseudoref with wrong old value' '
+ git update-ref PSEUDOREF $A &&
+ test_must_fail git update-ref -d PSEUDOREF $B 2>err &&
+ test $A = $(git rev-parse PSEUDOREF) &&
+- test_i18ngrep "unexpected object ID" err
++ test_i18ngrep "cannot lock ref.*expected" err
+ '
+
+ test_expect_success 'delete pseudoref with correct old value' '
- ## refs/refs-internal.h ##
-@@ refs/refs-internal.h: typedef int copy_ref_fn(struct ref_store *ref_store,
- const char *oldref, const char *newref,
- const char *logmsg);
-
-+typedef int write_pseudoref_fn(struct ref_store *ref_store,
-+ const char *pseudoref,
-+ const struct object_id *oid,
-+ const struct object_id *old_oid,
-+ struct strbuf *err);
-+
-+/*
-+ * Deletes a pseudoref. Deletion always succeeds (even if the pseudoref doesn't
-+ * exist.), except if old_oid is specified. If it is, it can fail due to lock
-+ * failure, failure reading the old OID, or an OID mismatch
-+ */
-+typedef int delete_pseudoref_fn(struct ref_store *ref_store,
-+ const char *pseudoref,
-+ const struct object_id *old_oid);
-+
- /*
- * Iterate over the references in `ref_store` whose names start with
- * `prefix`. `prefix` is matched as a literal string, without regard
-@@ refs/refs-internal.h: struct ref_storage_be {
- rename_ref_fn *rename_ref;
- copy_ref_fn *copy_ref;
-
-+ write_pseudoref_fn *write_pseudoref;
-+ delete_pseudoref_fn *delete_pseudoref;
-+
- ref_iterator_begin_fn *iterator_begin;
- read_raw_ref_fn *read_raw_ref;
-
+ ## t/t1405-main-ref-store.sh ##
+@@ t/t1405-main-ref-store.sh: test_expect_success 'create_symref(FOO, refs/heads/master)' '
+ test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '
+ git rev-parse FOO -- &&
+ git rev-parse refs/tags/new-tag -- &&
+- $RUN delete-refs 0 nothing FOO refs/tags/new-tag &&
++ m=$(git rev-parse master) &&
++ REF_NO_DEREF=1 &&
++ $RUN delete-refs $REF_NO_DEREF nothing FOO refs/tags/new-tag &&
++ test_must_fail git rev-parse --symbolic-full-name FOO &&
+ test_must_fail git rev-parse FOO -- &&
+ test_must_fail git rev-parse refs/tags/new-tag --
+ '
-: ---------- > 5: e9df96925b Make HEAD a PSEUDOREF rather than PER_WORKTREE.
5: c8cf2d2ce1 = 6: d33ea20fba Make refs_ref_exists public
6: e36b29de79 < -: ---------- Treat BISECT_HEAD as a pseudo ref
7: 1676df9851 ! 7: d12f6e905c Treat CHERRY_PICK_HEAD as a pseudo ref
@@ Metadata
## Commit message ##
Treat CHERRY_PICK_HEAD as a pseudo ref
- Check for existence and delete CHERRY_PICK_HEAD through pseudo ref functions.
+ Check for existence and delete CHERRY_PICK_HEAD through ref functions.
This will help cherry-pick work with alternate ref storage backends.
Signed-off-by: Han-Wen Nienhuys <[email protected]>
@@ sequencer.c: static void print_advice(struct repository *r, int show_hint,
* of the commit itself so remove CHERRY_PICK_HEAD
*/
- unlink(git_path_cherry_pick_head(r));
-+ refs_delete_pseudoref(get_main_ref_store(r), "CHERRY_PICK_HEAD",
-+ NULL);
++ refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
++ NULL, 0);
return;
}
@@ sequencer.c: static int do_commit(struct repository *r,
strbuf_release(&sb);
if (!res) {
- unlink(git_path_cherry_pick_head(r));
-+ refs_delete_pseudoref(get_main_ref_store(r),
-+ "CHERRY_PICK_HEAD", NULL);
++ refs_delete_ref(get_main_ref_store(r), "",
++ "CHERRY_PICK_HEAD", NULL, 0);
unlink(git_path_merge_msg(r));
if (!is_rebase_i(opts))
print_commit_summary(r, NULL, &oid,
@@ sequencer.c: static int do_pick_commit(struct repository *r,
} else if (allow == 2) {
drop_commit = 1;
- unlink(git_path_cherry_pick_head(r));
-+ refs_delete_pseudoref(get_main_ref_store(r), "CHERRY_PICK_HEAD",
-+ NULL);
++ refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
++ NULL, 0);
unlink(git_path_merge_msg(r));
fprintf(stderr,
_("dropping %s %s -- patch contents already upstream\n"),
@@ sequencer.c: void sequencer_post_commit_cleanup(struct repository *r, int verbos
- if (file_exists(git_path_cherry_pick_head(r))) {
- if (!unlink(git_path_cherry_pick_head(r)) && verbose)
+ if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD")) {
-+ if (!refs_delete_pseudoref(get_main_ref_store(r),
-+ "CHERRY_PICK_HEAD", NULL) &&
++ if (!refs_delete_ref(get_main_ref_store(r), "",
++ "CHERRY_PICK_HEAD", NULL, 0) &&
+ verbose)
warning(_("cancelling a cherry picking in progress"));
opts.action = REPLAY_PICK;
@@ sequencer.c: static int do_merge(struct repository *r,
strbuf_release(&ref_name);
- unlink(git_path_cherry_pick_head(r));
-+ refs_delete_pseudoref(get_main_ref_store(r), "CHERRY_PICK_HEAD",
-+ NULL);
++ refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
++ NULL, 0);
rollback_lock_file(&lock);
rollback_lock_file(&lock);
@@ sequencer.c: static int commit_staged_changes(struct repository *r,
- if (file_exists(cherry_pick_head) && unlink(cherry_pick_head))
+ if (refs_ref_exists(get_main_ref_store(r),
+ "CHERRY_PICK_HEAD") &&
-+ refs_delete_pseudoref(get_main_ref_store(r),
-+ "CHERRY_PICK_HEAD", NULL))
++ refs_delete_ref(get_main_ref_store(r), "",
++ "CHERRY_PICK_HEAD", NULL, 0))
return error(_("could not remove CHERRY_PICK_HEAD"));
if (!final_fixup)
return 0;
8: 0a5f97ea34 ! 8: 224f7bf224 Treat REVERT_HEAD as a pseudo ref
@@ sequencer.c: void sequencer_post_commit_cleanup(struct repository *r, int verbos
- if (file_exists(git_path_revert_head(r))) {
- if (!unlink(git_path_revert_head(r)) && verbose)
+ if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) {
-+ if (!refs_delete_pseudoref(get_main_ref_store(r), "REVERT_HEAD",
-+ NULL) &&
++ if (!refs_delete_ref(get_main_ref_store(r), "", "REVERT_HEAD",
++ NULL, 0) &&
+ verbose)
warning(_("cancelling a revert in progress"));
opts.action = REPLAY_REVERT;
9: 9463ed9093 = 9: 4b601b545c Move REF_LOG_ONLY to refs-internal.h
10: a116aebe11 ! 10: ea8a78374f Iterate over the "refs/" namespace in for_each_[raw]ref @@ Metadata
Author: Han-Wen Nienhuys <[email protected]>
## Commit message ##
- Iterate over the "refs/" namespace in for_each_[raw]ref
+ Iteration over entire ref namespace is iterating over "refs/"
+
+ This changes behavior for in for_each_[raw]ref and
+ for_each_fullref_in_pattern.
This happens implicitly in the files/packed ref backend; making it explicit simplifies adding alternate ref storage backends, such as
@@ Commit message
+ ## ref-filter.c ##
+@@ ref-filter.c: static int for_each_fullref_in_pattern(struct ref_filter *filter,
+ {
+ struct string_list prefixes = STRING_LIST_INIT_DUP;
+ struct string_list_item *prefix;
++ const char *all = "refs/";
+ int ret;
+
+ if (!filter->match_as_path) {
+@@ ref-filter.c: static int for_each_fullref_in_pattern(struct ref_filter *filter,
+ * prefixes like "refs/heads/" etc. are stripped off,
+ * so we have to look at everything:
+ */
+- return for_each_fullref_in("", cb, cb_data, broken);
++ return for_each_fullref_in(all, cb, cb_data, broken);
+ }
+
+ if (filter->ignore_case) {
+@@ ref-filter.c: static int for_each_fullref_in_pattern(struct ref_filter *filter,
+ * so just return everything and let the caller
+ * sort it out.
+ */
+- return for_each_fullref_in("", cb, cb_data, broken);
++ return for_each_fullref_in(all, cb, cb_data, broken);
+ }
+
+ if (!filter->name_patterns[0]) {
+ /* no patterns; we have to look at everything */
+- return for_each_fullref_in("", cb, cb_data, broken);
++ return for_each_fullref_in(all, cb, cb_data, broken);
+ }
+
+ find_longest_prefixes(&prefixes, filter->name_patterns);
+
## refs.c ##
@@ refs.c: static int do_for_each_ref(struct ref_store *refs, const char *prefix,
11: e4545658ed = 11: 0606b2a1c8 Add .gitattributes for the reftable/ directory
12: 169f6c7f54 = 12: 12d98125c2 Add reftable library
13: d155240b16 = 13: 70ce4ce49d Add standalone build infrastructure for reftable
14: 073bff7279 ! 14: 7af3b2340e Reftable support for git-core
@@ builtin/init-db.c: int init_db(const char *git_dir, const char *real_git_dir,
git_config_set("receive.denyNonFastforwards", "true");
}
-+ git_config_set("extensions.refStorage", ref_storage_format);
++ if (!strcmp(ref_storage_format, "reftable"))
++ git_config_set("extensions.refStorage", ref_storage_format);
+
if (!(flags & INIT_DB_QUIET)) {
int len = strlen(git_dir);
@@ cache.h: int path_inside_repo(const char *prefix, const char *path);
void sanitize_stdfds(void);
int daemonize(void);
@@ cache.h: struct repository_format {
+ int is_bare;
int hash_algo;
- int has_extensions;
char *work_tree;
+ char *ref_storage;
struct string_list unknown_extensions;
+ struct string_list v1_only_extensions;
};
-
## refs.c ##
@@
@@ refs.c: struct ref_store *get_main_ref_store(struct repository *r)
- r->refs_private = ref_store_init(r->gitdir, REF_STORE_ALL_CAPS);
+ r->refs_private = ref_store_init(r->gitdir,
+ r->ref_storage_format ?
-+ r->ref_storage_format :
-+ default_ref_storage(),
++ r->ref_storage_format :
++ default_ref_storage(),
+ REF_STORE_ALL_CAPS);
return r->refs_private;
}
@@ refs/reftable-backend.c (new)
+ struct reftable_addition *add = NULL;
+ struct reftable_stack *stack =
+ transaction->nr ?
-+ stack_for(refs, transaction->updates[0]->refname) :
-+ refs->main_stack;
++ stack_for(refs, transaction->updates[0]->refname) :
++ refs->main_stack;
+ int err = refs->err;
+ if (err < 0) {
+ goto done;
@@ refs/reftable-backend.c (new)
+ return git_reftable_transaction_finish(ref_store, transaction, errmsg);
+}
+
-+struct write_pseudoref_arg {
-+ struct reftable_stack *stack;
-+ const char *pseudoref;
-+ const struct object_id *new_oid;
-+ const struct object_id *old_oid;
-+};
-+
-+static int write_pseudoref_table(struct reftable_writer *writer, void *argv)
-+{
-+ struct write_pseudoref_arg *arg = (struct write_pseudoref_arg *)argv;
-+ uint64_t ts = reftable_stack_next_update_index(arg->stack);
-+ int err = 0;
-+ struct reftable_ref_record read_ref = { NULL };
-+ struct reftable_ref_record write_ref = { NULL };
-+
-+ reftable_writer_set_limits(writer, ts, ts);
-+ if (arg->old_oid) {
-+ struct object_id read_oid;
-+ err = reftable_stack_read_ref(arg->stack, arg->pseudoref,
-+ &read_ref);
-+ if (err < 0)
-+ goto done;
-+
-+ if ((err > 0) != is_null_oid(arg->old_oid)) {
-+ err = REFTABLE_LOCK_ERROR;
-+ goto done;
-+ }
-+
-+ /* XXX If old_oid is set, and we have a symref? */
-+
-+ if (err == 0 && read_ref.value == NULL) {
-+ err = REFTABLE_LOCK_ERROR;
-+ goto done;
-+ }
-+
-+ hashcpy(read_oid.hash, read_ref.value);
-+ if (!oideq(arg->old_oid, &read_oid)) {
-+ err = REFTABLE_LOCK_ERROR;
-+ goto done;
-+ }
-+ }
-+
-+ write_ref.refname = (char *)arg->pseudoref;
-+ write_ref.update_index = ts;
-+ if (!is_null_oid(arg->new_oid))
-+ write_ref.value = (uint8_t *)arg->new_oid->hash;
-+
-+ err = reftable_writer_add_ref(writer, &write_ref);
-+done:
-+ assert(err != REFTABLE_API_ERROR);
-+ reftable_ref_record_clear(&read_ref);
-+ return err;
-+}
-+
-+static int git_reftable_write_pseudoref(struct ref_store *ref_store,
-+ const char *pseudoref,
-+ const struct object_id *oid,
-+ const struct object_id *old_oid,
-+ struct strbuf *errbuf)
-+{
-+ struct git_reftable_ref_store *refs =
-+ (struct git_reftable_ref_store *)ref_store;
-+ struct reftable_stack *stack = stack_for(refs, pseudoref);
-+ struct write_pseudoref_arg arg = {
-+ .stack = stack,
-+ .pseudoref = pseudoref,
-+ .new_oid = oid,
-+ };
-+ struct reftable_addition *add = NULL;
-+ int err = refs->err;
-+ if (err < 0) {
-+ goto done;
-+ }
-+
-+ err = reftable_stack_reload(stack);
-+ if (err) {
-+ goto done;
-+ }
-+ err = reftable_stack_new_addition(&add, stack);
-+ if (err) {
-+ goto done;
-+ }
-+ if (old_oid) {
-+ struct object_id actual_old_oid;
-+
-+ /* XXX this is cut & paste from files-backend - should factor
-+ * out? */
-+ if (read_ref(pseudoref, &actual_old_oid)) {
-+ if (!is_null_oid(old_oid)) {
-+ strbuf_addf(errbuf,
-+ _("could not read ref '%s'"),
-+ pseudoref);
-+ goto done;
-+ }
-+ } else if (is_null_oid(old_oid)) {
-+ strbuf_addf(errbuf, _("ref '%s' already exists"),
-+ pseudoref);
-+ goto done;
-+ } else if (!oideq(&actual_old_oid, old_oid)) {
-+ strbuf_addf(errbuf,
-+ _("unexpected object ID when writing '%s'"),
-+ pseudoref);
-+ goto done;
-+ }
-+ }
-+
-+ err = reftable_addition_add(add, &write_pseudoref_table, &arg);
-+ if (err < 0) {
-+ strbuf_addf(errbuf, "reftable: pseudoref update failure: %s",
-+ reftable_error_str(err));
-+ }
-+
-+ err = reftable_addition_commit(add);
-+ if (err < 0) {
-+ strbuf_addf(errbuf, "reftable: pseudoref commit failure: %s",
-+ reftable_error_str(err));
-+ }
-+
-+done:
-+ assert(err != REFTABLE_API_ERROR);
-+ reftable_addition_destroy(add);
-+ return err;
-+}
-+
-+static int reftable_delete_pseudoref(struct ref_store *ref_store,
-+ const char *pseudoref,
-+ const struct object_id *old_oid)
-+{
-+ struct strbuf errbuf = STRBUF_INIT;
-+ int ret = git_reftable_write_pseudoref(ref_store, pseudoref, &null_oid,
-+ old_oid, &errbuf);
-+ /* XXX what to do with the error message? */
-+ strbuf_release(&errbuf);
-+ return ret;
-+}
-+
+struct write_delete_refs_arg {
+ struct reftable_stack *stack;
+ struct string_list *refnames;
@@ refs/reftable-backend.c (new)
+static int git_reftable_reflog_exists(struct ref_store *ref_store,
+ const char *refname)
+{
-+ /* always exists. */
-+ return 1;
++ struct reftable_iterator it = { NULL };
++ struct git_reftable_ref_store *refs =
++ (struct git_reftable_ref_store *)ref_store;
++ struct reftable_stack *stack = stack_for(refs, refname);
++ struct reftable_merged_table *mt = reftable_stack_merged_table(stack);
++ struct reftable_log_record log = { NULL };
++ int err = refs->err;
++
++ if (err < 0) {
++ goto done;
++ }
++ err = reftable_merged_table_seek_log(mt, &it, refname);
++ if (err) {
++ goto done;
++ }
++ err = reftable_iterator_next_log(&it, &log);
++ if (err) {
++ goto done;
++ }
++
++ if (strcmp(log.refname, refname)) {
++ err = 1;
++ }
++
++done:
++ reftable_iterator_destroy(&it);
++ reftable_log_record_clear(&log);
++ return !err;
+}
+
+static int git_reftable_create_reflog(struct ref_store *ref_store,
@@ refs/reftable-backend.c (new)
+ git_reftable_rename_ref,
+ git_reftable_copy_ref,
+
-+ git_reftable_write_pseudoref,
-+ reftable_delete_pseudoref,
-+
+ git_reftable_ref_iterator_begin,
+ git_reftable_read_raw_ref,
+
@@ repository.h: struct repository {
*/
## setup.c ##
-@@ setup.c: static int check_repo_format(const char *var, const char *value, void *vdata)
- if (!value)
- return config_error_nonbool(var);
- data->partial_clone = xstrdup(value);
-- } else if (!strcmp(ext, "worktreeconfig"))
-+ } else if (!strcmp(ext, "worktreeconfig")) {
- data->worktree_config = git_config_bool(var, value);
-- else
-+ } else if (!strcmp(ext, "refstorage")) {
-+ data->ref_storage = xstrdup(value);
-+ } else
- string_list_append(&data->unknown_extensions, ext);
+@@ setup.c: static enum extension_result handle_extension(const char *var,
+ {
+ if (!strcmp(ext, "noop-v1")) {
+ return EXTENSION_OK;
++ } else if (!strcmp(ext, "refstorage")) {
++ data->ref_storage = xstrdup(value);
++ return EXTENSION_OK;
}
+-
+ return EXTENSION_UNKNOWN;
+ }
@@ setup.c: void clear_repository_format(struct repository_format *format)
- string_list_clear(&format->unknown_extensions, 0);
+ string_list_clear(&format->v1_only_extensions, 0);
free(format->work_tree);
free(format->partial_clone);
+ free(format->ref_storage);
-: ---------- > 15: a6449d4346 Read FETCH_HEAD as loose ref
15: 04c86e7395 ! 16: f13e4798c4 Hookup unittests for the reftable library. @@ Makefile: t/helper/test-svn-fe$X: $(VCSSVN_LIB)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
check-sha1:: t/helper/test-tool$X
+@@ Makefile: cocciclean:
+ clean: profile-clean coverage-clean cocciclean
+ $(RM) *.res
+ $(RM) $(OBJECTS)
+- $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) $(REFTABLE_LIB)
++ $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB)
+ $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
+ $(RM) $(TEST_PROGRAMS)
+ $(RM) $(FUZZ_PROGRAMS)
## t/helper/test-reftable.c (new) ##
@@
16: c751265705 ! 17: 679dc37871 Add GIT_DEBUG_REFS debugging mechanism
@@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam
strbuf_reset(&sb);
- if (get_main_ref_store(the_repository)->be == &refs_be_reftable) {
+
-+ /* XXX: check GIT_TEST_REFTABLE because GIT_DEBUG_REFS obscures the
-+ * instance type. */
-+ if (get_main_ref_store(the_repository)->be == &refs_be_reftable ||
-+ getenv("GIT_TEST_REFTABLE") != NULL) {
++ if (!strcmp(ref_store_backend_name(get_main_ref_store(the_repository)),
++ "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");
## refs.c ##
+@@ refs.c: int ref_storage_backend_exists(const char *name)
+ return find_ref_storage_backend(name) != NULL;
+ }
+
++const char *ref_store_backend_name(struct ref_store *refs)
++{
++ return refs->be->name;
++}
++
+ /*
+ * How to handle various characters in refnames:
+ * 0: An acceptable character for refs
@@ refs.c: struct ref_store *get_main_ref_store(struct repository *r)
- r->ref_storage_format :
- default_ref_storage(),
+ r->ref_storage_format :
+ default_ref_storage(),
REF_STORE_ALL_CAPS);
+ if (getenv("GIT_DEBUG_REFS")) {
+ r->refs_private = debug_wrap(r->gitdir, r->refs_private);
@@ refs.c: struct ref_store *get_main_ref_store(struct repository *r)
}
+ ## refs.h ##
+@@ refs.h: int reflog_expire(const char *refname, const struct object_id *oid,
+ int ref_storage_backend_exists(const char *name);
+
+ struct ref_store *get_main_ref_store(struct repository *r);
++const char *ref_store_backend_name(struct ref_store *refs);
+
+ /**
+ * Submodules
+
## refs/debug.c (new) ##
@@
+
@@ refs/debug.c (new)
+struct ref_store *debug_wrap(const char *gitdir, struct ref_store *store)
+{
+ struct debug_ref_store *res = malloc(sizeof(struct debug_ref_store));
++ struct ref_storage_be *be_copy = malloc(sizeof(*be_copy));
++ *be_copy = refs_be_debug;
++ be_copy->name = ref_store_backend_name(store);
+ fprintf(stderr, "ref_store for %s\n", gitdir);
+ res->refs = store;
-+ base_ref_store_init((struct ref_store *)res, &refs_be_debug);
++ base_ref_store_init((struct ref_store *)res, be_copy);
+ return (struct ref_store *)res;
+}
+
@@ refs/debug.c (new)
+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
+ int res =
+ drefs->refs->be->delete_refs(drefs->refs, msg, refnames, flags);
++ int i = 0;
++ fprintf(stderr, "delete_refs {\n");
++ for (i = 0; i < refnames->nr; i++)
++ fprintf(stderr, "%s\n", refnames->items[i].string);
++ fprintf(stderr, "}: %d\n", res);
+ return res;
+}
+
@@ refs/debug.c (new)
+ return res;
+}
+
-+static int debug_write_pseudoref(struct ref_store *ref_store,
-+ const char *pseudoref,
-+ const struct object_id *oid,
-+ const struct object_id *old_oid,
-+ struct strbuf *err)
-+{
-+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
-+ int res = drefs->refs->be->write_pseudoref(drefs->refs, pseudoref, oid,
-+ old_oid, err);
-+ char o[100] = "null";
-+ char n[100] = "null";
-+ if (oid)
-+ oid_to_hex_r(o, oid);
-+ if (old_oid)
-+ oid_to_hex_r(n, old_oid);
-+ fprintf(stderr, "write_pseudoref: %s, %s => %s, err %s: %d\n",
-+ pseudoref, o, n, err->buf, res);
-+ return res;
-+}
-+
-+static int debug_delete_pseudoref(struct ref_store *ref_store,
-+ const char *pseudoref,
-+ const struct object_id *old_oid)
-+{
-+ struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
-+ int res = drefs->refs->be->delete_pseudoref(drefs->refs, pseudoref,
-+ old_oid);
-+ char hex[100] = "null";
-+ if (old_oid)
-+ oid_to_hex_r(hex, old_oid);
-+ fprintf(stderr, "delete_pseudoref: %s (%s): %d\n", pseudoref, hex, res);
-+ return res;
-+}
-+
+struct debug_ref_iterator {
+ struct ref_iterator base;
+ struct ref_iterator *iter;
@@ refs/debug.c (new)
+ debug_rename_ref,
+ debug_copy_ref,
+
-+ debug_write_pseudoref,
-+ debug_delete_pseudoref,
-+
+ debug_ref_iterator_begin,
+ debug_read_raw_ref,
+
@@ refs/debug.c (new)
+};
## refs/refs-internal.h ##
-@@ refs/refs-internal.h: struct ref_store {
+@@ refs/refs-internal.h: int parse_loose_ref_contents(const char *buf, struct object_id *oid,
void base_ref_store_init(struct ref_store *refs,
const struct ref_storage_be *be);17: ef0dd45f07 = 18: 9e6cdbe204 vcxproj: adjust for the reftable changes 18: d4dc1deae5 = 19: 9cd73da381 git-prompt: prepare for reftable refs backend 19: 5a85abf9c4 = 20: a2e5b3082e Add reftable testing infrastructure 20: 0ebf7beb95 = 21: 539fa0935f Add "test-tool dump-reftable" command.
Signature · 1 line
gitgitgadget
PATCH v20 1/21 refs: add \t to reflog in the files backend
From: Han-Wen Nienhuys <[email protected]>
commit 523fa69c3 (Jul 10, 2020) "reflog: cleanse messages in the refs.c layer" centralized reflog normalizaton. However, the normalizaton added a leading "\t" to the message. This is an artifact of the reflog storage format in the files backend, so it should be added there.
Routines that parse back the reflog (such as grab_nth_branch_switch) expect the "\t" to not be in the message, so without this fix, git with reftable cannot process the "@{-1}" syntax.
--- refs.c | 1 - refs/files-backend.c | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-)
refs.c +0 -1
@@ -907,7 +907,6 @@ static void copy_reflog_msg(struct strbuf *sb, const char *msg)char c;int wasspace =1 ; -strbuf_addch (sb,'\t' );while ((c = *msg++)) {if (wasspace &&isspace (c))continue ;
refs/files-backend.c +3 -1
@@ -1628,8 +1628,10 @@ static int log_ref_write_fd(int fd, const struct object_id *old_oid,int ret =0 ;strbuf_addf (&sb,"%s %s %s" ,oid_to_hex (old_oid),oid_to_hex (new_oid), committer); -if (msg && *msg) +if (msg && *msg) { +strbuf_addch (&sb,'\t' );strbuf_addstr (&sb, msg); + }strbuf_addch (&sb,'\n' );if (write_in_full (fd, sb.buf, sb.len) <0 ) ret =-1 ;
Signature · 1 line
gitgitgadget
PATCH v20 2/21 Split off reading loose ref data in separate function
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This prepares for handling FETCH_HEAD (which is not a regular ref) separately in the reftable backend.
--- refs/files-backend.c | 34 +++++++++++++++++++--------------- refs/refs-internal.h | 6 ++++++ 2 files changed, 25 insertions(+), 15 deletions(-)
refs/files-backend.c +19 -15
@@ -360,7 +360,6 @@ static int files_read_raw_ref(struct ref_store *ref_store,struct strbuf sb_path =STRBUF_INIT ;const char *path;const char *buf; -const char *p;struct stat st;int fd;int ret =-1 ;
@@ -465,6 +464,21 @@ static int files_read_raw_ref(struct ref_store *ref_store,close (fd);strbuf_rtrim (&sb_contents); buf = sb_contents.buf; + + ret =parse_loose_ref_contents (buf, oid, referent, type); + +out: + save_errno = errno; +strbuf_release (&sb_path); +strbuf_release (&sb_contents); + errno = save_errno; +return ret; +} + +int parse_loose_ref_contents (const char *buf,struct object_id *oid, +struct strbuf *referent,unsigned int *type) +{ +const char *p;if (skip_prefix (buf,"ref:" , &buf)) {while (isspace (*buf)) buf++;
@@ -472,29 +486,19 @@ static int files_read_raw_ref(struct ref_store *ref_store,strbuf_reset (referent);strbuf_addstr (referent, buf); *type |=REF_ISSYMREF ; - ret =0 ; - goto out; +return 0 ; }/* -* Please note that FETCH_HEAD has additional -* data after the sha. +* FETCH_HEAD has additional data after the sha. */ if (parse_oid_hex (buf, oid, &p) || (*p !='\0' && !isspace (*p))) { *type |=REF_ISBROKEN ; errno =EINVAL ; - goto out; +return -1 ; } - - ret =0 ; - -out: - save_errno = errno; -strbuf_release (&sb_path); -strbuf_release (&sb_contents); - errno = save_errno; -return ret; +return 0 ; }static void unlock_ref (struct ref_lock *lock)
refs/refs-internal.h +6 -0
@@ -674,6 +674,12 @@ struct ref_store {const struct ref_storage_be *be; }; +/* +* Parse contents of a loose ref file. +*/ +int parse_loose_ref_contents (const char *buf,struct object_id *oid, +struct strbuf *referent,unsigned int *type); +/* * Fill in the generic part of refs and add it to our collection of * reference stores.
Signature · 1 line
gitgitgadget
PATCH v20 3/21 t1400: use git rev-parse for testing PSEUDOREF existence
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This will allow these tests to run with alternative ref backends
--- t/t1400-update-ref.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
t/t1400-update-ref.sh +10 -10
@@ -475,57 +475,57 @@ test_expect_success 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER test_expect_success 'given old value for missing pseudoref, do not create' ' test_must_fail git update-ref PSEUDOREF $A $B 2>err && - test_path_is_missing .git/PSEUDOREF && + test_must_fail git rev-parse PSEUDOREF && test_i18ngrep "could not read ref" err ' test_expect_success 'create pseudoref' ' git update-ref PSEUDOREF $A && - test $A = $(cat .git/PSEUDOREF) + test $A = $(git rev-parse PSEUDOREF) ' test_expect_success 'overwrite pseudoref with no old value given' ' git update-ref PSEUDOREF $B && - test $B = $(cat .git/PSEUDOREF) + test $B = $(git rev-parse PSEUDOREF) ' test_expect_success 'overwrite pseudoref with correct old value' ' git update-ref PSEUDOREF $C $B && - test $C = $(cat .git/PSEUDOREF) + test $C = $(git rev-parse PSEUDOREF) ' test_expect_success 'do not overwrite pseudoref with wrong old value' ' test_must_fail git update-ref PSEUDOREF $D $E 2>err && - test $C = $(cat .git/PSEUDOREF) && + test $C = $(git rev-parse PSEUDOREF) && test_i18ngrep "unexpected object ID" err ' test_expect_success 'delete pseudoref' ' git update-ref -d PSEUDOREF && - test_path_is_missing .git/PSEUDOREF + test_must_fail git rev-parse PSEUDOREF ' test_expect_success 'do not delete pseudoref with wrong old value' ' git update-ref PSEUDOREF $A && test_must_fail git update-ref -d PSEUDOREF $B 2>err && - test $A = $(cat .git/PSEUDOREF) && + test $A = $(git rev-parse PSEUDOREF) && test_i18ngrep "unexpected object ID" err ' test_expect_success 'delete pseudoref with correct old value' ' git update-ref -d PSEUDOREF $A && - test_path_is_missing .git/PSEUDOREF + test_must_fail git rev-parse PSEUDOREF ' test_expect_success 'create pseudoref with old OID zero' ' git update-ref PSEUDOREF $A $Z && - test $A = $(cat .git/PSEUDOREF) + test $A = $(git rev-parse PSEUDOREF) ' test_expect_success 'do not overwrite pseudoref with old OID zero' ' test_when_finished git update-ref -d PSEUDOREF && test_must_fail git update-ref PSEUDOREF $B $Z 2>err && - test $A = $(cat .git/PSEUDOREF) && + test $A = $(git rev-parse PSEUDOREF) && test_i18ngrep "already exists" err '
Signature · 1 line
gitgitgadget
PATCH v20 4/21 Modify pseudo refs through ref backend storage
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
The previous behavior was introduced in commit 74ec19d4be ("pseudorefs: create and use pseudoref update and delete functions", Jul 31, 2015), with the justification "alternate ref backends still need to store pseudorefs in GIT_DIR".
Refs such as REBASE_HEAD are read through the ref backend. This can only work consistently if they are written through the ref backend as well. Tooling that works directly on files under .git should be updated to use git commands to read refs instead.
The following behaviors change:
* Updates to pseudorefs (eg. ORIG_HEAD) with core.logAllRefUpdates=always will create reflogs for the pseudoref.
* non-HEAD pseudoref symrefs are also dereferenced on deletion. Update t1405 accordingly.
--- Documentation/git-update-ref.txt | 13 ++-- refs.c | 120 ++----------------------------- t/t1400-update-ref.sh | 10 +-- t/t1405-main-ref-store.sh | 5 +- 4 files changed, 23 insertions(+), 125 deletions(-)
Documentation/git-update-ref.txt +7 -6
@@ -148,12 +148,13 @@ still see a subset of the modifications. LOGGING UPDATES --------------- -If config parameter "core.logAllRefUpdates" is true and the ref is one under -"refs/heads/", "refs/remotes/", "refs/notes/", or the symbolic ref HEAD; or -the file "$GIT_DIR/logs/<ref>" exists then `git update-ref` will append -a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all -symbolic refs before creating the log name) describing the change -in ref value. Log lines are formatted as: +If config parameter "core.logAllRefUpdates" is true and the ref is one +under "refs/heads/", "refs/remotes/", "refs/notes/", or a pseudoref +like HEAD or ORIG_HEAD; or the file "$GIT_DIR/logs/<ref>" exists then +`git update-ref` will append a line to the log file +"$GIT_DIR/logs/<ref>" (dereferencing all symbolic refs before creating +the log name) describing the change in ref value. Log lines are +formatted as: oldsha1 SP newsha1 SP committer LF
refs.c +7 -113
138 lines not on this page · raw message
t/t1400-update-ref.sh +5 -5
@@ -160,10 +160,10 @@ test_expect_success 'core.logAllRefUpdates=always creates reflog by default' ' git reflog exists $outside ' -test_expect_success 'core.logAllRefUpdates=always creates no reflog for ORIG_HEAD' ' +test_expect_success 'core.logAllRefUpdates=always creates reflog for ORIG_HEAD' ' test_config core.logAllRefUpdates always && git update-ref ORIG_HEAD $A && - test_must_fail git reflog exists ORIG_HEAD + git reflog exists ORIG_HEAD ' test_expect_success '--no-create-reflog overrides core.logAllRefUpdates=always' '
@@ -476,7 +476,7 @@ test_expect_success 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER test_expect_success 'given old value for missing pseudoref, do not create' ' test_must_fail git update-ref PSEUDOREF $A $B 2>err && test_must_fail git rev-parse PSEUDOREF && - test_i18ngrep "could not read ref" err + test_i18ngrep "unable to resolve reference" err ' test_expect_success 'create pseudoref' '
@@ -497,7 +497,7 @@ test_expect_success 'overwrite pseudoref with correct old value' ' test_expect_success 'do not overwrite pseudoref with wrong old value' ' test_must_fail git update-ref PSEUDOREF $D $E 2>err && test $C = $(git rev-parse PSEUDOREF) && - test_i18ngrep "unexpected object ID" err + test_i18ngrep "cannot lock ref.*expected" err ' test_expect_success 'delete pseudoref' '
@@ -509,7 +509,7 @@ test_expect_success 'do not delete pseudoref with wrong old value' ' git update-ref PSEUDOREF $A && test_must_fail git update-ref -d PSEUDOREF $B 2>err && test $A = $(git rev-parse PSEUDOREF) && - test_i18ngrep "unexpected object ID" err + test_i18ngrep "cannot lock ref.*expected" err ' test_expect_success 'delete pseudoref with correct old value' '
t/t1405-main-ref-store.sh +4 -1
@@ -31,7 +31,10 @@ test_expect_success 'create_symref(FOO, refs/heads/master)' ' test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' ' git rev-parse FOO -- && git rev-parse refs/tags/new-tag -- && - $RUN delete-refs 0 nothing FOO refs/tags/new-tag && + m=$(git rev-parse master) && + REF_NO_DEREF=1 && + $RUN delete-refs $REF_NO_DEREF nothing FOO refs/tags/new-tag && + test_must_fail git rev-parse --symbolic-full-name FOO && test_must_fail git rev-parse FOO -- && test_must_fail git rev-parse refs/tags/new-tag -- '
Signature · 1 line
gitgitgadget
PATCH v20 5/21 Make HEAD a PSEUDOREF rather than PER_WORKTREE.
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
This is consistent with the definition of REF_TYPE_PSEUDOREF (uppercase in the root ref namespace).
--- refs.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
refs.c +3 -4
@@ -708,10 +708,9 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log)static int is_per_worktree_ref (const char *refname) { -return !strcmp (refname,"HEAD" ) || -starts_with (refname,"refs/worktree/" ) || -starts_with (refname,"refs/bisect/" ) || -starts_with (refname,"refs/rewritten/" ); +return starts_with (refname,"refs/worktree/" ) || +starts_with (refname,"refs/bisect/" ) || +starts_with (refname,"refs/rewritten/" ); }static int is_pseudoref_syntax (const char *refname)
Signature · 1 line
gitgitgadget
PATCH v20 6/21 Make refs_ref_exists public
In reply to Han-Wen Nienhuys via GitGitGadgetFrom: Han-Wen Nienhuys <[email protected]>
--- refs.c | 2 +- refs.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-)
refs.c +1 -1
@@ -313,7 +313,7 @@ int read_ref(const char *refname, struct object_id *oid)return read_ref_full (refname,RESOLVE_REF_READING , oid,NULL ); } -static int refs_ref_exists (struct ref_store *refs,const char *refname) +int refs_ref_exists (struct ref_store *refs,const char *refname) {return !!refs_resolve_ref_unsafe (refs, refname,RESOLVE_REF_READING ,NULL ,NULL ); }
refs.h +2 -0
@@ -105,6 +105,8 @@ int refs_verify_refname_available(struct ref_store *refs,const struct string_list *skip, struct strbuf *err); +int refs_ref_exists (struct ref_store *refs,const char *refname); +int ref_exists (const char *refname);int should_autocreate_reflog (const char *refname);
Signature · 1 line
gitgitgadget