All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Han-Wen Nienhuys via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: "Han-Wen Nienhuys" <hanwen@google.com>,
	"Jeff King" <peff@peff.net>,
	"Ramsay Jones" <ramsay@ramsayjones.plus.com>,
	"Jonathan Nieder" <jrnieder@gmail.com>,
	"Johannes Schindelin" <Johannes.Schindelin@gmx.de>,
	"Jonathan Tan" <jonathantanmy@google.com>,
	"Josh Steadmon" <steadmon@google.com>,
	"Emily Shaffer" <emilyshaffer@google.com>,
	"Patrick Steinhardt" <ps@pks.im>,
	"Ævar Arnfjörð Bjarmason" <avarab@gmail.com>,
	"Han-Wen Nienhuys" <hanwenn@gmail.com>
Subject: [PATCH v3 00/16] reftable library
Date: Thu, 26 Nov 2020 19:42:15 +0000	[thread overview]
Message-ID: <pull.847.v3.git.git.1606419752.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.847.v2.git.git.1601568663.gitgitgadget@gmail.com>

This splits the giant commit from 
https://github.com/gitgitgadget/git/pull/539 into a series of smaller
commits, which build and have unittests.

The final commit should also be split up, but I want to wait until we have
consensus that the bottom commits look good.

version 26 Nov 2020:

 * split public API header into multiple headers
 * minor adaptions in response to review feedback
 * filter out code for standalone compile
 * get rid of test framework

Han-Wen Nienhuys (15):
  move sleep_millisec to git-compat-util.h
  init-db: set the_repository->hash_algo early on
  reftable: 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.
  reftable: reading/writing blocks
  reftable: a generic binary tree implementation
  reftable: write reftable files
  reftable: read reftable files
  reftable: reftable file level tests
  reftable: rest of library
  Reftable support for git-core
  Add "test-tool dump-reftable" command.

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

 .../technical/repository-version.txt          |    7 +
 Makefile                                      |   46 +-
 builtin/clone.c                               |    5 +-
 builtin/init-db.c                             |   56 +-
 builtin/worktree.c                            |   27 +-
 cache.h                                       |    9 +-
 config.mak.uname                              |    2 +-
 contrib/buildsystems/Generators/Vcxproj.pm    |   11 +-
 contrib/completion/git-prompt.sh              |    7 +-
 git-compat-util.h                             |    2 +
 refs.c                                        |   27 +-
 refs.h                                        |    3 +
 refs/refs-internal.h                          |    1 +
 refs/reftable-backend.c                       | 1418 +++++++++++++++++
 reftable/.gitattributes                       |    1 +
 reftable/LICENSE                              |   31 +
 reftable/VERSION                              |    1 +
 reftable/basics.c                             |  144 ++
 reftable/basics.h                             |   56 +
 reftable/basics_test.c                        |   56 +
 reftable/block.c                              |  440 +++++
 reftable/block.h                              |  129 ++
 reftable/block_test.c                         |  119 ++
 reftable/blocksource.c                        |  148 ++
 reftable/blocksource.h                        |   22 +
 reftable/compat.h                             |   48 +
 reftable/constants.h                          |   21 +
 reftable/dump.c                               |  219 +++
 reftable/error.c                              |   39 +
 reftable/iter.c                               |  242 +++
 reftable/iter.h                               |   75 +
 reftable/merged.c                             |  366 +++++
 reftable/merged.h                             |   35 +
 reftable/merged_test.c                        |  333 ++++
 reftable/pq.c                                 |  115 ++
 reftable/pq.h                                 |   32 +
 reftable/publicbasics.c                       |   58 +
 reftable/reader.c                             |  733 +++++++++
 reftable/reader.h                             |   75 +
 reftable/record.c                             | 1116 +++++++++++++
 reftable/record.h                             |  139 ++
 reftable/record_test.c                        |  404 +++++
 reftable/refname.c                            |  209 +++
 reftable/refname.h                            |   29 +
 reftable/refname_test.c                       |  100 ++
 reftable/reftable-blocksource.h               |   50 +
 reftable/reftable-error.h                     |   62 +
 reftable/reftable-generic.h                   |   49 +
 reftable/reftable-iterator.h                  |   37 +
 reftable/reftable-malloc.h                    |   20 +
 reftable/reftable-merged.h                    |   68 +
 reftable/reftable-reader.h                    |   89 ++
 reftable/reftable-record.h                    |   73 +
 reftable/reftable-stack.h                     |  116 ++
 reftable/reftable-tests.h                     |   22 +
 reftable/reftable-writer.h                    |  149 ++
 reftable/reftable.c                           |   98 ++
 reftable/reftable_test.c                      |  577 +++++++
 reftable/stack.c                              | 1247 +++++++++++++++
 reftable/stack.h                              |   41 +
 reftable/stack_test.c                         |  781 +++++++++
 reftable/system.h                             |   32 +
 reftable/test_framework.c                     |   23 +
 reftable/test_framework.h                     |   48 +
 reftable/tree.c                               |   63 +
 reftable/tree.h                               |   34 +
 reftable/tree_test.c                          |   61 +
 reftable/writer.c                             |  676 ++++++++
 reftable/writer.h                             |   50 +
 reftable/zlib-compat.c                        |   92 ++
 repository.c                                  |    2 +
 repository.h                                  |    3 +
 setup.c                                       |    9 +-
 t/helper/test-reftable.c                      |   20 +
 t/helper/test-tool.c                          |    4 +-
 t/helper/test-tool.h                          |    2 +
 t/t0031-reftable.sh                           |  199 +++
 t/t0032-reftable-unittest.sh                  |   14 +
 t/t1409-avoid-packing-refs.sh                 |    6 +
 t/t1450-fsck.sh                               |    6 +
 t/t3210-pack-refs.sh                          |    6 +
 t/test-lib.sh                                 |    5 +
 82 files changed, 11951 insertions(+), 39 deletions(-)
 create mode 100644 refs/reftable-backend.c
 create mode 100644 reftable/.gitattributes
 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/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/compat.h
 create mode 100644 reftable/constants.h
 create mode 100644 reftable/dump.c
 create mode 100644 reftable/error.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/publicbasics.c
 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-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/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/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 reftable/zlib-compat.c
 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: faefdd61ec7c7f6f3c8c9907891465ac9a2a1475
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-847%2Fhanwen%2Flibreftable-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-847/hanwen/libreftable-v3
Pull-Request: https://github.com/git/git/pull/847

