Subject: [PATCH v3 00/25] Support reftable ref backend for Git

From: "Han-Wen Nienhuys via GitGitGadget" <[email-masked]>
Date: Tue, 17 Aug 2021 13:39:42 +0000

This continues the work in https://github.com/git/git/pull/847, which the
gitgitgadget erroneously closed.

Changes relative to last series (version 20 Jul 2021, tip: 4a5891f )

 * fix HTTP serving
 * fix t1501 (use absolute paths.)
 * fix git-new-workdir
 * fixes for FreeBSD (thanks, [email-masked]!)

For the 'seen' branch, the following should be applied:

diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index d7137d1213..9323931eeb 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -108,7 +108,7 @@ static const char *bare_ref_name(const char *ref)
 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);
+                     unsigned int *type, int *failure_errno);
 
 static void clear_reftable_log_record(struct reftable_log_record *log)
 {
@@ -425,13 +424,14 @@ static int fixup_symrefs(struct ref_store *ref_store,
     for (i = 0; i < transaction->nr; i++) {
         struct ref_update *update = transaction->updates[i];
         struct object_id old_oid;
+        int failure_errno;
 
         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 &&
+                        &update->type, &failure_errno);
+        if (err < 0 && failure_errno == ENOENT &&
             is_null_oid(&update->old_oid)) {
             err = 0;
         }
@@ -1602,7 +1603,7 @@ static int reftable_error_to_errno(int err)
 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)
+                     unsigned int *type, int *failure_errno)
 {
     struct git_reftable_ref_store *refs =
         (struct git_reftable_ref_store *)ref_store;
@@ -1626,13 +1627,11 @@ static int git_reftable_read_raw_ref(struct ref_store *ref_store,
 
     err = reftable_stack_read_ref(stack, refname, &ref);
     if (err > 0) {
-        errno = ENOENT;
+        *failure_errno = ENOENT;
         err = -1;
         goto done;
     }
     if (err < 0) {
-        errno = reftable_error_to_errno(err);
-        err = -1;
         goto done;
     }

Han-Wen Nienhuys (24):
  hash.h: provide constants for the hash IDs
  init-db: set the_repository->hash_algo early on
  reftable: RFC: add LICENSE
  reftable: add error related functionality
  reftable: utility functions
  reftable: add blocksource, an abstraction for random access reads
  reftable: (de)serialization for the polymorphic record type.
  Provide zlib's uncompress2 from compat/zlib-compat.c
  reftable: reading/writing blocks
  reftable: a generic binary tree implementation
  reftable: write reftable files
  reftable: generic interface to tables
  reftable: read reftable files
  reftable: reftable file level tests
  reftable: add a heap-based priority queue for reftable records
  reftable: add merged table view
  reftable: implement refname validation
  reftable: implement stack, a mutable database of reftable files.
  reftable: add dump utility
  refs: RFC: Reftable support for git-core
  Add "test-tool dump-reftable" command.
  t1301: document what needs to be done for reftable
  t1401,t2011: parameterize HEAD.lock for REFFILES
  t1404: annotate test cases with REFFILES

SZEDER Gábor (1):
  git-prompt: prepare for reftable refs backend

 Documentation/config/extensions.txt           |    9 +
 .../technical/repository-version.txt          |    7 +
 Makefile                                      |   54 +-
 builtin/clone.c                               |    5 +-
 builtin/init-db.c                             |   60 +-
 builtin/stash.c                               |    8 +-
 builtin/worktree.c                            |   27 +-
 cache.h                                       |    8 +-
 ci/lib.sh                                     |    1 +
 compat/.gitattributes                         |    1 +
 compat/zlib-uncompress2.c                     |   92 +
 config.mak.uname                              |    3 +-
 configure.ac                                  |   13 +
 contrib/buildsystems/CMakeLists.txt           |   14 +-
 contrib/buildsystems/Generators/Vcxproj.pm    |   11 +-
 contrib/completion/git-prompt.sh              |    7 +-
 contrib/workdir/git-new-workdir               |    2 +-
 hash.h                                        |    6 +
 object-file.c                                 |    7 +-
 refs.c                                        |   26 +-
 refs.h                                        |    3 +
 refs/refs-internal.h                          |    1 +
 refs/reftable-backend.c                       | 1691 +++++++++++++++++
 reftable/LICENSE                              |   31 +
 reftable/basics.c                             |  128 ++
 reftable/basics.h                             |   60 +
 reftable/basics_test.c                        |   98 +
 reftable/block.c                              |  448 +++++
 reftable/block.h                              |  127 ++
 reftable/block_test.c                         |  120 ++
 reftable/blocksource.c                        |  148 ++
 reftable/blocksource.h                        |   22 +
 reftable/constants.h                          |   21 +
 reftable/dump.c                               |  107 ++
 reftable/error.c                              |   41 +
 reftable/generic.c                            |  169 ++
 reftable/generic.h                            |   32 +
 reftable/iter.c                               |  194 ++
 reftable/iter.h                               |   69 +
 reftable/merged.c                             |  362 ++++
 reftable/merged.h                             |   35 +
 reftable/merged_test.c                        |  292 +++
 reftable/pq.c                                 |  115 ++
 reftable/pq.h                                 |   32 +
 reftable/pq_test.c                            |   72 +
 reftable/publicbasics.c                       |   58 +
 reftable/reader.c                             |  801 ++++++++
 reftable/reader.h                             |   66 +
 reftable/readwrite_test.c                     |  652 +++++++
 reftable/record.c                             | 1212 ++++++++++++
 reftable/record.h                             |  139 ++
 reftable/record_test.c                        |  412 ++++
 reftable/refname.c                            |  209 ++
 reftable/refname.h                            |   29 +
 reftable/refname_test.c                       |  102 +
 reftable/reftable-blocksource.h               |   49 +
 reftable/reftable-error.h                     |   62 +
 reftable/reftable-generic.h                   |   47 +
 reftable/reftable-iterator.h                  |   39 +
 reftable/reftable-malloc.h                    |   18 +
 reftable/reftable-merged.h                    |   72 +
 reftable/reftable-reader.h                    |  101 +
 reftable/reftable-record.h                    |  114 ++
 reftable/reftable-stack.h                     |  128 ++
 reftable/reftable-tests.h                     |   23 +
 reftable/reftable-writer.h                    |  148 ++
 reftable/reftable.c                           |  115 ++
 reftable/stack.c                              | 1396 ++++++++++++++
 reftable/stack.h                              |   41 +
 reftable/stack_test.c                         |  948 +++++++++
 reftable/system.h                             |   24 +
 reftable/test_framework.c                     |   23 +
 reftable/test_framework.h                     |   53 +
 reftable/tree.c                               |   63 +
 reftable/tree.h                               |   34 +
 reftable/tree_test.c                          |   61 +
 reftable/writer.c                             |  690 +++++++
 reftable/writer.h                             |   50 +
 repository.c                                  |    2 +
 repository.h                                  |    3 +
 setup.c                                       |    8 +
 t/helper/test-reftable.c                      |   21 +
 t/helper/test-tool.c                          |    4 +-
 t/helper/test-tool.h                          |    2 +
 t/t0031-reftable.sh                           |  316 +++
 t/t0032-reftable-unittest.sh                  |   15 +
 t/t1301-shared-repo.sh                        |    8 +-
 t/t1401-symbolic-ref.sh                       |   11 +-
 t/t1404-update-ref-errors.sh                  |   56 +-
 t/t1409-avoid-packing-refs.sh                 |    6 +
 t/t1450-fsck.sh                               |    6 +
 t/t2011-checkout-invalid-head.sh              |   11 +-
 t/t3210-pack-refs.sh                          |    6 +
 t/test-lib.sh                                 |    7 +-
 94 files changed, 13178 insertions(+), 62 deletions(-)
 create mode 100644 compat/.gitattributes
 create mode 100644 compat/zlib-uncompress2.c
 create mode 100644 refs/reftable-backend.c
 create mode 100644 reftable/LICENSE
 create mode 100644 reftable/basics.c
 create mode 100644 reftable/basics.h
 create mode 100644 reftable/basics_test.c
 create mode 100644 reftable/block.c
 create mode 100644 reftable/block.h
 create mode 100644 reftable/block_test.c
 create mode 100644 reftable/blocksource.c
 create mode 100644 reftable/blocksource.h
 create mode 100644 reftable/constants.h
 create mode 100644 reftable/dump.c
 create mode 100644 reftable/error.c
 create mode 100644 reftable/generic.c
 create mode 100644 reftable/generic.h
 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/pq_test.c
 create mode 100644 reftable/publicbasics.c
 create mode 100644 reftable/reader.c
 create mode 100644 reftable/reader.h
 create mode 100644 reftable/readwrite_test.c
 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-blocksource.h
 create mode 100644 reftable/reftable-error.h
 create mode 100644 reftable/reftable-generic.h
 create mode 100644 reftable/reftable-iterator.h
 create mode 100644 reftable/reftable-malloc.h
 create mode 100644 reftable/reftable-merged.h
 create mode 100644 reftable/reftable-reader.h
 create mode 100644 reftable/reftable-record.h
 create mode 100644 reftable/reftable-stack.h
 create mode 100644 reftable/reftable-tests.h
 create mode 100644 reftable/reftable-writer.h
 create mode 100644 reftable/reftable.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 100644 reftable/writer.c
 create mode 100644 reftable/writer.h
 create mode 100644 t/helper/test-reftable.c
 create mode 100755 t/t0031-reftable.sh
 create mode 100755 t/t0032-reftable-unittest.sh

base-commit: 225bc32a989d7a22fa6addafd4ce7dcd04675dbf
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1054%2Fhanwen%2Flibreftable-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1054/hanwen/libreftable-v3
Pull-Request: https://github.com/git/git/pull/1054

Range-diff vs v2:

  1:  c40393e83e1 =  1:  cad3c4835a2 hash.h: provide constants for the hash IDs
  2:  49dffeeae58 =  2:  97aba3ae406 init-db: set the_repository->hash_algo early on
  3:  dba354eca12 =  3:  fca162dd676 reftable: RFC: add LICENSE
  4:  75864034af2 =  4:  44923074a5a reftable: add error related functionality
  5:  eabb25d95b3 !  5:  2f2beb70078 reftable: utility functions
     @@ reftable/system.h (new)
      +#ifndef SYSTEM_H
      +#define SYSTEM_H
      +
     -+// This header glues the reftable library to the rest of Git
     ++/* This header glues the reftable library to the rest of Git */
      +
      +#include "git-compat-util.h"
      +#include "strbuf.h"
  6:  ad54d17f84b =  6:  79d4a23764d reftable: add blocksource, an abstraction for random access reads
  7:  282d8e4d817 !  7:  2c143c75cd5 reftable: (de)serialization for the polymorphic record type.
     @@ reftable/record.c (new)
      +		break;
      +	case REFTABLE_LOG_UPDATE:
      +		printf("log{%s(%" PRIu64 ") %s <%s> %" PRIu64 " %04d\n",
     -+		       log->refname, log->update_index, log->update.name,
     -+		       log->update.email, log->update.time,
     -+		       log->update.tz_offset);
     -+		hex_format(hex, log->update.old_hash, hash_size(hash_id));
     ++		       log->refname, log->update_index, log->value.update.name,
     ++		       log->value.update.email, log->value.update.time,
     ++		       log->value.update.tz_offset);
     ++		hex_format(hex, log->value.update.old_hash, hash_size(hash_id));
      +		printf("%s => ", hex);
     -+		hex_format(hex, log->update.new_hash, hash_size(hash_id));
     -+		printf("%s\n\n%s\n}\n", hex, log->update.message);
     ++		hex_format(hex, log->value.update.new_hash, hash_size(hash_id));
     ++		printf("%s\n\n%s\n}\n", hex, log->value.update.message);
      +		break;
      +	}
      +}
     @@ reftable/record.c (new)
      +	case REFTABLE_LOG_DELETION:
      +		break;
      +	case REFTABLE_LOG_UPDATE:
     -+		if (dst->update.email) {
     -+			dst->update.email = xstrdup(dst->update.email);
     ++		if (dst->value.update.email) {
     ++			dst->value.update.email =
     ++				xstrdup(dst->value.update.email);
      +		}
     -+		if (dst->update.name) {
     -+			dst->update.name = xstrdup(dst->update.name);
     ++		if (dst->value.update.name) {
     ++			dst->value.update.name =
     ++				xstrdup(dst->value.update.name);
      +		}
     -+		if (dst->update.message) {
     -+			dst->update.message = xstrdup(dst->update.message);
     ++		if (dst->value.update.message) {
     ++			dst->value.update.message =
     ++				xstrdup(dst->value.update.message);
      +		}
      +
     -+		if (dst->update.new_hash) {
     -+			dst->update.new_hash = reftable_malloc(hash_size);
     -+			memcpy(dst->update.new_hash, src->update.new_hash,
     -+			       hash_size);
     ++		if (dst->value.update.new_hash) {
     ++			dst->value.update.new_hash = reftable_malloc(hash_size);
     ++			memcpy(dst->value.update.new_hash,
     ++			       src->value.update.new_hash, hash_size);
      +		}
     -+		if (dst->update.old_hash) {
     -+			dst->update.old_hash = reftable_malloc(hash_size);
     -+			memcpy(dst->update.old_hash, src->update.old_hash,
     -+			       hash_size);
     ++		if (dst->value.update.old_hash) {
     ++			dst->value.update.old_hash = reftable_malloc(hash_size);
     ++			memcpy(dst->value.update.old_hash,
     ++			       src->value.update.old_hash, hash_size);
      +		}
      +		break;
      +	}
     @@ reftable/record.c (new)
      +	case REFTABLE_LOG_DELETION:
      +		break;
      +	case REFTABLE_LOG_UPDATE:
     -+		reftable_free(r->update.new_hash);
     -+		reftable_free(r->update.old_hash);
     -+		reftable_free(r->update.name);
     -+		reftable_free(r->update.email);
     -+		reftable_free(r->update.message);
     ++		reftable_free(r->value.update.new_hash);
     ++		reftable_free(r->value.update.old_hash);
     ++		reftable_free(r->value.update.name);
     ++		reftable_free(r->value.update.email);
     ++		reftable_free(r->value.update.message);
      +		break;
      +	}
      +	memset(r, 0, sizeof(struct reftable_log_record));
     @@ reftable/record.c (new)
      +	if (reftable_log_record_is_deletion(r))
      +		return 0;
      +
     -+	oldh = r->update.old_hash;
     -+	newh = r->update.new_hash;
     ++	oldh = r->value.update.old_hash;
     ++	newh = r->value.update.new_hash;
      +	if (!oldh) {
      +		oldh = zero;
      +	}
     @@ reftable/record.c (new)
      +	memcpy(s.buf + hash_size, newh, hash_size);
      +	string_view_consume(&s, 2 * hash_size);
      +
     -+	n = encode_string(r->update.name ? r->update.name : "", s);
     ++	n = encode_string(r->value.update.name ? r->value.update.name : "", s);
      +	if (n < 0)
      +		return -1;
      +	string_view_consume(&s, n);
      +
     -+	n = encode_string(r->update.email ? r->update.email : "", s);
     ++	n = encode_string(r->value.update.email ? r->value.update.email : "",
     ++			  s);
      +	if (n < 0)
      +		return -1;
      +	string_view_consume(&s, n);
      +
     -+	n = put_var_int(&s, r->update.time);
     ++	n = put_var_int(&s, r->value.update.time);
      +	if (n < 0)
      +		return -1;
      +	string_view_consume(&s, n);
     @@ reftable/record.c (new)
      +	if (s.len < 2)
      +		return -1;
      +
     -+	put_be16(s.buf, r->update.tz_offset);
     ++	put_be16(s.buf, r->value.update.tz_offset);
      +	string_view_consume(&s, 2);
      +
     -+	n = encode_string(r->update.message ? r->update.message : "", s);
     ++	n = encode_string(
     ++		r->value.update.message ? r->value.update.message : "", s);
      +	if (n < 0)
      +		return -1;
      +	string_view_consume(&s, n);
     @@ reftable/record.c (new)
      +	if (val_type != r->value_type) {
      +		switch (r->value_type) {
      +		case REFTABLE_LOG_UPDATE:
     -+			FREE_AND_NULL(r->update.old_hash);
     -+			FREE_AND_NULL(r->update.new_hash);
     -+			FREE_AND_NULL(r->update.message);
     -+			FREE_AND_NULL(r->update.email);
     -+			FREE_AND_NULL(r->update.name);
     ++			FREE_AND_NULL(r->value.update.old_hash);
     ++			FREE_AND_NULL(r->value.update.new_hash);
     ++			FREE_AND_NULL(r->value.update.message);
     ++			FREE_AND_NULL(r->value.update.email);
     ++			FREE_AND_NULL(r->value.update.name);
      +			break;
      +		case REFTABLE_LOG_DELETION:
      +			break;
     @@ reftable/record.c (new)
      +	if (in.len < 2 * hash_size)
      +		return REFTABLE_FORMAT_ERROR;
      +
     -+	r->update.old_hash = reftable_realloc(r->update.old_hash, hash_size);
     -+	r->update.new_hash = reftable_realloc(r->update.new_hash, hash_size);
     ++	r->value.update.old_hash =
     ++		reftable_realloc(r->value.update.old_hash, hash_size);
     ++	r->value.update.new_hash =
     ++		reftable_realloc(r->value.update.new_hash, hash_size);
      +
     -+	memcpy(r->update.old_hash, in.buf, hash_size);
     -+	memcpy(r->update.new_hash, in.buf + hash_size, hash_size);
     ++	memcpy(r->value.update.old_hash, in.buf, hash_size);
     ++	memcpy(r->value.update.new_hash, in.buf + hash_size, hash_size);
      +
      +	string_view_consume(&in, 2 * hash_size);
      +
     @@ reftable/record.c (new)
      +		goto done;
      +	string_view_consume(&in, n);
      +
     -+	r->update.name = reftable_realloc(r->update.name, dest.len + 1);
     -+	memcpy(r->update.name, dest.buf, dest.len);
     -+	r->update.name[dest.len] = 0;
     ++	r->value.update.name =
     ++		reftable_realloc(r->value.update.name, dest.len + 1);
     ++	memcpy(r->value.update.name, dest.buf, dest.len);
     ++	r->value.update.name[dest.len] = 0;
      +
      +	strbuf_reset(&dest);
      +	n = decode_string(&dest, in);
     @@ reftable/record.c (new)
      +		goto done;
      +	string_view_consume(&in, n);
      +
     -+	r->update.email = reftable_realloc(r->update.email, dest.len + 1);
     -+	memcpy(r->update.email, dest.buf, dest.len);
     -+	r->update.email[dest.len] = 0;
     ++	r->value.update.email =
     ++		reftable_realloc(r->value.update.email, dest.len + 1);
     ++	memcpy(r->value.update.email, dest.buf, dest.len);
     ++	r->value.update.email[dest.len] = 0;
      +
      +	ts = 0;
      +	n = get_var_int(&ts, &in);
      +	if (n < 0)
      +		goto done;
      +	string_view_consume(&in, n);
     -+	r->update.time = ts;
     ++	r->value.update.time = ts;
      +	if (in.len < 2)
      +		goto done;
      +
     -+	r->update.tz_offset = get_be16(in.buf);
     ++	r->value.update.tz_offset = get_be16(in.buf);
      +	string_view_consume(&in, 2);
      +
      +	strbuf_reset(&dest);
     @@ reftable/record.c (new)
      +		goto done;
      +	string_view_consume(&in, n);
      +
     -+	r->update.message = reftable_realloc(r->update.message, dest.len + 1);
     -+	memcpy(r->update.message, dest.buf, dest.len);
     -+	r->update.message[dest.len] = 0;
     ++	r->value.update.message =
     ++		reftable_realloc(r->value.update.message, dest.len + 1);
     ++	memcpy(r->value.update.message, dest.buf, dest.len);
     ++	r->value.update.message[dest.len] = 0;
      +
      +	strbuf_release(&dest);
      +	return start.len - in.len;
     @@ reftable/record.c (new)
      +	case REFTABLE_LOG_DELETION:
      +		return 1;
      +	case REFTABLE_LOG_UPDATE:
     -+		return null_streq(a->update.name, b->update.name) &&
     -+		       a->update.time == b->update.time &&
     -+		       a->update.tz_offset == b->update.tz_offset &&
     -+		       null_streq(a->update.email, b->update.email) &&
     -+		       null_streq(a->update.message, b->update.message) &&
     -+		       zero_hash_eq(a->update.old_hash, b->update.old_hash,
     -+				    hash_size) &&
     -+		       zero_hash_eq(a->update.new_hash, b->update.new_hash,
     -+				    hash_size);
     ++		return null_streq(a->value.update.name, b->value.update.name) &&
     ++		       a->value.update.time == b->value.update.time &&
     ++		       a->value.update.tz_offset == b->value.update.tz_offset &&
     ++		       null_streq(a->value.update.email,
     ++				  b->value.update.email) &&
     ++		       null_streq(a->value.update.message,
     ++				  b->value.update.message) &&
     ++		       zero_hash_eq(a->value.update.old_hash,
     ++				    b->value.update.old_hash, hash_size) &&
     ++		       zero_hash_eq(a->value.update.new_hash,
     ++				    b->value.update.new_hash, hash_size);
      +	}
      +
      +	abort();
     @@ reftable/record_test.c (new)
      +			.refname = xstrdup("refs/heads/master"),
      +			.update_index = 42,
      +			.value_type = REFTABLE_LOG_UPDATE,
     -+			.update = {
     -+				.old_hash = reftable_malloc(GIT_SHA1_RAWSZ),
     -+				.new_hash = reftable_malloc(GIT_SHA1_RAWSZ),
     -+				.name = xstrdup("han-wen"),
     -+				.email = xstrdup("[email-masked]"),
     -+				.message = xstrdup("test"),
     -+				.time = 1577123507,
     -+				.tz_offset = 100,
     ++			.value = {
     ++				.update = {
     ++					.old_hash = reftable_malloc(GIT_SHA1_RAWSZ),
     ++					.new_hash = reftable_malloc(GIT_SHA1_RAWSZ),
     ++					.name = xstrdup("han-wen"),
     ++					.email = xstrdup("[email-masked]"),
     ++					.message = xstrdup("test"),
     ++					.time = 1577123507,
     ++					.tz_offset = 100,
     ++				},
      +			}
      +		},
      +		{
     @@ reftable/record_test.c (new)
      +			.value_type = REFTABLE_LOG_DELETION,
      +		}
      +	};
     -+	set_test_hash(in[0].update.new_hash, 1);
     -+	set_test_hash(in[0].update.old_hash, 2);
     ++	set_test_hash(in[0].value.update.new_hash, 1);
     ++	set_test_hash(in[0].value.update.old_hash, 2);
      +	for (i = 0; i < ARRAY_SIZE(in); i++) {
      +		struct reftable_record rec = { NULL };
      +		struct strbuf key = STRBUF_INIT;
     @@ reftable/record_test.c (new)
      +		struct reftable_log_record out = {
      +			.refname = xstrdup("old name"),
      +			.value_type = REFTABLE_LOG_UPDATE,
     -+			.update = {
     -+				.new_hash = reftable_calloc(GIT_SHA1_RAWSZ),
     -+				.old_hash = reftable_calloc(GIT_SHA1_RAWSZ),
     -+				.name = xstrdup("old name"),
     -+				.email = xstrdup("old@email"),
     -+				.message = xstrdup("old message"),
     ++			.value = {
     ++				.update = {
     ++					.new_hash = reftable_calloc(GIT_SHA1_RAWSZ),
     ++					.old_hash = reftable_calloc(GIT_SHA1_RAWSZ),
     ++					.name = xstrdup("old name"),
     ++					.email = xstrdup("old@email"),
     ++					.message = xstrdup("old message"),
     ++				},
      +			},
      +		};
      +		struct reftable_record rec_out = { NULL };
     @@ reftable/reftable-record.h (new)
      +			int16_t tz_offset;
      +			char *message;
      +		} update;
     -+	};
     ++	} value;
      +};
      +
      +/* returns whether 'ref' represents the deletion of a log record. */
  8:  893ee4ca21b =  8:  d92338467d6 Provide zlib's uncompress2 from compat/zlib-compat.c
  9:  e4da0a2ef37 !  9:  74ac0b135fa reftable: reading/writing blocks
     @@ reftable/block_test.c (new)
      +#include "block.h"
      +
      +#include "system.h"
     -+
      +#include "blocksource.h"
      +#include "basics.h"
      +#include "constants.h"
 10:  0f325881389 = 10:  a2219670079 reftable: a generic binary tree implementation
 11:  26cbc468f1f ! 11:  ac3063fd08c reftable: write reftable files
     @@ reftable/writer.c (new)
      +	if (log->refname == NULL)
      +		return REFTABLE_API_ERROR;
      +
     -+	input_log_message = log->update.message;
     -+	if (!w->opts.exact_log_message && log->update.message) {
     -+		strbuf_addstr(&cleaned_message, log->update.message);
     ++	input_log_message = log->value.update.message;
     ++	if (!w->opts.exact_log_message && log->value.update.message) {
     ++		strbuf_addstr(&cleaned_message, log->value.update.message);
      +		while (cleaned_message.len &&
      +		       cleaned_message.buf[cleaned_message.len - 1] == '\n')
      +			strbuf_setlen(&cleaned_message,
      +				      cleaned_message.len - 1);
      +		if (strchr(cleaned_message.buf, '\n')) {
     -+			// multiple lines not allowed.
     ++			/* multiple lines not allowed. */
      +			err = REFTABLE_API_ERROR;
      +			goto done;
      +		}
      +		strbuf_addstr(&cleaned_message, "\n");
     -+		log->update.message = cleaned_message.buf;
     ++		log->value.update.message = cleaned_message.buf;
      +	}
      +
      +	err = reftable_writer_add_log_verbatim(w, log);
     -+	log->update.message = input_log_message;
     ++	log->value.update.message = input_log_message;
      +done:
      +	strbuf_release(&cleaned_message);
      +	return err;
 12:  69129fca086 = 12:  e0f3476555e reftable: generic interface to tables
 13:  22599d785b7 = 13:  f1fe338f67e reftable: read reftable files
 14:  45565bfb9c4 ! 14:  7da9a85c1db reftable: reftable file level tests
     @@ reftable/readwrite_test.c (new)
      +		log.refname = name;
      +		log.update_index = update_index;
      +		log.value_type = REFTABLE_LOG_UPDATE;
     -+		log.update.new_hash = hash;
     -+		log.update.message = "message";
     ++		log.value.update.new_hash = hash;
     ++		log.value.update.message = "message";
      +
      +		n = reftable_writer_add_log(w, &log);
      +		EXPECT(n == 0);
     @@ reftable/readwrite_test.c (new)
      +	for (i = 0; i < stats->ref_stats.blocks; i++) {
      +		int off = i * opts.block_size;
      +		if (off == 0) {
     -+			off = header_size((hash_id == GIT_SHA256_FORMAT_ID) ? 2 :
     -+										  1);
     ++			off = header_size(
     ++				(hash_id == GIT_SHA256_FORMAT_ID) ? 2 : 1);
      +		}
      +		EXPECT(buf->buf[off] == 'r');
      +	}
     @@ reftable/readwrite_test.c (new)
      +	};
      +	int err;
      +	int i;
     -+	struct reftable_log_record log = { .refname = "refs/heads/master",
     -+					   .update_index = 0xa,
     -+					   .value_type = REFTABLE_LOG_UPDATE,
     -+					   .update = {
     -+						   .name = "Han-Wen Nienhuys",
     -+						   .email = "[email-masked]",
     -+						   .tz_offset = 100,
     -+						   .time = 0x5e430672,
     -+						   .message = "commit: 9\n",
     -+					   } };
     ++	struct reftable_log_record
     ++		log = { .refname = "refs/heads/master",
     ++			.update_index = 0xa,
     ++			.value_type = REFTABLE_LOG_UPDATE,
     ++			.value = { .update = {
     ++					   .name = "Han-Wen Nienhuys",
     ++					   .email = "[email-masked]",
     ++					   .tz_offset = 100,
     ++					   .time = 0x5e430672,
     ++					   .message = "commit: 9\n",
     ++				   } } };
      +	struct reftable_writer *w =
      +		reftable_new_writer(&strbuf_add_void, &buf, &opts);
      +
     @@ reftable/readwrite_test.c (new)
      +		hash1[i] = (uint8_t)(rand() % 256);
      +		hash2[i] = (uint8_t)(rand() % 256);
      +	}
     -+	log.update.old_hash = hash1;
     -+	log.update.new_hash = hash2;
     ++	log.value.update.old_hash = hash1;
     ++	log.value.update.new_hash = hash2;
      +	reftable_writer_set_limits(w, update_index, update_index);
      +	err = reftable_writer_add_log(w, &log);
      +	EXPECT_ERR(err);
     @@ reftable/readwrite_test.c (new)
      +		log.refname = names[i];
      +		log.update_index = i;
      +		log.value_type = REFTABLE_LOG_UPDATE;
     -+		log.update.old_hash = hash1;
     -+		log.update.new_hash = hash2;
     ++		log.value.update.old_hash = hash1;
     ++		log.value.update.new_hash = hash2;
      +
      +		err = reftable_writer_add_log(w, &log);
      +		EXPECT_ERR(err);
 15:  57b3e709c3e = 15:  953590034a8 reftable: add a heap-based priority queue for reftable records
 16:  f834cc5299f = 16:  64fe3c8edc5 reftable: add merged table view
 17:  15799d93ff7 = 17:  acb0d435a20 reftable: implement refname validation
 18:  b7728ceaef2 ! 18:  764d45fccd4 reftable: implement stack, a mutable database of reftable files.
     @@ reftable/stack.c (new)
      +			reftable_reader_free(st->readers[i]);
      +
      +			if (filename.len) {
     -+				// On Windows, can only unlink after closing.
     ++				/* On Windows, can only unlink after closing. */
      +				unlink(filename.buf);
      +			}
      +		}
     @@ reftable/stack.c (new)
      +			reader_close(cur[i]);
      +			reftable_reader_free(cur[i]);
      +
     -+			// On Windows, can only unlink after closing.
     ++			/* On Windows, can only unlink after closing. */
      +			unlink(filename.buf);
      +
      +			strbuf_release(&filename);
     @@ reftable/stack.c (new)
      +		}
      +
      +		if (config && config->time > 0 &&
     -+		    log.update.time < config->time) {
     ++		    log.value.update.time < config->time) {
      +			continue;
      +		}
      +
     @@ reftable/stack_test.c (new)
      +		logs[i].update_index = N + i + 1;
      +		logs[i].value_type = REFTABLE_LOG_UPDATE;
      +
     -+		logs[i].update.new_hash = reftable_malloc(GIT_SHA1_RAWSZ);
     -+		logs[i].update.email = xstrdup("identity@invalid");
     -+		set_test_hash(logs[i].update.new_hash, i);
     ++		logs[i].value.update.new_hash = reftable_malloc(GIT_SHA1_RAWSZ);
     ++		logs[i].value.update.email = xstrdup("identity@invalid");
     ++		set_test_hash(logs[i].value.update.new_hash, i);
      +	}
      +
      +	for (i = 0; i < N; i++) {
     @@ reftable/stack_test.c (new)
      +	struct reftable_log_record input = { .refname = "branch",
      +					     .update_index = 1,
      +					     .value_type = REFTABLE_LOG_UPDATE,
     -+					     .update = {
     -+						     .new_hash = h1,
     -+						     .old_hash = h2,
     -+					     } };
     ++					     .value = { .update = {
     ++								.new_hash = h1,
     ++								.old_hash = h2,
     ++							} } };
      +	struct reftable_log_record dest = {
      +		.update_index = 0,
      +	};
     @@ reftable/stack_test.c (new)
      +	err = reftable_new_stack(&st, dir, cfg);
      +	EXPECT_ERR(err);
      +
     -+	input.update.message = "one\ntwo";
     ++	input.value.update.message = "one\ntwo";
      +	err = reftable_stack_add(st, &write_test_log, &arg);
      +	EXPECT(err == REFTABLE_API_ERROR);
      +
     -+	input.update.message = "one";
     ++	input.value.update.message = "one";
      +	err = reftable_stack_add(st, &write_test_log, &arg);
      +	EXPECT_ERR(err);
      +
      +	err = reftable_stack_read_log(st, input.refname, &dest);
      +	EXPECT_ERR(err);
     -+	EXPECT(0 == strcmp(dest.update.message, "one\n"));
     ++	EXPECT(0 == strcmp(dest.value.update.message, "one\n"));
      +
     -+	input.update.message = "two\n";
     ++	input.value.update.message = "two\n";
      +	arg.update_index = 2;
      +	err = reftable_stack_add(st, &write_test_log, &arg);
      +	EXPECT_ERR(err);
      +	err = reftable_stack_read_log(st, input.refname, &dest);
      +	EXPECT_ERR(err);
     -+	EXPECT(0 == strcmp(dest.update.message, "two\n"));
     ++	EXPECT(0 == strcmp(dest.value.update.message, "two\n"));
      +
      +	/* cleanup */
      +	reftable_stack_destroy(st);
     @@ reftable/stack_test.c (new)
      +		logs[i].update_index = 42;
      +		if (i % 2 == 0) {
      +			logs[i].value_type = REFTABLE_LOG_UPDATE;
     -+			logs[i].update.new_hash =
     ++			logs[i].value.update.new_hash =
      +				reftable_malloc(GIT_SHA1_RAWSZ);
     -+			set_test_hash(logs[i].update.new_hash, i);
     -+			logs[i].update.email = xstrdup("identity@invalid");
     ++			set_test_hash(logs[i].value.update.new_hash, i);
     ++			logs[i].value.update.email =
     ++				xstrdup("identity@invalid");
      +		}
      +	}
      +	for (i = 0; i < N; i++) {
     @@ reftable/stack_test.c (new)
      +		logs[i].refname = xstrdup(buf);
      +		logs[i].update_index = i;
      +		logs[i].value_type = REFTABLE_LOG_UPDATE;
     -+		logs[i].update.time = i;
     -+		logs[i].update.new_hash = reftable_malloc(GIT_SHA1_RAWSZ);
     -+		logs[i].update.email = xstrdup("identity@invalid");
     -+		set_test_hash(logs[i].update.new_hash, i);
     ++		logs[i].value.update.time = i;
     ++		logs[i].value.update.new_hash = reftable_malloc(GIT_SHA1_RAWSZ);
     ++		logs[i].value.update.email = xstrdup("identity@invalid");
     ++		set_test_hash(logs[i].value.update.new_hash, i);
      +	}
      +
      +	for (i = 1; i <= N; i++) {
 19:  0f682be20bd = 19:  88dbab683dc reftable: add dump utility
 20:  754085752f4 ! 20:  3d57f7c4430 refs: RFC: Reftable support for git-core
     @@ refs/reftable-backend.c (new)
      +	log->refname = NULL;
      +	switch (log->value_type) {
      +	case REFTABLE_LOG_UPDATE:
     -+		log->update.old_hash = NULL;
     -+		log->update.new_hash = NULL;
     -+		log->update.message = NULL;
     ++		log->value.update.old_hash = NULL;
     ++		log->value.update.new_hash = NULL;
     ++		log->value.update.message = NULL;
      +		break;
      +	case REFTABLE_LOG_DELETION:
      +		break;
     @@ refs/reftable-backend.c (new)
      +
      +	reftable_log_record_release(log);
      +	log->value_type = REFTABLE_LOG_UPDATE;
     -+	log->update.name =
     ++	log->value.update.name =
      +		xstrndup(split.name_begin, split.name_end - split.name_begin);
     -+	log->update.email =
     ++	log->value.update.email =
      +		xstrndup(split.mail_begin, split.mail_end - split.mail_begin);
     -+	log->update.time = atol(split.date_begin);
     ++	log->value.update.time = atol(split.date_begin);
      +	if (*split.tz_begin == '-') {
      +		sign = -1;
      +		split.tz_begin++;
     @@ refs/reftable-backend.c (new)
      +		split.tz_begin++;
      +	}
      +
     -+	log->update.tz_offset = sign * atoi(split.tz_begin);
     ++	log->value.update.tz_offset = sign * atoi(split.tz_begin);
      +}
      +
      +static int has_suffix(struct strbuf *b, const char *suffix)
     @@ refs/reftable-backend.c (new)
      +		log->update_index = ts;
      +		log->value_type = REFTABLE_LOG_UPDATE;
      +		log->refname = (char *)u->refname;
     -+		log->update.new_hash = u->new_oid.hash;
     -+		log->update.message = u->msg;
     ++		log->value.update.new_hash = u->new_oid.hash;
     ++		log->value.update.message = u->msg;
      +
      +		err = reftable_table_read_ref(&tab, u->refname, &ref);
      +		if (err < 0)
     @@ refs/reftable-backend.c (new)
      +
      +		/* XXX fold together with the old_id check below? */
      +
     -+		log->update.old_hash = old_id.hash;
     ++		log->value.update.old_hash = old_id.hash;
      +		if (u->flags & REF_LOG_ONLY) {
      +			continue;
      +		}
     @@ refs/reftable-backend.c (new)
      +		log.update_index = ts;
      +		log.refname = (char *)arg->refnames->items[i].string;
      +
     -+		log.update.message = xstrdup(arg->logmsg);
     -+		log.update.new_hash = NULL;
     -+		log.update.old_hash = NULL;
     ++		log.value.update.message = xstrdup(arg->logmsg);
     ++		log.value.update.new_hash = NULL;
     ++		log.value.update.old_hash = NULL;
      +		if (reftable_stack_read_ref(arg->stack, log.refname,
      +					    &current) == 0) {
     -+			log.update.old_hash =
     ++			log.value.update.old_hash =
      +				reftable_ref_record_val1(&current);
      +		}
      +		err = reftable_writer_add_log(writer, &log);
     -+		log.update.old_hash = NULL;
     ++		log.value.update.old_hash = NULL;
      +		reftable_ref_record_release(&current);
      +
      +		clear_reftable_log_record(&log);
     @@ refs/reftable-backend.c (new)
      +		fill_reftable_log_record(&log);
      +		log.refname = (char *)create->refname;
      +		log.update_index = ts;
     -+		log.update.message = (char *)create->logmsg;
     ++		log.value.update.message = (char *)create->logmsg;
      +		if (refs_resolve_ref_unsafe(
      +			    (struct ref_store *)create->refs, create->refname,
      +			    RESOLVE_REF_READING, &old_oid, NULL) != NULL) {
     -+			log.update.old_hash = old_oid.hash;
     ++			log.value.update.old_hash = old_oid.hash;
      +		}
      +
      +		if (refs_resolve_ref_unsafe((struct ref_store *)create->refs,
      +					    create->target, RESOLVE_REF_READING,
      +					    &new_oid, NULL) != NULL) {
     -+			log.update.new_hash = new_oid.hash;
     ++			log.value.update.new_hash = new_oid.hash;
      +		}
      +
     -+		if (log.update.old_hash != NULL ||
     -+		    log.update.new_hash != NULL) {
     ++		if (log.value.update.old_hash != NULL ||
     ++		    log.value.update.new_hash != NULL) {
      +			err = reftable_writer_add_log(writer, &log);
      +		}
      +		log.refname = NULL;
     -+		log.update.message = NULL;
     -+		log.update.old_hash = NULL;
     -+		log.update.new_hash = NULL;
     ++		log.value.update.message = NULL;
     ++		log.value.update.old_hash = NULL;
     ++		log.value.update.new_hash = NULL;
      +		clear_reftable_log_record(&log);
      +	}
      +	return err;
     @@ refs/reftable-backend.c (new)
      +
      +		todo[0].refname = (char *)arg->oldname;
      +		todo[0].update_index = ts;
     -+		todo[0].update.message = (char *)arg->logmsg;
     -+		todo[0].update.old_hash = val1;
     -+		todo[0].update.new_hash = NULL;
     ++		todo[0].value.update.message = (char *)arg->logmsg;
     ++		todo[0].value.update.old_hash = val1;
     ++		todo[0].value.update.new_hash = NULL;
      +
      +		todo[1].refname = (char *)arg->newname;
      +		todo[1].update_index = ts;
     -+		todo[1].update.old_hash = NULL;
     -+		todo[1].update.new_hash = val1;
     -+		todo[1].update.message = (char *)arg->logmsg;
     ++		todo[1].value.update.old_hash = NULL;
     ++		todo[1].value.update.new_hash = val1;
     ++		todo[1].value.update.message = (char *)arg->logmsg;
      +
      +		err = reftable_writer_add_logs(writer, todo, 2);
      +
     @@ refs/reftable-backend.c (new)
      +
      +		free(ri->last_name);
      +		ri->last_name = xstrdup(ri->log.refname);
     -+		oidread(&ri->oid, ri->log.update.new_hash);
     ++		oidread(&ri->oid, ri->log.value.update.new_hash);
      +		return ITER_OK;
      +	}
      +}
     @@ refs/reftable-backend.c (new)
      +			break;
      +		}
      +
     -+		oidread(&old_oid, log.update.old_hash);
     -+		oidread(&new_oid, log.update.new_hash);
     ++		oidread(&old_oid, log.value.update.old_hash);
     ++		oidread(&new_oid, log.value.update.new_hash);
      +
     -+		full_committer = fmt_ident(log.update.name, log.update.email,
     ++		full_committer = fmt_ident(log.value.update.name,
     ++					   log.value.update.email,
      +					   WANT_COMMITTER_IDENT,
      +					   /*date*/ NULL, IDENT_NO_DATE);
     -+		err = fn(&old_oid, &new_oid, full_committer, log.update.time,
     -+			 log.update.tz_offset, log.update.message, cb_data);
     ++		err = fn(&old_oid, &new_oid, full_committer,
     ++			 log.value.update.time, log.value.update.tz_offset,
     ++			 log.value.update.message, cb_data);
      +		if (err)
      +			break;
      +	}
     @@ refs/reftable-backend.c (new)
      +		struct object_id new_oid;
      +		const char *full_committer = "";
      +
     -+		oidread(&old_oid, log->update.old_hash);
     -+		oidread(&new_oid, log->update.new_hash);
     ++		oidread(&old_oid, log->value.update.old_hash);
     ++		oidread(&new_oid, log->value.update.new_hash);
      +
     -+		full_committer = fmt_ident(log->update.name, log->update.email,
     ++		full_committer = fmt_ident(log->value.update.name,
     ++					   log->value.update.email,
      +					   WANT_COMMITTER_IDENT, NULL,
      +					   IDENT_NO_DATE);
     -+		err = fn(&old_oid, &new_oid, full_committer, log->update.time,
     -+			 log->update.tz_offset, log->update.message, cb_data);
     ++		err = fn(&old_oid, &new_oid, full_committer,
     ++			 log->value.update.time, log->value.update.tz_offset,
     ++			 log->value.update.message, cb_data);
      +		if (err) {
      +			break;
      +		}
     @@ refs/reftable-backend.c (new)
      +		struct reftable_log_record *dest = &rewritten[i];
      +
      +		*dest = logs[i];
     -+		oidread(&ooid, logs[i].update.old_hash);
     -+		oidread(&noid, logs[i].update.new_hash);
     -+
     -+		if (should_prune_fn(&ooid, &noid, logs[i].update.email,
     -+				    (timestamp_t)logs[i].update.time,
     -+				    logs[i].update.tz_offset,
     -+				    logs[i].update.message, policy_cb_data)) {
     ++		oidread(&ooid, logs[i].value.update.old_hash);
     ++		oidread(&noid, logs[i].value.update.new_hash);
     ++
     ++		if (should_prune_fn(&ooid, &noid, logs[i].value.update.email,
     ++				    (timestamp_t)logs[i].value.update.time,
     ++				    logs[i].value.update.tz_offset,
     ++				    logs[i].value.update.message,
     ++				    policy_cb_data)) {
      +			dest->value_type = REFTABLE_LOG_DELETION;
      +		} else {
      +			if ((flags & EXPIRE_REFLOGS_REWRITE) &&
      +			    last_hash != NULL) {
     -+				dest->update.old_hash = last_hash;
     ++				dest->value.update.old_hash = last_hash;
      +			}
     -+			last_hash = logs[i].update.new_hash;
     ++			last_hash = logs[i].value.update.new_hash;
      +		}
      +	}
      +
 21:  79c4b90d0f4 = 21:  460658a13cb git-prompt: prepare for reftable refs backend
 22:  1f48091e549 = 22:  b305fcd49f1 Add "test-tool dump-reftable" command.
 23:  64dc87746db = 23:  a44956d7b08 t1301: document what needs to be done for reftable
 24:  c248b69c6e3 = 24:  f0b7b1e2380 t1401,t2011: parameterize HEAD.lock for REFFILES
 25:  9f1b792459e = 25:  3110d6e6758 t1404: annotate test cases with REFFILES

-- 
gitgitgadget