Range-diff vs v2:

  -:  ---------- >  1:  030998a732 move sleep_millisec to git-compat-util.h
  -:  ---------- >  2:  cf667653df init-db: set the_repository->hash_algo early on
  1:  6228103b4a =  3:  91c3ac2449 reftable: add LICENSE
  2:  5d1b946ab5 !  4:  2aa30f536f reftable: define the public API
     @@ Metadata
      Author: Han-Wen Nienhuys <hanwen@google.com>
      
       ## Commit message ##
     -    reftable: define the public API
     +    reftable: add error related functionality
     +
     +    The reftable/ directory is structured as a library, so it cannot
     +    crash on misuse. Instead, it returns an error codes.
     +
     +    In addition, the error code can be used to signal conditions from lower levels
     +    of the library to be handled by higher levels of the library. For example, a
     +    transaction might legitimately write an empty reftable file, but in that case,
     +    we'd want to shortcut the transaction overhead.
      
          Signed-off-by: Han-Wen Nienhuys <hanwen@google.com>
      
     - ## reftable/reftable.h (new) ##
     + ## reftable/error.c (new) ##
      @@
      +/*
      +Copyright 2020 Google LLC
     @@ reftable/reftable.h (new)
      +https://developers.google.com/open-source/licenses/bsd
      +*/
      +
     -+#ifndef REFTABLE_H
     -+#define REFTABLE_H
     -+
     -+#include <stdint.h>
     -+#include <stddef.h>
     -+
     -+void reftable_set_alloc(void *(*malloc)(size_t),
     -+			void *(*realloc)(void *, size_t), void (*free)(void *));
     -+
     -+/****************************************************************
     -+ Basic data types
     -+
     -+ Reftables store the state of each ref in struct reftable_ref_record, and they
     -+ store a sequence of reflog updates in struct reftable_log_record.
     -+ ****************************************************************/
     -+
     -+/* reftable_ref_record holds a ref database entry target_value */
     -+struct reftable_ref_record {
     -+	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. */
     -+	uint8_t *target_value; /* peeled annotated tag, or NULL. malloced. */
     -+	char *target; /* symref, or NULL. malloced. */
     -+};
     -+
     -+/* returns whether 'ref' represents a deletion */
     -+int reftable_ref_record_is_deletion(const struct reftable_ref_record *ref);
     -+
     -+/* prints a reftable_ref_record onto stdout */
     -+void reftable_ref_record_print(struct reftable_ref_record *ref,
     -+			       uint32_t hash_id);
     -+
     -+/* frees and nulls all pointer values. */
     -+void reftable_ref_record_clear(struct reftable_ref_record *ref);
     -+
     -+/* returns whether two reftable_ref_records are the same */
     -+int reftable_ref_record_equal(struct reftable_ref_record *a,
     -+			      struct reftable_ref_record *b, int hash_size);
     -+
     -+/* reftable_log_record holds a reflog entry */
     -+struct reftable_log_record {
     -+	char *refname;
     -+	uint64_t update_index; /* logical timestamp of a transactional update.
     -+				*/
     -+	uint8_t *new_hash;
     -+	uint8_t *old_hash;
     -+	char *name;
     -+	char *email;
     -+	uint64_t time;
     -+	int16_t tz_offset;
     -+	char *message;
     -+};
     -+
     -+/* returns whether 'ref' represents the deletion of a log record. */
     -+int reftable_log_record_is_deletion(const struct reftable_log_record *log);
     -+
     -+/* frees and nulls all pointer values. */
     -+void reftable_log_record_clear(struct reftable_log_record *log);
     -+
     -+/* returns whether two records are equal. */
     -+int reftable_log_record_equal(struct reftable_log_record *a,
     -+			      struct reftable_log_record *b, int hash_size);
     -+
     -+/* dumps a reftable_log_record on stdout, for debugging/testing. */
     -+void reftable_log_record_print(struct reftable_log_record *log,
     -+			       uint32_t hash_id);
     ++#include "reftable-error.h"
     ++
     ++#include <stdio.h>
     ++
     ++const char *reftable_error_str(int err)
     ++{
     ++	static char buf[250];
     ++	switch (err) {
     ++	case REFTABLE_IO_ERROR:
     ++		return "I/O error";
     ++	case REFTABLE_FORMAT_ERROR:
     ++		return "corrupt reftable file";
     ++	case REFTABLE_NOT_EXIST_ERROR:
     ++		return "file does not exist";
     ++	case REFTABLE_LOCK_ERROR:
     ++		return "data is outdated";
     ++	case REFTABLE_API_ERROR:
     ++		return "misuse of the reftable API";
     ++	case REFTABLE_ZLIB_ERROR:
     ++		return "zlib failure";
     ++	case REFTABLE_NAME_CONFLICT:
     ++		return "file/directory conflict";
     ++	case REFTABLE_REFNAME_ERROR:
     ++		return "invalid refname";
     ++	case -1:
     ++		return "general error";
     ++	default:
     ++		snprintf(buf, sizeof(buf), "unknown error code %d", err);
     ++		return buf;
     ++	}
     ++}
     +
     + ## reftable/reftable-error.h (new) ##
     +@@
     ++/*
     ++Copyright 2020 Google LLC
      +
     -+/****************************************************************
     -+ Error handling
     ++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
     ++*/
      +
     -+ Error are signaled with negative integer return values. 0 means success.
     -+ ****************************************************************/
     ++#ifndef REFTABLE_ERROR_H
     ++#define REFTABLE_ERROR_H
      +
     -+/* different types of errors */
     ++/*
     ++ Errors in reftable calls are signaled with negative integer return values. 0
     ++ means success.
     ++*/
      +enum reftable_error {
      +	/* Unexpected file system behavior */
      +	REFTABLE_IO_ERROR = -2,
     @@ reftable/reftable.h (new)
      +	   - on writing a log record with multiline message with
      +	   exact_log_message unset
      +	   - on reading a reftable_ref_record from log iterator, or vice versa.
     ++
     ++	  When a call misuses the API, the internal state of the library is kept
     ++	  unchanged.
      +	*/
      +	REFTABLE_API_ERROR = -6,
      +
     @@ reftable/reftable.h (new)
      + * deallocated. */
      +const char *reftable_error_str(int err);
      +
     -+/*
     -+ * Convert the numeric error code to an equivalent errno code.
     -+ */
     -+int reftable_error_to_errno(int err);
     -+
     -+/****************************************************************
     -+ Writing
     -+
     -+ Writing single reftables
     -+ ****************************************************************/
     -+
     -+/* reftable_write_options sets options for writing a single reftable. */
     -+struct reftable_write_options {
     -+	/* boolean: do not pad out blocks to block size. */
     -+	int unpadded;
     -+
     -+	/* the blocksize. Should be less than 2^24. */
     -+	uint32_t block_size;
     -+
     -+	/* boolean: do not generate a SHA1 => ref index. */
     -+	int skip_index_objects;
     -+
     -+	/* how often to write complete keys in each block. */
     -+	int restart_interval;
     -+
     -+	/* 4-byte identifier ("sha1", "s256") of the hash.
     -+	 * Defaults to SHA1 if unset
     -+	 */
     -+	uint32_t hash_id;
     -+
     -+	/* boolean: do not check ref names for validity or dir/file conflicts.
     -+	 */
     -+	int skip_name_check;
     -+
     -+	/* boolean: copy log messages exactly. If unset, check that the message
     -+	 *   is a single line, and add '\n' if missing.
     -+	 */
     -+	int exact_log_message;
     -+};
     -+
     -+/* reftable_block_stats holds statistics for a single block type */
     -+struct reftable_block_stats {
     -+	/* total number of entries written */
     -+	int entries;
     -+	/* total number of key restarts */
     -+	int restarts;
     -+	/* total number of blocks */
     -+	int blocks;
     -+	/* total number of index blocks */
     -+	int index_blocks;
     -+	/* depth of the index */
     -+	int max_index_level;
     -+
     -+	/* offset of the first block for this type */
     -+	uint64_t offset;
     -+	/* offset of the top level index block for this type, or 0 if not
     -+	 * present */
     -+	uint64_t index_offset;
     -+};
     -+
     -+/* stats holds overall statistics for a single reftable */
     -+struct reftable_stats {
     -+	/* total number of blocks written. */
     -+	int blocks;
     -+	/* stats for ref data */
     -+	struct reftable_block_stats ref_stats;
     -+	/* stats for the SHA1 to ref map. */
     -+	struct reftable_block_stats obj_stats;
     -+	/* stats for index blocks */
     -+	struct reftable_block_stats idx_stats;
     -+	/* stats for log blocks */
     -+	struct reftable_block_stats log_stats;
     -+
     -+	/* disambiguation length of shortened object IDs. */
     -+	int object_id_len;
     -+};
     -+
     -+/* reftable_new_writer creates a new writer */
     -+struct reftable_writer *
     -+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, 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_stack_next_update_index(), or REFTABLE_API_ERROR is returned.
     -+
     -+   For transactional updates, typically min==max. When converting an existing
     -+   ref database into a single reftable, this would be a range of update-index
     -+   timestamps.
     -+ */
     -+void reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
     -+				uint64_t max);
     -+
     -+/* adds a reftable_ref_record. Must be called in ascending
     -+   order. The update_index must be within the limits set by
     -+   reftable_writer_set_limits(), or REFTABLE_API_ERROR is returned.
     -+
     -+   It is an error to write a ref record after a log record.
     -+ */
     -+int reftable_writer_add_ref(struct reftable_writer *w,
     -+			    struct reftable_ref_record *ref);
     -+
     -+/* Convenience function to add multiple refs. Will sort the refs by
     -+   name before adding. */
     -+int reftable_writer_add_refs(struct reftable_writer *w,
     -+			     struct reftable_ref_record *refs, int n);
     -+
     -+/* adds a reftable_log_record. Must be called in ascending order (with more
     -+   recent log entries first.)
     -+ */
     -+int reftable_writer_add_log(struct reftable_writer *w,
     -+			    struct reftable_log_record *log);
     -+
     -+/* Convenience function to add multiple logs. Will sort the records by
     -+   key before adding. */
     -+int reftable_writer_add_logs(struct reftable_writer *w,
     -+			     struct reftable_log_record *logs, int n);
     -+
     -+/* reftable_writer_close finalizes the reftable. The writer is retained so
     -+ * statistics can be inspected. */
     -+int reftable_writer_close(struct reftable_writer *w);
     -+
     -+/* writer_stats returns the statistics on the reftable being written.
     -+
     -+   This struct becomes invalid when the writer is freed.
     -+ */
     -+const struct reftable_stats *writer_stats(struct reftable_writer *w);
     -+
     -+/* reftable_writer_free deallocates memory for the writer */
     -+void reftable_writer_free(struct reftable_writer *w);
     -+
     -+/****************************************************************
     -+ * ITERATING
     -+ ****************************************************************/
     -+
     -+/* iterator is the generic interface for walking over data stored in a
     -+   reftable. It is generally passed around by value.
     -+*/
     -+struct reftable_iterator {
     -+	struct reftable_iterator_vtable *ops;
     -+	void *iter_arg;
     -+};
     -+
     -+/* reads the next reftable_ref_record. Returns < 0 for error, 0 for OK and > 0:
     -+   end of iteration.
     -+*/
     -+int reftable_iterator_next_ref(struct reftable_iterator *it,
     -+			       struct reftable_ref_record *ref);
     -+
     -+/* reads the next reftable_log_record. Returns < 0 for error, 0 for OK and > 0:
     -+   end of iteration.
     -+*/
     -+int reftable_iterator_next_log(struct reftable_iterator *it,
     -+			       struct reftable_log_record *log);
     -+
     -+/* releases resources associated with an iterator. */
     -+void reftable_iterator_destroy(struct reftable_iterator *it);
     -+
     -+/****************************************************************
     -+ Reading single tables
     -+
     -+ The follow routines are for reading single files. For an application-level
     -+ interface, skip ahead to struct reftable_merged_table and struct
     -+ reftable_stack.
     -+ ****************************************************************/
     -+
     -+/* block_source is a generic wrapper for a seekable readable file.
     -+   It is generally passed around by value.
     -+ */
     -+struct reftable_block_source {
     -+	struct reftable_block_source_vtable *ops;
     -+	void *arg;
     -+};
     -+
     -+/* a contiguous segment of bytes. It keeps track of its generating block_source
     -+   so it can return itself into the pool.
     -+*/
     -+struct reftable_block {
     -+	uint8_t *data;
     -+	int len;
     -+	struct reftable_block_source source;
     -+};
     -+
     -+/* block_source_vtable are the operations that make up block_source */
     -+struct reftable_block_source_vtable {
     -+	/* returns the size of a block source */
     -+	uint64_t (*size)(void *source);
     -+
     -+	/* reads a segment from the block source. It is an error to read
     -+	   beyond the end of the block */
     -+	int (*read_block)(void *source, struct reftable_block *dest,
     -+			  uint64_t off, uint32_t size);
     -+	/* mark the block as read; may return the data back to malloc */
     -+	void (*return_block)(void *source, struct reftable_block *blockp);
     -+
     -+	/* release all resources associated with the block source */
     -+	void (*close)(void *source);
     -+};
     -+
     -+/* opens a file on the file system as a block_source */
     -+int reftable_block_source_from_file(struct reftable_block_source *block_src,
     -+				    const char *name);
     -+
     -+/* The reader struct is a handle to an open reftable file. */
     -+struct reftable_reader;
     -+
     -+/* reftable_new_reader opens a reftable for reading. If successful, returns 0
     -+ * code and sets pp. The name is used for creating a stack. Typically, it is the
     -+ * basename of the file. The block source `src` is owned by the reader, and is
     -+ * closed on calling reftable_reader_destroy().
     -+ */
     -+int reftable_new_reader(struct reftable_reader **pp,
     -+			struct reftable_block_source *src, const char *name);
     -+
     -+/* reftable_reader_seek_ref returns an iterator where 'name' would be inserted
     -+   in the table.  To seek to the start of the table, use name = "".
     -+
     -+   example:
     -+
     -+   struct reftable_reader *r = NULL;
     -+   int err = reftable_new_reader(&r, &src, "filename");
     -+   if (err < 0) { ... }
     -+   struct reftable_iterator it  = {0};
     -+   err = reftable_reader_seek_ref(r, &it, "refs/heads/master");
     -+   if (err < 0) { ... }
     -+   struct reftable_ref_record ref  = {0};
     -+   while (1) {
     -+     err = reftable_iterator_next_ref(&it, &ref);
     -+     if (err > 0) {
     -+       break;
     -+     }
     -+     if (err < 0) {
     -+       ..error handling..
     -+     }
     -+     ..found..
     -+   }
     -+   reftable_iterator_destroy(&it);
     -+   reftable_ref_record_clear(&ref);
     -+ */
     -+int reftable_reader_seek_ref(struct reftable_reader *r,
     -+			     struct reftable_iterator *it, const char *name);
     -+
     -+/* returns the hash ID used in this table. */
     -+uint32_t reftable_reader_hash_id(struct reftable_reader *r);
     -+
     -+/* seek to logs for the given name, older than update_index. To seek to the
     -+   start of the table, use name = "".
     -+ */
     -+int reftable_reader_seek_log_at(struct reftable_reader *r,
     -+				struct reftable_iterator *it, const char *name,
     -+				uint64_t update_index);
     -+
     -+/* seek to newest log entry for given name. */
     -+int reftable_reader_seek_log(struct reftable_reader *r,
     -+			     struct reftable_iterator *it, const char *name);
     -+
     -+/* closes and deallocates a reader. */
     -+void reftable_reader_free(struct reftable_reader *);
     -+
     -+/* return an iterator for the refs pointing to `oid`. */
     -+int reftable_reader_refs_for(struct reftable_reader *r,
     -+			     struct reftable_iterator *it, uint8_t *oid);
     -+
     -+/* return the max_update_index for a table */
     -+uint64_t reftable_reader_max_update_index(struct reftable_reader *r);
     -+
     -+/* return the min_update_index for a table */
     -+uint64_t reftable_reader_min_update_index(struct reftable_reader *r);
     -+
     -+/****************************************************************
     -+ Merged tables
     -+
     -+ A ref database kept in a sequence of table files. The merged_table presents a
     -+ unified view to reading (seeking, iterating) a sequence of immutable tables.
     -+ ****************************************************************/
     -+
     -+/* 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_table *stack, int n,
     -+			      uint32_t hash_id);
     -+
     -+/* returns an iterator positioned just before 'name' */
     -+int reftable_merged_table_seek_ref(struct reftable_merged_table *mt,
     -+				   struct reftable_iterator *it,
     -+				   const char *name);
     -+
     -+/* returns an iterator for log entry, at given update_index */
     -+int reftable_merged_table_seek_log_at(struct reftable_merged_table *mt,
     -+				      struct reftable_iterator *it,
     -+				      const char *name, uint64_t update_index);
     -+
     -+/* like reftable_merged_table_seek_log_at but look for the newest entry. */
     -+int reftable_merged_table_seek_log(struct reftable_merged_table *mt,
     -+				   struct reftable_iterator *it,
     -+				   const char *name);
     -+
     -+/* returns the max update_index covered by this merged table. */
     -+uint64_t
     -+reftable_merged_table_max_update_index(struct reftable_merged_table *mt);
     -+
     -+/* returns the min update_index covered by this merged table. */
     -+uint64_t
     -+reftable_merged_table_min_update_index(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
     -+
     -+ A unified API for reading tables, either merged tables, or single readers.
     -+ ****************************************************************/
     -+
     -+struct reftable_table {
     -+	struct reftable_table_vtable *ops;
     -+	void *table_arg;
     -+};
     -+
     -+int reftable_table_seek_ref(struct reftable_table *tab,
     -+			    struct reftable_iterator *it, const char *name);
     -+
     -+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,
     -+			    struct reftable_ref_record *ref);
     -+
     -+/****************************************************************
     -+ Mutable ref database
     -+
     -+ The stack presents an interface to a mutable sequence of reftables.
     -+ ****************************************************************/
     -+
     -+/* a stack is a stack of reftables, which can be mutated by pushing a table to
     -+ * the top of the stack */
     -+struct reftable_stack;
     -+
     -+/* open a new reftable stack. The tables along with the table list will be
     -+   stored in 'dir'. Typically, this should be .git/reftables.
     -+*/
     -+int reftable_new_stack(struct reftable_stack **dest, const char *dir,
     -+		       struct reftable_write_options config);
     -+
     -+/* returns the update_index at which a next table should be written. */
     -+uint64_t reftable_stack_next_update_index(struct reftable_stack *st);
     -+
     -+/* holds a transaction to add tables at the top of a stack. */
     -+struct reftable_addition;
     -+
     -+/*
     -+  returns a new transaction to add reftables to the given stack. As a side
     -+  effect, the ref database is locked.
     -+*/
     -+int reftable_stack_new_addition(struct reftable_addition **dest,
     -+				struct reftable_stack *st);
     -+
     -+/* Adds a reftable to transaction. */
     -+int reftable_addition_add(struct reftable_addition *add,
     -+			  int (*write_table)(struct reftable_writer *wr,
     -+					     void *arg),
     -+			  void *arg);
     -+
     -+/* Commits the transaction, releasing the lock. */
     -+int reftable_addition_commit(struct reftable_addition *add);
     -+
     -+/* Release all non-committed data from the transaction, and deallocate the
     -+   transaction. Releases the lock if held. */
     -+void reftable_addition_destroy(struct reftable_addition *add);
     -+
     -+/* add a new table to the stack. The write_table function must call
     -+   reftable_writer_set_limits, add refs and return an error value. */
     -+int reftable_stack_add(struct reftable_stack *st,
     -+		       int (*write_table)(struct reftable_writer *wr,
     -+					  void *write_arg),
     -+		       void *write_arg);
     -+
     -+/* returns the merged_table for seeking. This table is valid until the
     -+   next write or reload, and should not be closed or deleted.
     -+*/
     -+struct reftable_merged_table *
     -+reftable_stack_merged_table(struct reftable_stack *st);
     -+
     -+/* frees all resources associated with the stack. */
     -+void reftable_stack_destroy(struct reftable_stack *st);
     -+
     -+/* Reloads the stack if necessary. This is very cheap to run if the stack was up
     -+ * to date */
     -+int reftable_stack_reload(struct reftable_stack *st);
     -+
     -+/* Policy for expiring reflog entries. */
     -+struct reftable_log_expiry_config {
     -+	/* Drop entries older than this timestamp */
     -+	uint64_t time;
     -+
     -+	/* Drop older entries */
     -+	uint64_t min_update_index;
     -+};
     -+
     -+/* compacts all reftables into a giant table. Expire reflog entries if config is
     -+ * non-NULL */
     -+int reftable_stack_compact_all(struct reftable_stack *st,
     -+			       struct reftable_log_expiry_config *config);
     -+
     -+/* heuristically compact unbalanced table stack. */
     -+int reftable_stack_auto_compact(struct reftable_stack *st);
     -+
     -+/* convenience function to read a single ref. Returns < 0 for error, 0
     -+   for success, and 1 if ref not found. */
     -+int reftable_stack_read_ref(struct reftable_stack *st, const char *refname,
     -+			    struct reftable_ref_record *ref);
     -+
     -+/* convenience function to read a single log. Returns < 0 for error, 0
     -+   for success, and 1 if ref not found. */
     -+int reftable_stack_read_log(struct reftable_stack *st, const char *refname,
     -+			    struct reftable_log_record *log);
     -+
     -+/* statistics on past compactions. */
     -+struct reftable_compaction_stats {
     -+	uint64_t bytes; /* total number of bytes written */
     -+	uint64_t entries_written; /* total number of entries written, including
     -+				     failures. */
     -+	int attempts; /* how often we tried to compact */
     -+	int failures; /* failures happen on concurrent updates */
     -+};
     -+
     -+/* return statistics for compaction up till now. */
     -+struct reftable_compaction_stats *
     -+reftable_stack_compaction_stats(struct reftable_stack *st);
     -+
      +#endif
  3:  01a669a731 <  -:  ---------- vcxproj: adjust for the reftable changes
  5:  4190da597e !  5:  987d1d186f reftable: utility functions
     @@ Commit message
      
          This commit provides basic utility classes for the reftable library.
      
     -    Since the reftable library must compile standalone, there may be some overlap
     -    with git-core utility functions.
     -
          Signed-off-by: Han-Wen Nienhuys <hanwen@google.com>
      
       ## Makefile ##
     @@ Makefile: TEST_SHELL_PATH = $(SHELL_PATH)
      +REFTABLE_LIB = reftable/libreftable.a
      +REFTABLE_TEST_LIB = reftable/libreftable_test.a
       
     - GENERATED_H += config-list.h
       GENERATED_H += command-list.h
     + GENERATED_H += config-list.h
      @@ Makefile: THIRD_PARTY_SOURCES += compat/regex/%
       THIRD_PARTY_SOURCES += sha1collisiondetection/%
       THIRD_PARTY_SOURCES += sha1dc/%
     @@ Makefile: XDIFF_OBJS += xdiff/xpatience.o
       XDIFF_OBJS += xdiff/xutils.o
       
      +REFTABLE_OBJS += reftable/basics.o
     -+REFTABLE_OBJS += reftable/blocksource.o
     ++REFTABLE_OBJS += reftable/error.o
      +REFTABLE_OBJS += reftable/publicbasics.o
     -+REFTABLE_OBJS += reftable/compat.o
     -+REFTABLE_OBJS += reftable/strbuf.o
      +
     -+REFTABLE_TEST_OBJS += reftable/strbuf_test.o
      +REFTABLE_TEST_OBJS += reftable/test_framework.o
     ++REFTABLE_TEST_OBJS += reftable/basics_test.o
      +
       TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
       OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
     @@ reftable/basics.c (new)
      +	}
      +
      +	return *a == *b;
     ++}
     ++
     ++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;
      +}
      
       ## reftable/basics.h (new) ##
     @@ reftable/basics.h (new)
      +#ifndef BASICS_H
      +#define BASICS_H
      +
     ++/*
     ++ * miscellaneous utilities that are not provided by Git.
     ++ */
     ++
      +#include "system.h"
      +
      +/* Bigendian en/decoding of integers */
     @@ reftable/basics.h (new)
      +void reftable_free(void *p);
      +void *reftable_calloc(size_t sz);
      +
     ++/* Find the longest shared prefix size of `a` and `b` */
     ++struct strbuf;
     ++int common_prefix_size(struct strbuf *a, struct strbuf *b);
     ++
      +#endif
      
     - ## reftable/blocksource.c (new) ##
     + ## reftable/basics_test.c (new) ##
      @@
      +/*
      +Copyright 2020 Google LLC
     @@ reftable/blocksource.c (new)
      +#include "system.h"
      +
      +#include "basics.h"
     -+#include "blocksource.h"
     -+#include "strbuf.h"
     -+#include "reftable.h"
     -+
     -+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;
     -+}
     -+
     -+static uint64_t strbuf_size(void *b)
     -+{
     -+	return ((struct strbuf *)b)->len;
     -+}
     -+
     -+static 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);
     -+}
     -+
     -+static struct reftable_block_source_vtable malloc_vtable = {
     -+	.return_block = &malloc_return_block,
     -+};
     -+
     -+static struct reftable_block_source malloc_block_source_instance = {
     -+	.ops = &malloc_vtable,
     -+};
     -+
     -+struct reftable_block_source malloc_block_source(void)
     -+{
     -+	return malloc_block_source_instance;
     -+}
     ++#include "test_framework.h"
     ++#include "reftable-tests.h"
      +
     -+struct file_block_source {
     -+	int fd;
     -+	uint64_t size;
     ++struct binsearch_args {
     ++	int key;
     ++	int *arr;
      +};
      +
     -+static uint64_t file_size(void *b)
     ++static int binsearch_func(size_t i, void *void_args)
      +{
     -+	return ((struct file_block_source *)b)->size;
     -+}
     ++	struct binsearch_args *args = (struct binsearch_args *)void_args;
      +
     -+static void file_return_block(void *b, struct reftable_block *dest)
     -+{
     -+	memset(dest->data, 0xff, dest->len);
     -+	reftable_free(dest->data);
     ++	return args->key < args->arr[i];
      +}
      +
     -+static void file_close(void *b)
     -+{
     -+	int fd = ((struct file_block_source *)b)->fd;
     -+	if (fd > 0) {
     -+		close(fd);
     -+		((struct file_block_source *)b)->fd = 0;
     -+	}
     -+
     -+	reftable_free(b);
     -+}
     -+
     -+static int file_read_block(void *v, struct reftable_block *dest, uint64_t off,
     -+			   uint32_t size)
     -+{
     -+	struct file_block_source *b = (struct file_block_source *)v;
     -+	assert(off + size <= b->size);
     -+	dest->data = reftable_malloc(size);
     -+	if (pread(b->fd, dest->data, size, off) != size)
     -+		return -1;
     -+	dest->len = size;
     -+	return size;
     -+}
     -+
     -+static struct reftable_block_source_vtable file_vtable = {
     -+	.size = &file_size,
     -+	.read_block = &file_read_block,
     -+	.return_block = &file_return_block,
     -+	.close = &file_close,
     -+};
     -+
     -+int reftable_block_source_from_file(struct reftable_block_source *bs,
     -+				    const char *name)
     ++static void test_binsearch(void)
      +{
     -+	struct stat st = { 0 };
     -+	int err = 0;
     -+	int fd = open(name, O_RDONLY);
     -+	struct file_block_source *p = NULL;
     -+	if (fd < 0) {
     -+		if (errno == ENOENT) {
     -+			return REFTABLE_NOT_EXIST_ERROR;
     -+		}
     -+		return -1;
     -+	}
     -+
     -+	err = fstat(fd, &st);
     -+	if (err < 0)
     -+		return -1;
     -+
     -+	p = reftable_calloc(sizeof(struct file_block_source));
     -+	p->size = st.st_size;
     -+	p->fd = fd;
     -+
     -+	assert(bs->ops == NULL);
     -+	bs->ops = &file_vtable;
     -+	bs->arg = p;
     -+	return 0;
     -+}
     -
     - ## reftable/blocksource.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 BLOCKSOURCE_H
     -+#define BLOCKSOURCE_H
     -+
     -+#include "strbuf.h"
     -+
     -+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);
     ++	int arr[] = { 2, 4, 6, 8, 10 };
     ++	size_t sz = ARRAY_SIZE(arr);
     ++	struct binsearch_args args = {
     ++		.arr = arr,
     ++	};
      +
     -+struct reftable_block_source malloc_block_source(void);
     -+
     -+#endif
     -
     - ## reftable/compat.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
     -+
     -+*/
     -+
     -+/* compat.c - compatibility functions for standalone compilation */
     -+
     -+#include "system.h"
     -+#include "basics.h"
     -+
     -+#ifdef REFTABLE_STANDALONE
     -+
     -+#include <dirent.h>
     -+
     -+void put_be32(void *p, uint32_t i)
     -+{
     -+	uint8_t *out = (uint8_t *)p;
     -+
     -+	out[0] = (uint8_t)((i >> 24) & 0xff);
     -+	out[1] = (uint8_t)((i >> 16) & 0xff);
     -+	out[2] = (uint8_t)((i >> 8) & 0xff);
     -+	out[3] = (uint8_t)((i)&0xff);
     -+}
     -+
     -+uint32_t get_be32(uint8_t *in)
     -+{
     -+	return (uint32_t)(in[0]) << 24 | (uint32_t)(in[1]) << 16 |
     -+	       (uint32_t)(in[2]) << 8 | (uint32_t)(in[3]);
     -+}
     -+
     -+void put_be64(void *p, uint64_t v)
     -+{
     -+	uint8_t *out = (uint8_t *)p;
     -+	int i = sizeof(uint64_t);
     -+	while (i--) {
     -+		out[i] = (uint8_t)(v & 0xff);
     -+		v >>= 8;
     -+	}
     -+}
     -+
     -+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)(bytes[i] & 0xff);
     -+	}
     -+	return v;
     -+}
     -+
     -+uint16_t get_be16(uint8_t *in)
     -+{
     -+	return (uint32_t)(in[0]) << 8 | (uint32_t)(in[1]);
     -+}
     -+
     -+char *xstrdup(const char *s)
     -+{
     -+	int l = strlen(s);
     -+	char *dest = (char *)reftable_malloc(l + 1);
     -+	strncpy(dest, s, l + 1);
     -+	return dest;
     -+}
     -+
     -+void sleep_millisec(int millisecs)
     -+{
     -+	usleep(millisecs * 1000);
     -+}
     -+
     -+void reftable_clear_dir(const char *dirname)
     -+{
     -+	DIR *dir = opendir(dirname);
     -+	struct dirent *ent = NULL;
     -+	assert(dir);
     -+	while ((ent = readdir(dir)) != NULL) {
     -+		unlinkat(dirfd(dir), ent->d_name, 0);
     ++	for (i = 1; i < 11; i++) {
     ++		int res;
     ++		args.key = i;
     ++		res = binsearch(sz, &binsearch_func, &args);
     ++
     ++		if (res < sz) {
     ++			EXPECT(args.key < arr[res]);
     ++			if (res > 0) {
     ++				EXPECT(args.key >= arr[res - 1]);
     ++			}
     ++		} else {
     ++			EXPECT(args.key == 10 || args.key == 11);
     ++		}
      +	}
     -+	closedir(dir);
     -+	rmdir(dirname);
      +}
      +
     -+#else
     -+
     -+#include "../dir.h"
     -+
     -+void reftable_clear_dir(const char *dirname)
     ++int basics_test_main(int argc, const char *argv[])
      +{
     -+	struct strbuf path = STRBUF_INIT;
     -+	strbuf_addstr(&path, dirname);
     -+	remove_dir_recursively(&path, 0);
     -+	strbuf_release(&path);
     -+}
     -+
     -+#endif
     -+
     -+int hash_size(uint32_t id)
     -+{
     -+	switch (id) {
     -+	case 0:
     -+	case SHA1_ID:
     -+		return SHA1_SIZE;
     -+	case SHA256_ID:
     -+		return SHA256_SIZE;
     -+	}
     -+	abort();
     ++	test_binsearch();
     ++	return 0;
      +}
      
       ## reftable/compat.h (new) ##
     @@ reftable/publicbasics.c (new)
      +https://developers.google.com/open-source/licenses/bsd
      +*/
      +
     -+#include "reftable.h"
     ++#include "reftable-malloc.h"
      +
      +#include "basics.h"
      +#include "system.h"
      +
     -+const char *reftable_error_str(int err)
     -+{
     -+	static char buf[250];
     -+	switch (err) {
     -+	case REFTABLE_IO_ERROR:
     -+		return "I/O error";
     -+	case REFTABLE_FORMAT_ERROR:
     -+		return "corrupt reftable file";
     -+	case REFTABLE_NOT_EXIST_ERROR:
     -+		return "file does not exist";
     -+	case REFTABLE_LOCK_ERROR:
     -+		return "data is outdated";
     -+	case REFTABLE_API_ERROR:
     -+		return "misuse of the reftable API";
     -+	case REFTABLE_ZLIB_ERROR:
     -+		return "zlib failure";
     -+	case REFTABLE_NAME_CONFLICT:
     -+		return "file/directory conflict";
     -+	case REFTABLE_REFNAME_ERROR:
     -+		return "invalid refname";
     -+	case -1:
     -+		return "general error";
     -+	default:
     -+		snprintf(buf, sizeof(buf), "unknown error code %d", err);
     -+		return buf;
     -+	}
     -+}
     -+
     -+int reftable_error_to_errno(int err)
     -+{
     -+	switch (err) {
     -+	case REFTABLE_IO_ERROR:
     -+		return EIO;
     -+	case REFTABLE_FORMAT_ERROR:
     -+		return EFAULT;
     -+	case REFTABLE_NOT_EXIST_ERROR:
     -+		return ENOENT;
     -+	case REFTABLE_LOCK_ERROR:
     -+		return EBUSY;
     -+	case REFTABLE_API_ERROR:
     -+		return EINVAL;
     -+	case REFTABLE_ZLIB_ERROR:
     -+		return EDOM;
     -+	default:
     -+		return ERANGE;
     -+	}
     -+}
     -+
      +static void *(*reftable_malloc_ptr)(size_t sz) = &malloc;
      +static void *(*reftable_realloc_ptr)(void *, size_t) = &realloc;
      +static void (*reftable_free_ptr)(void *) = &free;
     @@ reftable/publicbasics.c (new)
      +	reftable_free_ptr = free;
      +}
      +
     -+int reftable_fd_write(void *arg, const void *data, size_t sz)
     ++int hash_size(uint32_t id)
      +{
     -+	int *fdp = (int *)arg;
     -+	return write(*fdp, data, sz);
     ++	switch (id) {
     ++	case 0:
     ++	case SHA1_ID:
     ++		return SHA1_SIZE;
     ++	case SHA256_ID:
     ++		return SHA256_SIZE;
     ++	}
     ++	abort();
      +}
      
     - ## reftable/reftable-tests.h (new) ##
     + ## reftable/reftable-malloc.h (new) ##
      @@
      +/*
      +Copyright 2020 Google LLC
     @@ reftable/reftable-tests.h (new)
      +https://developers.google.com/open-source/licenses/bsd
      +*/
      +
     -+#ifndef REFTABLE_TESTS_H
     -+#define REFTABLE_TESTS_H
     ++#ifndef REFTABLE_H
     ++#define REFTABLE_H
      +
     -+int block_test_main(int argc, const char **argv);
     -+int merged_test_main(int argc, const char **argv);
     -+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 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);
     ++#include <stddef.h>
     ++
     ++/*
     ++  Overrides the functions to use for memory management.
     ++ */
     ++void reftable_set_alloc(void *(*malloc)(size_t),
     ++			void *(*realloc)(void *, size_t), void (*free)(void *));
      +
      +#endif
      
     - ## reftable/strbuf.c (new) ##
     + ## reftable/reftable-tests.h (new) ##
      @@
      +/*
      +Copyright 2020 Google LLC
     @@ reftable/strbuf.c (new)
      +https://developers.google.com/open-source/licenses/bsd
      +*/
      +
     -+#include "strbuf.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;
     -+}
     ++#ifndef REFTABLE_TESTS_H
     ++#define REFTABLE_TESTS_H
      +
     -+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;
     -+}
     ++int basics_test_main(int argc, const char **argv);
     ++int block_test_main(int argc, const char **argv);
     ++int merged_test_main(int argc, const char **argv);
     ++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 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);
      +
      +#endif
     -+
     -+int strbuf_add_void(void *b, const void *data, size_t sz)
     -+{
     -+	strbuf_add((struct strbuf *)b, data, sz);
     -+	return sz;
     -+}
     -+
     -+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) ##
     + ## reftable/system.h (new) ##
      @@
      +/*
      +Copyright 2020 Google LLC
     @@ reftable/strbuf.h (new)
      +https://developers.google.com/open-source/licenses/bsd
      +*/
      +
     -+#ifndef SLICE_H
     -+#define SLICE_H
     -+
     -+#ifdef REFTABLE_STANDALONE
     -+
     -+#include "basics.h"
     -+
     -+/*
     -+  Provides a bounds-checked, growable byte ranges. To use, initialize as "strbuf
     -+  x = STRBUF_INIT;"
     -+ */
     -+struct strbuf {
     -+	size_t len;
     -+	size_t 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);
     ++#ifndef SYSTEM_H
     ++#define SYSTEM_H
      +
     -+/* Append `add` to `dest. */
     -+void strbuf_addbuf(struct strbuf *dest, struct strbuf *add);
     ++#include "git-compat-util.h"
     ++#include "strbuf.h"
      +
     -+#else
     ++#include <zlib.h>
      +
     -+#include "../git-compat-util.h"
     -+#include "../strbuf.h"
     ++struct strbuf;
     ++/* In git, this is declared in dir.h */
     ++int remove_dir_recursively(struct strbuf *path, int flags);
      +
     -+#endif
     -+
     -+extern struct strbuf reftable_empty_strbuf;
     ++#define SHA1_ID 0x73686131
     ++#define SHA256_ID 0x73323536
     ++#define SHA1_SIZE 20
     ++#define SHA256_SIZE 32
      +
     -+/* Like strbuf_add, but suitable for passing to reftable_new_writer
     ++/* This is uncompress2, which is only available in zlib as of 2017.
      + */
     -+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);
     ++int uncompress_return_consumed(Bytef *dest, uLongf *destLen,
     ++			       const Bytef *source, uLong *sourceLen);
     ++int hash_size(uint32_t id);
      +
      +#endif
      
     - ## reftable/strbuf_test.c (new) ##
     + ## reftable/test_framework.c (new) ##
      @@
      +/*
      +Copyright 2020 Google LLC
     @@ reftable/strbuf_test.c (new)
      +https://developers.google.com/open-source/licenses/bsd
      +*/
      +
     -+#include "strbuf.h"
     -+
      +#include "system.h"
     ++#include "test_framework.h"
      +
      +#include "basics.h"
     -+#include "test_framework.h"
     -+#include "reftable-tests.h"
      +
     -+static void test_strbuf(void)
     ++void set_test_hash(uint8_t *p, int i)
      +{
     -+	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);
     ++	memset(p, (uint8_t)i, hash_size(SHA1_ID));
      +}
      +
     -+int strbuf_test_main(int argc, const char *argv[])
     ++int strbuf_add_void(void *b, const void *data, size_t sz)
      +{
     -+	add_test_case("test_strbuf", &test_strbuf);
     -+	return test_main(argc, argv);
     ++	strbuf_add((struct strbuf *)b, data, sz);
     ++	return sz;
      +}
      
     - ## reftable/system.h (new) ##
     + ## reftable/test_framework.h (new) ##
      @@
      +/*
      +Copyright 2020 Google LLC
     @@ reftable/system.h (new)
      +https://developers.google.com/open-source/licenses/bsd
      +*/
      +
     -+#ifndef SYSTEM_H
     -+#define SYSTEM_H
     -+
     -+#ifndef REFTABLE_STANDALONE
     -+
     -+#include "git-compat-util.h"
     -+#include "cache.h"
     -+#include <zlib.h>
     -+
     -+#else
     -+
     -+#include <assert.h>
     -+#include <errno.h>
     -+#include <fcntl.h>
     -+#include <inttypes.h>
     -+#include <stdint.h>
     -+#include <stdio.h>
     -+#include <stdlib.h>
     -+#include <string.h>
     -+#include <sys/stat.h>
     -+#include <sys/time.h>
     -+#include <sys/types.h>
     -+#include <unistd.h>
     -+#include <zlib.h>
     ++#ifndef TEST_FRAMEWORK_H
     ++#define TEST_FRAMEWORK_H
      +
     -+#include "compat.h"
     ++#include "system.h"
     ++#include "reftable-error.h"
     ++
     ++#define EXPECT_ERR(c)                                                  \
     ++	if (c != 0) {                                                  \
     ++		fflush(stderr);                                        \
     ++		fflush(stdout);                                        \
     ++		fprintf(stderr, "%s: %d: error == %d (%s), want 0\n",  \
     ++			__FILE__, __LINE__, c, reftable_error_str(c)); \
     ++		abort();                                               \
     ++	}
      +
     -+#endif /* REFTABLE_STANDALONE */
     ++#define EXPECT_STREQ(a, b)                                               \
     ++	if (strcmp(a, b)) {                                              \
     ++		fflush(stderr);                                          \
     ++		fflush(stdout);                                          \
     ++		fprintf(stderr, "%s:%d: %s (%s) != %s (%s)\n", __FILE__, \
     ++			__LINE__, #a, a, #b, b);                         \
     ++		abort();                                                 \
     ++	}
      +
     -+void reftable_clear_dir(const char *dirname);
     ++#define EXPECT(c)                                                          \
     ++	if (!(c)) {                                                        \
     ++		fflush(stderr);                                            \
     ++		fflush(stdout);                                            \
     ++		fprintf(stderr, "%s: %d: failed assertion %s\n", __FILE__, \
     ++			__LINE__, #c);                                     \
     ++		abort();                                                   \
     ++	}
      +
     -+#define SHA1_ID 0x73686131
     -+#define SHA256_ID 0x73323536
     -+#define SHA1_SIZE 20
     -+#define SHA256_SIZE 32
     ++void set_test_hash(uint8_t *p, int i);
      +
     -+/* This is uncompress2, which is only available in zlib as of 2017.
     ++/* Like strbuf_add, but suitable for passing to reftable_new_writer
      + */
     -+int uncompress_return_consumed(Bytef *dest, uLongf *destLen,
     -+			       const Bytef *source, uLong *sourceLen);
     -+int hash_size(uint32_t id);
     ++int strbuf_add_void(void *b, const void *data, size_t sz);
      +
      +#endif
      
     @@ t/helper/test-reftable.c (new)
      +
      +int cmd__reftable(int argc, const char **argv)
      +{
     -+	strbuf_test_main(argc, argv);
     ++	basics_test_main(argc, argv);
     ++
      +	return 0;
      +}
      
       ## t/helper/test-tool.c ##
      @@ t/helper/test-tool.c: static struct test_cmd cmds[] = {
     + 	{ "path-utils", cmd__path_utils },
     + 	{ "pkt-line", cmd__pkt_line },
     + 	{ "prio-queue", cmd__prio_queue },
     +-	{ "proc-receive", cmd__proc_receive},
     ++	{ "proc-receive", cmd__proc_receive },
     + 	{ "progress", cmd__progress },
     + 	{ "reach", cmd__reach },
     + 	{ "read-cache", cmd__read_cache },
       	{ "read-graph", cmd__read_graph },
       	{ "read-midx", cmd__read_midx },
       	{ "ref-store", cmd__ref_store },
     @@ t/helper/test-tool.h: int cmd__read_cache(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/t0032-reftable-unittest.sh (new) ##
     +@@
     ++#!/bin/sh
     ++#
     ++# Copyright (c) 2020 Google LLC
     ++#
     ++
     ++test_description='reftable unittests'
     ++
     ++. ./test-lib.sh
     ++
     ++test_expect_success 'unittests' '
     ++       test-tool reftable
     ++'
     ++
     ++test_done
  4:  b94c5f5c61 !  6:  f71e82b995 reftable: add a barebones unittest framework
     @@ Metadata
      Author: Han-Wen Nienhuys <hanwen@google.com>
      
       ## Commit message ##
     -    reftable: add a barebones unittest framework
     +    reftable: add blocksource, an abstraction for random access reads
     +
     +    The reftable format is usually used with files for storage. However, we abstract
     +    away this using the blocksource data structure. This has two advantages:
     +
     +    * log blocks are zlib compressed, and handling them is simplified if we can
     +      discard byte segments from within the block layer.
     +
     +    * for unittests, it is useful to read and write in-memory. The blocksource
     +      allows us to abstract the data away from on-disk files.
      
          Signed-off-by: Han-Wen Nienhuys <hanwen@google.com>
      
     - ## reftable/test_framework.c (new) ##
     + ## Makefile ##
     +@@ Makefile: XDIFF_OBJS += xdiff/xutils.o
     + 
     + REFTABLE_OBJS += reftable/basics.o
     + REFTABLE_OBJS += reftable/error.o
     ++REFTABLE_OBJS += reftable/blocksource.o
     + REFTABLE_OBJS += reftable/publicbasics.o
     + 
     + REFTABLE_TEST_OBJS += reftable/test_framework.o
     +
     + ## reftable/blocksource.c (new) ##
      @@
      +/*
      +Copyright 2020 Google LLC
     @@ reftable/test_framework.c (new)
      +https://developers.google.com/open-source/licenses/bsd
      +*/
      +
     -+#include "test_framework.h"
     -+
      +#include "system.h"
     ++
      +#include "basics.h"
     ++#include "blocksource.h"
     ++#include "reftable-blocksource.h"
     ++#include "reftable-error.h"
      +
     -+static struct test_case **test_cases;
     -+static int test_case_len;
     -+static int test_case_cap;
     ++static void strbuf_return_block(void *b, struct reftable_block *dest)
     ++{
     ++	memset(dest->data, 0xff, dest->len);
     ++	reftable_free(dest->data);
     ++}
      +
     -+static struct test_case *new_test_case(const char *name, void (*testfunc)(void))
     ++static void strbuf_close(void *b)
      +{
     -+	struct test_case *tc = reftable_malloc(sizeof(struct test_case));
     -+	tc->name = name;
     -+	tc->testfunc = testfunc;
     -+	return tc;
      +}
      +
     -+struct test_case *add_test_case(const char *name, void (*testfunc)(void))
     ++static int strbuf_read_block(void *v, struct reftable_block *dest, uint64_t off,
     ++			     uint32_t size)
      +{
     -+	struct test_case *tc = new_test_case(name, testfunc);
     -+	if (test_case_len == test_case_cap) {
     -+		test_case_cap = 2 * test_case_cap + 1;
     -+		test_cases = reftable_realloc(
     -+			test_cases, sizeof(struct test_case) * test_case_cap);
     -+	}
     ++	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;
     ++}
      +
     -+	test_cases[test_case_len++] = tc;
     -+	return tc;
     ++static uint64_t strbuf_size(void *b)
     ++{
     ++	return ((struct strbuf *)b)->len;
      +}
      +
     -+int test_main(int argc, const char *argv[])
     ++static 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)
      +{
     -+	const char *filter = NULL;
     -+	int i = 0;
     -+	if (argc > 1) {
     -+		filter = argv[1];
     -+	}
     ++	assert(bs->ops == NULL);
     ++	bs->ops = &strbuf_vtable;
     ++	bs->arg = buf;
     ++}
      +
     -+	for (i = 0; i < test_case_len; i++) {
     -+		const char *name = test_cases[i]->name;
     -+		if (filter == NULL || strstr(name, filter) != NULL) {
     -+			printf("case %s\n", name);
     -+			test_cases[i]->testfunc();
     -+		} else {
     -+			printf("skip %s\n", name);
     -+		}
     ++static void malloc_return_block(void *b, struct reftable_block *dest)
     ++{
     ++	memset(dest->data, 0xff, dest->len);
     ++	reftable_free(dest->data);
     ++}
     ++
     ++static struct reftable_block_source_vtable malloc_vtable = {
     ++	.return_block = &malloc_return_block,
     ++};
     ++
     ++static struct reftable_block_source malloc_block_source_instance = {
     ++	.ops = &malloc_vtable,
     ++};
      +
     -+		reftable_free(test_cases[i]);
     ++struct reftable_block_source malloc_block_source(void)
     ++{
     ++	return malloc_block_source_instance;
     ++}
     ++
     ++struct file_block_source {
     ++	int fd;
     ++	uint64_t size;
     ++};
     ++
     ++static uint64_t file_size(void *b)
     ++{
     ++	return ((struct file_block_source *)b)->size;
     ++}
     ++
     ++static void file_return_block(void *b, struct reftable_block *dest)
     ++{
     ++	memset(dest->data, 0xff, dest->len);
     ++	reftable_free(dest->data);
     ++}
     ++
     ++static void file_close(void *b)
     ++{
     ++	int fd = ((struct file_block_source *)b)->fd;
     ++	if (fd > 0) {
     ++		close(fd);
     ++		((struct file_block_source *)b)->fd = 0;
      +	}
     -+	reftable_free(test_cases);
     -+	test_cases = NULL;
     -+	test_case_len = 0;
     -+	test_case_cap = 0;
     -+	return 0;
     ++
     ++	reftable_free(b);
      +}
      +
     -+void set_test_hash(uint8_t *p, int i)
     ++static int file_read_block(void *v, struct reftable_block *dest, uint64_t off,
     ++			   uint32_t size)
      +{
     -+	memset(p, (uint8_t)i, hash_size(SHA1_ID));
     ++	struct file_block_source *b = (struct file_block_source *)v;
     ++	assert(off + size <= b->size);
     ++	dest->data = reftable_malloc(size);
     ++	if (pread(b->fd, dest->data, size, off) != size)
     ++		return -1;
     ++	dest->len = size;
     ++	return size;
     ++}
     ++
     ++static struct reftable_block_source_vtable file_vtable = {
     ++	.size = &file_size,
     ++	.read_block = &file_read_block,
     ++	.return_block = &file_return_block,
     ++	.close = &file_close,
     ++};
     ++
     ++int reftable_block_source_from_file(struct reftable_block_source *bs,
     ++				    const char *name)
     ++{
     ++	struct stat st = { 0 };
     ++	int err = 0;
     ++	int fd = open(name, O_RDONLY);
     ++	struct file_block_source *p = NULL;
     ++	if (fd < 0) {
     ++		if (errno == ENOENT) {
     ++			return REFTABLE_NOT_EXIST_ERROR;
     ++		}
     ++		return -1;
     ++	}
     ++
     ++	err = fstat(fd, &st);
     ++	if (err < 0)
     ++		return -1;
     ++
     ++	p = reftable_calloc(sizeof(struct file_block_source));
     ++	p->size = st.st_size;
     ++	p->fd = fd;
     ++
     ++	assert(bs->ops == NULL);
     ++	bs->ops = &file_vtable;
     ++	bs->arg = p;
     ++	return 0;
      +}
      
     - ## reftable/test_framework.h (new) ##
     + ## reftable/blocksource.h (new) ##
      @@
      +/*
      +Copyright 2020 Google LLC
     @@ reftable/test_framework.h (new)
      +https://developers.google.com/open-source/licenses/bsd
      +*/
      +
     -+#ifndef TEST_FRAMEWORK_H
     -+#define TEST_FRAMEWORK_H
     ++#ifndef BLOCKSOURCE_H
     ++#define BLOCKSOURCE_H
      +
      +#include "system.h"
      +
     -+#ifdef NDEBUG
     -+#undef NDEBUG
     -+#endif
     ++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);
      +
     -+#ifdef assert
     -+#undef assert
      +#endif
     +
     + ## reftable/reftable-blocksource.h (new) ##
     +@@
     ++/*
     ++Copyright 2020 Google LLC
      +
     -+#define assert_err(c)                                                  \
     -+	if (c != 0) {                                                  \
     -+		fflush(stderr);                                        \
     -+		fflush(stdout);                                        \
     -+		fprintf(stderr, "%s: %d: error == %d (%s), want 0\n",  \
     -+			__FILE__, __LINE__, c, reftable_error_str(c)); \
     -+		abort();                                               \
     -+	}
     ++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
     ++*/
      +
     -+#define assert_streq(a, b)                                               \
     -+	if (strcmp(a, b)) {                                              \
     -+		fflush(stderr);                                          \
     -+		fflush(stdout);                                          \
     -+		fprintf(stderr, "%s:%d: %s (%s) != %s (%s)\n", __FILE__, \
     -+			__LINE__, #a, a, #b, b);                         \
     -+		abort();                                                 \
     -+	}
     ++#ifndef REFTABLE_BLOCKSOURCE_H
     ++#define REFTABLE_BLOCKSOURCE_H
      +
     -+#define assert(c)                                                          \
     -+	if (!(c)) {                                                        \
     -+		fflush(stderr);                                            \
     -+		fflush(stdout);                                            \
     -+		fprintf(stderr, "%s: %d: failed assertion %s\n", __FILE__, \
     -+			__LINE__, #c);                                     \
     -+		abort();                                                   \
     -+	}
     ++#include <stdint.h>
     ++
     ++/* block_source is a generic wrapper for a seekable readable file.
     ++ */
     ++struct reftable_block_source {
     ++	struct reftable_block_source_vtable *ops;
     ++	void *arg;
     ++};
      +
     -+struct test_case {
     -+	const char *name;
     -+	void (*testfunc)(void);
     ++/* a contiguous segment of bytes. It keeps track of its generating block_source
     ++   so it can return itself into the pool.
     ++*/
     ++struct reftable_block {
     ++	uint8_t *data;
     ++	int len;
     ++	struct reftable_block_source source;
      +};
      +
     -+struct test_case *add_test_case(const char *name, void (*testfunc)(void));
     -+int test_main(int argc, const char *argv[]);
     ++/* block_source_vtable are the operations that make up block_source */
     ++struct reftable_block_source_vtable {
     ++	/* returns the size of a block source */
     ++	uint64_t (*size)(void *source);
     ++
     ++	/* reads a segment from the block source. It is an error to read
     ++	   beyond the end of the block */
     ++	int (*read_block)(void *source, struct reftable_block *dest,
     ++			  uint64_t off, uint32_t size);
     ++	/* mark the block as read; may return the data back to malloc */
     ++	void (*return_block)(void *source, struct reftable_block *blockp);
     ++
     ++	/* release all resources associated with the block source */
     ++	void (*close)(void *source);
     ++};
      +
     -+void set_test_hash(uint8_t *p, int i);
     ++/* opens a file on the file system as a block_source */
     ++int reftable_block_source_from_file(struct reftable_block_source *block_src,
     ++				    const char *name);
      +
      +#endif
  6:  8eb944ea9b !  7:  bbe2ed7173 reftable: (de)serialization for the polymorphic record type.
     @@ Metadata
       ## Commit message ##
          reftable: (de)serialization for the polymorphic record type.
      
     +    The reftable format is structured as a sequence of blocks, and each block
     +    contains a sequence of prefix-compressed key-value records. There are 4 types of
     +    records, and they have similarities in how they must be handled. This is
     +    achieved by introducing a polymorphic 'record' type that encapsulates ref, log,
     +    index and object records.
     +
          Signed-off-by: Han-Wen Nienhuys <hanwen@google.com>
      
       ## Makefile ##
      @@ Makefile: REFTABLE_OBJS += reftable/basics.o
     + REFTABLE_OBJS += reftable/error.o
       REFTABLE_OBJS += reftable/blocksource.o
       REFTABLE_OBJS += reftable/publicbasics.o
     - REFTABLE_OBJS += reftable/compat.o
      +REFTABLE_OBJS += reftable/record.o
     - REFTABLE_OBJS += reftable/strbuf.o
       
      +REFTABLE_TEST_OBJS += reftable/record_test.o
     - REFTABLE_TEST_OBJS += reftable/strbuf_test.o
       REFTABLE_TEST_OBJS += reftable/test_framework.o
     + REFTABLE_TEST_OBJS += reftable/basics_test.o
       
      
       ## reftable/constants.h (new) ##
     @@ reftable/record.c (new)
      +
      +#include "system.h"
      +#include "constants.h"
     -+#include "reftable.h"
     ++#include "reftable-error.h"
      +#include "basics.h"
      +
      +int get_var_int(uint64_t *dest, struct string_view *in)
     @@ reftable/record.c (new)
      +
      +	/* This is simple and correct, but we could probably reuse the hash
      +	   fields. */
     -+	reftable_ref_record_clear(ref);
     ++	reftable_ref_record_release(ref);
      +	if (src->refname != NULL) {
      +		ref->refname = xstrdup(src->refname);
      +	}
     @@ reftable/record.c (new)
      +	printf("}\n");
      +}
      +
     -+static void reftable_ref_record_clear_void(void *rec)
     ++static void reftable_ref_record_release_void(void *rec)
      +{
     -+	reftable_ref_record_clear((struct reftable_ref_record *)rec);
     ++	reftable_ref_record_release((struct reftable_ref_record *)rec);
      +}
      +
     -+void reftable_ref_record_clear(struct reftable_ref_record *ref)
     ++void reftable_ref_record_release(struct reftable_ref_record *ref)
      +{
      +	reftable_free(ref->refname);
      +	reftable_free(ref->target);
     @@ reftable/record.c (new)
      +	.val_type = &reftable_ref_record_val_type,
      +	.encode = &reftable_ref_record_encode,
      +	.decode = &reftable_ref_record_decode,
     -+	.clear = &reftable_ref_record_clear_void,
     ++	.release = &reftable_ref_record_release_void,
      +	.is_deletion = &reftable_ref_record_is_deletion_void,
      +};
      +
     @@ reftable/record.c (new)
      +	strbuf_add(dest, rec->hash_prefix, rec->hash_prefix_len);
      +}
      +
     -+static void reftable_obj_record_clear(void *rec)
     ++static void reftable_obj_record_release(void *rec)
      +{
      +	struct reftable_obj_record *obj = (struct reftable_obj_record *)rec;
      +	FREE_AND_NULL(obj->hash_prefix);
     @@ reftable/record.c (new)
      +		(const struct reftable_obj_record *)src_rec;
      +	int olen;
      +
     -+	reftable_obj_record_clear(obj);
     ++	reftable_obj_record_release(obj);
      +	*obj = *src;
      +	obj->hash_prefix = reftable_malloc(obj->hash_prefix_len);
      +	memcpy(obj->hash_prefix, src->hash_prefix, obj->hash_prefix_len);
     @@ reftable/record.c (new)
      +	.val_type = &reftable_obj_record_val_type,
      +	.encode = &reftable_obj_record_encode,
      +	.decode = &reftable_obj_record_decode,
     -+	.clear = &reftable_obj_record_clear,
     ++	.release = &reftable_obj_record_release,
      +	.is_deletion = not_a_deletion,
      +};
      +
     @@ reftable/record.c (new)
      +	const struct reftable_log_record *src =
      +		(const struct reftable_log_record *)src_rec;
      +
     -+	reftable_log_record_clear(dst);
     ++	reftable_log_record_release(dst);
      +	*dst = *src;
      +	if (dst->refname != NULL) {
      +		dst->refname = xstrdup(dst->refname);
     @@ reftable/record.c (new)
      +	}
      +}
      +
     -+static void reftable_log_record_clear_void(void *rec)
     ++static void reftable_log_record_release_void(void *rec)
      +{
      +	struct reftable_log_record *r = (struct reftable_log_record *)rec;
     -+	reftable_log_record_clear(r);
     ++	reftable_log_record_release(r);
      +}
      +
     -+void reftable_log_record_clear(struct reftable_log_record *r)
     ++void reftable_log_record_release(struct reftable_log_record *r)
      +{
      +	reftable_free(r->refname);
      +	reftable_free(r->new_hash);
     @@ reftable/record.c (new)
      +	.val_type = &reftable_log_record_val_type,
      +	.encode = &reftable_log_record_encode,
      +	.decode = &reftable_log_record_decode,
     -+	.clear = &reftable_log_record_clear_void,
     ++	.release = &reftable_log_record_release_void,
      +	.is_deletion = &reftable_log_record_is_deletion_void,
      +};
      +
     @@ reftable/record.c (new)
      +
      +void reftable_record_destroy(struct reftable_record *rec)
      +{
     -+	reftable_record_clear(rec);
     ++	reftable_record_release(rec);
      +	reftable_free(reftable_record_yield(rec));
      +}
      +
     @@ reftable/record.c (new)
      +	dst->offset = src->offset;
      +}
      +
     -+static void reftable_index_record_clear(void *rec)
     ++static void reftable_index_record_release(void *rec)
      +{
      +	struct reftable_index_record *idx = (struct reftable_index_record *)rec;
      +	strbuf_release(&idx->last_key);
     @@ reftable/record.c (new)
      +	.val_type = &reftable_index_record_val_type,
      +	.encode = &reftable_index_record_encode,
      +	.decode = &reftable_index_record_decode,
     -+	.clear = &reftable_index_record_clear,
     ++	.release = &reftable_index_record_release,
      +	.is_deletion = &not_a_deletion,
      +};
      +
     @@ reftable/record.c (new)
      +	return rec->ops->decode(rec->data, key, extra, src, hash_size);
      +}
      +
     -+void reftable_record_clear(struct reftable_record *rec)
     ++void reftable_record_release(struct reftable_record *rec)
      +{
     -+	rec->ops->clear(rec->data);
     ++	rec->ops->release(rec->data);
      +}
      +
      +int reftable_record_is_deletion(struct reftable_record *rec)
     @@ reftable/record.h (new)
      +#ifndef RECORD_H
      +#define RECORD_H
      +
     -+#include "reftable.h"
     -+#include "strbuf.h"
      +#include "system.h"
      +
     ++#include <stdint.h>
     ++
     ++#include "reftable-record.h"
     ++
      +/*
      +  A substring of existing string data. This structure takes no responsibility
      +  for the lifetime of the data it points to.
     @@ reftable/record.h (new)
      +		      struct string_view src, int hash_size);
      +
      +	/* deallocate and null the record. */
     -+	void (*clear)(void *rec);
     ++	void (*release)(void *rec);
      +
      +	/* is this a tombstone? */
      +	int (*is_deletion)(const void *rec);
     @@ reftable/record.h (new)
      +int reftable_record_is_deletion(struct reftable_record *rec);
      +
      +/* zeroes out the embedded record */
     -+void reftable_record_clear(struct reftable_record *rec);
     ++void reftable_record_release(struct reftable_record *rec);
      +
      +/* clear and deallocate embedded record, and zero `rec`. */
      +void reftable_record_destroy(struct reftable_record *rec);
     @@ reftable/record_test.c (new)
      +#include "system.h"
      +#include "basics.h"
      +#include "constants.h"
     -+#include "reftable.h"
      +#include "test_framework.h"
      +#include "reftable-tests.h"
      +
     @@ reftable/record_test.c (new)
      +	reftable_record_copy_from(&copy, rec, SHA1_SIZE);
      +	switch (reftable_record_type(&copy)) {
      +	case BLOCK_TYPE_REF:
     -+		assert(reftable_ref_record_equal(reftable_record_as_ref(&copy),
     ++		EXPECT(reftable_ref_record_equal(reftable_record_as_ref(&copy),
      +						 reftable_record_as_ref(rec),
      +						 SHA1_SIZE));
      +		break;
      +	case BLOCK_TYPE_LOG:
     -+		assert(reftable_log_record_equal(reftable_record_as_log(&copy),
     ++		EXPECT(reftable_log_record_equal(reftable_record_as_log(&copy),
      +						 reftable_record_as_log(rec),
      +						 SHA1_SIZE));
      +		break;
     @@ reftable/record_test.c (new)
      +		int n = put_var_int(&out, in);
      +		uint64_t got = 0;
      +
     -+		assert(n > 0);
     ++		EXPECT(n > 0);
      +		out.len = n;
      +		n = get_var_int(&got, &out);
     -+		assert(n > 0);
     ++		EXPECT(n > 0);
      +
     -+		assert(got == in);
     ++		EXPECT(got == in);
      +	}
      +}
      +
     @@ reftable/record_test.c (new)
      +		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);
     ++		EXPECT(common_prefix_size(&a, &b) == cases[i].want);
      +
      +		strbuf_release(&a);
      +		strbuf_release(&b);
     @@ reftable/record_test.c (new)
      +		reftable_record_from_ref(&rec, &in);
      +		test_copy(&rec);
      +
     -+		assert(reftable_record_val_type(&rec) == i);
     ++		EXPECT(reftable_record_val_type(&rec) == i);
      +
      +		reftable_record_key(&rec, &key);
      +		n = reftable_record_encode(&rec, dest, SHA1_SIZE);
     -+		assert(n > 0);
     ++		EXPECT(n > 0);
      +
      +		/* decode into a non-zero reftable_record to test for leaks. */
      +
      +		reftable_record_from_ref(&rec_out, &out);
      +		m = reftable_record_decode(&rec_out, key, i, dest, SHA1_SIZE);
     -+		assert(n == m);
     ++		EXPECT(n == m);
      +
     -+		assert((out.value != NULL) == (in.value != NULL));
     -+		assert((out.target_value != NULL) == (in.target_value != NULL));
     -+		assert((out.target != NULL) == (in.target != NULL));
     -+		reftable_record_clear(&rec_out);
     ++		EXPECT((out.value != NULL) == (in.value != NULL));
     ++		EXPECT((out.target_value != NULL) == (in.target_value != NULL));
     ++		EXPECT((out.target != NULL) == (in.target != NULL));
     ++		reftable_record_release(&rec_out);
      +
      +		strbuf_release(&key);
     -+		reftable_ref_record_clear(&in);
     ++		reftable_ref_record_release(&in);
      +	}
      +}
      +
     @@ reftable/record_test.c (new)
      +		}
      +	};
      +
     -+	assert(!reftable_log_record_equal(&in[0], &in[1], SHA1_SIZE));
     ++	EXPECT(!reftable_log_record_equal(&in[0], &in[1], SHA1_SIZE));
      +	in[1].update_index = in[0].update_index;
     -+	assert(reftable_log_record_equal(&in[0], &in[1], SHA1_SIZE));
     -+	reftable_log_record_clear(&in[0]);
     -+	reftable_log_record_clear(&in[1]);
     ++	EXPECT(reftable_log_record_equal(&in[0], &in[1], SHA1_SIZE));
     ++	reftable_log_record_release(&in[0]);
     ++	reftable_log_record_release(&in[1]);
      +}
      +
      +static void test_reftable_log_record_roundtrip(void)
     @@ reftable/record_test.c (new)
      +		reftable_record_key(&rec, &key);
      +
      +		n = reftable_record_encode(&rec, dest, SHA1_SIZE);
     -+		assert(n >= 0);
     ++		EXPECT(n >= 0);
      +		reftable_record_from_log(&rec_out, &out);
      +		valtype = reftable_record_val_type(&rec);
      +		m = reftable_record_decode(&rec_out, key, valtype, dest,
      +					   SHA1_SIZE);
     -+		assert(n == m);
     ++		EXPECT(n == m);
      +
     -+		assert(reftable_log_record_equal(&in[i], &out, SHA1_SIZE));
     -+		reftable_log_record_clear(&in[i]);
     ++		EXPECT(reftable_log_record_equal(&in[i], &out, SHA1_SIZE));
     ++		reftable_log_record_release(&in[i]);
      +		strbuf_release(&key);
     -+		reftable_record_clear(&rec_out);
     ++		reftable_record_release(&rec_out);
      +	}
      +}
      +
     @@ reftable/record_test.c (new)
      +	uint32_t out;
      +	put_be24(dest, in);
      +	out = get_be24(dest);
     -+	assert(in == out);
     ++	EXPECT(in == out);
      +}
      +
      +static void test_key_roundtrip(void)
     @@ reftable/record_test.c (new)
      +	strbuf_addstr(&key, "refs/tags/bla");
      +	extra = 6;
      +	n = reftable_encode_key(&restart, dest, last_key, key, extra);
     -+	assert(!restart);
     -+	assert(n > 0);
     ++	EXPECT(!restart);
     ++	EXPECT(n > 0);
      +
      +	m = reftable_decode_key(&roundtrip, &rt_extra, last_key, dest);
     -+	assert(n == m);
     -+	assert(0 == strbuf_cmp(&key, &roundtrip));
     -+	assert(rt_extra == extra);
     ++	EXPECT(n == m);
     ++	EXPECT(0 == strbuf_cmp(&key, &roundtrip));
     ++	EXPECT(rt_extra == extra);
      +
      +	strbuf_release(&last_key);
      +	strbuf_release(&key);
     @@ reftable/record_test.c (new)
      +		test_copy(&rec);
      +		reftable_record_key(&rec, &key);
      +		n = reftable_record_encode(&rec, dest, SHA1_SIZE);
     -+		assert(n > 0);
     ++		EXPECT(n > 0);
      +		extra = reftable_record_val_type(&rec);
      +		reftable_record_from_obj(&rec_out, &out);
      +		m = reftable_record_decode(&rec_out, key, extra, dest,
      +					   SHA1_SIZE);
     -+		assert(n == m);
     ++		EXPECT(n == m);
      +
     -+		assert(in.hash_prefix_len == out.hash_prefix_len);
     -+		assert(in.offset_len == out.offset_len);
     ++		EXPECT(in.hash_prefix_len == out.hash_prefix_len);
     ++		EXPECT(in.offset_len == out.offset_len);
      +
     -+		assert(!memcmp(in.hash_prefix, out.hash_prefix,
     ++		EXPECT(!memcmp(in.hash_prefix, out.hash_prefix,
      +			       in.hash_prefix_len));
     -+		assert(0 == memcmp(in.offsets, out.offsets,
     ++		EXPECT(0 == memcmp(in.offsets, out.offsets,
      +				   sizeof(uint64_t) * in.offset_len));
      +		strbuf_release(&key);
     -+		reftable_record_clear(&rec_out);
     ++		reftable_record_release(&rec_out);
      +	}
      +}
      +
     @@ reftable/record_test.c (new)
      +	reftable_record_key(&rec, &key);
      +	test_copy(&rec);
      +
     -+	assert(0 == strbuf_cmp(&key, &in.last_key));
     ++	EXPECT(0 == strbuf_cmp(&key, &in.last_key));
      +	n = reftable_record_encode(&rec, dest, SHA1_SIZE);
     -+	assert(n > 0);
     ++	EXPECT(n > 0);
      +
      +	extra = reftable_record_val_type(&rec);
      +	reftable_record_from_index(&out_rec, &out);
      +	m = reftable_record_decode(&out_rec, key, extra, dest, SHA1_SIZE);
     -+	assert(m == n);
     ++	EXPECT(m == n);
      +
     -+	assert(in.offset == out.offset);
     ++	EXPECT(in.offset == out.offset);
      +
     -+	reftable_record_clear(&out_rec);
     ++	reftable_record_release(&out_rec);
      +	strbuf_release(&key);
      +	strbuf_release(&in.last_key);
      +}
      +
      +int record_test_main(int argc, const char *argv[])
      +{
     -+	add_test_case("test_reftable_log_record_equal",
     -+		      &test_reftable_log_record_equal);
     -+	add_test_case("test_reftable_log_record_roundtrip",
     -+		      &test_reftable_log_record_roundtrip);
     -+	add_test_case("test_reftable_ref_record_roundtrip",
     -+		      &test_reftable_ref_record_roundtrip);
     -+	add_test_case("test_varint_roundtrip", &test_varint_roundtrip);
     -+	add_test_case("test_key_roundtrip", &test_key_roundtrip);
     -+	add_test_case("test_common_prefix", &test_common_prefix);
     -+	add_test_case("test_reftable_obj_record_roundtrip",
     -+		      &test_reftable_obj_record_roundtrip);
     -+	add_test_case("test_reftable_index_record_roundtrip",
     -+		      &test_reftable_index_record_roundtrip);
     -+	add_test_case("test_u24_roundtrip", &test_u24_roundtrip);
     -+	return test_main(argc, argv);
     ++	test_reftable_log_record_equal();
     ++	test_reftable_log_record_roundtrip();
     ++	test_reftable_ref_record_roundtrip();
     ++	test_varint_roundtrip();
     ++	test_key_roundtrip();
     ++	test_common_prefix();
     ++	test_reftable_obj_record_roundtrip();
     ++	test_reftable_index_record_roundtrip();
     ++	test_u24_roundtrip();
     ++	return 0;
      +}
      
     + ## reftable/reftable-record.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 REFTABLE_RECORD_H
     ++#define REFTABLE_RECORD_H
     ++
     ++#include <stdint.h>
     ++
     ++/*
     ++ Basic data types
     ++
     ++ Reftables store the state of each ref in struct reftable_ref_record, and they
     ++ store a sequence of reflog updates in struct reftable_log_record.
     ++*/
     ++
     ++/* reftable_ref_record holds a ref database entry target_value */
     ++struct reftable_ref_record {
     ++	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. */
     ++	uint8_t *target_value; /* peeled annotated tag, or NULL. malloced. */
     ++	char *target; /* symref, or NULL. malloced. */
     ++};
     ++
     ++/* returns whether 'ref' represents a deletion */
     ++int reftable_ref_record_is_deletion(const struct reftable_ref_record *ref);
     ++
     ++/* prints a reftable_ref_record onto stdout */
     ++void reftable_ref_record_print(struct reftable_ref_record *ref,
     ++			       uint32_t hash_id);
     ++
     ++/* frees and nulls all pointer values. */
     ++void reftable_ref_record_release(struct reftable_ref_record *ref);
     ++
     ++/* returns whether two reftable_ref_records are the same */
     ++int reftable_ref_record_equal(struct reftable_ref_record *a,
     ++			      struct reftable_ref_record *b, int hash_size);
     ++
     ++/* reftable_log_record holds a reflog entry */
     ++struct reftable_log_record {
     ++	char *refname;
     ++	uint64_t update_index; /* logical timestamp of a transactional update.
     ++				*/
     ++	uint8_t *new_hash;
     ++	uint8_t *old_hash;
     ++	char *name;
     ++	char *email;
     ++	uint64_t time;
     ++	int16_t tz_offset;
     ++	char *message;
     ++};
     ++
     ++/* returns whether 'ref' represents the deletion of a log record. */
     ++int reftable_log_record_is_deletion(const struct reftable_log_record *log);
     ++
     ++/* frees and nulls all pointer values. */
     ++void reftable_log_record_release(struct reftable_log_record *log);
     ++
     ++/* returns whether two records are equal. Useful for testing. */
     ++int reftable_log_record_equal(struct reftable_log_record *a,
     ++			      struct reftable_log_record *b, int hash_size);
     ++
     ++/* dumps a reftable_log_record on stdout, for debugging/testing. */
     ++void reftable_log_record_print(struct reftable_log_record *log,
     ++			       uint32_t hash_id);
     ++
     ++#endif
     +
       ## t/helper/test-reftable.c ##
      @@
     - 
       int cmd__reftable(int argc, const char **argv)
       {
     + 	basics_test_main(argc, argv);
     +-
      +	record_test_main(argc, argv);
     - 	strbuf_test_main(argc, argv);
       	return 0;
       }
  7:  757dd30fe2 !  8:  a358b052d5 reftable: reading/writing blocks
     @@ Metadata
       ## Commit message ##
          reftable: reading/writing blocks
      
     +    The reftable format is structured as a sequence of block. Within a block,
     +    records are prefix compressed, with an index of offsets for fully expand keys to
     +    enable binary search within blocks.
     +
     +    This commit provides the logic to read and write these blocks.
     +
          Includes a code snippet copied from zlib
      
          Signed-off-by: Han-Wen Nienhuys <hanwen@google.com>
      
       ## Makefile ##
     -@@ Makefile: XDIFF_OBJS += xdiff/xprepare.o
     - XDIFF_OBJS += xdiff/xutils.o
     +@@ Makefile: XDIFF_OBJS += xdiff/xutils.o
       
       REFTABLE_OBJS += reftable/basics.o
     + REFTABLE_OBJS += reftable/error.o
      +REFTABLE_OBJS += reftable/block.o
       REFTABLE_OBJS += reftable/blocksource.o
       REFTABLE_OBJS += reftable/publicbasics.o
     - REFTABLE_OBJS += reftable/compat.o
       REFTABLE_OBJS += reftable/record.o
     - REFTABLE_OBJS += reftable/strbuf.o
      +REFTABLE_OBJS += reftable/zlib-compat.o
       
      +REFTABLE_TEST_OBJS += reftable/block_test.o
       REFTABLE_TEST_OBJS += reftable/record_test.o
     - REFTABLE_TEST_OBJS += reftable/strbuf_test.o
       REFTABLE_TEST_OBJS += reftable/test_framework.o
     + REFTABLE_TEST_OBJS += reftable/basics_test.o
      
       ## reftable/.gitattributes (new) ##
      @@
     @@ reftable/block.c (new)
      +#include "blocksource.h"
      +#include "constants.h"
      +#include "record.h"
     -+#include "reftable.h"
     ++#include "reftable-error.h"
      +#include "system.h"
      +#include "zlib.h"
      +
     @@ reftable/block.c (new)
      +	return -1;
      +}
      +
     -+
      +int block_writer_finish(struct block_writer *w)
      +{
      +	int i = 0;
     @@ reftable/block.c (new)
      +	return err;
      +}
      +
     -+void block_writer_clear(struct block_writer *bw)
     ++void block_writer_release(struct block_writer *bw)
      +{
      +	FREE_AND_NULL(bw->restarts);
      +	strbuf_release(&bw->last_key);
     @@ reftable/block.h (new)
      +
      +#include "basics.h"
      +#include "record.h"
     -+#include "reftable.h"
     ++#include "reftable-blocksource.h"
      +
      +/*
      +  Writes reftable blocks. The block_writer is reused across blocks to minimize
     @@ reftable/block.h (new)
      +int block_writer_finish(struct block_writer *w);
      +
      +/* clears out internally allocated block_writer members. */
     -+void block_writer_clear(struct block_writer *bw);
     ++void block_writer_release(struct block_writer *bw);
      +
      +/* Read a block. */
      +struct block_reader {
     @@ reftable/block_test.c (new)
      +#include "basics.h"
      +#include "constants.h"
      +#include "record.h"
     -+#include "reftable.h"
      +#include "test_framework.h"
      +#include "reftable-tests.h"
      +
     -+struct binsearch_args {
     -+	int key;
     -+	int *arr;
     -+};
     -+
     -+static int binsearch_func(size_t i, void *void_args)
     -+{
     -+	struct binsearch_args *args = (struct binsearch_args *)void_args;
     -+
     -+	return args->key < args->arr[i];
     -+}
     -+
     -+static void test_binsearch(void)
     -+{
     -+	int arr[] = { 2, 4, 6, 8, 10 };
     -+	size_t sz = ARRAY_SIZE(arr);
     -+	struct binsearch_args args = {
     -+		.arr = arr,
     -+	};
     -+
     -+	int i = 0;
     -+	for (i = 1; i < 11; i++) {
     -+		int res;
     -+		args.key = i;
     -+		res = binsearch(sz, &binsearch_func, &args);
     -+
     -+		if (res < sz) {
     -+			assert(args.key < arr[res]);
     -+			if (res > 0) {
     -+				assert(args.key >= arr[res - 1]);
     -+			}
     -+		} else {
     -+			assert(args.key == 10 || args.key == 11);
     -+		}
     -+	}
     -+}
     -+
      +static void test_block_read_write(void)
      +{
      +	const int header_off = 21; /* random */
     @@ reftable/block_test.c (new)
      +		n = block_writer_add(&bw, &rec);
      +		ref.refname = NULL;
      +		ref.value = NULL;
     -+		assert(n == 0);
     ++		EXPECT(n == 0);
      +	}
      +
      +	n = block_writer_finish(&bw);
     -+	assert(n > 0);
     ++	EXPECT(n > 0);
      +
     -+	block_writer_clear(&bw);
     ++	block_writer_release(&bw);
      +
      +	block_reader_init(&br, &block, header_off, block_size, SHA1_SIZE);
      +
     @@ reftable/block_test.c (new)
      +
      +	while (1) {
      +		int r = block_iter_next(&it, &rec);
     -+		assert(r >= 0);
     ++		EXPECT(r >= 0);
      +		if (r > 0) {
      +			break;
      +		}
     -+		assert_streq(names[j], ref.refname);
     ++		EXPECT_STREQ(names[j], ref.refname);
      +		j++;
      +	}
      +
     -+	reftable_record_clear(&rec);
     ++	reftable_record_release(&rec);
      +	block_iter_close(&it);
      +
      +	for (i = 0; i < N; i++) {
     @@ reftable/block_test.c (new)
      +		strbuf_addstr(&want, names[i]);
      +
      +		n = block_reader_seek(&br, &it, &want);
     -+		assert(n == 0);
     ++		EXPECT(n == 0);
      +
      +		n = block_iter_next(&it, &rec);
     -+		assert(n == 0);
     ++		EXPECT(n == 0);
      +
     -+		assert_streq(names[i], ref.refname);
     ++		EXPECT_STREQ(names[i], ref.refname);
      +
      +		want.len--;
      +		n = block_reader_seek(&br, &it, &want);
     -+		assert(n == 0);
     ++		EXPECT(n == 0);
      +
      +		n = block_iter_next(&it, &rec);
     -+		assert(n == 0);
     -+		assert_streq(names[10 * (i / 10)], ref.refname);
     ++		EXPECT(n == 0);
     ++		EXPECT_STREQ(names[10 * (i / 10)], ref.refname);
      +
      +		block_iter_close(&it);
      +	}
      +
     -+	reftable_record_clear(&rec);
     ++	reftable_record_release(&rec);
      +	reftable_block_done(&br.block);
      +	strbuf_release(&want);
      +	for (i = 0; i < N; i++) {
     @@ reftable/block_test.c (new)
      +
      +int block_test_main(int argc, const char *argv[])
      +{
     -+	add_test_case("binsearch", &test_binsearch);
     -+	add_test_case("block_read_write", &test_block_read_write);
     -+	return test_main(argc, argv);
     ++	test_block_read_write();
     ++	return 0;
      +}
      
       ## reftable/zlib-compat.c (new) ##
     @@ reftable/zlib-compat.c (new)
      
       ## t/helper/test-reftable.c ##
      @@
     - 
       int cmd__reftable(int argc, const char **argv)
       {
     + 	basics_test_main(argc, argv);
      +	block_test_main(argc, argv);
       	record_test_main(argc, argv);
     - 	strbuf_test_main(argc, argv);
       	return 0;
     + }
  8:  e30a7e0281 !  9:  24afac9c91 reftable: a generic binary tree implementation
     @@ Metadata
       ## Commit message ##
          reftable: a generic binary tree implementation
      
     -    This is necessary for building a OID => ref map on write
     +    The reftable format includes support for an (OID => ref) map. This map can speed
     +    up visibility and reachability checks. In particular, various operations along
     +    the fetch/push path within Gerrit have ben sped up by using this structure.
     +
     +    The map is constructed with help of a binary tree. Object IDs are hashes, so
     +    they are uniformly distributed. Hence, the tree does not attempt forced
     +    rebalancing.
      
          Signed-off-by: Han-Wen Nienhuys <hanwen@google.com>
      
       ## Makefile ##
     -@@ Makefile: REFTABLE_OBJS += reftable/publicbasics.o
     - REFTABLE_OBJS += reftable/compat.o
     +@@ Makefile: REFTABLE_OBJS += reftable/block.o
     + REFTABLE_OBJS += reftable/blocksource.o
     + REFTABLE_OBJS += reftable/publicbasics.o
       REFTABLE_OBJS += reftable/record.o
     - REFTABLE_OBJS += reftable/strbuf.o
      +REFTABLE_OBJS += reftable/tree.o
       REFTABLE_OBJS += reftable/zlib-compat.o
       
     ++REFTABLE_TEST_OBJS += reftable/basics_test.o
       REFTABLE_TEST_OBJS += reftable/block_test.o
       REFTABLE_TEST_OBJS += reftable/record_test.o
     - REFTABLE_TEST_OBJS += reftable/strbuf_test.o
       REFTABLE_TEST_OBJS += reftable/test_framework.o
     +-REFTABLE_TEST_OBJS += reftable/basics_test.o
      +REFTABLE_TEST_OBJS += reftable/tree_test.o
       
       TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
     @@ reftable/tree_test.c (new)
      +
      +#include "basics.h"
      +#include "record.h"
     -+#include "reftable.h"
      +#include "test_framework.h"
      +#include "reftable-tests.h"
      +
     @@ reftable/tree_test.c (new)
      +
      +int tree_test_main(int argc, const char *argv[])
      +{
     -+	add_test_case("test_tree", &test_tree);
     -+	return test_main(argc, argv);
     ++	test_tree();
     ++	return 0;
      +}
      
       ## t/helper/test-reftable.c ##
      @@ t/helper/test-reftable.c: int cmd__reftable(int argc, const char **argv)
     + 	basics_test_main(argc, argv);
       	block_test_main(argc, argv);
       	record_test_main(argc, argv);
     - 	strbuf_test_main(argc, argv);
      +	tree_test_main(argc, argv);
       	return 0;
       }
  9:  68aee16e60 ! 10:  5f0b7c5592 reftable: write reftable files
     @@ Commit message
          Signed-off-by: Han-Wen Nienhuys <hanwen@google.com>
      
       ## Makefile ##
     -@@ Makefile: REFTABLE_OBJS += reftable/compat.o
     +@@ Makefile: REFTABLE_OBJS += reftable/blocksource.o
     + REFTABLE_OBJS += reftable/publicbasics.o
       REFTABLE_OBJS += reftable/record.o
     - REFTABLE_OBJS += reftable/strbuf.o
       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/basics_test.o
     +
     + ## reftable/reftable-writer.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 REFTABLE_WRITER_H
     ++#define REFTABLE_WRITER_H
     ++
     ++#include <stdint.h>
     ++
     ++#include "reftable-record.h"
     ++
     ++/*
     ++ Writing single reftables
     ++*/
     ++
     ++/* reftable_write_options sets options for writing a single reftable. */
     ++struct reftable_write_options {
     ++	/* boolean: do not pad out blocks to block size. */
     ++	unsigned unpadded : 1;
     ++
     ++	/* the blocksize. Should be less than 2^24. */
     ++	uint32_t block_size;
     ++
     ++	/* boolean: do not generate a SHA1 => ref index. */
     ++	unsigned skip_index_objects : 1;
     ++
     ++	/* how often to write complete keys in each block. */
     ++	int restart_interval;
     ++
     ++	/* 4-byte identifier ("sha1", "s256") of the hash.
     ++	 * Defaults to SHA1 if unset
     ++	 */
     ++	uint32_t hash_id;
     ++
     ++	/* boolean: do not check ref names for validity or dir/file conflicts.
     ++	 */
     ++	unsigned skip_name_check : 1;
     ++
     ++	/* boolean: copy log messages exactly. If unset, check that the message
     ++	 *   is a single line, and add '\n' if missing.
     ++	 */
     ++	unsigned exact_log_message : 1;
     ++};
     ++
     ++/* reftable_block_stats holds statistics for a single block type */
     ++struct reftable_block_stats {
     ++	/* total number of entries written */
     ++	int entries;
     ++	/* total number of key restarts */
     ++	int restarts;
     ++	/* total number of blocks */
     ++	int blocks;
     ++	/* total number of index blocks */
     ++	int index_blocks;
     ++	/* depth of the index */
     ++	int max_index_level;
     ++
     ++	/* offset of the first block for this type */
     ++	uint64_t offset;
     ++	/* offset of the top level index block for this type, or 0 if not
     ++	 * present */
     ++	uint64_t index_offset;
     ++};
     ++
     ++/* stats holds overall statistics for a single reftable */
     ++struct reftable_stats {
     ++	/* total number of blocks written. */
     ++	int blocks;
     ++	/* stats for ref data */
     ++	struct reftable_block_stats ref_stats;
     ++	/* stats for the SHA1 to ref map. */
     ++	struct reftable_block_stats obj_stats;
     ++	/* stats for index blocks */
     ++	struct reftable_block_stats idx_stats;
     ++	/* stats for log blocks */
     ++	struct reftable_block_stats log_stats;
     ++
     ++	/* disambiguation length of shortened object IDs. */
     ++	int object_id_len;
     ++};
     ++
     ++/* reftable_new_writer creates a new writer */
     ++struct reftable_writer *
     ++reftable_new_writer(int (*writer_func)(void *, const void *, size_t),
     ++		    void *writer_arg, struct reftable_write_options *opts);
     ++
     ++/* 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_stack_next_update_index(), or REFTABLE_API_ERROR is returned.
     ++
     ++   For transactional updates to a stack, typically min==max, and the
     ++   update_index can be obtained by inspeciting the stack. When converting an
     ++   existing ref database into a single reftable, this would be a range of
     ++   update-index timestamps.
     ++ */
     ++void reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
     ++				uint64_t max);
     ++
     ++/*
     ++  Add a reftable_ref_record. The record should have names that come after
     ++  already added records.
     ++
     ++  The update_index must be within the limits set by
     ++  reftable_writer_set_limits(), or REFTABLE_API_ERROR is returned. It is an
     ++  REFTABLE_API_ERROR error to write a ref record after a log record.
     ++*/
     ++int reftable_writer_add_ref(struct reftable_writer *w,
     ++			    struct reftable_ref_record *ref);
     ++
     ++/*
     ++  Convenience function to add multiple reftable_ref_records; the function sorts
     ++  the records before adding them, reordering the records array passed in.
     ++*/
     ++int reftable_writer_add_refs(struct reftable_writer *w,
     ++			     struct reftable_ref_record *refs, int n);
     ++
     ++/*
     ++  adds reftable_log_records. Log records are keyed by (refname, decreasing
     ++  update_index). The key for the record added must come after the already added
     ++  log records.
     ++*/
     ++int reftable_writer_add_log(struct reftable_writer *w,
     ++			    struct reftable_log_record *log);
     ++
     ++/*
     ++  Convenience function to add multiple reftable_log_records; the function sorts
     ++  the records before adding them, reordering records array passed in.
     ++*/
     ++int reftable_writer_add_logs(struct reftable_writer *w,
     ++			     struct reftable_log_record *logs, int n);
     ++
     ++/* reftable_writer_close finalizes the reftable. The writer is retained so
     ++ * statistics can be inspected. */
     ++int reftable_writer_close(struct reftable_writer *w);
     ++
     ++/* writer_stats returns the statistics on the reftable being written.
     ++
     ++   This struct becomes invalid when the writer is freed.
     ++ */
     ++const struct reftable_stats *writer_stats(struct reftable_writer *w);
     ++
     ++/* reftable_writer_free deallocates memory for the writer */
     ++void reftable_writer_free(struct reftable_writer *w);
     ++
     ++#endif
      
       ## reftable/writer.c (new) ##
      @@
     @@ reftable/writer.c (new)
      +#include "block.h"
      +#include "constants.h"
      +#include "record.h"
     -+#include "reftable.h"
      +#include "tree.h"
     ++#include "reftable-error.h"
      +
      +/* finishes a block, and writes it to storage */
      +static int writer_flush_block(struct reftable_writer *w);
     @@ reftable/writer.c (new)
      +	w->block_writer->restart_interval = w->opts.restart_interval;
      +}
      +
     ++static struct strbuf reftable_empty_strbuf = STRBUF_INIT;
     ++
      +struct reftable_writer *
      +reftable_new_writer(int (*writer_func)(void *, const void *, size_t),
      +		    void *writer_arg, struct reftable_write_options *opts)
     @@ reftable/writer.c (new)
      +	int err = 0;
      +	int i = 0;
      +	QSORT(logs, n, reftable_log_record_compare_key);
     ++
      +	for (i = 0; err == 0 && i < n; i++) {
      +		err = reftable_writer_add_log(w, &logs[i]);
      +	}
     @@ reftable/writer.c (new)
      +
      +done:
      +	/* free up memory. */
     -+	block_writer_clear(&w->block_writer_data);
     ++	block_writer_release(&w->block_writer_data);
      +	writer_clear_index(w);
      +	strbuf_release(&w->last_key);
      +	return err;
     @@ reftable/writer.h (new)
      +
      +#include "basics.h"
      +#include "block.h"
     -+#include "reftable.h"
     -+#include "strbuf.h"
      +#include "tree.h"
     ++#include "reftable-writer.h"
      +
      +struct reftable_writer {
      +	int (*write)(void *, const void *, size_t);
 10:  c196de7f06 ! 11:  9aa2caade8 reftable: read reftable files
     @@ Metadata
       ## Commit message ##
          reftable: read reftable files
      
     +    This supports reading a single reftable file.
     +
     +    The commit introduces an abstract iterator type, which captures the usecases
     +    both of reading individual refs, and iterating over a segment of the ref
     +    namespace.
     +
          Signed-off-by: Han-Wen Nienhuys <hanwen@google.com>
      
       ## Makefile ##
     -@@ Makefile: REFTABLE_OBJS += reftable/block.o
     +@@ Makefile: REFTABLE_OBJS += reftable/basics.o
     + REFTABLE_OBJS += reftable/error.o
     + REFTABLE_OBJS += reftable/block.o
       REFTABLE_OBJS += reftable/blocksource.o
     - REFTABLE_OBJS += reftable/publicbasics.o
     - REFTABLE_OBJS += reftable/compat.o
      +REFTABLE_OBJS += reftable/iter.o
     + REFTABLE_OBJS += reftable/publicbasics.o
      +REFTABLE_OBJS += reftable/reader.o
       REFTABLE_OBJS += reftable/record.o
      +REFTABLE_OBJS += reftable/reftable.o
     - REFTABLE_OBJS += reftable/strbuf.o
       REFTABLE_OBJS += reftable/tree.o
       REFTABLE_OBJS += reftable/writer.o
     + REFTABLE_OBJS += reftable/zlib-compat.o
      
       ## reftable/iter.c (new) ##
      @@
     @@ reftable/iter.c (new)
      +#include "block.h"
      +#include "constants.h"
      +#include "reader.h"
     -+#include "reftable.h"
     ++#include "reftable-error.h"
      +
      +int iterator_is_null(struct reftable_iterator *it)
      +{
     @@ reftable/iter.c (new)
      +int reftable_iterator_next_ref(struct reftable_iterator *it,
      +			       struct reftable_ref_record *ref)
      +{
     -+	struct reftable_record rec = { 0 };
     ++	struct reftable_record rec = { NULL };
      +	reftable_record_from_ref(&rec, ref);
      +	return iterator_next(it, &rec);
      +}
     @@ reftable/iter.c (new)
      +int reftable_iterator_next_log(struct reftable_iterator *it,
      +			       struct reftable_log_record *log)
      +{
     -+	struct reftable_record rec = { 0 };
     ++	struct reftable_record rec = { NULL };
      +	reftable_record_from_log(&rec, log);
      +	return iterator_next(it, &rec);
      +}
     @@ reftable/iter.c (new)
      +		}
      +
      +		if (fri->double_check) {
     -+			struct reftable_iterator it = { 0 };
     ++			struct reftable_iterator it = { NULL };
      +
      +			err = reftable_table_seek_ref(&fri->tab, &it,
      +						      ref->refname);
     @@ reftable/iter.c (new)
      +		}
      +	}
      +
     -+	reftable_ref_record_clear(ref);
     ++	reftable_ref_record_release(ref);
      +	return err;
      +}
      +
     @@ reftable/iter.h (new)
      +#ifndef ITER_H
      +#define ITER_H
      +
     ++#include "system.h"
      +#include "block.h"
      +#include "record.h"
     -+#include "strbuf.h"
     ++
     ++#include "reftable-iterator.h"
     ++#include "reftable-generic.h"
      +
      +struct reftable_iterator_vtable {
      +	int (*next)(void *iter_arg, struct reftable_record *rec);
     @@ reftable/reader.c (new)
      +#include "constants.h"
      +#include "iter.h"
      +#include "record.h"
     -+#include "reftable.h"
     ++#include "reftable-error.h"
      +#include "tree.h"
      +
      +uint64_t block_source_size(struct reftable_block_source *source)
     @@ reftable/reader.c (new)
      +int init_reader(struct reftable_reader *r, struct reftable_block_source *source,
      +		const char *name)
      +{
     -+	struct reftable_block footer = { 0 };
     -+	struct reftable_block header = { 0 };
     ++	struct reftable_block footer = { NULL };
     ++	struct reftable_block header = { NULL };
      +	int err = 0;
      +
      +	memset(r, 0, sizeof(struct reftable_reader));
     @@ reftable/reader.c (new)
      +{
      +	int32_t guess_block_size = r->block_size ? r->block_size :
      +							 DEFAULT_BLOCK_SIZE;
     -+	struct reftable_block block = { 0 };
     ++	struct reftable_block block = { NULL };
      +	uint8_t block_typ = 0;
      +	int err = 0;
      +	uint32_t header_off = next_off ? 0 : header_size(r->version);
     @@ reftable/reader.c (new)
      +			       struct reftable_record *rec)
      +{
      +	struct reftable_index_record want_index = { .last_key = STRBUF_INIT };
     -+	struct reftable_record want_index_rec = { 0 };
     ++	struct reftable_record want_index_rec = { NULL };
      +	struct reftable_index_record index_result = { .last_key = STRBUF_INIT };
     -+	struct reftable_record index_result_rec = { 0 };
     ++	struct reftable_record index_result_rec = { NULL };
      +	struct table_iter index_iter = TABLE_ITER_INIT;
      +	struct table_iter next = TABLE_ITER_INIT;
      +	int err = 0;
     @@ reftable/reader.c (new)
      +done:
      +	block_iter_close(&next.bi);
      +	table_iter_close(&index_iter);
     -+	reftable_record_clear(&want_index_rec);
     -+	reftable_record_clear(&index_result_rec);
     ++	reftable_record_release(&want_index_rec);
     ++	reftable_record_release(&index_result_rec);
      +	return err;
      +}
      +
     @@ reftable/reader.c (new)
      +	struct reftable_ref_record ref = {
      +		.refname = (char *)name,
      +	};
     -+	struct reftable_record rec = { 0 };
     ++	struct reftable_record rec = { NULL };
      +	reftable_record_from_ref(&rec, &ref);
      +	return reader_seek(r, it, &rec);
      +}
     @@ reftable/reader.c (new)
      +		.refname = (char *)name,
      +		.update_index = update_index,
      +	};
     -+	struct reftable_record rec = { 0 };
     ++	struct reftable_record rec = { NULL };
      +	reftable_record_from_log(&rec, &log);
      +	return reader_seek(r, it, &rec);
      +}
     @@ reftable/reader.c (new)
      +		.hash_prefix = oid,
      +		.hash_prefix_len = r->object_id_len,
      +	};
     -+	struct reftable_record want_rec = { 0 };
     -+	struct reftable_iterator oit = { 0 };
     -+	struct reftable_obj_record got = { 0 };
     -+	struct reftable_record got_rec = { 0 };
     ++	struct reftable_record want_rec = { NULL };
     ++	struct reftable_iterator oit = { NULL };
     ++	struct reftable_obj_record got = { NULL };
     ++	struct reftable_record got_rec = { NULL };
      +	int err = 0;
      +	struct indexed_table_ref_iter *itr = NULL;
      +
     @@ reftable/reader.c (new)
      +
      +done:
      +	reftable_iterator_destroy(&oit);
     -+	reftable_record_clear(&got_rec);
     ++	reftable_record_release(&got_rec);
      +	return err;
      +}
      +
     @@ reftable/reader.h (new)
      +
      +#include "block.h"
      +#include "record.h"
     -+#include "reftable.h"
     ++#include "reftable-iterator.h"
     ++#include "reftable-reader.h"
      +
      +uint64_t block_source_size(struct reftable_block_source *source);
      +
     @@ reftable/reader.h (new)
      +	uint64_t (*max_update_index)(void *tab);
      +};
      +
     -+int reftable_table_seek_record(struct reftable_table *tab,
     -+			       struct reftable_iterator *it,
     -+			       struct reftable_record *rec);
     -+
      +#endif
      
     - ## reftable/reftable.c (new) ##
     + ## reftable/reftable-iterator.h (new) ##
      @@
      +/*
      +Copyright 2020 Google LLC
     @@ reftable/reftable.c (new)
      +https://developers.google.com/open-source/licenses/bsd
      +*/
      +
     -+#include "reftable.h"
     -+#include "record.h"
     -+#include "reader.h"
     ++#ifndef REFTABLE_ITERATOR_H
     ++#define REFTABLE_ITERATOR_H
      +
     -+static int reftable_reader_seek_void(void *tab, struct reftable_iterator *it,
     -+				     struct reftable_record *rec)
     -+{
     -+	return reader_seek((struct reftable_reader *)tab, it, rec);
     -+}
     ++#include "reftable-record.h"
      +
     -+static uint32_t reftable_reader_hash_id_void(void *tab)
     -+{
     -+	return reftable_reader_hash_id((struct reftable_reader *)tab);
     -+}
     ++/* iterator is the generic interface for walking over data stored in a
     ++   reftable.
     ++*/
     ++struct reftable_iterator {
     ++	struct reftable_iterator_vtable *ops;
     ++	void *iter_arg;
     ++};
      +
     -+static uint64_t reftable_reader_min_update_index_void(void *tab)
     -+{
     -+	return reftable_reader_min_update_index((struct reftable_reader *)tab);
     -+}
     ++/* reads the next reftable_ref_record. Returns < 0 for error, 0 for OK and > 0:
     ++   end of iteration.
     ++*/
     ++int reftable_iterator_next_ref(struct reftable_iterator *it,
     ++			       struct reftable_ref_record *ref);
      +
     -+static uint64_t reftable_reader_max_update_index_void(void *tab)
     -+{
     -+	return reftable_reader_max_update_index((struct reftable_reader *)tab);
     -+}
     ++/* reads the next reftable_log_record. Returns < 0 for error, 0 for OK and > 0:
     ++   end of iteration.
     ++*/
     ++int reftable_iterator_next_log(struct reftable_iterator *it,
     ++			       struct reftable_log_record *log);
      +
     -+static struct reftable_table_vtable reader_vtable = {
     -+	.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,
     -+};
     ++/* releases resources associated with an iterator. */
     ++void reftable_iterator_destroy(struct reftable_iterator *it);
      +
     -+int reftable_table_seek_ref(struct reftable_table *tab,
     -+			    struct reftable_iterator *it, const char *name)
     -+{
     -+	struct reftable_ref_record ref = {
     -+		.refname = (char *)name,
     -+	};
     -+	struct reftable_record rec = { 0 };
     -+	reftable_record_from_ref(&rec, &ref);
     -+	return tab->ops->seek_record(tab->table_arg, it, &rec);
     -+}
     ++#endif
     +
     + ## reftable/reftable-reader.h (new) ##
     +@@
     ++/*
     ++Copyright 2020 Google LLC
      +
     -+void reftable_table_from_reader(struct reftable_table *tab,
     -+				struct reftable_reader *reader)
     -+{
     -+	assert(tab->ops == NULL);
     -+	tab->ops = &reader_vtable;
     -+	tab->table_arg = reader;
     -+}
     ++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
     ++*/
      +
     -+int reftable_table_read_ref(struct reftable_table *tab, const char *name,
     -+			    struct reftable_ref_record *ref)
     -+{
     -+	struct reftable_iterator it = { 0 };
     -+	int err = reftable_table_seek_ref(tab, &it, name);
     -+	if (err)
     -+		goto done;
     ++#ifndef REFTABLE_READER_H
     ++#define REFTABLE_READER_H
      +
     -+	err = reftable_iterator_next_ref(&it, ref);
     -+	if (err)
     -+		goto done;
     ++#include "reftable-iterator.h"
     ++#include "reftable-blocksource.h"
      +
     -+	if (strcmp(ref->refname, name) ||
     -+	    reftable_ref_record_is_deletion(ref)) {
     -+		reftable_ref_record_clear(ref);
     -+		err = 1;
     -+		goto done;
     -+	}
     ++/*
     ++ Reading single tables
      +
     -+done:
     -+	reftable_iterator_destroy(&it);
     -+	return err;
     -+}
     ++ The follow routines are for reading single files. For an application-level
     ++ interface, skip ahead to struct reftable_merged_table and struct
     ++ reftable_stack.
     ++*/
      +
     -+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);
     -+}
     ++/* The reader struct is a handle to an open reftable file. */
     ++struct reftable_reader;
      +
     -+uint64_t reftable_table_max_update_index(struct reftable_table *tab)
     -+{
     -+	return tab->ops->max_update_index(tab->table_arg);
     -+}
     ++/* reftable_new_reader opens a reftable for reading. If successful, returns 0
     ++ * code and sets pp. The name is used for creating a stack. Typically, it is the
     ++ * basename of the file. The block source `src` is owned by the reader, and is
     ++ * closed on calling reftable_reader_destroy().
     ++ */
     ++int reftable_new_reader(struct reftable_reader **pp,
     ++			struct reftable_block_source *src, const char *name);
     ++
     ++/* reftable_reader_seek_ref returns an iterator where 'name' would be inserted
     ++   in the table.  To seek to the start of the table, use name = "".
     ++
     ++   example:
     ++
     ++   struct reftable_reader *r = NULL;
     ++   int err = reftable_new_reader(&r, &src, "filename");
     ++   if (err < 0) { ... }
     ++   struct reftable_iterator it  = {0};
     ++   err = reftable_reader_seek_ref(r, &it, "refs/heads/master");
     ++   if (err < 0) { ... }
     ++   struct reftable_ref_record ref  = {0};
     ++   while (1) {
     ++     err = reftable_iterator_next_ref(&it, &ref);
     ++     if (err > 0) {
     ++       break;
     ++     }
     ++     if (err < 0) {
     ++       ..error handling..
     ++     }
     ++     ..found..
     ++   }
     ++   reftable_iterator_destroy(&it);
     ++   reftable_ref_record_release(&ref);
     ++ */
     ++int reftable_reader_seek_ref(struct reftable_reader *r,
     ++			     struct reftable_iterator *it, const char *name);
      +
     -+uint64_t reftable_table_min_update_index(struct reftable_table *tab)
     -+{
     -+	return tab->ops->min_update_index(tab->table_arg);
     -+}
     ++/* returns the hash ID used in this table. */
     ++uint32_t reftable_reader_hash_id(struct reftable_reader *r);
      +
     -+uint32_t reftable_table_hash_id(struct reftable_table *tab)
     -+{
     -+	return tab->ops->hash_id(tab->table_arg);
     -+}
     ++/* seek to logs for the given name, older than update_index. To seek to the
     ++   start of the table, use name = "".
     ++ */
     ++int reftable_reader_seek_log_at(struct reftable_reader *r,
     ++				struct reftable_iterator *it, const char *name,
     ++				uint64_t update_index);
     ++
     ++/* seek to newest log entry for given name. */
     ++int reftable_reader_seek_log(struct reftable_reader *r,
     ++			     struct reftable_iterator *it, const char *name);
     ++
     ++/* closes and deallocates a reader. */
     ++void reftable_reader_free(struct reftable_reader *);
     ++
     ++/* return an iterator for the refs pointing to `oid`. */
     ++int reftable_reader_refs_for(struct reftable_reader *r,
     ++			     struct reftable_iterator *it, uint8_t *oid);
     ++
     ++/* return the max_update_index for a table */
     ++uint64_t reftable_reader_max_update_index(struct reftable_reader *r);
     ++
     ++/* return the min_update_index for a table */
     ++uint64_t reftable_reader_min_update_index(struct reftable_reader *r);
     ++
     ++#endif
 11:  bf6b929b86 ! 12:  581a4200d6 reftable: file level tests
     @@ Metadata
      Author: Han-Wen Nienhuys <hanwen@google.com>
      
       ## Commit message ##
     -    reftable: file level tests
     +    reftable: reftable file level tests
     +
     +    With support for reading and writing files in place, we can construct files (in
     +    memory) and attempt to read them back.
     +
     +    Because some sections of the format are optional (eg. indices, log entries), we
     +    have to exercise this code using multiple sizes of input data
      
          Signed-off-by: Han-Wen Nienhuys <hanwen@google.com>
      
       ## Makefile ##
      @@ Makefile: REFTABLE_OBJS += reftable/zlib-compat.o
     - 
     + REFTABLE_TEST_OBJS += reftable/basics_test.o
       REFTABLE_TEST_OBJS += reftable/block_test.o
       REFTABLE_TEST_OBJS += reftable/record_test.o
      +REFTABLE_TEST_OBJS += reftable/reftable_test.o
     - REFTABLE_TEST_OBJS += reftable/strbuf_test.o
       REFTABLE_TEST_OBJS += reftable/test_framework.o
       REFTABLE_TEST_OBJS += reftable/tree_test.o
     + 
      
       ## reftable/reftable_test.c (new) ##
      @@
     @@ reftable/reftable_test.c (new)
      +https://developers.google.com/open-source/licenses/bsd
      +*/
      +
     -+#include "reftable.h"
     -+
      +#include "system.h"
      +
      +#include "basics.h"
     @@ reftable/reftable_test.c (new)
      +#include "record.h"
      +#include "test_framework.h"
      +#include "reftable-tests.h"
     ++#include "reftable-stack.h"
      +
      +static const int update_index = 5;
      +
     @@ reftable/reftable_test.c (new)
      +	uint8_t in[] = "hello";
      +	strbuf_add(&buf, in, sizeof(in));
      +	block_source_from_strbuf(&source, &buf);
     -+	assert(block_source_size(&source) == 6);
     ++	EXPECT(block_source_size(&source) == 6);
      +	n = block_source_read_block(&source, &out, 0, sizeof(in));
     -+	assert(n == sizeof(in));
     -+	assert(!memcmp(in, out.data, n));
     ++	EXPECT(n == sizeof(in));
     ++	EXPECT(!memcmp(in, out.data, n));
      +	reftable_block_done(&out);
      +
      +	n = block_source_read_block(&source, &out, 1, 2);
     -+	assert(n == 2);
     -+	assert(!memcmp(out.data, "el", 2));
     ++	EXPECT(n == 2);
     ++	EXPECT(!memcmp(out.data, "el", 2));
      +
      +	reftable_block_done(&out);
      +	block_source_close(&source);
     @@ reftable/reftable_test.c (new)
      +		(*names)[i] = xstrdup(name);
      +
      +		n = reftable_writer_add_ref(w, &ref);
     -+		assert(n == 0);
     ++		EXPECT(n == 0);
      +	}
      +
      +	for (i = 0; i < N; i++) {
     @@ reftable/reftable_test.c (new)
      +		log.message = "message";
      +
      +		n = reftable_writer_add_log(w, &log);
     -+		assert(n == 0);
     ++		EXPECT(n == 0);
      +	}
      +
      +	n = reftable_writer_close(w);
     -+	assert(n == 0);
     ++	EXPECT(n == 0);
      +
      +	stats = writer_stats(w);
      +	for (i = 0; i < stats->ref_stats.blocks; i++) {
     @@ reftable/reftable_test.c (new)
      +		if (off == 0) {
      +			off = header_size((hash_id == SHA256_ID) ? 2 : 1);
      +		}
     -+		assert(buf->buf[off] == 'r');
     ++		EXPECT(buf->buf[off] == 'r');
      +	}
      +
     -+	assert(stats->log_stats.blocks > 0);
     ++	EXPECT(stats->log_stats.blocks > 0);
      +	reftable_writer_free(w);
      +}
      +
     @@ reftable/reftable_test.c (new)
      +	log.new_hash = hash2;
      +	reftable_writer_set_limits(w, update_index, update_index);
      +	err = reftable_writer_add_log(w, &log);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +	err = reftable_writer_close(w);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +	reftable_writer_free(w);
      +	strbuf_release(&buf);
      +}
     @@ reftable/reftable_test.c (new)
      +		ref.update_index = i;
      +
      +		err = reftable_writer_add_ref(w, &ref);
     -+		assert_err(err);
     ++		EXPECT_ERR(err);
      +	}
      +	for (i = 0; i < N; i++) {
      +		uint8_t hash1[SHA1_SIZE], hash2[SHA1_SIZE];
     @@ reftable/reftable_test.c (new)
      +		log.new_hash = hash2;
      +
      +		err = reftable_writer_add_log(w, &log);
     -+		assert_err(err);
     ++		EXPECT_ERR(err);
      +	}
      +
      +	n = reftable_writer_close(w);
     -+	assert(n == 0);
     ++	EXPECT(n == 0);
      +
      +	stats = writer_stats(w);
     -+	assert(stats->log_stats.blocks > 0);
     ++	EXPECT(stats->log_stats.blocks > 0);
      +	reftable_writer_free(w);
      +	w = NULL;
      +
      +	block_source_from_strbuf(&source, &buf);
      +
      +	err = init_reader(&rd, &source, "file.log");
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_reader_seek_ref(&rd, &it, names[N - 1]);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_iterator_next_ref(&it, &ref);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	/* end of iteration. */
      +	err = reftable_iterator_next_ref(&it, &ref);
     -+	assert(0 < err);
     ++	EXPECT(0 < err);
      +
      +	reftable_iterator_destroy(&it);
     -+	reftable_ref_record_clear(&ref);
     ++	reftable_ref_record_release(&ref);
      +
      +	err = reftable_reader_seek_log(&rd, &it, "");
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	i = 0;
      +	while (1) {
     @@ reftable/reftable_test.c (new)
      +			break;
      +		}
      +
     -+		assert_err(err);
     -+		assert_streq(names[i], log.refname);
     -+		assert(i == log.update_index);
     ++		EXPECT_ERR(err);
     ++		EXPECT_STREQ(names[i], log.refname);
     ++		EXPECT(i == log.update_index);
      +		i++;
     -+		reftable_log_record_clear(&log);
     ++		reftable_log_record_release(&log);
      +	}
      +
     -+	assert(i == N);
     ++	EXPECT(i == N);
      +	reftable_iterator_destroy(&it);
      +
      +	/* cleanup. */
     @@ reftable/reftable_test.c (new)
      +	block_source_from_strbuf(&source, &buf);
      +
      +	err = init_reader(&rd, &source, "file.ref");
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_reader_seek_ref(&rd, &it, "");
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	while (1) {
      +		struct reftable_ref_record ref = { NULL };
      +		int r = reftable_iterator_next_ref(&it, &ref);
     -+		assert(r >= 0);
     ++		EXPECT(r >= 0);
      +		if (r > 0) {
      +			break;
      +		}
     -+		assert(0 == strcmp(names[j], ref.refname));
     -+		assert(update_index == ref.update_index);
     ++		EXPECT(0 == strcmp(names[j], ref.refname));
     ++		EXPECT(update_index == ref.update_index);
      +
      +		j++;
     -+		reftable_ref_record_clear(&ref);
     ++		reftable_ref_record_release(&ref);
      +	}
     -+	assert(j == N);
     ++	EXPECT(j == N);
      +	reftable_iterator_destroy(&it);
      +	strbuf_release(&buf);
      +	free_names(names);
     @@ reftable/reftable_test.c (new)
      +	struct strbuf buf = STRBUF_INIT;
      +	int N = 1;
      +	write_table(&names, &buf, N, 4096, SHA1_ID);
     -+	assert(buf.len < 200);
     ++	EXPECT(buf.len < 200);
      +	strbuf_release(&buf);
      +	free_names(names);
      +}
     @@ reftable/reftable_test.c (new)
      +	block_source_from_strbuf(&source, &buf);
      +
      +	err = init_reader(&rd, &source, "file.ref");
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_reader_seek_ref(&rd, &it, names[0]);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_iterator_next_log(&it, &log);
     -+	assert(err == REFTABLE_API_ERROR);
     ++	EXPECT(err == REFTABLE_API_ERROR);
      +
      +	strbuf_release(&buf);
      +	for (i = 0; i < N; i++) {
     @@ reftable/reftable_test.c (new)
      +	block_source_from_strbuf(&source, &buf);
      +
      +	err = init_reader(&rd, &source, "file.ref");
     -+	assert_err(err);
     -+	assert(hash_id == reftable_reader_hash_id(&rd));
     ++	EXPECT_ERR(err);
     ++	EXPECT(hash_id == reftable_reader_hash_id(&rd));
      +
      +	if (!index) {
      +		rd.ref_offsets.index_offset = 0;
      +	} else {
     -+		assert(rd.ref_offsets.index_offset > 0);
     ++		EXPECT(rd.ref_offsets.index_offset > 0);
      +	}
      +
      +	for (i = 1; i < N; i++) {
      +		int err = reftable_reader_seek_ref(&rd, &it, names[i]);
     -+		assert_err(err);
     ++		EXPECT_ERR(err);
      +		err = reftable_iterator_next_ref(&it, &ref);
     -+		assert_err(err);
     -+		assert(0 == strcmp(names[i], ref.refname));
     -+		assert(i == ref.value[0]);
     ++		EXPECT_ERR(err);
     ++		EXPECT(0 == strcmp(names[i], ref.refname));
     ++		EXPECT(i == ref.value[0]);
      +
     -+		reftable_ref_record_clear(&ref);
     ++		reftable_ref_record_release(&ref);
      +		reftable_iterator_destroy(&it);
      +	}
      +
     @@ reftable/reftable_test.c (new)
      +	if (err == 0) {
      +		struct reftable_ref_record ref = { NULL };
      +		int err = reftable_iterator_next_ref(&it, &ref);
     -+		assert(err > 0);
     ++		EXPECT(err > 0);
      +	} else {
     -+		assert(err > 0);
     ++		EXPECT(err > 0);
      +	}
      +
      +	strbuf_release(&pastLast);
     @@ reftable/reftable_test.c (new)
      +		 */
      +		/* blocks. */
      +		n = reftable_writer_add_ref(w, &ref);
     -+		assert(n == 0);
     ++		EXPECT(n == 0);
      +
      +		if (!memcmp(hash1, want_hash, SHA1_SIZE) ||
      +		    !memcmp(hash2, want_hash, SHA1_SIZE)) {
     @@ reftable/reftable_test.c (new)
      +	}
      +
      +	n = reftable_writer_close(w);
     -+	assert(n == 0);
     ++	EXPECT(n == 0);
      +
      +	reftable_writer_free(w);
      +	w = NULL;
     @@ reftable/reftable_test.c (new)
      +	block_source_from_strbuf(&source, &buf);
      +
      +	err = init_reader(&rd, &source, "file.ref");
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +	if (!indexed) {
      +		rd.obj_offsets.is_present = 0;
      +	}
      +
      +	err = reftable_reader_seek_ref(&rd, &it, "");
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +	reftable_iterator_destroy(&it);
      +
      +	err = reftable_reader_refs_for(&rd, &it, want_hash);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	j = 0;
      +	while (1) {
      +		int err = reftable_iterator_next_ref(&it, &ref);
     -+		assert(err >= 0);
     ++		EXPECT(err >= 0);
      +		if (err > 0) {
      +			break;
      +		}
      +
     -+		assert(j < want_names_len);
     -+		assert(0 == strcmp(ref.refname, want_names[j]));
     ++		EXPECT(j < want_names_len);
     ++		EXPECT(0 == strcmp(ref.refname, want_names[j]));
      +		j++;
     -+		reftable_ref_record_clear(&ref);
     ++		reftable_ref_record_release(&ref);
      +	}
     -+	assert(j == want_names_len);
     ++	EXPECT(j == want_names_len);
      +
      +	strbuf_release(&buf);
      +	free_names(want_names);
     @@ reftable/reftable_test.c (new)
      +	reftable_writer_set_limits(w, 1, 1);
      +
      +	err = reftable_writer_close(w);
     -+	assert(err == REFTABLE_EMPTY_TABLE_ERROR);
     ++	EXPECT(err == REFTABLE_EMPTY_TABLE_ERROR);
      +	reftable_writer_free(w);
      +
     -+	assert(buf.len == header_size(1) + footer_size(1));
     ++	EXPECT(buf.len == header_size(1) + footer_size(1));
      +
      +	block_source_from_strbuf(&source, &buf);
      +
      +	err = reftable_new_reader(&rd, &source, "filename");
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_reader_seek_ref(rd, &it, "");
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_iterator_next_ref(&it, &rec);
     -+	assert(err > 0);
     ++	EXPECT(err > 0);
      +
      +	reftable_iterator_destroy(&it);
      +	reftable_reader_free(rd);
     @@ reftable/reftable_test.c (new)
      +
      +int reftable_test_main(int argc, const char *argv[])
      +{
     -+	add_test_case("test_log_write_read", test_log_write_read);
     -+	add_test_case("test_table_read_write_seek_linear_sha256",
     -+		      &test_table_read_write_seek_linear_sha256);
     -+	add_test_case("test_log_buffer_size", test_log_buffer_size);
     -+	add_test_case("test_table_write_small_table",
     -+		      &test_table_write_small_table);
     -+	add_test_case("test_buffer", &test_buffer);
     -+	add_test_case("test_table_read_api", &test_table_read_api);
     -+	add_test_case("test_table_read_write_sequential",
     -+		      &test_table_read_write_sequential);
     -+	add_test_case("test_table_read_write_seek_linear",
     -+		      &test_table_read_write_seek_linear);
     -+	add_test_case("test_table_read_write_seek_index",
     -+		      &test_table_read_write_seek_index);
     -+	add_test_case("test_table_read_write_refs_for_no_index",
     -+		      &test_table_refs_for_no_index);
     -+	add_test_case("test_table_read_write_refs_for_obj_index",
     -+		      &test_table_refs_for_obj_index);
     -+	add_test_case("test_table_empty", &test_table_empty);
     -+	return test_main(argc, argv);
     ++	test_log_write_read();
     ++	test_table_read_write_seek_linear_sha256();
     ++	test_log_buffer_size();
     ++	test_table_write_small_table();
     ++	test_buffer();
     ++	test_table_read_api();
     ++	test_table_read_write_sequential();
     ++	test_table_read_write_seek_linear();
     ++	test_table_read_write_seek_index();
     ++	test_table_refs_for_no_index();
     ++	test_table_refs_for_obj_index();
     ++	test_table_empty();
     ++	return 0;
      +}
      
       ## t/helper/test-reftable.c ##
      @@ t/helper/test-reftable.c: int cmd__reftable(int argc, const char **argv)
     - {
     + 	basics_test_main(argc, argv);
       	block_test_main(argc, argv);
       	record_test_main(argc, argv);
      +	reftable_test_main(argc, argv);
     - 	strbuf_test_main(argc, argv);
       	tree_test_main(argc, argv);
       	return 0;
     + }
 12:  4e38db7f48 ! 13:  a26fb180ad reftable: rest of library
     @@ Metadata
       ## Commit message ##
          reftable: rest of library
      
     -    This will be further split up once preceding commits have passed review.
     -
          Signed-off-by: Han-Wen Nienhuys <hanwen@google.com>
      
       ## Makefile ##
     -@@ Makefile: XDIFF_OBJS += xdiff/xutils.o
     - REFTABLE_OBJS += reftable/basics.o
     +@@ Makefile: REFTABLE_OBJS += reftable/error.o
       REFTABLE_OBJS += reftable/block.o
       REFTABLE_OBJS += reftable/blocksource.o
     --REFTABLE_OBJS += reftable/publicbasics.o
     - REFTABLE_OBJS += reftable/compat.o
       REFTABLE_OBJS += reftable/iter.o
      +REFTABLE_OBJS += reftable/merged.o
      +REFTABLE_OBJS += reftable/pq.o
     -+REFTABLE_OBJS += reftable/publicbasics.o
     + REFTABLE_OBJS += reftable/publicbasics.o
       REFTABLE_OBJS += reftable/reader.o
       REFTABLE_OBJS += reftable/record.o
      +REFTABLE_OBJS += reftable/refname.o
       REFTABLE_OBJS += reftable/reftable.o
      +REFTABLE_OBJS += reftable/stack.o
     - REFTABLE_OBJS += reftable/strbuf.o
       REFTABLE_OBJS += reftable/tree.o
       REFTABLE_OBJS += reftable/writer.o
     -@@ Makefile: REFTABLE_OBJS += reftable/zlib-compat.o
     - 
     - REFTABLE_TEST_OBJS += reftable/block_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/stack_test.o
     - REFTABLE_TEST_OBJS += reftable/strbuf_test.o
     - REFTABLE_TEST_OBJS += reftable/test_framework.o
     - REFTABLE_TEST_OBJS += reftable/tree_test.o
     + REFTABLE_OBJS += reftable/zlib-compat.o
      
       ## reftable/VERSION (new) ##
      @@
     -+7134eb9f8171a9759800f4187f9e6dde997335e7 C: NULL iso 0 for init
     ++b8ec0f74c74cb6752eb2033ad8e755a9c19aad15 C: add missing header
      
       ## reftable/dump.c (new) ##
      @@
     @@ reftable/dump.c (new)
      +
      +static int dump_table(const char *tablename)
      +{
     -+	struct reftable_block_source src = { NULL };
     ++	struct reftable_block_source src = { 0 };
      +	int err = reftable_block_source_from_file(&src, tablename);
     -+	struct reftable_iterator it = { NULL };
     -+	struct reftable_ref_record ref = { NULL };
     -+	struct reftable_log_record log = { NULL };
     ++	struct reftable_iterator it = { 0 };
     ++	struct reftable_ref_record ref = { 0 };
     ++	struct reftable_log_record log = { 0 };
      +	struct reftable_reader *r = NULL;
      +
      +	if (err < 0)
     @@ reftable/dump.c (new)
      +{
      +	struct reftable_stack *stack = NULL;
      +	struct reftable_write_options cfg = {};
     -+	struct reftable_iterator it = { NULL };
     -+	struct reftable_ref_record ref = { NULL };
     -+	struct reftable_log_record log = { NULL };
     ++	struct reftable_iterator it = { 0 };
     ++	struct reftable_ref_record ref = { 0 };
     ++	struct reftable_log_record log = { 0 };
      +	struct reftable_merged_table *merged = NULL;
      +
      +	int err = reftable_new_stack(&stack, stackdir, cfg);
     @@ reftable/merged.c (new)
      +#include "pq.h"
      +#include "reader.h"
      +#include "record.h"
     -+#include "reftable.h"
     ++#include "reftable-merged.h"
     ++#include "reftable-error.h"
      +#include "system.h"
      +
      +static int merged_iter_init(struct merged_iter *mi)
     @@ reftable/merged.c (new)
      +{
      +	struct merged_iter *mi = (struct merged_iter *)p;
      +	int i = 0;
     -+	merged_iter_pqueue_clear(&mi->pq);
     ++	merged_iter_pqueue_release(&mi->pq);
      +	for (i = 0; i < mi->stack_len; i++) {
      +		reftable_iterator_destroy(&mi->stack[i]);
      +	}
     @@ reftable/merged.c (new)
      +}
      +
      +/* clears the list of subtable, without affecting the readers themselves. */
     -+void merged_table_clear(struct reftable_merged_table *mt)
     ++void merged_table_release(struct reftable_merged_table *mt)
      +{
      +	FREE_AND_NULL(mt->stack);
      +	mt->stack_len = 0;
     @@ reftable/merged.c (new)
      +	if (mt == NULL) {
      +		return;
      +	}
     -+	merged_table_clear(mt);
     ++	merged_table_release(mt);
      +	reftable_free(mt);
      +}
      +
     @@ reftable/merged.c (new)
      +	return mt->min;
      +}
      +
     ++static 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);
     ++}
     ++
      +static int merged_table_seek_record(struct reftable_merged_table *mt,
      +				    struct reftable_iterator *it,
      +				    struct reftable_record *rec)
     @@ reftable/merged.h (new)
      +#define MERGED_H
      +
      +#include "pq.h"
     -+#include "reftable.h"
      +
      +struct reftable_merged_table {
      +	struct reftable_table *stack;
     @@ reftable/merged.h (new)
      +	struct merged_iter_pqueue pq;
      +};
      +
     -+void merged_table_clear(struct reftable_merged_table *mt);
     ++void merged_table_release(struct reftable_merged_table *mt);
      +
      +#endif
      
     @@ reftable/merged_test.c (new)
      +#include "pq.h"
      +#include "reader.h"
      +#include "record.h"
     -+#include "reftable.h"
      +#include "test_framework.h"
     ++#include "reftable-merged.h"
      +#include "reftable-tests.h"
     ++#include "reftable-generic.h"
     ++#include "reftable-stack.h"
      +
      +static void test_pq(void)
      +{
     @@ reftable/merged_test.c (new)
      +		reftable_free(names[i]);
      +	}
      +
     -+	merged_iter_pqueue_clear(&pq);
     ++	merged_iter_pqueue_release(&pq);
      +}
      +
      +static void write_test_table(struct strbuf *buf,
     @@ reftable/merged_test.c (new)
      +	}
      +
      +	err = reftable_writer_close(w);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	reftable_writer_free(w);
      +}
     @@ reftable/merged_test.c (new)
      +
      +		err = reftable_new_reader(&(*readers)[i], &(*source)[i],
      +					  "name");
     -+		assert_err(err);
     ++		EXPECT_ERR(err);
      +		reftable_table_from_reader(&tabs[i], (*readers)[i]);
      +	}
      +
      +	err = reftable_new_merged_table(&mt, tabs, n, SHA1_ID);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +	return mt;
      +}
      +
     @@ reftable/merged_test.c (new)
      +	struct reftable_ref_record ref = { NULL };
      +	struct reftable_iterator it = { NULL };
      +	int err = reftable_merged_table_seek_ref(mt, &it, "a");
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_iterator_next_ref(&it, &ref);
     -+	assert_err(err);
     -+	assert(ref.update_index == 2);
     -+	reftable_ref_record_clear(&ref);
     ++	EXPECT_ERR(err);
     ++	EXPECT(ref.update_index == 2);
     ++	reftable_ref_record_release(&ref);
      +	reftable_iterator_destroy(&it);
      +	readers_destroy(readers, 2);
      +	reftable_merged_table_free(mt);
     @@ reftable/merged_test.c (new)
      +	size_t cap = 0;
      +	int i = 0;
      +
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +	while (len < 100) { /* cap loops/recursion. */
      +		struct reftable_ref_record ref = { NULL };
      +		int err = reftable_iterator_next_ref(&it, &ref);
     @@ reftable/merged_test.c (new)
      +		assert(reftable_ref_record_equal(&want[i], &out[i], SHA1_SIZE));
      +	}
      +	for (i = 0; i < len; i++) {
     -+		reftable_ref_record_clear(&out[i]);
     ++		reftable_ref_record_release(&out[i]);
      +	}
      +	reftable_free(out);
      +
     @@ reftable/merged_test.c (new)
      +	reftable_writer_set_limits(w, 1, 1);
      +
      +	err = reftable_writer_add_ref(w, &rec);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_writer_close(w);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +	reftable_writer_free(w);
      +
      +	block_source_from_strbuf(&source, &buf);
      +
      +	err = reftable_new_reader(&rd, &source, "filename");
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	hash_id = reftable_reader_hash_id(rd);
      +	assert(hash_id == SHA1_ID);
      +
      +	reftable_table_from_reader(&tab[0], rd);
      +	err = reftable_new_merged_table(&merged, tab, 1, SHA1_ID);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	reftable_reader_free(rd);
      +	reftable_merged_table_free(merged);
     @@ reftable/merged_test.c (new)
      +
      +int merged_test_main(int argc, const char *argv[])
      +{
     -+	add_test_case("test_merged_between", &test_merged_between);
     -+	add_test_case("test_pq", &test_pq);
     -+	add_test_case("test_merged", &test_merged);
     -+	add_test_case("test_default_write_opts", &test_default_write_opts);
     -+	return test_main(argc, argv);
     ++	test_merged_between();
     ++	test_pq();
     ++	test_merged();
     ++	test_default_write_opts();
     ++	return 0;
      +}
      
       ## reftable/pq.c (new) ##
     @@ reftable/pq.c (new)
      +
      +#include "pq.h"
      +
     -+#include "reftable.h"
     ++#include "reftable-record.h"
      +#include "system.h"
      +#include "basics.h"
      +
     @@ reftable/pq.c (new)
      +	}
      +}
      +
     -+void merged_iter_pqueue_clear(struct merged_iter_pqueue *pq)
     ++void merged_iter_pqueue_release(struct merged_iter_pqueue *pq)
      +{
      +	int i = 0;
      +	for (i = 0; i < pq->len; i++) {
     @@ reftable/pq.h (new)
      +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);
     ++void merged_iter_pqueue_release(struct merged_iter_pqueue *pq);
      +
      +#endif
      
     @@ reftable/refname.c (new)
      +*/
      +
      +#include "system.h"
     -+#include "reftable.h"
     ++#include "reftable-error.h"
      +#include "basics.h"
      +#include "refname.h"
     -+#include "strbuf.h"
     ++#include "reftable-iterator.h"
      +
      +struct find_arg {
      +	char **names;
     @@ reftable/refname.c (new)
      +	}
      +
      +	err = reftable_table_read_ref(&mod->tab, name, &ref);
     -+	reftable_ref_record_clear(&ref);
     ++	reftable_ref_record_release(&ref);
      +	return err;
      +}
      +
     -+static void modification_clear(struct modification *mod)
     ++static void modification_release(struct modification *mod)
      +{
      +	/* don't delete the strings themselves; they're owned by ref records.
      +	 */
     @@ reftable/refname.c (new)
      +	}
      +
      +done:
     -+	reftable_ref_record_clear(&ref);
     ++	reftable_ref_record_release(&ref);
      +	reftable_iterator_destroy(&it);
      +	return err;
      +}
     @@ reftable/refname.c (new)
      +	}
      +
      +	err = modification_validate(&mod);
     -+	modification_clear(&mod);
     ++	modification_release(&mod);
      +	return err;
      +}
      +
     @@ reftable/refname.h (new)
      +#ifndef REFNAME_H
      +#define REFNAME_H
      +
     -+#include "reftable.h"
     ++#include "reftable-record.h"
     ++#include "reftable-generic.h"
      +
      +struct modification {
      +	struct reftable_table tab;
     @@ reftable/refname_test.c (new)
      +https://developers.google.com/open-source/licenses/bsd
      +*/
      +
     -+#include "reftable.h"
     -+
      +#include "basics.h"
      +#include "block.h"
      +#include "blocksource.h"
     @@ reftable/refname_test.c (new)
      +#include "reader.h"
      +#include "record.h"
      +#include "refname.h"
     ++#include "reftable-error.h"
     ++#include "reftable-writer.h"
      +#include "system.h"
      +
      +#include "test_framework.h"
     @@ reftable/refname_test.c (new)
      +	reftable_writer_set_limits(w, 1, 1);
      +
      +	err = reftable_writer_add_ref(w, &rec);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_writer_close(w);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +	reftable_writer_free(w);
      +
      +	block_source_from_strbuf(&source, &buf);
      +	err = reftable_new_reader(&rd, &source, "filename");
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	reftable_table_from_reader(&tab, rd);
      +
     @@ reftable/refname_test.c (new)
      +		}
      +
      +		err = modification_validate(&mod);
     -+		assert(err == cases[i].error_code);
     ++		EXPECT(err == cases[i].error_code);
      +	}
      +
      +	reftable_reader_free(rd);
     @@ reftable/refname_test.c (new)
      +
      +int refname_test_main(int argc, const char *argv[])
      +{
     -+	add_test_case("test_conflict", &test_conflict);
     -+	return test_main(argc, argv);
     ++	test_conflict();
     ++	return 0;
      +}
      
     - ## reftable/reftable.c ##
     -@@ reftable/reftable.c: int reftable_table_seek_ref(struct reftable_table *tab,
     - 	struct reftable_ref_record ref = {
     - 		.refname = (char *)name,
     - 	};
     --	struct reftable_record rec = { 0 };
     + ## reftable/reftable-generic.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 REFTABLE_GENERIC_H
     ++#define REFTABLE_GENERIC_H
     ++
     ++#include "reftable-iterator.h"
     ++#include "reftable-reader.h"
     ++#include "reftable-merged.h"
     ++
     ++/*
     ++ Provides a unified API for reading tables, either merged tables, or single
     ++ readers.
     ++*/
     ++struct reftable_table {
     ++	struct reftable_table_vtable *ops;
     ++	void *table_arg;
     ++};
     ++
     ++int reftable_table_seek_ref(struct reftable_table *tab,
     ++			    struct reftable_iterator *it, const char *name);
     ++
     ++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,
     ++			    struct reftable_ref_record *ref);
     ++
     ++#endif
     +
     + ## reftable/reftable-merged.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 REFTABLE_MERGED_H
     ++#define REFTABLE_MERGED_H
     ++
     ++#include "reftable-iterator.h"
     ++
     ++/*
     ++ Merged tables
     ++
     ++ A ref database kept in a sequence of table files. The merged_table presents a
     ++ unified view to reading (seeking, iterating) a sequence of immutable tables.
     ++
     ++ The merged tables are on purpose kept disconnected from their actual storage
     ++ (eg. files on disk), because it is useful to merge tables aren't files. For
     ++ example, the per-workspace and global ref namespace can be implemented as a
     ++ merged table of two stacks of file-backed reftables.
     ++*/
     ++
     ++/* 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_table *stack, int n,
     ++			      uint32_t hash_id);
     ++
     ++/* returns an iterator positioned just before 'name' */
     ++int reftable_merged_table_seek_ref(struct reftable_merged_table *mt,
     ++				   struct reftable_iterator *it,
     ++				   const char *name);
     ++
     ++/* returns an iterator for log entry, at given update_index */
     ++int reftable_merged_table_seek_log_at(struct reftable_merged_table *mt,
     ++				      struct reftable_iterator *it,
     ++				      const char *name, uint64_t update_index);
     ++
     ++/* like reftable_merged_table_seek_log_at but look for the newest entry. */
     ++int reftable_merged_table_seek_log(struct reftable_merged_table *mt,
     ++				   struct reftable_iterator *it,
     ++				   const char *name);
     ++
     ++/* returns the max update_index covered by this merged table. */
     ++uint64_t
     ++reftable_merged_table_max_update_index(struct reftable_merged_table *mt);
     ++
     ++/* returns the min update_index covered by this merged table. */
     ++uint64_t
     ++reftable_merged_table_min_update_index(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);
     ++
     ++#endif
     +
     + ## reftable/reftable-stack.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 REFTABLE_STACK_H
     ++#define REFTABLE_STACK_H
     ++
     ++#include "reftable-writer.h"
     ++
     ++/*
     ++  The stack presents an interface to a mutable sequence of reftables.
     ++
     ++  A stack can be mutated by pushing a table to the top of the stack.
     ++
     ++  The reftable_stack automatically compacts files on disk to ensure good
     ++  amortized performance.
     ++*/
     ++struct reftable_stack;
     ++
     ++/* open a new reftable stack. The tables along with the table list will be
     ++   stored in 'dir'. Typically, this should be .git/reftables.
     ++*/
     ++int reftable_new_stack(struct reftable_stack **dest, const char *dir,
     ++		       struct reftable_write_options config);
     ++
     ++/* returns the update_index at which a next table should be written. */
     ++uint64_t reftable_stack_next_update_index(struct reftable_stack *st);
     ++
     ++/* holds a transaction to add tables at the top of a stack. */
     ++struct reftable_addition;
     ++
     ++/*
     ++  returns a new transaction to add reftables to the given stack. As a side
     ++  effect, the ref database is locked.
     ++*/
     ++int reftable_stack_new_addition(struct reftable_addition **dest,
     ++				struct reftable_stack *st);
     ++
     ++/* Adds a reftable to transaction. */
     ++int reftable_addition_add(struct reftable_addition *add,
     ++			  int (*write_table)(struct reftable_writer *wr,
     ++					     void *arg),
     ++			  void *arg);
     ++
     ++/* Commits the transaction, releasing the lock. */
     ++int reftable_addition_commit(struct reftable_addition *add);
     ++
     ++/* Release all non-committed data from the transaction, and deallocate the
     ++   transaction. Releases the lock if held. */
     ++void reftable_addition_destroy(struct reftable_addition *add);
     ++
     ++/* add a new table to the stack. The write_table function must call
     ++   reftable_writer_set_limits, add refs and return an error value. */
     ++int reftable_stack_add(struct reftable_stack *st,
     ++		       int (*write_table)(struct reftable_writer *wr,
     ++					  void *write_arg),
     ++		       void *write_arg);
     ++
     ++/* returns the merged_table for seeking. This table is valid until the
     ++   next write or reload, and should not be closed or deleted.
     ++*/
     ++struct reftable_merged_table *
     ++reftable_stack_merged_table(struct reftable_stack *st);
     ++
     ++/* frees all resources associated with the stack. */
     ++void reftable_stack_destroy(struct reftable_stack *st);
     ++
     ++/* Reloads the stack if necessary. This is very cheap to run if the stack was up
     ++ * to date */
     ++int reftable_stack_reload(struct reftable_stack *st);
     ++
     ++/* Policy for expiring reflog entries. */
     ++struct reftable_log_expiry_config {
     ++	/* Drop entries older than this timestamp */
     ++	uint64_t time;
     ++
     ++	/* Drop older entries */
     ++	uint64_t min_update_index;
     ++};
     ++
     ++/* compacts all reftables into a giant table. Expire reflog entries if config is
     ++ * non-NULL */
     ++int reftable_stack_compact_all(struct reftable_stack *st,
     ++			       struct reftable_log_expiry_config *config);
     ++
     ++/* heuristically compact unbalanced table stack. */
     ++int reftable_stack_auto_compact(struct reftable_stack *st);
     ++
     ++/* convenience function to read a single ref. Returns < 0 for error, 0
     ++   for success, and 1 if ref not found. */
     ++int reftable_stack_read_ref(struct reftable_stack *st, const char *refname,
     ++			    struct reftable_ref_record *ref);
     ++
     ++/* convenience function to read a single log. Returns < 0 for error, 0
     ++   for success, and 1 if ref not found. */
     ++int reftable_stack_read_log(struct reftable_stack *st, const char *refname,
     ++			    struct reftable_log_record *log);
     ++
     ++/* statistics on past compactions. */
     ++struct reftable_compaction_stats {
     ++	uint64_t bytes; /* total number of bytes written */
     ++	uint64_t entries_written; /* total number of entries written, including
     ++				     failures. */
     ++	int attempts; /* how often we tried to compact */
     ++	int failures; /* failures happen on concurrent updates */
     ++};
     ++
     ++/* return statistics for compaction up till now. */
     ++struct reftable_compaction_stats *
     ++reftable_stack_compaction_stats(struct reftable_stack *st);
     ++
     ++#endif
     +
     + ## reftable/reftable.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 "record.h"
     ++#include "reader.h"
     ++#include "reftable-iterator.h"
     ++#include "reftable-generic.h"
     ++
     ++static int reftable_reader_seek_void(void *tab, struct reftable_iterator *it,
     ++				     struct reftable_record *rec)
     ++{
     ++	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_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,
     ++};
     ++
     ++int reftable_table_seek_ref(struct reftable_table *tab,
     ++			    struct reftable_iterator *it, const char *name)
     ++{
     ++	struct reftable_ref_record ref = {
     ++		.refname = (char *)name,
     ++	};
      +	struct reftable_record rec = { NULL };
     - 	reftable_record_from_ref(&rec, &ref);
     - 	return tab->ops->seek_record(tab->table_arg, it, &rec);
     - }
     -@@ reftable/reftable.c: void reftable_table_from_reader(struct reftable_table *tab,
     - int reftable_table_read_ref(struct reftable_table *tab, const char *name,
     - 			    struct reftable_ref_record *ref)
     - {
     --	struct reftable_iterator it = { 0 };
     ++	reftable_record_from_ref(&rec, &ref);
     ++	return tab->ops->seek_record(tab->table_arg, it, &rec);
     ++}
     ++
     ++void reftable_table_from_reader(struct reftable_table *tab,
     ++				struct reftable_reader *reader)
     ++{
     ++	assert(tab->ops == NULL);
     ++	tab->ops = &reader_vtable;
     ++	tab->table_arg = reader;
     ++}
     ++
     ++int reftable_table_read_ref(struct reftable_table *tab, const char *name,
     ++			    struct reftable_ref_record *ref)
     ++{
      +	struct reftable_iterator it = { NULL };
     - 	int err = reftable_table_seek_ref(tab, &it, name);
     - 	if (err)
     - 		goto done;
     ++	int err = reftable_table_seek_ref(tab, &it, name);
     ++	if (err)
     ++		goto done;
     ++
     ++	err = reftable_iterator_next_ref(&it, ref);
     ++	if (err)
     ++		goto done;
     ++
     ++	if (strcmp(ref->refname, name) ||
     ++	    reftable_ref_record_is_deletion(ref)) {
     ++		reftable_ref_record_release(ref);
     ++		err = 1;
     ++		goto done;
     ++	}
     ++
     ++done:
     ++	reftable_iterator_destroy(&it);
     ++	return err;
     ++}
     ++
     ++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/stack.c (new) ##
      @@
     @@ reftable/stack.c (new)
      +#include "merged.h"
      +#include "reader.h"
      +#include "refname.h"
     -+#include "reftable.h"
     ++#include "reftable-error.h"
     ++#include "reftable-record.h"
      +#include "writer.h"
      +
      +static int stack_try_add(struct reftable_stack *st,
     @@ reftable/stack.c (new)
      +static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
      +					     int reuse_open);
      +
     ++static int reftable_fd_write(void *arg, const void *data, size_t sz)
     ++{
     ++	int *fdp = (int *)arg;
     ++	return write(*fdp, data, sz);
     ++}
     ++
      +int reftable_new_stack(struct reftable_stack **dest, const char *dir,
      +		       struct reftable_write_options config)
      +{
     @@ reftable/stack.c (new)
      +	new_tables = NULL;
      +	st->readers_len = new_readers_len;
      +	if (st->merged != NULL) {
     -+		merged_table_clear(st->merged);
     ++		merged_table_release(st->merged);
      +		reftable_merged_table_free(st->merged);
      +	}
      +	if (st->readers != NULL) {
     @@ reftable/stack.c (new)
      +done:
      +	reftable_iterator_destroy(&it);
      +	if (mt != NULL) {
     -+		merged_table_clear(mt);
     ++		merged_table_release(mt);
      +		reftable_merged_table_free(mt);
      +	}
     -+	reftable_ref_record_clear(&ref);
     -+	reftable_log_record_clear(&log);
     ++	reftable_ref_record_release(&ref);
     ++	reftable_log_record_release(&log);
      +	st->stats.entries_written += entries;
      +	return err;
      +}
     @@ reftable/stack.c (new)
      +
      +done:
      +	if (err) {
     -+		reftable_log_record_clear(log);
     ++		reftable_log_record_release(log);
      +	}
      +	reftable_iterator_destroy(&it);
      +	return err;
     @@ reftable/stack.c (new)
      +
      +done:
      +	for (i = 0; i < len; i++) {
     -+		reftable_ref_record_clear(&refs[i]);
     ++		reftable_ref_record_release(&refs[i]);
      +	}
      +
      +	free(refs);
     @@ reftable/stack.h (new)
      +#ifndef STACK_H
      +#define STACK_H
      +
     -+#include "reftable.h"
      +#include "system.h"
     ++#include "reftable-writer.h"
     ++#include "reftable-stack.h"
      +
      +struct reftable_stack {
      +	char *list_file;
     @@ reftable/stack_test.c (new)
      +#include "basics.h"
      +#include "constants.h"
      +#include "record.h"
     -+#include "reftable.h"
      +#include "test_framework.h"
      +#include "reftable-tests.h"
      +
      +#include <sys/types.h>
      +#include <dirent.h>
      +
     ++static void clear_dir(const char *dirname)
     ++{
     ++	struct strbuf path = STRBUF_INIT;
     ++	strbuf_addstr(&path, dirname);
     ++	remove_dir_recursively(&path, 0);
     ++	strbuf_release(&path);
     ++}
     ++
      +static void test_read_file(void)
      +{
      +	char fn[256] = "/tmp/stack.test_read_file.XXXXXX";
     @@ reftable/stack_test.c (new)
      +	char *want[] = { "line1", "line2", "line3" };
      +	int i = 0;
      +
     -+	assert(fd > 0);
     ++	EXPECT(fd > 0);
      +	n = write(fd, out, strlen(out));
     -+	assert(n == strlen(out));
     ++	EXPECT(n == strlen(out));
      +	err = close(fd);
     -+	assert(err >= 0);
     ++	EXPECT(err >= 0);
      +
      +	err = read_lines(fn, &names);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	for (i = 0; names[i] != NULL; i++) {
     -+		assert(0 == strcmp(want[i], names[i]));
     ++		EXPECT(0 == strcmp(want[i], names[i]));
      +	}
      +	free_names(names);
      +	remove(fn);
     @@ reftable/stack_test.c (new)
      +	char **names = NULL;
      +	parse_names(buf, strlen(buf), &names);
      +
     -+	assert(NULL != names[0]);
     -+	assert(0 == strcmp(names[0], "line"));
     -+	assert(NULL == names[1]);
     ++	EXPECT(NULL != names[0]);
     ++	EXPECT(0 == strcmp(names[0], "line"));
     ++	EXPECT(NULL == names[1]);
      +	free_names(names);
      +}
      +
     @@ reftable/stack_test.c (new)
      +	char *b[] = { "a", "b", "d", NULL };
      +	char *c[] = { "a", "b", NULL };
      +
     -+	assert(names_equal(a, a));
     -+	assert(!names_equal(a, b));
     -+	assert(!names_equal(a, c));
     ++	EXPECT(names_equal(a, a));
     ++	EXPECT(!names_equal(a, b));
     ++	EXPECT(!names_equal(a, c));
      +}
      +
      +static int write_test_ref(struct reftable_writer *wr, void *arg)
     @@ reftable/stack_test.c (new)
      +	};
      +	struct reftable_ref_record dest = { NULL };
      +
     -+	assert(mkdtemp(dir));
     ++	EXPECT(mkdtemp(dir));
      +
      +	err = reftable_new_stack(&st, dir, cfg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_add(st, &write_test_ref, &ref);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_read_ref(st, ref.refname, &dest);
     -+	assert_err(err);
     -+	assert(0 == strcmp("master", dest.target));
     ++	EXPECT_ERR(err);
     ++	EXPECT(0 == strcmp("master", dest.target));
      +
     -+	reftable_ref_record_clear(&dest);
     ++	reftable_ref_record_release(&dest);
      +	reftable_stack_destroy(st);
     -+	reftable_clear_dir(dir);
     ++	clear_dir(dir);
      +}
      +
      +static void test_reftable_stack_uptodate(void)
     @@ reftable/stack_test.c (new)
      +		.target = "master",
      +	};
      +
     -+	assert(mkdtemp(dir));
     ++	EXPECT(mkdtemp(dir));
      +
      +	err = reftable_new_stack(&st1, dir, cfg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_new_stack(&st2, dir, cfg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_add(st1, &write_test_ref, &ref1);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_add(st2, &write_test_ref, &ref2);
     -+	assert(err == REFTABLE_LOCK_ERROR);
     ++	EXPECT(err == REFTABLE_LOCK_ERROR);
      +
      +	err = reftable_stack_reload(st2);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_add(st2, &write_test_ref, &ref2);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +	reftable_stack_destroy(st1);
      +	reftable_stack_destroy(st2);
     -+	reftable_clear_dir(dir);
     ++	clear_dir(dir);
      +}
      +
      +static void test_reftable_stack_transaction_api(void)
     @@ reftable/stack_test.c (new)
      +	};
      +	struct reftable_ref_record dest = { NULL };
      +
     -+	assert(mkdtemp(dir));
     ++	EXPECT(mkdtemp(dir));
      +
      +	err = reftable_new_stack(&st, dir, cfg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	reftable_addition_destroy(add);
      +
      +	err = reftable_stack_new_addition(&add, st);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_addition_add(add, &write_test_ref, &ref);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_addition_commit(add);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	reftable_addition_destroy(add);
      +
      +	err = reftable_stack_read_ref(st, ref.refname, &dest);
     -+	assert_err(err);
     -+	assert(0 == strcmp("master", dest.target));
     ++	EXPECT_ERR(err);
     ++	EXPECT(0 == strcmp("master", dest.target));
      +
     -+	reftable_ref_record_clear(&dest);
     ++	reftable_ref_record_release(&dest);
      +	reftable_stack_destroy(st);
     -+	reftable_clear_dir(dir);
     ++	clear_dir(dir);
      +}
      +
      +static void test_reftable_stack_validate_refname(void)
     @@ reftable/stack_test.c (new)
      +	};
      +	char *additions[] = { "a", "a/b/c" };
      +
     -+	assert(mkdtemp(dir));
     ++	EXPECT(mkdtemp(dir));
      +	err = reftable_new_stack(&st, dir, cfg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_add(st, &write_test_ref, &ref);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	for (i = 0; i < ARRAY_SIZE(additions); i++) {
      +		struct reftable_ref_record ref = {
     @@ reftable/stack_test.c (new)
      +		};
      +
      +		err = reftable_stack_add(st, &write_test_ref, &ref);
     -+		assert(err == REFTABLE_NAME_CONFLICT);
     ++		EXPECT(err == REFTABLE_NAME_CONFLICT);
      +	}
      +
      +	reftable_stack_destroy(st);
     -+	reftable_clear_dir(dir);
     ++	clear_dir(dir);
      +}
      +
      +static int write_error(struct reftable_writer *wr, void *arg)
     @@ reftable/stack_test.c (new)
      +		.update_index = 1,
      +		.target = "master",
      +	};
     -+	assert(mkdtemp(dir));
     ++	EXPECT(mkdtemp(dir));
      +
      +	err = reftable_new_stack(&st, dir, cfg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_add(st, &write_test_ref, &ref1);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_add(st, &write_test_ref, &ref2);
     -+	assert(err == REFTABLE_API_ERROR);
     ++	EXPECT(err == REFTABLE_API_ERROR);
      +	reftable_stack_destroy(st);
     -+	reftable_clear_dir(dir);
     ++	clear_dir(dir);
      +}
      +
      +static void test_reftable_stack_lock_failure(void)
     @@ reftable/stack_test.c (new)
      +	struct reftable_write_options cfg = { 0 };
      +	struct reftable_stack *st = NULL;
      +	int err, i;
     -+	assert(mkdtemp(dir));
     ++	EXPECT(mkdtemp(dir));
      +
      +	err = reftable_new_stack(&st, dir, cfg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +	for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--) {
      +		err = reftable_stack_add(st, &write_error, &i);
     -+		assert(err == i);
     ++		EXPECT(err == i);
      +	}
      +
      +	reftable_stack_destroy(st);
     -+	reftable_clear_dir(dir);
     ++	clear_dir(dir);
      +}
      +
      +static void test_reftable_stack_add(void)
     @@ reftable/stack_test.c (new)
      +	struct reftable_log_record logs[2] = { { NULL } };
      +	int N = ARRAY_SIZE(refs);
      +
     -+	assert(mkdtemp(dir));
     ++	EXPECT(mkdtemp(dir));
      +
      +	err = reftable_new_stack(&st, dir, cfg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +	st->disable_auto_compact = 1;
      +
      +	for (i = 0; i < N; i++) {
     @@ reftable/stack_test.c (new)
      +
      +	for (i = 0; i < N; i++) {
      +		int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
     -+		assert_err(err);
     ++		EXPECT_ERR(err);
      +	}
      +
      +	for (i = 0; i < N; i++) {
     @@ reftable/stack_test.c (new)
      +			.update_index = reftable_stack_next_update_index(st),
      +		};
      +		int err = reftable_stack_add(st, &write_test_log, &arg);
     -+		assert_err(err);
     ++		EXPECT_ERR(err);
      +	}
      +
      +	err = reftable_stack_compact_all(st, NULL);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	for (i = 0; i < N; i++) {
      +		struct reftable_ref_record dest = { NULL };
      +
      +		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);
     ++		EXPECT_ERR(err);
     ++		EXPECT(reftable_ref_record_equal(&dest, refs + i, SHA1_SIZE));
     ++		reftable_ref_record_release(&dest);
      +	}
      +
      +	for (i = 0; i < N; i++) {
      +		struct reftable_log_record dest = { NULL };
      +		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);
     ++		EXPECT_ERR(err);
     ++		EXPECT(reftable_log_record_equal(&dest, logs + i, SHA1_SIZE));
     ++		reftable_log_record_release(&dest);
      +	}
      +
      +	/* cleanup */
      +	reftable_stack_destroy(st);
      +	for (i = 0; i < N; i++) {
     -+		reftable_ref_record_clear(&refs[i]);
     -+		reftable_log_record_clear(&logs[i]);
     ++		reftable_ref_record_release(&refs[i]);
     ++		reftable_log_record_release(&logs[i]);
      +	}
     -+	reftable_clear_dir(dir);
     ++	clear_dir(dir);
      +}
      +
      +static void test_reftable_stack_log_normalize(void)
     @@ reftable/stack_test.c (new)
      +		.update_index = 1,
      +	};
      +
     -+	assert(mkdtemp(dir));
     ++	EXPECT(mkdtemp(dir));
      +	err = reftable_new_stack(&st, dir, cfg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	input.message = "one\ntwo";
      +	err = reftable_stack_add(st, &write_test_log, &arg);
     -+	assert(err == REFTABLE_API_ERROR);
     ++	EXPECT(err == REFTABLE_API_ERROR);
      +
      +	input.message = "one";
      +	err = reftable_stack_add(st, &write_test_log, &arg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_read_log(st, input.refname, &dest);
     -+	assert_err(err);
     -+	assert(0 == strcmp(dest.message, "one\n"));
     ++	EXPECT_ERR(err);
     ++	EXPECT(0 == strcmp(dest.message, "one\n"));
      +
      +	input.message = "two\n";
      +	arg.update_index = 2;
      +	err = reftable_stack_add(st, &write_test_log, &arg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +	err = reftable_stack_read_log(st, input.refname, &dest);
     -+	assert_err(err);
     -+	assert(0 == strcmp(dest.message, "two\n"));
     ++	EXPECT_ERR(err);
     ++	EXPECT(0 == strcmp(dest.message, "two\n"));
      +
      +	/* cleanup */
      +	reftable_stack_destroy(st);
     -+	reftable_log_record_clear(&dest);
     -+	reftable_clear_dir(dir);
     ++	reftable_log_record_release(&dest);
     ++	clear_dir(dir);
      +}
      +
      +static void test_reftable_stack_tombstone(void)
     @@ reftable/stack_test.c (new)
      +	struct reftable_ref_record dest = { NULL };
      +	struct reftable_log_record log_dest = { NULL };
      +
     -+	assert(mkdtemp(dir));
     ++	EXPECT(mkdtemp(dir));
      +
      +	err = reftable_new_stack(&st, dir, cfg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	for (i = 0; i < N; i++) {
      +		const char *buf = "branch";
     @@ reftable/stack_test.c (new)
      +	}
      +	for (i = 0; i < N; i++) {
      +		int err = reftable_stack_add(st, &write_test_ref, &refs[i]);
     -+		assert_err(err);
     ++		EXPECT_ERR(err);
      +	}
      +	for (i = 0; i < N; i++) {
      +		struct write_log_arg arg = {
     @@ reftable/stack_test.c (new)
      +			.update_index = reftable_stack_next_update_index(st),
      +		};
      +		int err = reftable_stack_add(st, &write_test_log, &arg);
     -+		assert_err(err);
     ++		EXPECT_ERR(err);
      +	}
      +
      +	err = reftable_stack_read_ref(st, "branch", &dest);
     -+	assert(err == 1);
     -+	reftable_ref_record_clear(&dest);
     ++	EXPECT(err == 1);
     ++	reftable_ref_record_release(&dest);
      +
      +	err = reftable_stack_read_log(st, "branch", &log_dest);
     -+	assert(err == 1);
     -+	reftable_log_record_clear(&log_dest);
     ++	EXPECT(err == 1);
     ++	reftable_log_record_release(&log_dest);
      +
      +	err = reftable_stack_compact_all(st, NULL);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_read_ref(st, "branch", &dest);
     -+	assert(err == 1);
     ++	EXPECT(err == 1);
      +
      +	err = reftable_stack_read_log(st, "branch", &log_dest);
     -+	assert(err == 1);
     -+	reftable_ref_record_clear(&dest);
     -+	reftable_log_record_clear(&log_dest);
     ++	EXPECT(err == 1);
     ++	reftable_ref_record_release(&dest);
     ++	reftable_log_record_release(&log_dest);
      +
      +	/* cleanup */
      +	reftable_stack_destroy(st);
      +	for (i = 0; i < N; i++) {
     -+		reftable_ref_record_clear(&refs[i]);
     -+		reftable_log_record_clear(&logs[i]);
     ++		reftable_ref_record_release(&refs[i]);
     ++		reftable_log_record_release(&logs[i]);
      +	}
     -+	reftable_clear_dir(dir);
     ++	clear_dir(dir);
      +}
      +
      +static void test_reftable_stack_hash_id(void)
     @@ reftable/stack_test.c (new)
      +	struct reftable_stack *st_default = NULL;
      +	struct reftable_ref_record dest = { NULL };
      +
     -+	assert(mkdtemp(dir));
     ++	EXPECT(mkdtemp(dir));
      +	err = reftable_new_stack(&st, dir, cfg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_add(st, &write_test_ref, &ref);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	/* can't read it with the wrong hash ID. */
      +	err = reftable_new_stack(&st32, dir, cfg32);
     -+	assert(err == REFTABLE_FORMAT_ERROR);
     ++	EXPECT(err == REFTABLE_FORMAT_ERROR);
      +
      +	/* check that we can read it back with default config too. */
      +	err = reftable_new_stack(&st_default, dir, cfg_default);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_read_ref(st_default, "master", &dest);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
     -+	assert(!strcmp(dest.target, ref.target));
     -+	reftable_ref_record_clear(&dest);
     ++	EXPECT(!strcmp(dest.target, ref.target));
     ++	reftable_ref_record_release(&dest);
      +	reftable_stack_destroy(st);
      +	reftable_stack_destroy(st_default);
     -+	reftable_clear_dir(dir);
     ++	clear_dir(dir);
      +}
      +
      +static void test_log2(void)
      +{
     -+	assert(1 == fastlog2(3));
     -+	assert(2 == fastlog2(4));
     -+	assert(2 == fastlog2(5));
     ++	EXPECT(1 == fastlog2(3));
     ++	EXPECT(2 == fastlog2(4));
     ++	EXPECT(2 == fastlog2(5));
      +}
      +
      +static void test_sizes_to_segments(void)
     @@ reftable/stack_test.c (new)
      +	int seglen = 0;
      +	struct segment *segs =
      +		sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
     -+	assert(segs[2].log == 3);
     -+	assert(segs[2].start == 5);
     -+	assert(segs[2].end == 6);
     ++	EXPECT(segs[2].log == 3);
     ++	EXPECT(segs[2].start == 5);
     ++	EXPECT(segs[2].end == 6);
      +
     -+	assert(segs[1].log == 2);
     -+	assert(segs[1].start == 2);
     -+	assert(segs[1].end == 5);
     ++	EXPECT(segs[1].log == 2);
     ++	EXPECT(segs[1].start == 2);
     ++	EXPECT(segs[1].end == 5);
      +	reftable_free(segs);
      +}
      +
     @@ reftable/stack_test.c (new)
      +	int seglen = 0;
      +	struct segment *segs =
      +		sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
     -+	assert(seglen == 0);
     ++	EXPECT(seglen == 0);
      +	reftable_free(segs);
      +}
      +
     @@ reftable/stack_test.c (new)
      +	int seglen = 0;
      +	struct segment *segs =
      +		sizes_to_segments(&seglen, sizes, ARRAY_SIZE(sizes));
     -+	assert(seglen == 1);
     -+	assert(segs[0].start == 0);
     -+	assert(segs[0].end == 2);
     ++	EXPECT(seglen == 1);
     ++	EXPECT(segs[0].start == 0);
     ++	EXPECT(segs[0].end == 2);
      +	reftable_free(segs);
      +}
      +
     @@ reftable/stack_test.c (new)
      +	/* .................0    1    2  3   4  5  6 */
      +	struct segment min =
      +		suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
     -+	assert(min.start == 2);
     -+	assert(min.end == 7);
     ++	EXPECT(min.start == 2);
     ++	EXPECT(min.end == 7);
      +}
      +
      +static void test_suggest_compaction_segment_nothing(void)
     @@ reftable/stack_test.c (new)
      +	uint64_t sizes[] = { 64, 32, 16, 8, 4, 2 };
      +	struct segment result =
      +		suggest_compaction_segment(sizes, ARRAY_SIZE(sizes));
     -+	assert(result.start == result.end);
     ++	EXPECT(result.start == result.end);
      +}
      +
      +static void test_reflog_expire(void)
     @@ reftable/stack_test.c (new)
      +	};
      +	struct reftable_log_record log = { NULL };
      +
     -+	assert(mkdtemp(dir));
     ++	EXPECT(mkdtemp(dir));
      +
      +	err = reftable_new_stack(&st, dir, cfg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	for (i = 1; i <= N; i++) {
      +		char buf[256];
     @@ reftable/stack_test.c (new)
      +			.update_index = reftable_stack_next_update_index(st),
      +		};
      +		int err = reftable_stack_add(st, &write_test_log, &arg);
     -+		assert_err(err);
     ++		EXPECT_ERR(err);
      +	}
      +
      +	err = reftable_stack_compact_all(st, NULL);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_compact_all(st, &expiry);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_read_log(st, logs[9].refname, &log);
     -+	assert(err == 1);
     ++	EXPECT(err == 1);
      +
      +	err = reftable_stack_read_log(st, logs[11].refname, &log);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	expiry.min_update_index = 15;
      +	err = reftable_stack_compact_all(st, &expiry);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_read_log(st, logs[14].refname, &log);
     -+	assert(err == 1);
     ++	EXPECT(err == 1);
      +
      +	err = reftable_stack_read_log(st, logs[16].refname, &log);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	/* cleanup */
      +	reftable_stack_destroy(st);
      +	for (i = 0; i <= N; i++) {
     -+		reftable_log_record_clear(&logs[i]);
     ++		reftable_log_record_release(&logs[i]);
      +	}
     -+	reftable_clear_dir(dir);
     -+	reftable_log_record_clear(&log);
     ++	clear_dir(dir);
     ++	reftable_log_record_release(&log);
      +}
      +
      +static int write_nothing(struct reftable_writer *wr, void *arg)
     @@ reftable/stack_test.c (new)
      +	char dir[256] = "/tmp/stack_test.XXXXXX";
      +	struct reftable_stack *st2 = NULL;
      +
     -+	assert(mkdtemp(dir));
     ++	EXPECT(mkdtemp(dir));
      +
      +	err = reftable_new_stack(&st, dir, cfg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_stack_add(st, &write_nothing, NULL);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	err = reftable_new_stack(&st2, dir, cfg);
     -+	assert_err(err);
     -+	reftable_clear_dir(dir);
     ++	EXPECT_ERR(err);
     ++	clear_dir(dir);
      +	reftable_stack_destroy(st);
      +	reftable_stack_destroy(st2);
      +}
     @@ reftable/stack_test.c (new)
      +	char dir[256] = "/tmp/stack_test.XXXXXX";
      +	int err, i;
      +	int N = 100;
     -+	assert(mkdtemp(dir));
     ++	EXPECT(mkdtemp(dir));
      +
      +	err = reftable_new_stack(&st, dir, cfg);
     -+	assert_err(err);
     ++	EXPECT_ERR(err);
      +
      +	for (i = 0; i < N; i++) {
      +		char name[100];
     @@ reftable/stack_test.c (new)
      +		snprintf(name, sizeof(name), "branch%04d", i);
      +
      +		err = reftable_stack_add(st, &write_test_ref, &ref);
     -+		assert_err(err);
     ++		EXPECT_ERR(err);
      +
     -+		assert(i < 3 || st->merged->stack_len < 2 * fastlog2(i));
     ++		EXPECT(i < 3 || st->merged->stack_len < 2 * fastlog2(i));
      +	}
      +
     -+	assert(reftable_stack_compaction_stats(st)->entries_written <
     ++	EXPECT(reftable_stack_compaction_stats(st)->entries_written <
      +	       (uint64_t)(N * fastlog2(N)));
      +
      +	reftable_stack_destroy(st);
     -+	reftable_clear_dir(dir);
     ++	clear_dir(dir);
      +}
      +
      +int stack_test_main(int argc, const char *argv[])
      +{
     -+	add_test_case("test_reftable_stack_uptodate",
     -+		      &test_reftable_stack_uptodate);
     -+	add_test_case("test_reftable_stack_transaction_api",
     -+		      &test_reftable_stack_transaction_api);
     -+	add_test_case("test_reftable_stack_hash_id",
     -+		      &test_reftable_stack_hash_id);
     -+	add_test_case("test_sizes_to_segments_all_equal",
     -+		      &test_sizes_to_segments_all_equal);
     -+	add_test_case("test_reftable_stack_auto_compaction",
     -+		      &test_reftable_stack_auto_compaction);
     -+	add_test_case("test_reftable_stack_validate_refname",
     -+		      &test_reftable_stack_validate_refname);
     -+	add_test_case("test_reftable_stack_update_index_check",
     -+		      &test_reftable_stack_update_index_check);
     -+	add_test_case("test_reftable_stack_lock_failure",
     -+		      &test_reftable_stack_lock_failure);
     -+	add_test_case("test_reftable_stack_log_normalize",
     -+		      &test_reftable_stack_log_normalize);
     -+	add_test_case("test_reftable_stack_tombstone",
     -+		      &test_reftable_stack_tombstone);
     -+	add_test_case("test_reftable_stack_add_one",
     -+		      &test_reftable_stack_add_one);
     -+	add_test_case("test_empty_add", test_empty_add);
     -+	add_test_case("test_reflog_expire", test_reflog_expire);
     -+	add_test_case("test_suggest_compaction_segment",
     -+		      &test_suggest_compaction_segment);
     -+	add_test_case("test_suggest_compaction_segment_nothing",
     -+		      &test_suggest_compaction_segment_nothing);
     -+	add_test_case("test_sizes_to_segments", &test_sizes_to_segments);
     -+	add_test_case("test_sizes_to_segments_empty",
     -+		      &test_sizes_to_segments_empty);
     -+	add_test_case("test_log2", &test_log2);
     -+	add_test_case("test_parse_names", &test_parse_names);
     -+	add_test_case("test_read_file", &test_read_file);
     -+	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);
     ++	test_reftable_stack_uptodate();
     ++	test_reftable_stack_transaction_api();
     ++	test_reftable_stack_hash_id();
     ++	test_sizes_to_segments_all_equal();
     ++	test_reftable_stack_auto_compaction();
     ++	test_reftable_stack_validate_refname();
     ++	test_reftable_stack_update_index_check();
     ++	test_reftable_stack_lock_failure();
     ++	test_reftable_stack_log_normalize();
     ++	test_reftable_stack_tombstone();
     ++	test_reftable_stack_add_one();
     ++	test_empty_add();
     ++	test_reflog_expire();
     ++	test_suggest_compaction_segment();
     ++	test_suggest_compaction_segment_nothing();
     ++	test_sizes_to_segments();
     ++	test_sizes_to_segments_empty();
     ++	test_log2();
     ++	test_parse_names();
     ++	test_read_file();
     ++	test_names_equal();
     ++	test_reftable_stack_add();
     ++	return 0;
      +}
      
     - ## reftable/update.sh (new) ##
     -@@
     -+#!/bin/sh
     -+
     -+set -eu
     -+
     -+# Override this to import from somewhere else, say "../reftable".
     -+SRC=${SRC:-origin}
     -+BRANCH=${BRANCH:-master}
     -+
     -+((git --git-dir reftable-repo/.git fetch -f ${SRC} ${BRANCH}:import && cd reftable-repo && git checkout -f $(git rev-parse import) ) ||
     -+   git clone https://github.com/google/reftable reftable-repo)
     -+
     -+cp reftable-repo/c/*.[ch] reftable/
     -+cp reftable-repo/c/include/*.[ch] reftable/
     -+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
     -
       ## t/helper/test-reftable.c ##
     -@@
     - int cmd__reftable(int argc, const char **argv)
     +@@ t/helper/test-reftable.c: int cmd__reftable(int argc, const char **argv)
       {
     + 	basics_test_main(argc, 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;
  -:  ---------- > 14:  a590865a70 Reftable support for git-core
  -:  ---------- > 15:  57626bfe2d git-prompt: prepare for reftable refs backend
 13:  c535f838d6 ! 16:  6229da992e reftable: "test-tool dump-reftable" command.
     @@ Metadata
      Author: Han-Wen Nienhuys <hanwen@google.com>
      
       ## Commit message ##
     -    reftable: "test-tool dump-reftable" command.
     +    Add "test-tool dump-reftable" command.
      
          This command dumps individual tables or a stack of of tables.
      
          Signed-off-by: Han-Wen Nienhuys <hanwen@google.com>
      
       ## Makefile ##
     -@@ Makefile: REFTABLE_OBJS += reftable/writer.o
     - REFTABLE_OBJS += reftable/zlib-compat.o
     +@@ Makefile: REFTABLE_OBJS += reftable/zlib-compat.o
       
     + REFTABLE_TEST_OBJS += reftable/basics_test.o
       REFTABLE_TEST_OBJS += reftable/block_test.o
      +REFTABLE_TEST_OBJS += reftable/dump.o
     -+REFTABLE_TEST_OBJS += reftable/merged_test.o
     + REFTABLE_TEST_OBJS += reftable/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/iter.c ##
     -@@ reftable/iter.c: void reftable_iterator_destroy(struct reftable_iterator *it)
     - int reftable_iterator_next_ref(struct reftable_iterator *it,
     - 			       struct reftable_ref_record *ref)
     - {
     --	struct reftable_record rec = { 0 };
     -+	struct reftable_record rec = { NULL };
     - 	reftable_record_from_ref(&rec, ref);
     - 	return iterator_next(it, &rec);
     - }
     -@@ reftable/iter.c: int reftable_iterator_next_ref(struct reftable_iterator *it,
     - int reftable_iterator_next_log(struct reftable_iterator *it,
     - 			       struct reftable_log_record *log)
     - {
     --	struct reftable_record rec = { 0 };
     -+	struct reftable_record rec = { NULL };
     - 	reftable_record_from_log(&rec, log);
     - 	return iterator_next(it, &rec);
     - }
     -@@ reftable/iter.c: static int filtering_ref_iterator_next(void *iter_arg,
     - 		}
     + ## reftable/dump.c ##
     +@@ reftable/dump.c: license that can be found in the LICENSE file or at
     + #include <unistd.h>
     + #include <string.h>
       
     - 		if (fri->double_check) {
     --			struct reftable_iterator it = { 0 };
     -+			struct reftable_iterator it = { NULL };
     +-#include "reftable.h"
     ++#include "reftable-blocksource.h"
     ++#include "reftable-error.h"
     ++#include "reftable-merged.h"
     ++#include "reftable-record.h"
     + #include "reftable-tests.h"
     ++#include "reftable-writer.h"
     ++#include "reftable-iterator.h"
     ++#include "reftable-reader.h"
     ++#include "reftable-stack.h"
       
     - 			err = reftable_table_seek_ref(&fri->tab, &it,
     - 						      ref->refname);
     -
     - ## reftable/reader.c ##
     -@@ reftable/reader.c: static int parse_footer(struct reftable_reader *r, uint8_t *footer,
     - int init_reader(struct reftable_reader *r, struct reftable_block_source *source,
     - 		const char *name)
     - {
     --	struct reftable_block footer = { 0 };
     --	struct reftable_block header = { 0 };
     -+	struct reftable_block footer = { NULL };
     -+	struct reftable_block header = { NULL };
     - 	int err = 0;
     + static uint32_t hash_id;
       
     - 	memset(r, 0, sizeof(struct reftable_reader));
     -@@ reftable/reader.c: int reader_init_block_reader(struct reftable_reader *r, struct block_reader *br,
     + static int dump_table(const char *tablename)
       {
     - 	int32_t guess_block_size = r->block_size ? r->block_size :
     - 							 DEFAULT_BLOCK_SIZE;
     --	struct reftable_block block = { 0 };
     -+	struct reftable_block block = { NULL };
     - 	uint8_t block_typ = 0;
     - 	int err = 0;
     - 	uint32_t header_off = next_off ? 0 : header_size(r->version);
     -@@ reftable/reader.c: static int reader_seek_indexed(struct reftable_reader *r,
     - 			       struct reftable_record *rec)
     +-	struct reftable_block_source src = { 0 };
     ++	struct reftable_block_source src = { NULL };
     + 	int err = reftable_block_source_from_file(&src, tablename);
     +-	struct reftable_iterator it = { 0 };
     +-	struct reftable_ref_record ref = { 0 };
     +-	struct reftable_log_record log = { 0 };
     ++	struct reftable_iterator it = { NULL };
     ++	struct reftable_ref_record ref = { NULL };
     ++	struct reftable_log_record log = { NULL };
     + 	struct reftable_reader *r = NULL;
     + 
     + 	if (err < 0)
     +@@ reftable/dump.c: static int dump_table(const char *tablename)
     + 		reftable_ref_record_print(&ref, hash_id);
     + 	}
     + 	reftable_iterator_destroy(&it);
     +-	reftable_ref_record_clear(&ref);
     ++	reftable_ref_record_release(&ref);
     + 
     + 	err = reftable_reader_seek_log(r, &it, "");
     + 	if (err < 0) {
     +@@ reftable/dump.c: static int dump_table(const char *tablename)
     + 		reftable_log_record_print(&log, hash_id);
     + 	}
     + 	reftable_iterator_destroy(&it);
     +-	reftable_log_record_clear(&log);
     ++	reftable_log_record_release(&log);
     + 
     + 	reftable_reader_free(r);
     + 	return 0;
     +@@ reftable/dump.c: static int dump_stack(const char *stackdir)
       {
     - 	struct reftable_index_record want_index = { .last_key = STRBUF_INIT };
     --	struct reftable_record want_index_rec = { 0 };
     -+	struct reftable_record want_index_rec = { NULL };
     - 	struct reftable_index_record index_result = { .last_key = STRBUF_INIT };
     --	struct reftable_record index_result_rec = { 0 };
     -+	struct reftable_record index_result_rec = { NULL };
     - 	struct table_iter index_iter = TABLE_ITER_INIT;
     - 	struct table_iter next = TABLE_ITER_INIT;
     - 	int err = 0;
     -@@ reftable/reader.c: int reftable_reader_seek_ref(struct reftable_reader *r,
     - 	struct reftable_ref_record ref = {
     - 		.refname = (char *)name,
     - 	};
     --	struct reftable_record rec = { 0 };
     -+	struct reftable_record rec = { NULL };
     - 	reftable_record_from_ref(&rec, &ref);
     - 	return reader_seek(r, it, &rec);
     - }
     -@@ reftable/reader.c: int reftable_reader_seek_log_at(struct reftable_reader *r,
     - 		.refname = (char *)name,
     - 		.update_index = update_index,
     - 	};
     --	struct reftable_record rec = { 0 };
     -+	struct reftable_record rec = { NULL };
     - 	reftable_record_from_log(&rec, &log);
     - 	return reader_seek(r, it, &rec);
     - }
     -@@ reftable/reader.c: static int reftable_reader_refs_for_indexed(struct reftable_reader *r,
     - 		.hash_prefix = oid,
     - 		.hash_prefix_len = r->object_id_len,
     - 	};
     --	struct reftable_record want_rec = { 0 };
     --	struct reftable_iterator oit = { 0 };
     --	struct reftable_obj_record got = { 0 };
     --	struct reftable_record got_rec = { 0 };
     -+	struct reftable_record want_rec = { NULL };
     -+	struct reftable_iterator oit = { NULL };
     -+	struct reftable_obj_record got = { NULL };
     -+	struct reftable_record got_rec = { NULL };
     - 	int err = 0;
     - 	struct indexed_table_ref_iter *itr = NULL;
     + 	struct reftable_stack *stack = NULL;
     + 	struct reftable_write_options cfg = {};
     +-	struct reftable_iterator it = { 0 };
     +-	struct reftable_ref_record ref = { 0 };
     +-	struct reftable_log_record log = { 0 };
     ++	struct reftable_iterator it = { NULL };
     ++	struct reftable_ref_record ref = { NULL };
     ++	struct reftable_log_record log = { NULL };
     + 	struct reftable_merged_table *merged = NULL;
       
     + 	int err = reftable_new_stack(&stack, stackdir, cfg);
     +@@ reftable/dump.c: static int dump_stack(const char *stackdir)
     + 		reftable_ref_record_print(&ref, hash_id);
     + 	}
     + 	reftable_iterator_destroy(&it);
     +-	reftable_ref_record_clear(&ref);
     ++	reftable_ref_record_release(&ref);
     + 
     + 	err = reftable_merged_table_seek_log(merged, &it, "");
     + 	if (err < 0) {
     +@@ reftable/dump.c: static int dump_stack(const char *stackdir)
     + 		reftable_log_record_print(&log, hash_id);
     + 	}
     + 	reftable_iterator_destroy(&it);
     +-	reftable_log_record_clear(&log);
     ++	reftable_log_record_release(&log);
     + 
     + 	reftable_stack_destroy(stack);
     + 	return 0;
      
       ## t/helper/test-reftable.c ##
      @@ t/helper/test-reftable.c: int cmd__reftable(int argc, const char **argv)
     @@ t/helper/test-tool.h: int cmd__dump_cache_tree(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__fast_rebase(int argc, const char **argv);
       int cmd__genrandom(int argc, const char **argv);
     - int cmd__genzeros(int argc, const char **argv);

-- 
gitgitgadget

  parent reply	other threads:[~2020-11-26 19:42 UTC|newest]

Thread overview: 251+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-09-16 19:10 [PATCH 00/13] reftable library Han-Wen Nienhuys via GitGitGadget
2020-09-16 19:10 ` [PATCH 01/13] reftable: add LICENSE Han-Wen Nienhuys via GitGitGadget
2020-09-16 19:10 ` [PATCH 02/13] reftable: define the public API Han-Wen Nienhuys via GitGitGadget
2020-09-16 19:10 ` [PATCH 03/13] vcxproj: adjust for the reftable changes Johannes Schindelin via GitGitGadget
2020-09-16 19:10 ` [PATCH 04/13] reftable: add a barebones unittest framework Han-Wen Nienhuys via GitGitGadget
2020-09-16 19:10 ` [PATCH 05/13] reftable: utility functions Han-Wen Nienhuys via GitGitGadget
2020-09-16 19:10 ` [PATCH 06/13] reftable: (de)serialization for the polymorphic record type Han-Wen Nienhuys via GitGitGadget
2020-09-20  1:00   ` Junio C Hamano
2020-09-21 13:13     ` Han-Wen Nienhuys
2020-09-24  7:21       ` Jeff King
2020-09-24  7:31         ` Jeff King
2020-09-24 17:22           ` Junio C Hamano
2020-09-16 19:10 ` [PATCH 07/13] reftable: reading/writing blocks Han-Wen Nienhuys via GitGitGadget
2020-09-16 19:10 ` [PATCH 08/13] reftable: a generic binary tree implementation Han-Wen Nienhuys via GitGitGadget
2020-09-16 19:10 ` [PATCH 09/13] reftable: write reftable files Han-Wen Nienhuys via GitGitGadget
2020-09-16 19:10 ` [PATCH 10/13] reftable: read " Han-Wen Nienhuys via GitGitGadget
2020-09-16 19:10 ` [PATCH 11/13] reftable: file level tests Han-Wen Nienhuys via GitGitGadget
2020-09-16 19:10 ` [PATCH 12/13] reftable: rest of library Han-Wen Nienhuys via GitGitGadget
2020-09-16 19:10 ` [PATCH 13/13] reftable: "test-tool dump-reftable" command Han-Wen Nienhuys via GitGitGadget
2020-10-01 16:10 ` [PATCH v2 00/13] reftable library Han-Wen Nienhuys via GitGitGadget
2020-10-01 16:10   ` [PATCH v2 01/13] reftable: add LICENSE Han-Wen Nienhuys via GitGitGadget
2020-10-02  3:18     ` Jonathan Nieder
2020-10-01 16:10   ` [PATCH v2 02/13] reftable: define the public API Han-Wen Nienhuys via GitGitGadget
2020-10-02  3:58     ` Jonathan Nieder
2020-10-09 21:13       ` Emily Shaffer
2020-10-10 17:03         ` Han-Wen Nienhuys
2020-11-30 14:44         ` Han-Wen Nienhuys
2020-10-10 13:43       ` Han-Wen Nienhuys
2020-10-12 16:57         ` Jonathan Nieder
2020-11-30 14:55           ` Han-Wen Nienhuys
2020-10-08  1:41     ` Jonathan Tan
2020-10-10 16:57       ` Han-Wen Nienhuys
2020-10-01 16:10   ` [PATCH v2 03/13] vcxproj: adjust for the reftable changes Johannes Schindelin via GitGitGadget
2020-10-02  4:02     ` Jonathan Nieder
2020-10-02 11:43       ` Johannes Schindelin
2020-10-01 16:10   ` [PATCH v2 04/13] reftable: add a barebones unittest framework Han-Wen Nienhuys via GitGitGadget
2020-10-02  4:05     ` Jonathan Nieder
2020-10-08  1:45     ` Jonathan Tan
2020-10-08 22:31       ` Josh Steadmon
2020-10-01 16:10   ` [PATCH v2 05/13] reftable: utility functions Han-Wen Nienhuys via GitGitGadget
2020-10-02  4:12     ` Jonathan Nieder
2020-10-10 17:32       ` Han-Wen Nienhuys
2020-10-12 15:25         ` Jonathan Nieder
2020-10-12 17:05           ` Patrick Steinhardt
2020-10-12 17:45             ` Jonathan Nieder
2020-10-13 12:12             ` Johannes Schindelin
2020-10-13 15:47               ` Junio C Hamano
2020-10-15 11:46                 ` Johannes Schindelin
2020-10-15 16:23                   ` Junio C Hamano
2020-10-15 19:39                     ` Johannes Schindelin
2020-10-16  9:15                     ` Patrick Steinhardt
2020-10-02 14:01     ` Johannes Schindelin
2020-10-02 20:47       ` Junio C Hamano
2020-10-03  8:07         ` Johannes Schindelin
2020-10-08  1:48     ` Jonathan Tan
2020-10-10 17:28       ` Han-Wen Nienhuys
2020-10-11 10:52         ` Johannes Schindelin
2020-10-12 15:19           ` Jonathan Nieder
2020-10-12 18:44             ` Johannes Schindelin
2020-10-12 19:41               ` Jonathan Nieder
2020-10-12 20:27                 ` Johannes Schindelin
2020-10-12 16:42           ` Junio C Hamano
2020-10-12 19:01             ` Johannes Schindelin
2020-10-23  9:13         ` Ævar Arnfjörð Bjarmason
2020-10-23 17:36           ` Junio C Hamano
2020-10-01 16:10   ` [PATCH v2 06/13] reftable: (de)serialization for the polymorphic record type Han-Wen Nienhuys via GitGitGadget
2020-10-01 19:23     ` Junio C Hamano
2020-10-01 19:59       ` Ramsay Jones
2020-10-01 16:10   ` [PATCH v2 07/13] reftable: reading/writing blocks Han-Wen Nienhuys via GitGitGadget
2020-10-01 16:10   ` [PATCH v2 08/13] reftable: a generic binary tree implementation Han-Wen Nienhuys via GitGitGadget
2020-10-01 16:10   ` [PATCH v2 09/13] reftable: write reftable files Han-Wen Nienhuys via GitGitGadget
2020-10-01 16:11   ` [PATCH v2 10/13] reftable: read " Han-Wen Nienhuys via GitGitGadget
2020-10-01 16:11   ` [PATCH v2 11/13] reftable: file level tests Han-Wen Nienhuys via GitGitGadget
2020-10-01 16:11   ` [PATCH v2 12/13] reftable: rest of library Han-Wen Nienhuys via GitGitGadget
2020-10-02 13:57     ` Johannes Schindelin
2020-10-02 17:08       ` Junio C Hamano
2020-10-04 18:39         ` Johannes Schindelin
2020-10-01 16:11   ` [PATCH v2 13/13] reftable: "test-tool dump-reftable" command Han-Wen Nienhuys via GitGitGadget
2020-11-26 19:42   ` Han-Wen Nienhuys via GitGitGadget [this message]
2020-11-26 19:42     ` [PATCH v3 01/16] move sleep_millisec to git-compat-util.h Han-Wen Nienhuys via GitGitGadget
2020-11-26 19:42     ` [PATCH v3 02/16] init-db: set the_repository->hash_algo early on Han-Wen Nienhuys via GitGitGadget
2020-11-27 10:22       ` Ævar Arnfjörð Bjarmason
2020-11-26 19:42     ` [PATCH v3 03/16] reftable: add LICENSE Han-Wen Nienhuys via GitGitGadget
2020-11-27 10:23       ` Ævar Arnfjörð Bjarmason
2020-11-30 11:26         ` Han-Wen Nienhuys
2020-11-30 20:25           ` Han-Wen Nienhuys
2020-11-30 21:21             ` Felipe Contreras
2020-12-01  9:51               ` Han-Wen Nienhuys
2020-12-01 10:38                 ` Felipe Contreras
2020-12-01 11:45                   ` Ævar Arnfjörð Bjarmason
2020-12-01 13:34                     ` Han-Wen Nienhuys
2020-12-01 23:13                       ` Felipe Contreras
2020-12-01 23:03                     ` Felipe Contreras
2020-11-26 19:42     ` [PATCH v3 04/16] reftable: add error related functionality Han-Wen Nienhuys via GitGitGadget
2020-11-27  9:13       ` Felipe Contreras
2020-11-27 10:25       ` Ævar Arnfjörð Bjarmason
2020-11-30 11:27         ` Han-Wen Nienhuys
2020-11-26 19:42     ` [PATCH v3 05/16] reftable: utility functions Han-Wen Nienhuys via GitGitGadget
2020-11-27 10:18       ` Felipe Contreras
2020-11-27 10:33       ` Ævar Arnfjörð Bjarmason
2020-11-26 19:42     ` [PATCH v3 06/16] reftable: add blocksource, an abstraction for random access reads Han-Wen Nienhuys via GitGitGadget
2020-11-26 19:42     ` [PATCH v3 07/16] reftable: (de)serialization for the polymorphic record type Han-Wen Nienhuys via GitGitGadget
2020-11-26 19:42     ` [PATCH v3 08/16] reftable: reading/writing blocks Han-Wen Nienhuys via GitGitGadget
2020-11-26 19:42     ` [PATCH v3 09/16] reftable: a generic binary tree implementation Han-Wen Nienhuys via GitGitGadget
2020-11-26 19:42     ` [PATCH v3 10/16] reftable: write reftable files Han-Wen Nienhuys via GitGitGadget
2020-11-26 19:42     ` [PATCH v3 11/16] reftable: read " Han-Wen Nienhuys via GitGitGadget
2020-11-26 19:42     ` [PATCH v3 12/16] reftable: reftable file level tests Han-Wen Nienhuys via GitGitGadget
2020-11-26 19:42     ` [PATCH v3 13/16] reftable: rest of library Han-Wen Nienhuys via GitGitGadget
2020-11-26 19:42     ` [PATCH v3 14/16] Reftable support for git-core Han-Wen Nienhuys via GitGitGadget
2020-11-27 10:59       ` Ævar Arnfjörð Bjarmason
2020-11-26 19:42     ` [PATCH v3 15/16] git-prompt: prepare for reftable refs backend SZEDER Gábor via GitGitGadget
2020-11-26 19:42     ` [PATCH v3 16/16] Add "test-tool dump-reftable" command Han-Wen Nienhuys via GitGitGadget
2020-12-09 14:00     ` [PATCH v4 00/15] reftable library Han-Wen Nienhuys via GitGitGadget
2020-12-09 14:00       ` [PATCH v4 01/15] init-db: set the_repository->hash_algo early on Han-Wen Nienhuys via GitGitGadget
2020-12-09 14:00       ` [PATCH v4 02/15] reftable: add LICENSE Han-Wen Nienhuys via GitGitGadget
2020-12-09 14:00       ` [PATCH v4 03/15] reftable: add error related functionality Han-Wen Nienhuys via GitGitGadget
2020-12-09 14:00       ` [PATCH v4 04/15] reftable: utility functions Han-Wen Nienhuys via GitGitGadget
2020-12-09 14:00       ` [PATCH v4 05/15] reftable: add blocksource, an abstraction for random access reads Han-Wen Nienhuys via GitGitGadget
2020-12-09 14:00       ` [PATCH v4 06/15] reftable: (de)serialization for the polymorphic record type Han-Wen Nienhuys via GitGitGadget
2020-12-09 14:00       ` [PATCH v4 07/15] reftable: reading/writing blocks Han-Wen Nienhuys via GitGitGadget
2020-12-09 14:00       ` [PATCH v4 08/15] reftable: a generic binary tree implementation Han-Wen Nienhuys via GitGitGadget
2020-12-09 14:00       ` [PATCH v4 09/15] reftable: write reftable files Han-Wen Nienhuys via GitGitGadget
2020-12-09 14:00       ` [PATCH v4 10/15] reftable: read " Han-Wen Nienhuys via GitGitGadget
2020-12-09 14:00       ` [PATCH v4 11/15] reftable: reftable file level tests Han-Wen Nienhuys via GitGitGadget
2020-12-09 14:00       ` [PATCH v4 12/15] reftable: rest of library Han-Wen Nienhuys via GitGitGadget
2020-12-09 14:00       ` [PATCH v4 13/15] Reftable support for git-core Han-Wen Nienhuys via GitGitGadget
2021-01-21 15:55         ` Ævar Arnfjörð Bjarmason
2021-01-21 16:14           ` Han-Wen Nienhuys
2021-01-21 16:21             ` Han-Wen Nienhuys
2021-01-26 13:44               ` Ævar Arnfjörð Bjarmason
2021-04-23 10:22           ` Han-Wen Nienhuys
2021-04-26 13:23             ` Ævar Arnfjörð Bjarmason
2021-04-26 16:17               ` Han-Wen Nienhuys
2021-04-28 16:32                 ` Ævar Arnfjörð Bjarmason
2021-04-28 17:40                   ` Han-Wen Nienhuys
2021-02-22  0:41         ` [PATCH] refs: introduce API function to write invalid null ref Stefan Beller
2021-02-22  1:20           ` Eric Sunshine
2021-02-22  3:09             ` Eric Sunshine
2021-02-22 18:38           ` Han-Wen Nienhuys
2020-12-09 14:00       ` [PATCH v4 14/15] git-prompt: prepare for reftable refs backend SZEDER Gábor via GitGitGadget
2020-12-09 14:00       ` [PATCH v4 15/15] Add "test-tool dump-reftable" command Han-Wen Nienhuys via GitGitGadget
2021-03-12 20:19       ` [PATCH v5 00/15] reftable library Han-Wen Nienhuys via GitGitGadget
2021-03-12 20:19         ` [PATCH v5 01/15] init-db: set the_repository->hash_algo early on Han-Wen Nienhuys via GitGitGadget
2021-03-12 20:19         ` [PATCH v5 02/15] reftable: add LICENSE Han-Wen Nienhuys via GitGitGadget
2021-03-12 20:19         ` [PATCH v5 03/15] reftable: add error related functionality Han-Wen Nienhuys via GitGitGadget
2021-03-12 20:19         ` [PATCH v5 04/15] reftable: utility functions Han-Wen Nienhuys via GitGitGadget
2021-03-12 20:19         ` [PATCH v5 05/15] reftable: add blocksource, an abstraction for random access reads Han-Wen Nienhuys via GitGitGadget
2021-03-12 20:19         ` [PATCH v5 06/15] reftable: (de)serialization for the polymorphic record type Han-Wen Nienhuys via GitGitGadget
2021-03-12 20:19         ` [PATCH v5 07/15] reftable: reading/writing blocks Han-Wen Nienhuys via GitGitGadget
2021-03-12 20:19         ` [PATCH v5 08/15] reftable: a generic binary tree implementation Han-Wen Nienhuys via GitGitGadget
2021-03-12 20:19         ` [PATCH v5 09/15] reftable: write reftable files Han-Wen Nienhuys via GitGitGadget
2021-03-12 20:19         ` [PATCH v5 10/15] reftable: read " Han-Wen Nienhuys via GitGitGadget
2021-03-12 20:19         ` [PATCH v5 11/15] reftable: reftable file level tests Han-Wen Nienhuys via GitGitGadget
2021-03-12 20:19         ` [PATCH v5 12/15] reftable: rest of library Han-Wen Nienhuys via GitGitGadget
2021-03-12 20:19         ` [PATCH v5 13/15] Reftable support for git-core Han-Wen Nienhuys via GitGitGadget
2021-03-23 11:40           ` Derrick Stolee
2021-03-23 12:20             ` Ævar Arnfjörð Bjarmason
2021-03-23 20:14               ` Junio C Hamano
2021-03-23 20:12             ` Junio C Hamano
2021-03-12 20:19         ` [PATCH v5 14/15] git-prompt: prepare for reftable refs backend SZEDER Gábor via GitGitGadget
2021-03-12 20:19         ` [PATCH v5 15/15] Add "test-tool dump-reftable" command Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25         ` [PATCH v6 00/20] reftable library Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 01/20] init-db: set the_repository->hash_algo early on Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 02/20] reftable: add LICENSE Han-Wen Nienhuys via GitGitGadget
2021-04-13  7:28             ` Ævar Arnfjörð Bjarmason
2021-04-13 10:50               ` Han-Wen Nienhuys
2021-04-13 13:41                 ` Ævar Arnfjörð Bjarmason
2021-04-12 19:25           ` [PATCH v6 03/20] reftable: add error related functionality Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 04/20] reftable: utility functions Han-Wen Nienhuys via GitGitGadget
2021-04-13  8:02             ` Ævar Arnfjörð Bjarmason
2021-04-13 10:58               ` Han-Wen Nienhuys
2021-04-13 12:56                 ` Ævar Arnfjörð Bjarmason
2021-04-13 13:14             ` Ævar Arnfjörð Bjarmason
2021-04-15 15:00               ` Han-Wen Nienhuys
2021-04-12 19:25           ` [PATCH v6 05/20] reftable: add blocksource, an abstraction for random access reads Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 06/20] reftable: (de)serialization for the polymorphic record type Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 07/20] reftable: reading/writing blocks Han-Wen Nienhuys via GitGitGadget
2021-04-12 21:40             ` Junio C Hamano
2021-04-13  8:19             ` Ævar Arnfjörð Bjarmason
2021-04-15  8:57               ` Han-Wen Nienhuys
2021-04-12 19:25           ` [PATCH v6 08/20] reftable: a generic binary tree implementation Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 09/20] reftable: write reftable files Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 10/20] reftable: generic interface to tables Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 11/20] reftable: read reftable files Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 12/20] reftable: reftable file level tests Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 13/20] reftable: add a heap-based priority queue for reftable records Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 14/20] reftable: add merged table view Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 15/20] reftable: implement refname validation Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 16/20] reftable: implement stack, a mutable database of reftable files Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 17/20] reftable: add dump utility Han-Wen Nienhuys via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 18/20] Reftable support for git-core Han-Wen Nienhuys via GitGitGadget
2021-04-13  7:18             ` Ævar Arnfjörð Bjarmason
2021-04-14 16:44               ` Han-Wen Nienhuys
2021-04-16 14:55                 ` Ævar Arnfjörð Bjarmason
2021-04-16 18:47                 ` Junio C Hamano
2021-04-12 19:25           ` [PATCH v6 19/20] git-prompt: prepare for reftable refs backend SZEDER Gábor via GitGitGadget
2021-04-12 19:25           ` [PATCH v6 20/20] Add "test-tool dump-reftable" command Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37           ` [PATCH v7 00/28] reftable library Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 01/28] refs: ref_iterator_peel returns boolean, rather than peel_status Han-Wen Nienhuys via GitGitGadget
2021-04-20 18:47               ` Junio C Hamano
2021-04-21 10:15                 ` Han-Wen Nienhuys
2021-04-21 23:28                   ` Junio C Hamano
2021-04-19 11:37             ` [PATCH v7 02/28] refs: document reflog_expire_fn's flag argument Han-Wen Nienhuys via GitGitGadget
2021-04-20 19:34               ` Junio C Hamano
2021-04-27 15:21                 ` Han-Wen Nienhuys
2021-04-19 11:37             ` [PATCH v7 03/28] refs/debug: trace into reflog expiry too Han-Wen Nienhuys via GitGitGadget
2021-04-20 19:41               ` Junio C Hamano
2021-04-22 17:27                 ` Han-Wen Nienhuys
2021-04-19 11:37             ` [PATCH v7 04/28] hash.h: provide constants for the hash IDs Han-Wen Nienhuys via GitGitGadget
2021-04-20 19:49               ` Junio C Hamano
2021-04-21  1:04                 ` brian m. carlson
2021-04-21  9:43                   ` Han-Wen Nienhuys
2021-07-22  8:31                     ` Han-Wen Nienhuys
2021-04-19 11:37             ` [PATCH v7 05/28] init-db: set the_repository->hash_algo early on Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 06/28] reftable: add LICENSE Han-Wen Nienhuys via GitGitGadget
2021-04-21  7:48               ` Ævar Arnfjörð Bjarmason
2021-04-21  9:15                 ` Han-Wen Nienhuys
2021-04-19 11:37             ` [PATCH v7 07/28] reftable: add error related functionality Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 08/28] reftable: utility functions Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 09/28] reftable: add blocksource, an abstraction for random access reads Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 10/28] reftable: (de)serialization for the polymorphic record type Han-Wen Nienhuys via GitGitGadget
2021-05-04 17:23               ` Andrzej Hunt
2021-05-18 13:12                 ` Han-Wen Nienhuys
2021-04-19 11:37             ` [PATCH v7 11/28] Provide zlib's uncompress2 from compat/zlib-compat.c Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 12/28] reftable: reading/writing blocks Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 13/28] reftable: a generic binary tree implementation Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 14/28] reftable: write reftable files Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 15/28] reftable: generic interface to tables Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 16/28] reftable: read reftable files Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 17/28] reftable: reftable file level tests Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 18/28] reftable: add a heap-based priority queue for reftable records Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 19/28] reftable: add merged table view Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 20/28] reftable: implement refname validation Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 21/28] reftable: implement stack, a mutable database of reftable files Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 22/28] reftable: add dump utility Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 23/28] Reftable support for git-core Han-Wen Nienhuys via GitGitGadget
2021-04-20 22:44               ` Junio C Hamano
2021-04-21 10:19                 ` Han-Wen Nienhuys
2021-04-21 23:22                   ` Junio C Hamano
2021-05-04 17:24               ` Andrzej Hunt
2021-05-18 13:18                 ` Han-Wen Nienhuys
2021-05-18 13:30                   ` Han-Wen Nienhuys
2021-04-19 11:37             ` [PATCH v7 24/28] git-prompt: prepare for reftable refs backend SZEDER Gábor via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 25/28] Add "test-tool dump-reftable" command Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 26/28] t1301: document what needs to be done for REFTABLE Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 27/28] t1401,t2011: parameterize HEAD.lock " Han-Wen Nienhuys via GitGitGadget
2021-04-19 11:37             ` [PATCH v7 28/28] t1404: annotate test cases with REFFILES Han-Wen Nienhuys via GitGitGadget
2021-04-21  7:45             ` [PATCH v7 00/28] reftable library Ævar Arnfjörð Bjarmason
2021-04-21  9:52               ` Han-Wen Nienhuys
2021-04-21 11:21                 ` Ævar Arnfjörð Bjarmason
2021-04-26 17:59                   ` Han-Wen Nienhuys

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=pull.847.v3.git.git.1606419752.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=Johannes.Schindelin@gmx.de \
    --cc=avarab@gmail.com \
    --cc=emilyshaffer@google.com \
    --cc=git@vger.kernel.org \
    --cc=hanwen@google.com \
    --cc=hanwenn@gmail.com \
    --cc=jonathantanmy@google.com \
    --cc=jrnieder@gmail.com \
    --cc=peff@peff.net \
    --cc=ps@pks.im \
    --cc=ramsay@ramsayjones.plus.com \
    --cc=steadmon@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.