Skip to contentSkip to conversation

Reftable support git-core

PATCH Superseded Han-Wen Nienhuys via GitGitGadget 16 participants 409 messages Jan 23, 2020 – Jul 31, 2020 1 message not in the archive Page 6 of 6

Subsystems (2)
lore
Share & tools
lore
#388 lore raw

v20 cover Reftable support git-core

In reply to Han-Wen Nienhuys via GitGitGadget

This adds the reftable library, and hooks it up as a ref backend.

This is still based on next, as there are conflicts basing on master.

Includes testing support, to test: make -C t/ GIT_TEST_REFTABLE=1

Summary 455 tests fail

v24

* rebase
* FETCH_HEAD special casing

Han-Wen Nienhuys (19):
refs: add \t to reflog in the files backend
Split off reading loose ref data in separate function
t1400: use git rev-parse for testing PSEUDOREF existence Modify pseudo refs through ref backend storage
Make HEAD a PSEUDOREF rather than PER_WORKTREE.
Make refs_ref_exists public
Treat CHERRY_PICK_HEAD as a pseudo ref
Treat REVERT_HEAD as a pseudo ref
Move REF_LOG_ONLY to refs-internal.h
Iteration over entire ref namespace is iterating over "refs/" Add .gitattributes for the reftable/ directory
Add reftable library
Add standalone build infrastructure for reftable Reftable support for git-core
Read FETCH_HEAD as loose ref
Hookup unittests for the reftable library.
Add GIT_DEBUG_REFS debugging mechanism
Add reftable testing infrastructure
Add "test-tool dump-reftable" command.

Johannes Schindelin (1):
vcxproj: adjust for the reftable changes

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

 Documentation/git-update-ref.txt              |   13 +-
 .../technical/repository-version.txt          |    7 +
 Makefile                                      |   47 +-
 builtin/clone.c                               |    4 +-
 builtin/commit.c                              |   34 +-
 builtin/init-db.c                             |   55 +-
 builtin/merge.c                               |    2 +-
 builtin/worktree.c                            |   29 +-
 cache.h                                       |    8 +-
 config.mak.uname                              |    2 +-
 contrib/buildsystems/Generators/Vcxproj.pm    |   11 +-
 contrib/completion/git-prompt.sh              |    7 +-
 path.c                                        |    2 -
 path.h                                        |    9 +-
 ref-filter.c                                  |    7 +-
 refs.c                                        |  171 +-
 refs.h                                        |    6 +
 refs/debug.c                                  |  387 +++++
 refs/files-backend.c                          |   45 +-
 refs/refs-internal.h                          |   20 +
 refs/reftable-backend.c                       | 1415 +++++++++++++++++
 reftable/.gitattributes                       |    1 +
 reftable/BUILD                                |  203 +++
 reftable/LICENSE                              |   31 +
 reftable/README.md                            |   33 +
 reftable/VERSION                              |    1 +
 reftable/WORKSPACE                            |   14 +
 reftable/basics.c                             |  215 +++
 reftable/basics.h                             |   53 +
 reftable/block.c                              |  432 +++++
 reftable/block.h                              |  129 ++
 reftable/block_test.c                         |  157 ++
 reftable/compat.c                             |   98 ++
 reftable/compat.h                             |   48 +
 reftable/constants.h                          |   21 +
 reftable/dump.c                               |  212 +++
 reftable/file.c                               |   95 ++
 reftable/iter.c                               |  242 +++
 reftable/iter.h                               |   72 +
 reftable/merged.c                             |  317 ++++
 reftable/merged.h                             |   43 +
 reftable/merged_test.c                        |  286 ++++
 reftable/pq.c                                 |  115 ++
 reftable/pq.h                                 |   34 +
 reftable/reader.c                             |  744 +++++++++
 reftable/reader.h                             |   65 +
 reftable/record.c                             | 1126 +++++++++++++
 reftable/record.h                             |  143 ++
 reftable/record_test.c                        |  410 +++++
 reftable/refname.c                            |  209 +++
 reftable/refname.h                            |   38 +
 reftable/refname_test.c                       |   99 ++
 reftable/reftable-tests.h                     |   22 +
 reftable/reftable.c                           |  154 ++
 reftable/reftable.h                           |  585 +++++++
 reftable/reftable_test.c                      |  632 ++++++++
 reftable/stack.c                              | 1225 ++++++++++++++
 reftable/stack.h                              |   50 +
 reftable/stack_test.c                         |  787 +++++++++
 reftable/strbuf.c                             |  206 +++
 reftable/strbuf.h                             |   88 +
 reftable/strbuf_test.c                        |   39 +
 reftable/system.h                             |   53 +
 reftable/test_framework.c                     |   69 +
 reftable/test_framework.h                     |   64 +
 reftable/tree.c                               |   63 +
 reftable/tree.h                               |   34 +
 reftable/tree_test.c                          |   62 +
 reftable/update.sh                            |   19 +
 reftable/writer.c                             |  663 ++++++++
 reftable/writer.h                             |   60 +
 reftable/zlib-compat.c                        |   92 ++
 reftable/zlib.BUILD                           |   36 +
 repository.c                                  |    2 +
 repository.h                                  |    3 +
 sequencer.c                                   |   56 +-
 setup.c                                       |   10 +-
 t/helper/test-reftable.c                      |   20 +
 t/helper/test-tool.c                          |    2 +
 t/helper/test-tool.h                          |    2 +
 t/t0031-reftable.sh                           |  185 +++
 t/t0033-debug-refs.sh                         |   18 +
 t/t1400-update-ref.sh                         |   30 +-
 t/t1405-main-ref-store.sh                     |    5 +-
 t/t1409-avoid-packing-refs.sh                 |    6 +
 t/t1450-fsck.sh                               |    6 +
 t/t3210-pack-refs.sh                          |    6 +
 t/test-lib.sh                                 |    5 +
 wt-status.c                                   |    6 +-
 89 files changed, 13076 insertions(+), 256 deletions(-)
 create mode 100644 refs/debug.c
 create mode 100644 refs/reftable-backend.c
 create mode 100644 reftable/.gitattributes
 create mode 100644 reftable/BUILD
 create mode 100644 reftable/LICENSE
 create mode 100644 reftable/README.md
 create mode 100644 reftable/VERSION
 create mode 100644 reftable/WORKSPACE
 create mode 100644 reftable/basics.c
 create mode 100644 reftable/basics.h
 create mode 100644 reftable/block.c
 create mode 100644 reftable/block.h
 create mode 100644 reftable/block_test.c
 create mode 100644 reftable/compat.c
 create mode 100644 reftable/compat.h
 create mode 100644 reftable/constants.h
 create mode 100644 reftable/dump.c
 create mode 100644 reftable/file.c
 create mode 100644 reftable/iter.c
 create mode 100644 reftable/iter.h
 create mode 100644 reftable/merged.c
 create mode 100644 reftable/merged.h
 create mode 100644 reftable/merged_test.c
 create mode 100644 reftable/pq.c
 create mode 100644 reftable/pq.h
 create mode 100644 reftable/reader.c
 create mode 100644 reftable/reader.h
 create mode 100644 reftable/record.c
 create mode 100644 reftable/record.h
 create mode 100644 reftable/record_test.c
 create mode 100644 reftable/refname.c
 create mode 100644 reftable/refname.h
 create mode 100644 reftable/refname_test.c
 create mode 100644 reftable/reftable-tests.h
 create mode 100644 reftable/reftable.c
 create mode 100644 reftable/reftable.h
 create mode 100644 reftable/reftable_test.c
 create mode 100644 reftable/stack.c
 create mode 100644 reftable/stack.h
 create mode 100644 reftable/stack_test.c
 create mode 100644 reftable/strbuf.c
 create mode 100644 reftable/strbuf.h
 create mode 100644 reftable/strbuf_test.c
 create mode 100644 reftable/system.h
 create mode 100644 reftable/test_framework.c
 create mode 100644 reftable/test_framework.h
 create mode 100644 reftable/tree.c
 create mode 100644 reftable/tree.h
 create mode 100644 reftable/tree_test.c
 create mode 100755 reftable/update.sh
 create mode 100644 reftable/writer.c
 create mode 100644 reftable/writer.h
 create mode 100644 reftable/zlib-compat.c
 create mode 100644 reftable/zlib.BUILD
 create mode 100644 t/helper/test-reftable.c
 create mode 100755 t/t0031-reftable.sh
 create mode 100755 t/t0033-debug-refs.sh

base-commit: e8ab941b671da6890181aea5b5755d1d9eea24ec
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-539%2Fhanwen%2Freftable-v20
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-539/hanwen/reftable-v20
Pull-Request: https://github.com/gitgitgadget/git/pull/539

Range-diff vs v19:

  1:  596c316da4 <  -:  ---------- lib-t6000.sh: write tag using git-update-ref
  2:  277da0cf7e <  -:  ---------- t3432: use git-reflog to inspect the reflog for HEAD
  3:  125695ce92 <  -:  ---------- checkout: add '\n' to reflog message
  -:  ---------- >  1:  05ced51683 refs: add \t to reflog in the files backend
  -:  ---------- >  2:  d0f0680c0e Split off reading loose ref data in separate function
  -:  ---------- >  3:  716cb30c1b t1400: use git rev-parse for testing PSEUDOREF existence
  4:  5715681b3f !  4:  400ffe531f Write pseudorefs through ref backends.
     @@ Metadata
      Author: Han-Wen Nienhuys <[email protected]>
      
       ## Commit message ##
     -    Write pseudorefs through ref backends.
     +    Modify pseudo refs through ref backend storage
      
     -    Pseudorefs store transient data in in the repository. Examples are HEAD,
     -    CHERRY_PICK_HEAD, etc.
     +    The previous behavior was introduced in commit 74ec19d4be
     +    ("pseudorefs: create and use pseudoref update and delete functions",
     +    Jul 31, 2015), with the justification "alternate ref backends still
     +    need to store pseudorefs in GIT_DIR".
      
     -    These refs have always been read through the ref backends, but they were written
     -    in a one-off routine that wrote an object ID or symref directly into
     -    .git/<pseudo_ref_name>.
     +    Refs such as REBASE_HEAD are read through the ref backend. This can
     +    only work consistently if they are written through the ref backend as
     +    well. Tooling that works directly on files under .git should be
     +    updated to use git commands to read refs instead.
      
     -    This causes problems when introducing a new ref storage backend. To remedy this,
     -    extend the ref backend implementation with a write_pseudoref_fn and
     -    update_pseudoref_fn.
     +    The following behaviors change:
     +
     +    * Updates to pseudorefs (eg. ORIG_HEAD) with
     +      core.logAllRefUpdates=always will create reflogs for the pseudoref.
     +
     +    * non-HEAD pseudoref symrefs are also dereferenced on deletion. Update
     +      t1405 accordingly.
      
          Signed-off-by: Han-Wen Nienhuys <[email protected]>
      
     - ## refs.c ##
     -@@ refs.c: int ref_exists(const char *refname)
     - 	return refs_ref_exists(get_main_ref_store(the_repository), refname);
     - }
     + ## Documentation/git-update-ref.txt ##
     +@@ Documentation/git-update-ref.txt: still see a subset of the modifications.
     + 
     + LOGGING UPDATES
     + ---------------
     +-If config parameter "core.logAllRefUpdates" is true and the ref is one under
     +-"refs/heads/", "refs/remotes/", "refs/notes/", or the symbolic ref HEAD; or
     +-the file "$GIT_DIR/logs/<ref>" exists then `git update-ref` will append
     +-a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all
     +-symbolic refs before creating the log name) describing the change
     +-in ref value.  Log lines are formatted as:
     ++If config parameter "core.logAllRefUpdates" is true and the ref is one
     ++under "refs/heads/", "refs/remotes/", "refs/notes/", or a pseudoref
     ++like HEAD or ORIG_HEAD; or the file "$GIT_DIR/logs/<ref>" exists then
     ++`git update-ref` will append a line to the log file
     ++"$GIT_DIR/logs/<ref>" (dereferencing all symbolic refs before creating
     ++the log name) describing the change in ref value.  Log lines are
     ++formatted as:
     + 
     +     oldsha1 SP newsha1 SP committer LF
       
     -+int delete_pseudoref(const char *pseudoref, const struct object_id *old_oid)
     -+{
     -+	return refs_delete_pseudoref(get_main_ref_store(the_repository),
     -+				     pseudoref, old_oid);
     -+}
     -+
     - static int filter_refs(const char *refname, const struct object_id *oid,
     - 			   int flags, void *data)
     - {
     +
     + ## refs.c ##
      @@ refs.c: long get_files_ref_lock_timeout_ms(void)
       	return timeout_ms;
       }
     @@ refs.c: long get_files_ref_lock_timeout_ms(void)
      -
      -	return 0;
      -}
     - 
     +-
       int refs_delete_ref(struct ref_store *refs, const char *msg,
       		    const char *refname,
     + 		    const struct object_id *old_oid,
      @@ refs.c: int refs_delete_ref(struct ref_store *refs, const char *msg,
     + 	struct ref_transaction *transaction;
     + 	struct strbuf err = STRBUF_INIT;
       
     - 	if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
     - 		assert(refs == get_main_ref_store(the_repository));
     +-	if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
     +-		assert(refs == get_main_ref_store(the_repository));
      -		return delete_pseudoref(refname, old_oid);
     -+		return refs_delete_pseudoref(refs, refname, old_oid);
     - 	}
     - 
     +-	}
     +-
       	transaction = ref_store_transaction_begin(refs, &err);
     + 	if (!transaction ||
     + 	    ref_transaction_delete(transaction, refname, old_oid,
      @@ refs.c: int refs_update_ref(struct ref_store *refs, const char *msg,
     + 	struct strbuf err = STRBUF_INIT;
     + 	int ret = 0;
       
     - 	if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
     - 		assert(refs == get_main_ref_store(the_repository));
     +-	if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
     +-		assert(refs == get_main_ref_store(the_repository));
      -		ret = write_pseudoref(refname, new_oid, old_oid, &err);
     -+		ret = refs_write_pseudoref(refs, refname, new_oid, old_oid,
     -+					   &err);
     - 	} else {
     - 		t = ref_store_transaction_begin(refs, &err);
     - 		if (!t ||
     -@@ refs.c: int head_ref(each_ref_fn fn, void *cb_data)
     - 	return refs_head_ref(get_main_ref_store(the_repository), fn, cb_data);
     - }
     - 
     -+int refs_write_pseudoref(struct ref_store *refs, const char *pseudoref,
     -+			 const struct object_id *oid,
     -+			 const struct object_id *old_oid, struct strbuf *err)
     -+{
     -+	return refs->be->write_pseudoref(refs, pseudoref, oid, old_oid, err);
     -+}
     -+
     -+int refs_delete_pseudoref(struct ref_store *refs, const char *pseudoref,
     -+			  const struct object_id *old_oid)
     -+{
     -+	return refs->be->delete_pseudoref(refs, pseudoref, old_oid);
     -+}
     -+
     - struct ref_iterator *refs_ref_iterator_begin(
     - 		struct ref_store *refs,
     - 		const char *prefix, int trim, int flags)
     -
     - ## refs.h ##
     -@@ refs.h: int update_ref(const char *msg, const char *refname,
     - 	       const struct object_id *new_oid, const struct object_id *old_oid,
     - 	       unsigned int flags, enum action_on_err onerr);
     - 
     -+/* Pseudorefs (eg. HEAD, CHERRY_PICK_HEAD) have a separate routines for updating
     -+   and deletion as they cannot take part in normal transactional updates.
     -+   Pseudorefs should only be written for the main repository.
     -+*/
     -+int refs_write_pseudoref(struct ref_store *refs, const char *pseudoref,
     -+			 const struct object_id *oid,
     -+			 const struct object_id *old_oid, struct strbuf *err);
     -+int refs_delete_pseudoref(struct ref_store *refs, const char *pseudoref,
     -+			  const struct object_id *old_oid);
     -+int delete_pseudoref(const char *pseudoref, const struct object_id *old_oid);
     -+
     - int parse_hide_refs_config(const char *var, const char *value, const char *);
     - 
     - /*
     -
     - ## refs/files-backend.c ##
     -@@ refs/files-backend.c: static int lock_raw_ref(struct files_ref_store *refs,
     - 	return ret;
     - }
     - 
     -+static int files_write_pseudoref(struct ref_store *ref_store,
     -+				 const char *pseudoref,
     -+				 const struct object_id *oid,
     -+				 const struct object_id *old_oid,
     -+				 struct strbuf *err)
     -+{
     -+	struct files_ref_store *refs =
     -+		files_downcast(ref_store, REF_STORE_READ, "write_pseudoref");
     -+	int fd;
     -+	struct lock_file lock = LOCK_INIT;
     -+	struct strbuf filename = STRBUF_INIT;
     -+	struct strbuf buf = STRBUF_INIT;
     -+	int ret = -1;
     -+
     -+	if (!oid)
     -+		return 0;
     -+
     -+	strbuf_addf(&filename, "%s/%s", refs->gitdir, pseudoref);
     -+	fd = hold_lock_file_for_update_timeout(&lock, filename.buf, 0,
     -+					       get_files_ref_lock_timeout_ms());
     -+	if (fd < 0) {
     -+		strbuf_addf(err, _("could not open '%s' for writing: %s"),
     -+			    buf.buf, strerror(errno));
     -+		goto done;
     -+	}
     -+
     -+	if (old_oid) {
     -+		struct object_id actual_old_oid;
     -+
     -+		if (read_ref(pseudoref, &actual_old_oid)) {
     -+			if (!is_null_oid(old_oid)) {
     -+				strbuf_addf(err, _("could not read ref '%s'"),
     -+					    pseudoref);
     -+				rollback_lock_file(&lock);
     -+				goto done;
     -+			}
     -+		} else if (is_null_oid(old_oid)) {
     -+			strbuf_addf(err, _("ref '%s' already exists"),
     -+				    pseudoref);
     -+			rollback_lock_file(&lock);
     -+			goto done;
     -+		} else if (!oideq(&actual_old_oid, old_oid)) {
     -+			strbuf_addf(err,
     -+				    _("unexpected object ID when writing '%s'"),
     -+				    pseudoref);
     -+			rollback_lock_file(&lock);
     -+			goto done;
     -+		}
     -+	}
     -+
     -+	strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
     -+	if (write_in_full(fd, buf.buf, buf.len) < 0) {
     -+		strbuf_addf(err, _("could not write to '%s'"), filename.buf);
     -+		rollback_lock_file(&lock);
     -+		goto done;
     -+	}
     -+
     -+	commit_lock_file(&lock);
     -+	ret = 0;
     -+done:
     -+	strbuf_release(&buf);
     -+	strbuf_release(&filename);
     -+	return ret;
     -+}
     -+
     -+static int files_delete_pseudoref(struct ref_store *ref_store,
     -+				  const char *pseudoref,
     -+				  const struct object_id *old_oid)
     -+{
     -+	struct files_ref_store *refs =
     -+		files_downcast(ref_store, REF_STORE_READ, "delete_pseudoref");
     -+	struct strbuf filename = STRBUF_INIT;
     -+	int ret = -1;
     -+
     -+	strbuf_addf(&filename, "%s/%s", refs->gitdir, pseudoref);
     -+
     -+	if (old_oid && !is_null_oid(old_oid)) {
     -+		struct lock_file lock = LOCK_INIT;
     -+		int fd;
     -+		struct object_id actual_old_oid;
     -+
     -+		fd = hold_lock_file_for_update_timeout(
     -+			&lock, filename.buf, 0,
     -+			get_files_ref_lock_timeout_ms());
     -+		if (fd < 0) {
     -+			error_errno(_("could not open '%s' for writing"),
     -+				    filename.buf);
     -+			goto done;
     -+		}
     -+		if (read_ref(pseudoref, &actual_old_oid))
     -+			die(_("could not read ref '%s'"), pseudoref);
     -+		if (!oideq(&actual_old_oid, old_oid)) {
     -+			error(_("unexpected object ID when deleting '%s'"),
     -+			      pseudoref);
     -+			rollback_lock_file(&lock);
     -+			goto done;
     -+		}
     -+
     -+		unlink(filename.buf);
     -+		rollback_lock_file(&lock);
     -+	} else {
     -+		unlink(filename.buf);
     -+	}
     -+	ret = 0;
     -+done:
     -+	strbuf_release(&filename);
     -+	return ret;
     -+}
     -+
     - struct files_ref_iterator {
     - 	struct ref_iterator base;
     - 
     -@@ refs/files-backend.c: struct ref_storage_be refs_be_files = {
     - 	files_rename_ref,
     - 	files_copy_ref,
     - 
     -+	files_write_pseudoref,
     -+	files_delete_pseudoref,
     -+
     - 	files_ref_iterator_begin,
     - 	files_read_raw_ref,
     - 
     -@@ refs/files-backend.c: struct ref_storage_be refs_be_files = {
     - 	files_reflog_exists,
     - 	files_create_reflog,
     - 	files_delete_reflog,
     --	files_reflog_expire
     -+	files_reflog_expire,
     - };
     +-	} else {
     +-		t = ref_store_transaction_begin(refs, &err);
     +-		if (!t ||
     +-		    ref_transaction_update(t, refname, new_oid, old_oid,
     +-					   flags, msg, &err) ||
     +-		    ref_transaction_commit(t, &err)) {
     +-			ret = 1;
     +-			ref_transaction_free(t);
     +-		}
     ++	t = ref_store_transaction_begin(refs, &err);
     ++	if (!t ||
     ++	    ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
     ++				   &err) ||
     ++	    ref_transaction_commit(t, &err)) {
     ++		ret = 1;
     ++		ref_transaction_free(t);
     + 	}
     + 	if (ret) {
     + 		const char *str = _("update_ref failed for ref '%s': %s");
      
     - ## refs/packed-backend.c ##
     -@@ refs/packed-backend.c: static int packed_copy_ref(struct ref_store *ref_store,
     - 	BUG("packed reference store does not support copying references");
     - }
     - 
     -+static int packed_write_pseudoref(struct ref_store *ref_store,
     -+				  const char *pseudoref,
     -+				  const struct object_id *oid,
     -+				  const struct object_id *old_oid,
     -+				  struct strbuf *err)
     -+{
     -+	BUG("packed reference store does not support writing pseudo-references");
     -+}
     -+
     -+static int packed_delete_pseudoref(struct ref_store *ref_store,
     -+				   const char *pseudoref,
     -+				   const struct object_id *old_oid)
     -+{
     -+	BUG("packed reference store does not support deleting pseudo-references");
     -+}
     -+
     - static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_store)
     - {
     - 	return empty_ref_iterator_begin();
     -@@ refs/packed-backend.c: struct ref_storage_be refs_be_packed = {
     - 	packed_rename_ref,
     - 	packed_copy_ref,
     - 
     -+	packed_write_pseudoref,
     -+	packed_delete_pseudoref,
     -+
     - 	packed_ref_iterator_begin,
     - 	packed_read_raw_ref,
     - 
     -@@ refs/packed-backend.c: struct ref_storage_be refs_be_packed = {
     - 	packed_reflog_exists,
     - 	packed_create_reflog,
     - 	packed_delete_reflog,
     --	packed_reflog_expire
     -+	packed_reflog_expire,
     - };
     + ## t/t1400-update-ref.sh ##
     +@@ t/t1400-update-ref.sh: test_expect_success 'core.logAllRefUpdates=always creates reflog by default' '
     + 	git reflog exists $outside
     + '
     + 
     +-test_expect_success 'core.logAllRefUpdates=always creates no reflog for ORIG_HEAD' '
     ++test_expect_success 'core.logAllRefUpdates=always creates reflog for ORIG_HEAD' '
     + 	test_config core.logAllRefUpdates always &&
     + 	git update-ref ORIG_HEAD $A &&
     +-	test_must_fail git reflog exists ORIG_HEAD
     ++	git reflog exists ORIG_HEAD
     + '
     + 
     + test_expect_success '--no-create-reflog overrides core.logAllRefUpdates=always' '
     +@@ t/t1400-update-ref.sh: test_expect_success 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER
     + test_expect_success 'given old value for missing pseudoref, do not create' '
     + 	test_must_fail git update-ref PSEUDOREF $A $B 2>err &&
     + 	test_must_fail git rev-parse PSEUDOREF &&
     +-	test_i18ngrep "could not read ref" err
     ++	test_i18ngrep "unable to resolve reference" err
     + '
     + 
     + test_expect_success 'create pseudoref' '
     +@@ t/t1400-update-ref.sh: test_expect_success 'overwrite pseudoref with correct old value' '
     + test_expect_success 'do not overwrite pseudoref with wrong old value' '
     + 	test_must_fail git update-ref PSEUDOREF $D $E 2>err &&
     + 	test $C = $(git rev-parse PSEUDOREF) &&
     +-	test_i18ngrep "unexpected object ID" err
     ++	test_i18ngrep "cannot lock ref.*expected" err
     + '
     + 
     + test_expect_success 'delete pseudoref' '
     +@@ t/t1400-update-ref.sh: test_expect_success 'do not delete pseudoref with wrong old value' '
     + 	git update-ref PSEUDOREF $A &&
     + 	test_must_fail git update-ref -d PSEUDOREF $B 2>err &&
     + 	test $A = $(git rev-parse PSEUDOREF) &&
     +-	test_i18ngrep "unexpected object ID" err
     ++	test_i18ngrep "cannot lock ref.*expected" err
     + '
     + 
     + test_expect_success 'delete pseudoref with correct old value' '
      
     - ## refs/refs-internal.h ##
     -@@ refs/refs-internal.h: typedef int copy_ref_fn(struct ref_store *ref_store,
     - 			  const char *oldref, const char *newref,
     - 			  const char *logmsg);
     - 
     -+typedef int write_pseudoref_fn(struct ref_store *ref_store,
     -+			       const char *pseudoref,
     -+			       const struct object_id *oid,
     -+			       const struct object_id *old_oid,
     -+			       struct strbuf *err);
     -+
     -+/*
     -+ * Deletes a pseudoref. Deletion always succeeds (even if the pseudoref doesn't
     -+ * exist.), except if old_oid is specified. If it is, it can fail due to lock
     -+ * failure, failure reading the old OID, or an OID mismatch
     -+ */
     -+typedef int delete_pseudoref_fn(struct ref_store *ref_store,
     -+				const char *pseudoref,
     -+				const struct object_id *old_oid);
     -+
     - /*
     -  * Iterate over the references in `ref_store` whose names start with
     -  * `prefix`. `prefix` is matched as a literal string, without regard
     -@@ refs/refs-internal.h: struct ref_storage_be {
     - 	rename_ref_fn *rename_ref;
     - 	copy_ref_fn *copy_ref;
     - 
     -+	write_pseudoref_fn *write_pseudoref;
     -+	delete_pseudoref_fn *delete_pseudoref;
     -+
     - 	ref_iterator_begin_fn *iterator_begin;
     - 	read_raw_ref_fn *read_raw_ref;
     - 
     + ## t/t1405-main-ref-store.sh ##
     +@@ t/t1405-main-ref-store.sh: test_expect_success 'create_symref(FOO, refs/heads/master)' '
     + test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '
     + 	git rev-parse FOO -- &&
     + 	git rev-parse refs/tags/new-tag -- &&
     +-	$RUN delete-refs 0 nothing FOO refs/tags/new-tag &&
     ++	m=$(git rev-parse master) &&
     ++	REF_NO_DEREF=1 &&
     ++	$RUN delete-refs $REF_NO_DEREF nothing FOO refs/tags/new-tag &&
     ++	test_must_fail git rev-parse --symbolic-full-name FOO &&
     + 	test_must_fail git rev-parse FOO -- &&
     + 	test_must_fail git rev-parse refs/tags/new-tag --
     + '
  -:  ---------- >  5:  e9df96925b Make HEAD a PSEUDOREF rather than PER_WORKTREE.
  5:  c8cf2d2ce1 =  6:  d33ea20fba Make refs_ref_exists public
  6:  e36b29de79 <  -:  ---------- Treat BISECT_HEAD as a pseudo ref
  7:  1676df9851 !  7:  d12f6e905c Treat CHERRY_PICK_HEAD as a pseudo ref
     @@ Metadata
       ## Commit message ##
          Treat CHERRY_PICK_HEAD as a pseudo ref
      
     -    Check for existence and delete CHERRY_PICK_HEAD through pseudo ref functions.
     +    Check for existence and delete CHERRY_PICK_HEAD through ref functions.
          This will help cherry-pick work with alternate ref storage backends.
      
          Signed-off-by: Han-Wen Nienhuys <[email protected]>
     @@ sequencer.c: static void print_advice(struct repository *r, int show_hint,
       		 * of the commit itself so remove CHERRY_PICK_HEAD
       		 */
      -		unlink(git_path_cherry_pick_head(r));
     -+		refs_delete_pseudoref(get_main_ref_store(r), "CHERRY_PICK_HEAD",
     -+				      NULL);
     ++		refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
     ++				NULL, 0);
       		return;
       	}
       
     @@ sequencer.c: static int do_commit(struct repository *r,
       		strbuf_release(&sb);
       		if (!res) {
      -			unlink(git_path_cherry_pick_head(r));
     -+			refs_delete_pseudoref(get_main_ref_store(r),
     -+					      "CHERRY_PICK_HEAD", NULL);
     ++			refs_delete_ref(get_main_ref_store(r), "",
     ++					"CHERRY_PICK_HEAD", NULL, 0);
       			unlink(git_path_merge_msg(r));
       			if (!is_rebase_i(opts))
       				print_commit_summary(r, NULL, &oid,
     @@ sequencer.c: static int do_pick_commit(struct repository *r,
       	} else if (allow == 2) {
       		drop_commit = 1;
      -		unlink(git_path_cherry_pick_head(r));
     -+		refs_delete_pseudoref(get_main_ref_store(r), "CHERRY_PICK_HEAD",
     -+				      NULL);
     ++		refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
     ++				NULL, 0);
       		unlink(git_path_merge_msg(r));
       		fprintf(stderr,
       			_("dropping %s %s -- patch contents already upstream\n"),
     @@ sequencer.c: void sequencer_post_commit_cleanup(struct repository *r, int verbos
      -	if (file_exists(git_path_cherry_pick_head(r))) {
      -		if (!unlink(git_path_cherry_pick_head(r)) && verbose)
      +	if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD")) {
     -+		if (!refs_delete_pseudoref(get_main_ref_store(r),
     -+					   "CHERRY_PICK_HEAD", NULL) &&
     ++		if (!refs_delete_ref(get_main_ref_store(r), "",
     ++				     "CHERRY_PICK_HEAD", NULL, 0) &&
      +		    verbose)
       			warning(_("cancelling a cherry picking in progress"));
       		opts.action = REPLAY_PICK;
     @@ sequencer.c: static int do_merge(struct repository *r,
       
       		strbuf_release(&ref_name);
      -		unlink(git_path_cherry_pick_head(r));
     -+		refs_delete_pseudoref(get_main_ref_store(r), "CHERRY_PICK_HEAD",
     -+				      NULL);
     ++		refs_delete_ref(get_main_ref_store(r), "", "CHERRY_PICK_HEAD",
     ++				NULL, 0);
       		rollback_lock_file(&lock);
       
       		rollback_lock_file(&lock);
     @@ sequencer.c: static int commit_staged_changes(struct repository *r,
      -		if (file_exists(cherry_pick_head) && unlink(cherry_pick_head))
      +		if (refs_ref_exists(get_main_ref_store(r),
      +				    "CHERRY_PICK_HEAD") &&
     -+		    refs_delete_pseudoref(get_main_ref_store(r),
     -+					  "CHERRY_PICK_HEAD", NULL))
     ++		    refs_delete_ref(get_main_ref_store(r), "",
     ++				    "CHERRY_PICK_HEAD", NULL, 0))
       			return error(_("could not remove CHERRY_PICK_HEAD"));
       		if (!final_fixup)
       			return 0;
  8:  0a5f97ea34 !  8:  224f7bf224 Treat REVERT_HEAD as a pseudo ref
     @@ sequencer.c: void sequencer_post_commit_cleanup(struct repository *r, int verbos
      -	if (file_exists(git_path_revert_head(r))) {
      -		if (!unlink(git_path_revert_head(r)) && verbose)
      +	if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) {
     -+		if (!refs_delete_pseudoref(get_main_ref_store(r), "REVERT_HEAD",
     -+					   NULL) &&
     ++		if (!refs_delete_ref(get_main_ref_store(r), "", "REVERT_HEAD",
     ++				     NULL, 0) &&
      +		    verbose)
       			warning(_("cancelling a revert in progress"));
       		opts.action = REPLAY_REVERT;
  9:  9463ed9093 =  9:  4b601b545c Move REF_LOG_ONLY to refs-internal.h
 10:  a116aebe11 ! 10:  ea8a78374f Iterate over the "refs/" namespace in for_each_[raw]ref

@@ Metadata
Author: Han-Wen Nienhuys <[email protected]>

## Commit message ##
- Iterate over the "refs/" namespace in for_each_[raw]ref
+ Iteration over entire ref namespace is iterating over "refs/"
+
+ This changes behavior for in for_each_[raw]ref and
+ for_each_fullref_in_pattern.

This happens implicitly in the files/packed ref backend; making it explicit simplifies adding alternate ref storage backends, such as
@@ Commit message

+ ## ref-filter.c ##
+@@ ref-filter.c: static int for_each_fullref_in_pattern(struct ref_filter *filter,
+ {
+ struct string_list prefixes = STRING_LIST_INIT_DUP;
+ struct string_list_item *prefix;
++ const char *all = "refs/";
+ int ret;
+
+ if (!filter->match_as_path) {
+@@ ref-filter.c: static int for_each_fullref_in_pattern(struct ref_filter *filter,
+ * prefixes like "refs/heads/" etc. are stripped off,
+ * so we have to look at everything:
+ */
+- return for_each_fullref_in("", cb, cb_data, broken);
++ return for_each_fullref_in(all, cb, cb_data, broken);
+ }
+
+ if (filter->ignore_case) {
+@@ ref-filter.c: static int for_each_fullref_in_pattern(struct ref_filter *filter,
+ * so just return everything and let the caller
+ * sort it out.
+ */
+- return for_each_fullref_in("", cb, cb_data, broken);
++ return for_each_fullref_in(all, cb, cb_data, broken);
+ }
+
+ if (!filter->name_patterns[0]) {
+ /* no patterns; we have to look at everything */
+- return for_each_fullref_in("", cb, cb_data, broken);
++ return for_each_fullref_in(all, cb, cb_data, broken);
+ }
+
+ find_longest_prefixes(&prefixes, filter->name_patterns);
+
## refs.c ##
@@ refs.c: static int do_for_each_ref(struct ref_store *refs, const char *prefix,

 11:  e4545658ed = 11:  0606b2a1c8 Add .gitattributes for the reftable/ directory
 12:  169f6c7f54 = 12:  12d98125c2 Add reftable library
 13:  d155240b16 = 13:  70ce4ce49d Add standalone build infrastructure for reftable
 14:  073bff7279 ! 14:  7af3b2340e Reftable support for git-core
     @@ builtin/init-db.c: int init_db(const char *git_dir, const char *real_git_dir,
       		git_config_set("receive.denyNonFastforwards", "true");
       	}
       
     -+	git_config_set("extensions.refStorage", ref_storage_format);
     ++	if (!strcmp(ref_storage_format, "reftable"))
     ++		git_config_set("extensions.refStorage", ref_storage_format);
      +
       	if (!(flags & INIT_DB_QUIET)) {
       		int len = strlen(git_dir);
     @@ cache.h: int path_inside_repo(const char *prefix, const char *path);
       void sanitize_stdfds(void);
       int daemonize(void);
      @@ cache.h: struct repository_format {
     + 	int is_bare;
       	int hash_algo;
     - 	int has_extensions;
       	char *work_tree;
      +	char *ref_storage;
       	struct string_list unknown_extensions;
     + 	struct string_list v1_only_extensions;
       };
     - 
      
       ## refs.c ##
      @@
     @@ refs.c: struct ref_store *get_main_ref_store(struct repository *r)
      -	r->refs_private = ref_store_init(r->gitdir, REF_STORE_ALL_CAPS);
      +	r->refs_private = ref_store_init(r->gitdir,
      +					 r->ref_storage_format ?
     -+						       r->ref_storage_format :
     -+						       default_ref_storage(),
     ++						 r->ref_storage_format :
     ++						 default_ref_storage(),
      +					 REF_STORE_ALL_CAPS);
       	return r->refs_private;
       }
     @@ refs/reftable-backend.c (new)
      +	struct reftable_addition *add = NULL;
      +	struct reftable_stack *stack =
      +		transaction->nr ?
     -+			      stack_for(refs, transaction->updates[0]->refname) :
     -+			      refs->main_stack;
     ++			stack_for(refs, transaction->updates[0]->refname) :
     ++			refs->main_stack;
      +	int err = refs->err;
      +	if (err < 0) {
      +		goto done;
     @@ refs/reftable-backend.c (new)
      +	return git_reftable_transaction_finish(ref_store, transaction, errmsg);
      +}
      +
     -+struct write_pseudoref_arg {
     -+	struct reftable_stack *stack;
     -+	const char *pseudoref;
     -+	const struct object_id *new_oid;
     -+	const struct object_id *old_oid;
     -+};
     -+
     -+static int write_pseudoref_table(struct reftable_writer *writer, void *argv)
     -+{
     -+	struct write_pseudoref_arg *arg = (struct write_pseudoref_arg *)argv;
     -+	uint64_t ts = reftable_stack_next_update_index(arg->stack);
     -+	int err = 0;
     -+	struct reftable_ref_record read_ref = { NULL };
     -+	struct reftable_ref_record write_ref = { NULL };
     -+
     -+	reftable_writer_set_limits(writer, ts, ts);
     -+	if (arg->old_oid) {
     -+		struct object_id read_oid;
     -+		err = reftable_stack_read_ref(arg->stack, arg->pseudoref,
     -+					      &read_ref);
     -+		if (err < 0)
     -+			goto done;
     -+
     -+		if ((err > 0) != is_null_oid(arg->old_oid)) {
     -+			err = REFTABLE_LOCK_ERROR;
     -+			goto done;
     -+		}
     -+
     -+		/* XXX If old_oid is set, and we have a symref? */
     -+
     -+		if (err == 0 && read_ref.value == NULL) {
     -+			err = REFTABLE_LOCK_ERROR;
     -+			goto done;
     -+		}
     -+
     -+		hashcpy(read_oid.hash, read_ref.value);
     -+		if (!oideq(arg->old_oid, &read_oid)) {
     -+			err = REFTABLE_LOCK_ERROR;
     -+			goto done;
     -+		}
     -+	}
     -+
     -+	write_ref.refname = (char *)arg->pseudoref;
     -+	write_ref.update_index = ts;
     -+	if (!is_null_oid(arg->new_oid))
     -+		write_ref.value = (uint8_t *)arg->new_oid->hash;
     -+
     -+	err = reftable_writer_add_ref(writer, &write_ref);
     -+done:
     -+	assert(err != REFTABLE_API_ERROR);
     -+	reftable_ref_record_clear(&read_ref);
     -+	return err;
     -+}
     -+
     -+static int git_reftable_write_pseudoref(struct ref_store *ref_store,
     -+					const char *pseudoref,
     -+					const struct object_id *oid,
     -+					const struct object_id *old_oid,
     -+					struct strbuf *errbuf)
     -+{
     -+	struct git_reftable_ref_store *refs =
     -+		(struct git_reftable_ref_store *)ref_store;
     -+	struct reftable_stack *stack = stack_for(refs, pseudoref);
     -+	struct write_pseudoref_arg arg = {
     -+		.stack = stack,
     -+		.pseudoref = pseudoref,
     -+		.new_oid = oid,
     -+	};
     -+	struct reftable_addition *add = NULL;
     -+	int err = refs->err;
     -+	if (err < 0) {
     -+		goto done;
     -+	}
     -+
     -+	err = reftable_stack_reload(stack);
     -+	if (err) {
     -+		goto done;
     -+	}
     -+	err = reftable_stack_new_addition(&add, stack);
     -+	if (err) {
     -+		goto done;
     -+	}
     -+	if (old_oid) {
     -+		struct object_id actual_old_oid;
     -+
     -+		/* XXX this is cut & paste from files-backend - should factor
     -+		 * out? */
     -+		if (read_ref(pseudoref, &actual_old_oid)) {
     -+			if (!is_null_oid(old_oid)) {
     -+				strbuf_addf(errbuf,
     -+					    _("could not read ref '%s'"),
     -+					    pseudoref);
     -+				goto done;
     -+			}
     -+		} else if (is_null_oid(old_oid)) {
     -+			strbuf_addf(errbuf, _("ref '%s' already exists"),
     -+				    pseudoref);
     -+			goto done;
     -+		} else if (!oideq(&actual_old_oid, old_oid)) {
     -+			strbuf_addf(errbuf,
     -+				    _("unexpected object ID when writing '%s'"),
     -+				    pseudoref);
     -+			goto done;
     -+		}
     -+	}
     -+
     -+	err = reftable_addition_add(add, &write_pseudoref_table, &arg);
     -+	if (err < 0) {
     -+		strbuf_addf(errbuf, "reftable: pseudoref update failure: %s",
     -+			    reftable_error_str(err));
     -+	}
     -+
     -+	err = reftable_addition_commit(add);
     -+	if (err < 0) {
     -+		strbuf_addf(errbuf, "reftable: pseudoref commit failure: %s",
     -+			    reftable_error_str(err));
     -+	}
     -+
     -+done:
     -+	assert(err != REFTABLE_API_ERROR);
     -+	reftable_addition_destroy(add);
     -+	return err;
     -+}
     -+
     -+static int reftable_delete_pseudoref(struct ref_store *ref_store,
     -+				     const char *pseudoref,
     -+				     const struct object_id *old_oid)
     -+{
     -+	struct strbuf errbuf = STRBUF_INIT;
     -+	int ret = git_reftable_write_pseudoref(ref_store, pseudoref, &null_oid,
     -+					       old_oid, &errbuf);
     -+	/* XXX what to do with the error message? */
     -+	strbuf_release(&errbuf);
     -+	return ret;
     -+}
     -+
      +struct write_delete_refs_arg {
      +	struct reftable_stack *stack;
      +	struct string_list *refnames;
     @@ refs/reftable-backend.c (new)
      +static int git_reftable_reflog_exists(struct ref_store *ref_store,
      +				      const char *refname)
      +{
     -+	/* always exists. */
     -+	return 1;
     ++	struct reftable_iterator it = { NULL };
     ++	struct git_reftable_ref_store *refs =
     ++		(struct git_reftable_ref_store *)ref_store;
     ++	struct reftable_stack *stack = stack_for(refs, refname);
     ++	struct reftable_merged_table *mt = reftable_stack_merged_table(stack);
     ++	struct reftable_log_record log = { NULL };
     ++	int err = refs->err;
     ++
     ++	if (err < 0) {
     ++		goto done;
     ++	}
     ++	err = reftable_merged_table_seek_log(mt, &it, refname);
     ++	if (err) {
     ++		goto done;
     ++	}
     ++	err = reftable_iterator_next_log(&it, &log);
     ++	if (err) {
     ++		goto done;
     ++	}
     ++
     ++	if (strcmp(log.refname, refname)) {
     ++		err = 1;
     ++	}
     ++
     ++done:
     ++	reftable_iterator_destroy(&it);
     ++	reftable_log_record_clear(&log);
     ++	return !err;
      +}
      +
      +static int git_reftable_create_reflog(struct ref_store *ref_store,
     @@ refs/reftable-backend.c (new)
      +	git_reftable_rename_ref,
      +	git_reftable_copy_ref,
      +
     -+	git_reftable_write_pseudoref,
     -+	reftable_delete_pseudoref,
     -+
      +	git_reftable_ref_iterator_begin,
      +	git_reftable_read_raw_ref,
      +
     @@ repository.h: struct repository {
       	 */
      
       ## setup.c ##
     -@@ setup.c: static int check_repo_format(const char *var, const char *value, void *vdata)
     - 			if (!value)
     - 				return config_error_nonbool(var);
     - 			data->partial_clone = xstrdup(value);
     --		} else if (!strcmp(ext, "worktreeconfig"))
     -+		} else if (!strcmp(ext, "worktreeconfig")) {
     - 			data->worktree_config = git_config_bool(var, value);
     --		else
     -+		} else if (!strcmp(ext, "refstorage")) {
     -+			data->ref_storage = xstrdup(value);
     -+		} else
     - 			string_list_append(&data->unknown_extensions, ext);
     +@@ setup.c: static enum extension_result handle_extension(const char *var,
     + {
     + 	if (!strcmp(ext, "noop-v1")) {
     + 		return EXTENSION_OK;
     ++	} else if (!strcmp(ext, "refstorage")) {
     ++		data->ref_storage = xstrdup(value);
     ++		return EXTENSION_OK;
       	}
     +-
     + 	return EXTENSION_UNKNOWN;
     + }
       
      @@ setup.c: void clear_repository_format(struct repository_format *format)
     - 	string_list_clear(&format->unknown_extensions, 0);
     + 	string_list_clear(&format->v1_only_extensions, 0);
       	free(format->work_tree);
       	free(format->partial_clone);
      +	free(format->ref_storage);
  -:  ---------- > 15:  a6449d4346 Read FETCH_HEAD as loose ref
 15:  04c86e7395 ! 16:  f13e4798c4 Hookup unittests for the reftable library.

@@ Makefile: t/helper/test-svn-fe$X: $(VCSSVN_LIB)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)

check-sha1:: t/helper/test-tool$X
+@@ Makefile: cocciclean:
+ clean: profile-clean coverage-clean cocciclean
+ $(RM) *.res
+ $(RM) $(OBJECTS)
+- $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) $(REFTABLE_LIB)
++ $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB)
+ $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
+ $(RM) $(TEST_PROGRAMS)
+ $(RM) $(FUZZ_PROGRAMS)

## t/helper/test-reftable.c (new) ##
@@
16: c751265705 ! 17: 679dc37871 Add GIT_DEBUG_REFS debugging mechanism

     @@ builtin/worktree.c: static int add_worktree(const char *path, const char *refnam
       	strbuf_reset(&sb);
      -	if (get_main_ref_store(the_repository)->be == &refs_be_reftable) {
      +
     -+	/* XXX: check GIT_TEST_REFTABLE because GIT_DEBUG_REFS obscures the
     -+	 * instance type. */
     -+	if (get_main_ref_store(the_repository)->be == &refs_be_reftable ||
     -+	    getenv("GIT_TEST_REFTABLE") != NULL) {
     ++	if (!strcmp(ref_store_backend_name(get_main_ref_store(the_repository)),
     ++		    "reftable")) {
       		/* XXX this is cut & paste from reftable_init_db. */
       		strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
       		write_file(sb.buf, "%s", "ref: refs/heads/.invalid\n");
      
       ## refs.c ##
     +@@ refs.c: int ref_storage_backend_exists(const char *name)
     + 	return find_ref_storage_backend(name) != NULL;
     + }
     + 
     ++const char *ref_store_backend_name(struct ref_store *refs)
     ++{
     ++	return refs->be->name;
     ++}
     ++
     + /*
     +  * How to handle various characters in refnames:
     +  * 0: An acceptable character for refs
      @@ refs.c: struct ref_store *get_main_ref_store(struct repository *r)
     - 						       r->ref_storage_format :
     - 						       default_ref_storage(),
     + 						 r->ref_storage_format :
     + 						 default_ref_storage(),
       					 REF_STORE_ALL_CAPS);
      +	if (getenv("GIT_DEBUG_REFS")) {
      +		r->refs_private = debug_wrap(r->gitdir, r->refs_private);
     @@ refs.c: struct ref_store *get_main_ref_store(struct repository *r)
       }
       
      
     + ## refs.h ##
     +@@ refs.h: int reflog_expire(const char *refname, const struct object_id *oid,
     + int ref_storage_backend_exists(const char *name);
     + 
     + struct ref_store *get_main_ref_store(struct repository *r);
     ++const char *ref_store_backend_name(struct ref_store *refs);
     + 
     + /**
     +  * Submodules
     +
       ## refs/debug.c (new) ##
      @@
      +
     @@ refs/debug.c (new)
      +struct ref_store *debug_wrap(const char *gitdir, struct ref_store *store)
      +{
      +	struct debug_ref_store *res = malloc(sizeof(struct debug_ref_store));
     ++	struct ref_storage_be *be_copy = malloc(sizeof(*be_copy));
     ++	*be_copy = refs_be_debug;
     ++	be_copy->name = ref_store_backend_name(store);
      +	fprintf(stderr, "ref_store for %s\n", gitdir);
      +	res->refs = store;
     -+	base_ref_store_init((struct ref_store *)res, &refs_be_debug);
     ++	base_ref_store_init((struct ref_store *)res, be_copy);
      +	return (struct ref_store *)res;
      +}
      +
     @@ refs/debug.c (new)
      +	struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
      +	int res =
      +		drefs->refs->be->delete_refs(drefs->refs, msg, refnames, flags);
     ++	int i = 0;
     ++	fprintf(stderr, "delete_refs {\n");
     ++	for (i = 0; i < refnames->nr; i++)
     ++		fprintf(stderr, "%s\n", refnames->items[i].string);
     ++	fprintf(stderr, "}: %d\n", res);
      +	return res;
      +}
      +
     @@ refs/debug.c (new)
      +	return res;
      +}
      +
     -+static int debug_write_pseudoref(struct ref_store *ref_store,
     -+				 const char *pseudoref,
     -+				 const struct object_id *oid,
     -+				 const struct object_id *old_oid,
     -+				 struct strbuf *err)
     -+{
     -+	struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
     -+	int res = drefs->refs->be->write_pseudoref(drefs->refs, pseudoref, oid,
     -+						   old_oid, err);
     -+	char o[100] = "null";
     -+	char n[100] = "null";
     -+	if (oid)
     -+		oid_to_hex_r(o, oid);
     -+	if (old_oid)
     -+		oid_to_hex_r(n, old_oid);
     -+	fprintf(stderr, "write_pseudoref: %s, %s => %s, err %s: %d\n",
     -+		pseudoref, o, n, err->buf, res);
     -+	return res;
     -+}
     -+
     -+static int debug_delete_pseudoref(struct ref_store *ref_store,
     -+				  const char *pseudoref,
     -+				  const struct object_id *old_oid)
     -+{
     -+	struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;
     -+	int res = drefs->refs->be->delete_pseudoref(drefs->refs, pseudoref,
     -+						    old_oid);
     -+	char hex[100] = "null";
     -+	if (old_oid)
     -+		oid_to_hex_r(hex, old_oid);
     -+	fprintf(stderr, "delete_pseudoref: %s (%s): %d\n", pseudoref, hex, res);
     -+	return res;
     -+}
     -+
      +struct debug_ref_iterator {
      +	struct ref_iterator base;
      +	struct ref_iterator *iter;
     @@ refs/debug.c (new)
      +	debug_rename_ref,
      +	debug_copy_ref,
      +
     -+	debug_write_pseudoref,
     -+	debug_delete_pseudoref,
     -+
      +	debug_ref_iterator_begin,
      +	debug_read_raw_ref,
      +
     @@ refs/debug.c (new)
      +};
      
       ## refs/refs-internal.h ##
     -@@ refs/refs-internal.h: struct ref_store {
     +@@ refs/refs-internal.h: int parse_loose_ref_contents(const char *buf, struct object_id *oid,
       void base_ref_store_init(struct ref_store *refs,
       			 const struct ref_storage_be *be);
 17:  ef0dd45f07 = 18:  9e6cdbe204 vcxproj: adjust for the reftable changes
 18:  d4dc1deae5 = 19:  9cd73da381 git-prompt: prepare for reftable refs backend
 19:  5a85abf9c4 = 20:  a2e5b3082e Add reftable testing infrastructure
 20:  0ebf7beb95 = 21:  539fa0935f Add "test-tool dump-reftable" command.
Signature · 1 line
gitgitgadget
#391 lore raw

PATCH v20 3/21 t1400: use git rev-parse for testing PSEUDOREF existence

From: Han-Wen Nienhuys <[email protected]>

This will allow these tests to run with alternative ref backends

---
 t/t1400-update-ref.sh | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)
t/t1400-update-ref.sh +10 -10
index 27171f8261..7414b898f8 100755
@@ -475,57 +475,57 @@ test_expect_success 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER
 
 test_expect_success 'given old value for missing pseudoref, do not create' '
 	test_must_fail git update-ref PSEUDOREF $A $B 2>err &&
-	test_path_is_missing .git/PSEUDOREF &&
+	test_must_fail git rev-parse PSEUDOREF &&
 	test_i18ngrep "could not read ref" err
 '
 
 test_expect_success 'create pseudoref' '
 	git update-ref PSEUDOREF $A &&
-	test $A = $(cat .git/PSEUDOREF)
+	test $A = $(git rev-parse PSEUDOREF)
 '
 
 test_expect_success 'overwrite pseudoref with no old value given' '
 	git update-ref PSEUDOREF $B &&
-	test $B = $(cat .git/PSEUDOREF)
+	test $B = $(git rev-parse PSEUDOREF)
 '
 
 test_expect_success 'overwrite pseudoref with correct old value' '
 	git update-ref PSEUDOREF $C $B &&
-	test $C = $(cat .git/PSEUDOREF)
+	test $C = $(git rev-parse PSEUDOREF)
 '
 
 test_expect_success 'do not overwrite pseudoref with wrong old value' '
 	test_must_fail git update-ref PSEUDOREF $D $E 2>err &&
-	test $C = $(cat .git/PSEUDOREF) &&
+	test $C = $(git rev-parse PSEUDOREF) &&
 	test_i18ngrep "unexpected object ID" err
 '
 
 test_expect_success 'delete pseudoref' '
 	git update-ref -d PSEUDOREF &&
-	test_path_is_missing .git/PSEUDOREF
+	test_must_fail git rev-parse PSEUDOREF
 '
 
 test_expect_success 'do not delete pseudoref with wrong old value' '
 	git update-ref PSEUDOREF $A &&
 	test_must_fail git update-ref -d PSEUDOREF $B 2>err &&
-	test $A = $(cat .git/PSEUDOREF) &&
+	test $A = $(git rev-parse PSEUDOREF) &&
 	test_i18ngrep "unexpected object ID" err
 '
 
 test_expect_success 'delete pseudoref with correct old value' '
 	git update-ref -d PSEUDOREF $A &&
-	test_path_is_missing .git/PSEUDOREF
+	test_must_fail git rev-parse PSEUDOREF
 '
 
 test_expect_success 'create pseudoref with old OID zero' '
 	git update-ref PSEUDOREF $A $Z &&
-	test $A = $(cat .git/PSEUDOREF)
+	test $A = $(git rev-parse PSEUDOREF)
 '
 
 test_expect_success 'do not overwrite pseudoref with old OID zero' '
 	test_when_finished git update-ref -d PSEUDOREF &&
 	test_must_fail git update-ref PSEUDOREF $B $Z 2>err &&
-	test $A = $(cat .git/PSEUDOREF) &&
+	test $A = $(git rev-parse PSEUDOREF) &&
 	test_i18ngrep "already exists" err
 '
 
Signature · 1 line
gitgitgadget
#392 lore raw

PATCH v20 4/21 Modify pseudo refs through ref backend storage

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

The previous behavior was introduced in commit 74ec19d4be ("pseudorefs: create and use pseudoref update and delete functions", Jul 31, 2015), with the justification "alternate ref backends still need to store pseudorefs in GIT_DIR".

Refs such as REBASE_HEAD are read through the ref backend. This can only work consistently if they are written through the ref backend as well. Tooling that works directly on files under .git should be updated to use git commands to read refs instead.

The following behaviors change:

* Updates to pseudorefs (eg. ORIG_HEAD) with core.logAllRefUpdates=always will create reflogs for the pseudoref.

* non-HEAD pseudoref symrefs are also dereferenced on deletion. Update t1405 accordingly.

---
 Documentation/git-update-ref.txt |  13 ++--
 refs.c                           | 120 ++-----------------------------
 t/t1400-update-ref.sh            |  10 +--
 t/t1405-main-ref-store.sh        |   5 +-
 4 files changed, 23 insertions(+), 125 deletions(-)
Documentation/git-update-ref.txt +7 -6
index 3e737c2360..d401234b03 100644
@@ -148,12 +148,13 @@ still see a subset of the modifications.
 
 LOGGING UPDATES
 ---------------
-If config parameter "core.logAllRefUpdates" is true and the ref is one under
-"refs/heads/", "refs/remotes/", "refs/notes/", or the symbolic ref HEAD; or
-the file "$GIT_DIR/logs/<ref>" exists then `git update-ref` will append
-a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all
-symbolic refs before creating the log name) describing the change
-in ref value.  Log lines are formatted as:
+If config parameter "core.logAllRefUpdates" is true and the ref is one
+under "refs/heads/", "refs/remotes/", "refs/notes/", or a pseudoref
+like HEAD or ORIG_HEAD; or the file "$GIT_DIR/logs/<ref>" exists then
+`git update-ref` will append a line to the log file
+"$GIT_DIR/logs/<ref>" (dereferencing all symbolic refs before creating
+the log name) describing the change in ref value.  Log lines are
+formatted as:
 
     oldsha1 SP newsha1 SP committer LF
 
refs.c +7 -113

138 lines not on this page · raw message

t/t1400-update-ref.sh +5 -5
index 7414b898f8..65d349fb33 100755
@@ -160,10 +160,10 @@ test_expect_success 'core.logAllRefUpdates=always creates reflog by default' '
 	git reflog exists $outside
 '
 
-test_expect_success 'core.logAllRefUpdates=always creates no reflog for ORIG_HEAD' '
+test_expect_success 'core.logAllRefUpdates=always creates reflog for ORIG_HEAD' '
 	test_config core.logAllRefUpdates always &&
 	git update-ref ORIG_HEAD $A &&
-	test_must_fail git reflog exists ORIG_HEAD
+	git reflog exists ORIG_HEAD
 '
 
 test_expect_success '--no-create-reflog overrides core.logAllRefUpdates=always' '
@@ -476,7 +476,7 @@ test_expect_success 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER
 test_expect_success 'given old value for missing pseudoref, do not create' '
 	test_must_fail git update-ref PSEUDOREF $A $B 2>err &&
 	test_must_fail git rev-parse PSEUDOREF &&
-	test_i18ngrep "could not read ref" err
+	test_i18ngrep "unable to resolve reference" err
 '
 
 test_expect_success 'create pseudoref' '
@@ -497,7 +497,7 @@ test_expect_success 'overwrite pseudoref with correct old value' '
 test_expect_success 'do not overwrite pseudoref with wrong old value' '
 	test_must_fail git update-ref PSEUDOREF $D $E 2>err &&
 	test $C = $(git rev-parse PSEUDOREF) &&
-	test_i18ngrep "unexpected object ID" err
+	test_i18ngrep "cannot lock ref.*expected" err
 '
 
 test_expect_success 'delete pseudoref' '
@@ -509,7 +509,7 @@ test_expect_success 'do not delete pseudoref with wrong old value' '
 	git update-ref PSEUDOREF $A &&
 	test_must_fail git update-ref -d PSEUDOREF $B 2>err &&
 	test $A = $(git rev-parse PSEUDOREF) &&
-	test_i18ngrep "unexpected object ID" err
+	test_i18ngrep "cannot lock ref.*expected" err
 '
 
 test_expect_success 'delete pseudoref with correct old value' '
t/t1405-main-ref-store.sh +4 -1
index 331899ddc4..74af927fba 100755
@@ -31,7 +31,10 @@ test_expect_success 'create_symref(FOO, refs/heads/master)' '
 test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '
 	git rev-parse FOO -- &&
 	git rev-parse refs/tags/new-tag -- &&
-	$RUN delete-refs 0 nothing FOO refs/tags/new-tag &&
+	m=$(git rev-parse master) &&
+	REF_NO_DEREF=1 &&
+	$RUN delete-refs $REF_NO_DEREF nothing FOO refs/tags/new-tag &&
+	test_must_fail git rev-parse --symbolic-full-name FOO &&
 	test_must_fail git rev-parse FOO -- &&
 	test_must_fail git rev-parse refs/tags/new-tag --
 '
Signature · 1 line
gitgitgadget
#393 lore raw

PATCH v20 5/21 Make HEAD a PSEUDOREF rather than PER_WORKTREE.

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

This is consistent with the definition of REF_TYPE_PSEUDOREF (uppercase in the root ref namespace).

---
 refs.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)
refs.c +3 -4
index 5c25821ad5..947b51f781 100644
@@ -708,10 +708,9 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log)
 
 static int is_per_worktree_ref(const char *refname)
 {
-	return !strcmp(refname, "HEAD") ||
-		starts_with(refname, "refs/worktree/") ||
-		starts_with(refname, "refs/bisect/") ||
-		starts_with(refname, "refs/rewritten/");
+	return starts_with(refname, "refs/worktree/") ||
+	       starts_with(refname, "refs/bisect/") ||
+	       starts_with(refname, "refs/rewritten/");
 }
 
 static int is_pseudoref_syntax(const char *refname)
Signature · 1 line
gitgitgadget
#394 lore raw

PATCH v20 6/21 Make refs_ref_exists public

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

---
 refs.c | 2 +-
 refs.h | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)
refs.c +1 -1
index 947b51f781..5e65e79f24 100644
@@ -313,7 +313,7 @@ int read_ref(const char *refname, struct object_id *oid)
 	return read_ref_full(refname, RESOLVE_REF_READING, oid, NULL);
 }
 
-static int refs_ref_exists(struct ref_store *refs, const char *refname)
+int refs_ref_exists(struct ref_store *refs, const char *refname)
 {
 	return !!refs_resolve_ref_unsafe(refs, refname, RESOLVE_REF_READING, NULL, NULL);
 }
refs.h +2 -0
index f212f8945e..f8835f0aef 100644
@@ -105,6 +105,8 @@ int refs_verify_refname_available(struct ref_store *refs,
 				  const struct string_list *skip,
 				  struct strbuf *err);
 
+int refs_ref_exists(struct ref_store *refs, const char *refname);
+
 int ref_exists(const char *refname);
 
 int should_autocreate_reflog(const char *refname);
Signature · 1 line
gitgitgadget
#395 lore raw

PATCH v20 7/21 Treat CHERRY_PICK_HEAD as a pseudo ref

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

Check for existence and delete CHERRY_PICK_HEAD through ref functions. This will help cherry-pick work with alternate ref storage backends.

---
 builtin/commit.c | 34 +++++++++++++++++++---------------
 builtin/merge.c  |  2 +-
 path.c           |  1 -
 path.h           |  7 ++++---
 sequencer.c      | 42 ++++++++++++++++++++++++++----------------
 wt-status.c      |  4 ++--
 6 files changed, 52 insertions(+), 38 deletions(-)
builtin/commit.c +19 -15
index d1b7396052..e27120b982 100644
@@ -847,21 +847,25 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 			if (cleanup_mode == COMMIT_MSG_CLEANUP_SCISSORS &&
 				!merge_contains_scissors)
 				wt_status_add_cut_line(s->fp);
-			status_printf_ln(s, GIT_COLOR_NORMAL,
-			    whence == FROM_MERGE
-				? _("\n"
-					"It looks like you may be committing a merge.\n"
-					"If this is not correct, please remove the file\n"
-					"	%s\n"
-					"and try again.\n")
-				: _("\n"
-					"It looks like you may be committing a cherry-pick.\n"
-					"If this is not correct, please remove the file\n"
-					"	%s\n"
-					"and try again.\n"),
-				whence == FROM_MERGE ?
-					git_path_merge_head(the_repository) :
-					git_path_cherry_pick_head(the_repository));
+			if (whence == FROM_MERGE)
+				status_printf_ln(
+					s, GIT_COLOR_NORMAL,
+
+					_("\n"
+					  "It looks like you may be committing a merge.\n"
+					  "If this is not correct, please remove the file\n"
+					  "	%s\n"
+					  "and try again.\n"),
+					git_path_merge_head(the_repository));
+			else
+				status_printf_ln(
+					s, GIT_COLOR_NORMAL,
+
+					_("\n"
+					  "It looks like you may be committing a cherry-pick.\n"
+					  "If this is not correct, please run\n"
+					  "	git cherry-pick --abort\n"
+					  "and try again.\n"));
 		}
 
 		fprintf(s->fp, "\n");
builtin/merge.c +1 -1
index 7da707bf55..93b0a7b6ed 100644
@@ -1352,7 +1352,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 		else
 			die(_("You have not concluded your merge (MERGE_HEAD exists)."));
 	}
-	if (file_exists(git_path_cherry_pick_head(the_repository))) {
+	if (ref_exists("CHERRY_PICK_HEAD")) {
 		if (advice_resolve_conflict)
 			die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
 			    "Please, commit your changes before you merge."));
path.c +0 -1
index 8b2c753191..783cc2ae81 100644
@@ -1528,7 +1528,6 @@ char *xdg_cache_home(const char *filename)
 	return NULL;
 }
 
-REPO_GIT_PATH_FUNC(cherry_pick_head, "CHERRY_PICK_HEAD")
 REPO_GIT_PATH_FUNC(revert_head, "REVERT_HEAD")
 REPO_GIT_PATH_FUNC(squash_msg, "SQUASH_MSG")
 REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
path.h +4 -3
index 1f1bf8f87a..8941c018a9 100644
@@ -170,7 +170,6 @@ void report_linked_checkout_garbage(void);
 	}
 
 struct path_cache {
-	const char *cherry_pick_head;
 	const char *revert_head;
 	const char *squash_msg;
 	const char *merge_msg;
@@ -182,9 +181,11 @@ struct path_cache {
 	const char *shallow;
 };
 
-#define PATH_CACHE_INIT { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+#define PATH_CACHE_INIT                                              \
+	{                                                            \
+		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL \
+	}
 
-const char *git_path_cherry_pick_head(struct repository *r);
 const char *git_path_revert_head(struct repository *r);
 const char *git_path_squash_msg(struct repository *r);
 const char *git_path_merge_msg(struct repository *r);
sequencer.c +26 -16

114 lines not on this page · raw message

wt-status.c +2 -2
index 20f2075868..8fddf65a4b 100644
@@ -1673,8 +1673,8 @@ void wt_status_get_state(struct repository *r,
 		state->merge_in_progress = 1;
 	} else if (wt_status_check_rebase(NULL, state)) {
 		;		/* all set */
-	} else if (!stat(git_path_cherry_pick_head(r), &st) &&
-			!get_oid("CHERRY_PICK_HEAD", &oid)) {
+	} else if (refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
+		   !get_oid("CHERRY_PICK_HEAD", &oid)) {
 		state->cherry_pick_in_progress = 1;
 		oidcpy(&state->cherry_pick_head_oid, &oid);
 	}
Signature · 1 line
gitgitgadget
#396 lore raw

PATCH v20 8/21 Treat REVERT_HEAD as a pseudo ref

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

---
 path.c      |  1 -
 path.h      |  8 +++-----
 sequencer.c | 16 +++++++++-------
 wt-status.c |  2 +-
 4 files changed, 13 insertions(+), 14 deletions(-)
path.c +0 -1
index 783cc2ae81..7b385e5eb2 100644
@@ -1528,7 +1528,6 @@ char *xdg_cache_home(const char *filename)
 	return NULL;
 }
 
-REPO_GIT_PATH_FUNC(revert_head, "REVERT_HEAD")
 REPO_GIT_PATH_FUNC(squash_msg, "SQUASH_MSG")
 REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
 REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
path.h +3 -5
index 8941c018a9..e7e77da6aa 100644
@@ -170,7 +170,6 @@ void report_linked_checkout_garbage(void);
 	}
 
 struct path_cache {
-	const char *revert_head;
 	const char *squash_msg;
 	const char *merge_msg;
 	const char *merge_rr;
@@ -181,12 +180,11 @@ struct path_cache {
 	const char *shallow;
 };
 
-#define PATH_CACHE_INIT                                              \
-	{                                                            \
-		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL \
+#define PATH_CACHE_INIT                                        \
+	{                                                      \
+		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL \
 	}
 
-const char *git_path_revert_head(struct repository *r);
 const char *git_path_squash_msg(struct repository *r);
 const char *git_path_merge_msg(struct repository *r);
 const char *git_path_merge_rr(struct repository *r);
sequencer.c +9 -7
index 601b1cffcd..5e7b1634f3 100644
@@ -2317,8 +2317,10 @@ void sequencer_post_commit_cleanup(struct repository *r, int verbose)
 		need_cleanup = 1;
 	}
 
-	if (file_exists(git_path_revert_head(r))) {
-		if (!unlink(git_path_revert_head(r)) && verbose)
+	if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) {
+		if (!refs_delete_ref(get_main_ref_store(r), "", "REVERT_HEAD",
+				     NULL, 0) &&
+		    verbose)
 			warning(_("cancelling a revert in progress"));
 		opts.action = REPLAY_REVERT;
 		need_cleanup = 1;
@@ -2677,7 +2679,7 @@ static int create_seq_dir(struct repository *r)
 	const char *in_progress_error = NULL;
 	const char *in_progress_advice = NULL;
 	unsigned int advise_skip =
-		file_exists(git_path_revert_head(r)) ||
+		refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD") ||
 		refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD");
 
 	if (!sequencer_get_last_command(r, &action)) {
@@ -2778,7 +2780,7 @@ static int rollback_single_pick(struct repository *r)
 	struct object_id head_oid;
 
 	if (!refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
-	    !file_exists(git_path_revert_head(r)))
+	    !refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD"))
 		return error(_("no cherry-pick or revert in progress"));
 	if (read_ref_full("HEAD", 0, &head_oid, NULL))
 		return error(_("cannot resolve HEAD"));
@@ -2872,7 +2874,7 @@ int sequencer_skip(struct repository *r, struct replay_opts *opts)
 	 */
 	switch (opts->action) {
 	case REPLAY_REVERT:
-		if (!file_exists(git_path_revert_head(r))) {
+		if (!refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) {
 			if (action != REPLAY_REVERT)
 				return error(_("no revert in progress"));
 			if (!rollback_is_safe())
@@ -4210,7 +4212,7 @@ static int continue_single_pick(struct repository *r)
 	const char *argv[] = { "commit", NULL };
 
 	if (!refs_ref_exists(get_main_ref_store(r), "CHERRY_PICK_HEAD") &&
-	    !file_exists(git_path_revert_head(r)))
+	    !refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD"))
 		return error(_("no cherry-pick or revert in progress"));
 	return run_command_v_opt(argv, RUN_GIT_CMD);
 }
@@ -4390,7 +4392,7 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
 		/* Verify that the conflict has been resolved */
 		if (refs_ref_exists(get_main_ref_store(r),
 				    "CHERRY_PICK_HEAD") ||
-		    file_exists(git_path_revert_head(r))) {
+		    refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD")) {
 			res = continue_single_pick(r);
 			if (res)
 				goto release_todo_list;
wt-status.c +1 -1
index 8fddf65a4b..df81b8d7a7 100644
@@ -1679,7 +1679,7 @@ void wt_status_get_state(struct repository *r,
 		oidcpy(&state->cherry_pick_head_oid, &oid);
 	}
 	wt_status_check_bisect(NULL, state);
-	if (!stat(git_path_revert_head(r), &st) &&
+	if (refs_ref_exists(get_main_ref_store(r), "REVERT_HEAD") &&
 	    !get_oid("REVERT_HEAD", &oid)) {
 		state->revert_in_progress = 1;
 		oidcpy(&state->revert_head_oid, &oid);
Signature · 1 line
gitgitgadget
#398 lore raw

PATCH v20 10/21 Iteration over entire ref namespace is iterating over "refs/"

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

This changes behavior for in for_each_[raw]ref and for_each_fullref_in_pattern.

This happens implicitly in the files/packed ref backend; making it explicit simplifies adding alternate ref storage backends, such as reftable.

---
 ref-filter.c | 7 ++++---
 refs.c       | 6 +++---
 2 files changed, 7 insertions(+), 6 deletions(-)
ref-filter.c +4 -3
index f2b078db11..88c82b2cca 100644
@@ -1943,6 +1943,7 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
 {
 	struct string_list prefixes = STRING_LIST_INIT_DUP;
 	struct string_list_item *prefix;
+	const char *all = "refs/";
 	int ret;
 
 	if (!filter->match_as_path) {
@@ -1951,7 +1952,7 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
 		 * prefixes like "refs/heads/" etc. are stripped off,
 		 * so we have to look at everything:
 		 */
-		return for_each_fullref_in("", cb, cb_data, broken);
+		return for_each_fullref_in(all, cb, cb_data, broken);
 	}
 
 	if (filter->ignore_case) {
@@ -1960,12 +1961,12 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
 		 * so just return everything and let the caller
 		 * sort it out.
 		 */
-		return for_each_fullref_in("", cb, cb_data, broken);
+		return for_each_fullref_in(all, cb, cb_data, broken);
 	}
 
 	if (!filter->name_patterns[0]) {
 		/* no patterns; we have to look at everything */
-		return for_each_fullref_in("", cb, cb_data, broken);
+		return for_each_fullref_in(all, cb, cb_data, broken);
 	}
 
 	find_longest_prefixes(&prefixes, filter->name_patterns);
refs.c +3 -3
index 5e65e79f24..d4b5397c8f 100644
@@ -1458,7 +1458,7 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix,
 
 int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref(refs, "", fn, 0, 0, cb_data);
+	return do_for_each_ref(refs, "refs/", fn, 0, 0, cb_data);
 }
 
 int for_each_ref(each_ref_fn fn, void *cb_data)
@@ -1518,8 +1518,8 @@ int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
 
 int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-	return do_for_each_ref(refs, "", fn, 0,
-			       DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
+	return do_for_each_ref(refs, "refs/", fn, 0, DO_FOR_EACH_INCLUDE_BROKEN,
+			       cb_data);
 }
 
 int for_each_rawref(each_ref_fn fn, void *cb_data)
Signature · 1 line
gitgitgadget
#390 lore raw

PATCH v20 2/21 Split off reading loose ref data in separate function

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

This prepares for handling FETCH_HEAD (which is not a regular ref) separately in the reftable backend.

---
 refs/files-backend.c | 34 +++++++++++++++++++---------------
 refs/refs-internal.h |  6 ++++++
 2 files changed, 25 insertions(+), 15 deletions(-)
refs/files-backend.c +19 -15
index 985631f33e..3a3573986f 100644
@@ -360,7 +360,6 @@ static int files_read_raw_ref(struct ref_store *ref_store,
 	struct strbuf sb_path = STRBUF_INIT;
 	const char *path;
 	const char *buf;
-	const char *p;
 	struct stat st;
 	int fd;
 	int ret = -1;
@@ -465,6 +464,21 @@ static int files_read_raw_ref(struct ref_store *ref_store,
 	close(fd);
 	strbuf_rtrim(&sb_contents);
 	buf = sb_contents.buf;
+
+	ret = parse_loose_ref_contents(buf, oid, referent, type);
+
+out:
+	save_errno = errno;
+	strbuf_release(&sb_path);
+	strbuf_release(&sb_contents);
+	errno = save_errno;
+	return ret;
+}
+
+int parse_loose_ref_contents(const char *buf, struct object_id *oid,
+			     struct strbuf *referent, unsigned int *type)
+{
+	const char *p;
 	if (skip_prefix(buf, "ref:", &buf)) {
 		while (isspace(*buf))
 			buf++;
@@ -472,29 +486,19 @@ static int files_read_raw_ref(struct ref_store *ref_store,
 		strbuf_reset(referent);
 		strbuf_addstr(referent, buf);
 		*type |= REF_ISSYMREF;
-		ret = 0;
-		goto out;
+		return 0;
 	}
 
 	/*
-	 * Please note that FETCH_HEAD has additional
-	 * data after the sha.
+	 * FETCH_HEAD has additional data after the sha.
 	 */
 	if (parse_oid_hex(buf, oid, &p) ||
 	    (*p != '\0' && !isspace(*p))) {
 		*type |= REF_ISBROKEN;
 		errno = EINVAL;
-		goto out;
+		return -1;
 	}
-
-	ret = 0;
-
-out:
-	save_errno = errno;
-	strbuf_release(&sb_path);
-	strbuf_release(&sb_contents);
-	errno = save_errno;
-	return ret;
+	return 0;
 }
 
 static void unlock_ref(struct ref_lock *lock)
refs/refs-internal.h +6 -0
index 357359a0be..24d79fb5c1 100644
@@ -674,6 +674,12 @@ struct ref_store {
 	const struct ref_storage_be *be;
 };
 
+/*
+ * Parse contents of a loose ref file.
+ */
+int parse_loose_ref_contents(const char *buf, struct object_id *oid,
+			     struct strbuf *referent, unsigned int *type);
+
 /*
  * Fill in the generic part of refs and add it to our collection of
  * reference stores.
Signature · 1 line
gitgitgadget
#397 lore raw

PATCH v20 9/21 Move REF_LOG_ONLY to refs-internal.h

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

REF_LOG_ONLY is used in the transaction preparation: if a symref is involved in a transaction, the referent of the symref should be updated, and the symref itself should only be updated in the reflog. Other ref backends will need to duplicate this logic.

---
 refs/files-backend.c | 7 -------
 refs/refs-internal.h | 7 +++++++
 2 files changed, 7 insertions(+), 7 deletions(-)
refs/files-backend.c +0 -7
index 3a3573986f..5829d5f8f7 100644
@@ -38,13 +38,6 @@
  */
 #define REF_NEEDS_COMMIT (1 << 6)
 
-/*
- * Used as a flag in ref_update::flags when we want to log a ref
- * update but not actually perform it.  This is used when a symbolic
- * ref update is split up.
- */
-#define REF_LOG_ONLY (1 << 7)
-
 /*
  * Used as a flag in ref_update::flags when the ref_update was via an
  * update to HEAD.
refs/refs-internal.h +7 -0
index 24d79fb5c1..61fd2e661f 100644
@@ -31,6 +31,13 @@ struct ref_transaction;
  */
 #define REF_HAVE_OLD (1 << 3)
 
+/*
+ * Used as a flag in ref_update::flags when we want to log a ref
+ * update but not actually perform it.  This is used when a symbolic
+ * ref update is split up.
+ */
+#define REF_LOG_ONLY (1 << 7)
+
 /*
  * Return the length of time to retry acquiring a loose reference lock
  * before giving up, in milliseconds:
Signature · 1 line
gitgitgadget
#401 lore raw

PATCH v20 13/21 Add standalone build infrastructure for reftable

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

The reftable library is integrates with Git, so it become a first class supported storage mechanism, but is sufficiently complex that other projects (e.g. libgit2) may want to consume the same source code.

To make this possible, the library also compiles completely standalone, which is demonstrated with the Bazel BUILD/WORKSPACE files that this commit adds.

---
 reftable/BUILD      | 203 ++++++++++++++++++++++++++++++++++++++++++++
 reftable/README.md  |  33 +++++++
 reftable/WORKSPACE  |  14 +++
 reftable/zlib.BUILD |  36 ++++++++
 4 files changed, 286 insertions(+)
 create mode 100644 reftable/BUILD
 create mode 100644 reftable/README.md
 create mode 100644 reftable/WORKSPACE
 create mode 100644 reftable/zlib.BUILD
reftable/BUILD new file +203 -0
new file mode 100644
index 0000000000..d3946a717c
@@ -0,0 +1,203 @@
+# Mirror core-git COPTS so reftable compiles without warning in CGit.
+GIT_COPTS = [
+    "-DREFTABLE_STANDALONE",
+    "-Wall",
+    "-Werror",
+    "-Wdeclaration-after-statement",
+    "-Wstrict-prototypes",
+    "-Wformat-security",
+    "-Wno-format-zero-length",
+    "-Wold-style-definition",
+    "-Woverflow",
+    "-Wpointer-arith",
+    "-Wstrict-prototypes",
+    "-Wunused",
+    "-Wvla",
+    "-Wextra",
+    "-Wmissing-prototypes",
+    "-Wno-empty-body",
+    "-Wno-missing-field-initializers",
+    "-Wno-sign-compare",
+    "-Werror=strict-aliasing",
+    "-Wno-unused-parameter",
+]
+
+cc_library(
+    name = "reftable",
+    srcs = [
+        "basics.c",
+        "block.c",
+        "compat.c",
+        "file.c",
+        "iter.c",
+        "merged.c",
+        "pq.c",
+        "reader.c",
+        "record.c",
+        "refname.c",
+        "reftable.c",
+        "strbuf.c",
+        "stack.c",
+        "tree.c",
+        "writer.c",
+        "zlib-compat.c",
+        "basics.h",
+        "block.h",
+        "compat.h",
+        "constants.h",
+        "iter.h",
+        "merged.h",
+        "pq.h",
+        "reader.h",
+        "refname.h",
+        "record.h",
+        "strbuf.h",
+        "stack.h",
+        "system.h",
+        "tree.h",
+        "writer.h",
+    ],
+    hdrs = [
+        "reftable.h",
+    ],
+    includes = [
+        "include",
+    ],
+    copts = [
+        "-fvisibility=protected",
+    ] + GIT_COPTS,
+    deps = ["@zlib"],
+    visibility = ["//visibility:public"]
+)
+
+cc_library(
+    name = "testlib",
+    srcs = [
+        "test_framework.c",
+        "dump.c",
+	"test_framework.h",
+    ],
+    hdrs = [
+            "reftable-tests.h",
+    ],
+    copts = GIT_COPTS,
+    deps = [":reftable"],
+    visibility = ["//visibility:public"]
+)
+
+cc_test(
+    name = "record_test",
+    srcs = ["record_test.c"],
+    deps = [
+        ":reftable",
+        ":testlib",
+    ],
+    copts = [
+        "-Drecord_test_main=main",
+        "-fvisibility=protected",
+    ] + GIT_COPTS,
+)
+
+cc_test(
+    name = "reftable_test",
+    srcs = ["reftable_test.c"],
+    deps = [
+        ":reftable",
+        ":testlib",
+    ],
+    copts = [
+        "-Dreftable_test_main=main",
+        "-fvisibility=protected",
+    ] + GIT_COPTS,
+)
+
+cc_test(
+    name = "strbuf_test",
+    srcs = ["strbuf_test.c"],
+    deps = [
+        ":reftable",
+        ":testlib",
+    ] ,
+    copts = [
+        "-Dstrbuf_test_main=main",
+        "-fvisibility=protected",
+    ] + GIT_COPTS,
+)
+
+cc_test(
+    name = "stack_test",
+    srcs = ["stack_test.c"],
+    deps = [
+        ":reftable",
+        ":testlib",
+    ],
+    copts = [
+        "-Dstack_test_main=main",
+        "-fvisibility=protected",
+    ] + GIT_COPTS,
+)
+
+cc_test(
+    name = "tree_test",
+    srcs = ["tree_test.c"],
+    deps = [
+        ":reftable",
+        ":testlib",
+    ],
+    copts = [
+        "-Dtree_test_main=main",
+        "-fvisibility=protected",
+    ] + GIT_COPTS,
+)
+
+cc_test(
+    name = "block_test",
+    srcs = ["block_test.c"],
+    deps = [
+        ":reftable",
+        ":testlib",
+    ],
+    copts = [
+        "-Dblock_test_main=main",
+        "-fvisibility=protected",
+    ] + GIT_COPTS,
+)
+
+cc_test(
+    name = "refname_test",
+    srcs = ["refname_test.c"],
+    deps = [
+        ":reftable",
+        ":testlib",
+    ],
+    copts = [
+        "-Drefname_test_main=main",
+        "-fvisibility=protected",
+    ] + GIT_COPTS,
+)
+
+cc_test(
+    name = "merged_test",
+    srcs = ["merged_test.c"],
+    deps = [
+        ":reftable",
+        ":testlib",
+    ],
+    copts = [
+        "-Dmerged_test_main=main",
+        "-fvisibility=protected",
+    ] + GIT_COPTS,
+)
+
+[sh_test(
+    name = "%s_valgrind_test" % t,
+    srcs = [ "valgrind_test.sh" ],
+    args = [ t ],
+    data = [ t ])
+ for t in ["record_test",
+           "merged_test",
+           "refname_test",
+           "tree_test",
+           "block_test",
+           "strbuf_test",
+           "stack_test"]]
reftable/README.md new file +33 -0
new file mode 100644
index 0000000000..0345015c57
@@ -0,0 +1,33 @@
+This directory contains an implementation of the reftable library.
+
+The library is integrated into the git-core project, but can also be built
+standalone, by compiling with -DREFTABLE_STANDALONE. The standalone build is
+exercised by the accompanying BUILD/WORKSPACE files, and can be run as
+
+  bazel test :all
+
+It includes a fragment of the zlib library to provide uncompress2(), which is a
+recent addition to the API. zlib is licensed as follows:
+
+```
+ (C) 1995-2017 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  [email protected]          [email protected]
+```
reftable/WORKSPACE new file +14 -0
new file mode 100644
index 0000000000..9f91d16208
@@ -0,0 +1,14 @@
+workspace(name = "reftable")
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+http_archive(
+    name = "zlib",
+    build_file = "@//:zlib.BUILD",
+    sha256 = "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1",
+    strip_prefix = "zlib-1.2.11",
+    urls = [
+        "https://mirror.bazel.build/zlib.net/zlib-1.2.11.tar.gz",
+        "https://zlib.net/zlib-1.2.11.tar.gz",
+    ],
+)
reftable/zlib.BUILD new file +36 -0
new file mode 100644
index 0000000000..edb77fdf8e
@@ -0,0 +1,36 @@
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])  # BSD/MIT-like license (for zlib)
+
+cc_library(
+    name = "zlib",
+    srcs = [
+        "adler32.c",
+        "compress.c",
+        "crc32.c",
+        "crc32.h",
+        "deflate.c",
+        "deflate.h",
+        "gzclose.c",
+        "gzguts.h",
+        "gzlib.c",
+        "gzread.c",
+        "gzwrite.c",
+        "infback.c",
+        "inffast.c",
+        "inffast.h",
+        "inffixed.h",
+        "inflate.c",
+        "inflate.h",
+        "inftrees.c",
+        "inftrees.h",
+        "trees.c",
+        "trees.h",
+        "uncompr.c",
+        "zconf.h",
+        "zutil.c",
+        "zutil.h",
+    ],
+    hdrs = ["zlib.h"],
+    includes = ["."],
+)
Signature · 1 line
gitgitgadget
#399 lore raw

PATCH v20 11/21 Add .gitattributes for the reftable/ directory

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

---
 reftable/.gitattributes | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 reftable/.gitattributes
reftable/.gitattributes new file +1 -0
new file mode 100644
index 0000000000..f44451a379
@@ -0,0 +1 @@
+/zlib-compat.c	whitespace=-indent-with-non-tab,-trailing-space
Signature · 1 line
gitgitgadget
#403 lore raw

PATCH v20 15/21 Read FETCH_HEAD as loose ref

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

FETCH_HEAD is a loose ref (either symref or OID), followed by further metadata. It can therefore not be read through a ref backend normally. Special case this in reftable-backend.c.

This functionality is shared between all backends, but the ref backend interface doesn't have a method to retrieve the repository directory, hence this functionality must be pushed down to the backend implementation.

---
 refs/reftable-backend.c | 31 +++++++++++++++++++++++++++++++
 t/t0031-reftable.sh     |  9 ++++++++-
 2 files changed, 39 insertions(+), 1 deletion(-)
refs/reftable-backend.c +31 -0
index 93f3d337b6..8d30f3ea61 100644
@@ -1304,6 +1304,33 @@ git_reftable_reflog_expire(struct ref_store *ref_store, const char *refname,
 	return err;
 }
 
+static int read_fetch_head(struct git_reftable_ref_store *refs,
+			   const char *refname, struct object_id *oid,
+			   struct strbuf *referent, unsigned int *type)
+{
+	struct strbuf sb = STRBUF_INIT;
+	struct strbuf path_sb = STRBUF_INIT;
+	int ret = -1;
+	int fd;
+	strbuf_addf(&path_sb, "%s/%s", refs->repo_dir, refname);
+	fd = open(path_sb.buf, O_RDONLY);
+	if (fd < 0) {
+		goto out;
+	}
+	strbuf_reset(&sb);
+	if (strbuf_read(&sb, fd, 256) < 0) {
+		ret = -1;
+	}
+	strbuf_rtrim(&sb);
+
+	ret = parse_loose_ref_contents(sb.buf, oid, referent, type);
+out:
+	close(fd);
+	strbuf_release(&sb);
+	strbuf_release(&path_sb);
+	return ret;
+}
+
 static int git_reftable_read_raw_ref(struct ref_store *ref_store,
 				     const char *refname, struct object_id *oid,
 				     struct strbuf *referent,
@@ -1319,6 +1346,10 @@ static int git_reftable_read_raw_ref(struct ref_store *ref_store,
 		return refs->err;
 	}
 
+	if (!strcmp(refname, "FETCH_HEAD")) {
+		return read_fetch_head(refs, refname, oid, referent, type);
+	}
+
 	/* This is usually not needed, but Git doesn't signal to ref backend if
 	   a subprocess updated the ref DB.  So we always check.
 	*/
t/t0031-reftable.sh +8 -1
index a6634bc882..9f90c10030 100755
@@ -169,5 +169,12 @@ test_expect_success 'worktrees 2' '
 	git worktree add --detach existing_empty master
 '
 
-test_done
+test_expect_success 'FETCH_HEAD' '
+        initialize &&
+	test_commit one &&
+	(git init sub && cd sub && test_commit two) &&
+	git fetch sub &&
+	git checkout FETCH_HEAD
+'
 
+test_done
Signature · 1 line
gitgitgadget
#404 lore raw

PATCH v20 16/21 Hookup unittests for the reftable library.

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

The unittests are under reftable/*_test.c, so all of the reftable code stays in one directory. They are called from t/helpers/test-reftable.c in t0031-reftable.sh

---
 Makefile                 | 19 +++++++++++++++++--
 t/helper/test-reftable.c | 15 +++++++++++++++
 t/helper/test-tool.c     |  1 +
 t/helper/test-tool.h     |  1 +
 t/t0031-reftable.sh      |  5 +++++
 5 files changed, 39 insertions(+), 2 deletions(-)
 create mode 100644 t/helper/test-reftable.c
Makefile +17 -2
index 6571ed1246..5e69c16507 100644
@@ -725,6 +725,7 @@ TEST_BUILTINS_OBJS += test-read-cache.o
 TEST_BUILTINS_OBJS += test-read-graph.o
 TEST_BUILTINS_OBJS += test-read-midx.o
 TEST_BUILTINS_OBJS += test-ref-store.o
+TEST_BUILTINS_OBJS += test-reftable.o
 TEST_BUILTINS_OBJS += test-regex.o
 TEST_BUILTINS_OBJS += test-repository.o
 TEST_BUILTINS_OBJS += test-revision-walking.o
@@ -808,6 +809,7 @@ LIB_FILE = libgit.a
 XDIFF_LIB = xdiff/lib.a
 VCSSVN_LIB = vcs-svn/lib.a
 REFTABLE_LIB = reftable/libreftable.a
+REFTABLE_TEST_LIB = reftable/libreftable_test.a
 
 GENERATED_H += config-list.h
 GENERATED_H += command-list.h
@@ -2371,6 +2373,15 @@ REFTABLE_OBJS += reftable/tree.o
 REFTABLE_OBJS += reftable/writer.o
 REFTABLE_OBJS += reftable/zlib-compat.o
 
+REFTABLE_TEST_OBJS += reftable/block_test.o
+REFTABLE_TEST_OBJS += reftable/merged_test.o
+REFTABLE_TEST_OBJS += reftable/record_test.o
+REFTABLE_TEST_OBJS += reftable/refname_test.o
+REFTABLE_TEST_OBJS += reftable/reftable_test.o
+REFTABLE_TEST_OBJS += reftable/strbuf_test.o
+REFTABLE_TEST_OBJS += reftable/stack_test.o
+REFTABLE_TEST_OBJS += reftable/tree_test.o
+REFTABLE_TEST_OBJS += reftable/test_framework.o
 
 TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
 OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
@@ -2378,6 +2389,7 @@ OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
 	$(VCSSVN_OBJS) \
 	$(FUZZ_OBJS) \
 	$(REFTABLE_OBJS) \
+	$(REFTABLE_TEST_OBJS) \
 	common-main.o \
 	git.o
 ifndef NO_CURL
@@ -2521,6 +2533,9 @@ $(VCSSVN_LIB): $(VCSSVN_OBJS)
 $(REFTABLE_LIB): $(REFTABLE_OBJS)
 	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
 
+$(REFTABLE_TEST_LIB): $(REFTABLE_TEST_OBJS)
+	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
+
 export DEFAULT_EDITOR DEFAULT_PAGER
 
 Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
@@ -2803,7 +2818,7 @@ t/helper/test-svn-fe$X: $(VCSSVN_LIB)
 
 t/helper/test-tool$X: $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
 
-t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS)
+t/helper/test-%$X: t/helper/test-%.o GIT-LDFLAGS $(GITLIBS) $(REFTABLE_TEST_LIB)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
 
 check-sha1:: t/helper/test-tool$X
@@ -3136,7 +3151,7 @@ cocciclean:
 clean: profile-clean coverage-clean cocciclean
 	$(RM) *.res
 	$(RM) $(OBJECTS)
-	$(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) $(REFTABLE_LIB)
+	$(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) $(REFTABLE_LIB) $(REFTABLE_TEST_LIB)
 	$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
 	$(RM) $(TEST_PROGRAMS)
 	$(RM) $(FUZZ_PROGRAMS)
t/helper/test-reftable.c new file +15 -0
new file mode 100644
index 0000000000..def8883439
@@ -0,0 +1,15 @@
+#include "reftable/reftable-tests.h"
+#include "test-tool.h"
+
+int cmd__reftable(int argc, const char **argv)
+{
+	block_test_main(argc, argv);
+	merged_test_main(argc, argv);
+	record_test_main(argc, argv);
+	refname_test_main(argc, argv);
+	reftable_test_main(argc, argv);
+	strbuf_test_main(argc, argv);
+	stack_test_main(argc, argv);
+	tree_test_main(argc, argv);
+	return 0;
+}
t/helper/test-tool.c +1 -0
index 590b2efca7..10366b7b76 100644
@@ -52,6 +52,7 @@ static struct test_cmd cmds[] = {
 	{ "read-graph", cmd__read_graph },
 	{ "read-midx", cmd__read_midx },
 	{ "ref-store", cmd__ref_store },
+	{ "reftable", cmd__reftable },
 	{ "regex", cmd__regex },
 	{ "repository", cmd__repository },
 	{ "revision-walking", cmd__revision_walking },
t/helper/test-tool.h +1 -0
index ddc8e990e9..d52ba2f5e5 100644
@@ -41,6 +41,7 @@ int cmd__read_cache(int argc, const char **argv);
 int cmd__read_graph(int argc, const char **argv);
 int cmd__read_midx(int argc, const char **argv);
 int cmd__ref_store(int argc, const char **argv);
+int cmd__reftable(int argc, const char **argv);
 int cmd__regex(int argc, const char **argv);
 int cmd__repository(int argc, const char **argv);
 int cmd__revision_walking(int argc, const char **argv);
t/t0031-reftable.sh +5 -0
index 9f90c10030..34cc787a61 100755
@@ -15,6 +15,11 @@ initialize ()  {
 	mv .git/hooks .git/hooks-disabled
 }
 
+test_expect_success 'unittests' '
+	test-tool reftable
+'
+
+
 test_expect_success 'delete ref' '
 	initialize &&
 	test_commit file &&
Signature · 1 line
gitgitgadget
#402 lore raw

PATCH v20 14/21 Reftable support for git-core

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

For background, see the previous commit introducing the library.

This introduces the file refs/reftable-backend.c containing a reftable-powered ref storage backend.

It can be activated by passing --ref-storage=reftable to "git init", or setting GIT_TEST_REFTABLE in the environment.

Example use: see t/t0031-reftable.sh

---
 .../technical/repository-version.txt          |    7 +
 Makefile                                      |   28 +-
 builtin/clone.c                               |    4 +-
 builtin/init-db.c                             |   55 +-
 builtin/worktree.c                            |   27 +-
 cache.h                                       |    8 +-
 refs.c                                        |   27 +-
 refs.h                                        |    3 +
 refs/refs-internal.h                          |    1 +
 refs/reftable-backend.c                       | 1384 +++++++++++++++++
 reftable/update.sh                            |    3 -
 repository.c                                  |    2 +
 repository.h                                  |    3 +
 setup.c                                       |   10 +-
 t/t0031-reftable.sh                           |  173 +++
 15 files changed, 1698 insertions(+), 37 deletions(-)
 create mode 100644 refs/reftable-backend.c
 create mode 100755 t/t0031-reftable.sh
Documentation/technical/repository-version.txt +7 -0
index 7844ef30ff..7257623583 100644
@@ -100,3 +100,10 @@ If set, by default "git config" reads from both "config" and
 multiple working directory mode, "config" file is shared while
 "config.worktree" is per-working directory (i.e., it's in
 GIT_COMMON_DIR/worktrees/<id>/config.worktree)
+
+==== `refStorage`
+
+Specifies the file format for the ref database. Values are `files`
+(for the traditional packed + loose ref format) and `reftable` for the
+binary reftable format. See https://github.com/google/reftable for
+more information.
Makefile +26 -2
index 372139f1f2..6571ed1246 100644
@@ -807,6 +807,7 @@ TEST_SHELL_PATH = $(SHELL_PATH)
 LIB_FILE = libgit.a
 XDIFF_LIB = xdiff/lib.a
 VCSSVN_LIB = vcs-svn/lib.a
+REFTABLE_LIB = reftable/libreftable.a
 
 GENERATED_H += config-list.h
 GENERATED_H += command-list.h
@@ -959,6 +960,7 @@ LIB_OBJS += ref-filter.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/files-backend.o
+LIB_OBJS += refs/reftable-backend.o
 LIB_OBJS += refs/iterator.o
 LIB_OBJS += refs/packed-backend.o
 LIB_OBJS += refs/ref-cache.o
@@ -1162,7 +1164,7 @@ THIRD_PARTY_SOURCES += compat/regex/%
 THIRD_PARTY_SOURCES += sha1collisiondetection/%
 THIRD_PARTY_SOURCES += sha1dc/%
 
-GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB)
+GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB)
 EXTLIBS =
 
 GIT_USER_AGENT = git/$(GIT_VERSION)
@@ -2352,11 +2354,30 @@ VCSSVN_OBJS += vcs-svn/sliding_window.o
 VCSSVN_OBJS += vcs-svn/svndiff.o
 VCSSVN_OBJS += vcs-svn/svndump.o
 
+REFTABLE_OBJS += reftable/basics.o
+REFTABLE_OBJS += reftable/block.o
+REFTABLE_OBJS += reftable/compat.o
+REFTABLE_OBJS += reftable/file.o
+REFTABLE_OBJS += reftable/iter.o
+REFTABLE_OBJS += reftable/merged.o
+REFTABLE_OBJS += reftable/pq.o
+REFTABLE_OBJS += reftable/reader.o
+REFTABLE_OBJS += reftable/record.o
+REFTABLE_OBJS += reftable/refname.o
+REFTABLE_OBJS += reftable/reftable.o
+REFTABLE_OBJS += reftable/strbuf.o
+REFTABLE_OBJS += reftable/stack.o
+REFTABLE_OBJS += reftable/tree.o
+REFTABLE_OBJS += reftable/writer.o
+REFTABLE_OBJS += reftable/zlib-compat.o
+
+
 TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST_BUILTINS_OBJS))
 OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
 	$(XDIFF_OBJS) \
 	$(VCSSVN_OBJS) \
 	$(FUZZ_OBJS) \
+	$(REFTABLE_OBJS) \
 	common-main.o \
 	git.o
 ifndef NO_CURL
@@ -2497,6 +2518,9 @@ $(XDIFF_LIB): $(XDIFF_OBJS)
 $(VCSSVN_LIB): $(VCSSVN_OBJS)
 	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
 
+$(REFTABLE_LIB): $(REFTABLE_OBJS)
+	$(QUIET_AR)$(RM) $@ && $(AR) $(ARFLAGS) $@ $^
+
 export DEFAULT_EDITOR DEFAULT_PAGER
 
 Documentation/GIT-EXCLUDED-PROGRAMS: FORCE
@@ -3112,7 +3136,7 @@ cocciclean:
 clean: profile-clean coverage-clean cocciclean
 	$(RM) *.res
 	$(RM) $(OBJECTS)
-	$(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
+	$(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) $(REFTABLE_LIB)
 	$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
 	$(RM) $(TEST_PROGRAMS)
 	$(RM) $(FUZZ_PROGRAMS)
builtin/clone.c +2 -2
index a9f3312a54..4f78ec4a2b 100644
@@ -1120,7 +1120,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	}
 
 	init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL,
-		INIT_DB_QUIET);
+		default_ref_storage(), INIT_DB_QUIET);
 
 	if (real_git_dir)
 		git_dir = real_git_dir;
@@ -1235,7 +1235,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		 * Now that we know what algorithm the remote side is using,
 		 * let's set ours to the same thing.
 		 */
-		initialize_repository_version(hash_algo);
+		initialize_repository_version(hash_algo, default_ref_storage());
 		repo_set_hash_algo(the_repository, hash_algo);
 
 		mapped_refs = wanted_peer_refs(refs, &remote->fetch);
builtin/init-db.c +41 -14

133 lines not on this page · raw message

builtin/worktree.c +24 -3
index f0cbdef718..1935967072 100644
@@ -12,6 +12,7 @@
 #include "submodule.h"
 #include "utf8.h"
 #include "worktree.h"
+#include "../refs/refs-internal.h"
 
 static const char * const worktree_usage[] = {
 	N_("git worktree add [<options>] <path> [<commit-ish>]"),
@@ -402,9 +403,29 @@ static int add_worktree(const char *path, const char *refname,
 	 * worktree.
 	 */
 	strbuf_reset(&sb);
-	strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
-	write_file(sb.buf, "%s", oid_to_hex(&null_oid));
-	strbuf_reset(&sb);
+	if (get_main_ref_store(the_repository)->be == &refs_be_reftable) {
+		/* XXX this is cut & paste from reftable_init_db. */
+		strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
+		write_file(sb.buf, "%s", "ref: refs/heads/.invalid\n");
+		strbuf_reset(&sb);
+
+		strbuf_addf(&sb, "%s/refs", sb_repo.buf);
+		safe_create_dir(sb.buf, 1);
+		strbuf_reset(&sb);
+
+		strbuf_addf(&sb, "%s/refs/heads", sb_repo.buf);
+		write_file(sb.buf, "this repository uses the reftable format");
+		strbuf_reset(&sb);
+
+		strbuf_addf(&sb, "%s/reftable", sb_repo.buf);
+		safe_create_dir(sb.buf, 1);
+		strbuf_reset(&sb);
+	} else {
+		strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
+		write_file(sb.buf, "%s", oid_to_hex(&null_oid));
+		strbuf_reset(&sb);
+	}
+
 	strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
 	write_file(sb.buf, "../..");
 
cache.h +5 -3
index 0290849c19..6090da4967 100644
@@ -627,9 +627,10 @@ int path_inside_repo(const char *prefix, const char *path);
 #define INIT_DB_EXIST_OK 0x0002
 
 int init_db(const char *git_dir, const char *real_git_dir,
-	    const char *template_dir, int hash_algo,
-	    const char *initial_branch, unsigned int flags);
-void initialize_repository_version(int hash_algo);
+	    const char *template_dir, int hash_algo, const char *initial_branch,
+	    const char *ref_storage_format, unsigned int flags);
+void initialize_repository_version(int hash_algo,
+				   const char *ref_storage_format);
 
 void sanitize_stdfds(void);
 int daemonize(void);
@@ -1043,6 +1044,7 @@ struct repository_format {
 	int is_bare;
 	int hash_algo;
 	char *work_tree;
+	char *ref_storage;
 	struct string_list unknown_extensions;
 	struct string_list v1_only_extensions;
 };
refs.c +19 -8
index d4b5397c8f..3789bc1071 100644
@@ -19,10 +19,16 @@
 #include "repository.h"
 #include "sigchain.h"
 
+const char *default_ref_storage(void)
+{
+	const char *test = getenv("GIT_TEST_REFTABLE");
+	return test ? "reftable" : "files";
+}
+
 /*
  * List of all available backends
  */
-static struct ref_storage_be *refs_backends = &refs_be_files;
+static struct ref_storage_be *refs_backends = &refs_be_reftable;
 
 static struct ref_storage_be *find_ref_storage_backend(const char *name)
 {
@@ -1725,13 +1731,13 @@ static struct ref_store *lookup_ref_store_map(struct hashmap *map,
  * Create, record, and return a ref_store instance for the specified
  * gitdir.
  */
-static struct ref_store *ref_store_init(const char *gitdir,
+static struct ref_store *ref_store_init(const char *gitdir, const char *be_name,
 					unsigned int flags)
 {
-	const char *be_name = "files";
-	struct ref_storage_be *be = find_ref_storage_backend(be_name);
+	struct ref_storage_be *be;
 	struct ref_store *refs;
 
+	be = find_ref_storage_backend(be_name);
 	if (!be)
 		BUG("reference backend %s is unknown", be_name);
 
@@ -1747,7 +1753,11 @@ struct ref_store *get_main_ref_store(struct repository *r)
 	if (!r->gitdir)
 		BUG("attempting to get main_ref_store outside of repository");
 
-	r->refs_private = ref_store_init(r->gitdir, REF_STORE_ALL_CAPS);
+	r->refs_private = ref_store_init(r->gitdir,
+					 r->ref_storage_format ?
+						 r->ref_storage_format :
+						 default_ref_storage(),
+					 REF_STORE_ALL_CAPS);
 	return r->refs_private;
 }
 
@@ -1802,7 +1812,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
 		goto done;
 
 	/* assume that add_submodule_odb() has been called */
-	refs = ref_store_init(submodule_sb.buf,
+	refs = ref_store_init(submodule_sb.buf, default_ref_storage(),
 			      REF_STORE_READ | REF_STORE_ODB);
 	register_ref_store_map(&submodule_ref_stores, "submodule",
 			       refs, submodule);
@@ -1816,6 +1826,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
 
 struct ref_store *get_worktree_ref_store(const struct worktree *wt)
 {
+	const char *format = default_ref_storage();
 	struct ref_store *refs;
 	const char *id;
 
@@ -1829,9 +1840,9 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)
 
 	if (wt->id)
 		refs = ref_store_init(git_common_path("worktrees/%s", wt->id),
-				      REF_STORE_ALL_CAPS);
+				      format, REF_STORE_ALL_CAPS);
 	else
-		refs = ref_store_init(get_git_common_dir(),
+		refs = ref_store_init(get_git_common_dir(), format,
 				      REF_STORE_ALL_CAPS);
 
 	if (refs)
refs.h +3 -0
index f8835f0aef..581ec270dc 100644
@@ -9,6 +9,9 @@ struct string_list;
 struct string_list_item;
 struct worktree;
 
+/* Returns the ref storage backend to use by default. */
+const char *default_ref_storage(void);
+
 /*
  * Resolve a reference, recursively following symbolic refererences.
  *
refs/refs-internal.h +1 -0
index 61fd2e661f..b9b5c2e87c 100644
@@ -669,6 +669,7 @@ struct ref_storage_be {
 };
 
 extern struct ref_storage_be refs_be_files;
+extern struct ref_storage_be refs_be_reftable;
 extern struct ref_storage_be refs_be_packed;
 
 /*
refs/reftable-backend.c new file +1384 -0

1,384 lines not on this page · raw message

reftable/update.sh +0 -3
index 4dadd875f4..d816005db6 100755
@@ -16,7 +16,4 @@ cp reftable-repo/LICENSE reftable/
 git --git-dir reftable-repo/.git show --no-patch --format=oneline HEAD \
   > reftable/VERSION
 
-mv reftable/system.h reftable/system.h~
-sed 's|if REFTABLE_IN_GITCORE|if 1 /* REFTABLE_IN_GITCORE */|'  < reftable/system.h~ > reftable/system.h
-
 git add reftable/*.[ch] reftable/LICENSE reftable/VERSION
repository.c +2 -0
index 6f7f6f002b..087760bc18 100644
@@ -178,6 +178,8 @@ int repo_init(struct repository *repo,
 	if (worktree)
 		repo_set_worktree(repo, worktree);
 
+	repo->ref_storage_format = xstrdup_or_null(format.ref_storage);
+
 	clear_repository_format(&format);
 	return 0;
 
repository.h +3 -0
index 3c1f7d54bd..293b346304 100644
@@ -74,6 +74,9 @@ struct repository {
 	 */
 	struct ref_store *refs_private;
 
+	/* The format to use for the ref database. */
+	char *ref_storage_format;
+
 	/*
 	 * Contains path to often used file names.
 	 */
setup.c +8 -2
index 78e7ae1be7..d8ad7e9166 100644
@@ -490,8 +490,10 @@ static enum extension_result handle_extension(const char *var,
 {
 	if (!strcmp(ext, "noop-v1")) {
 		return EXTENSION_OK;
+	} else if (!strcmp(ext, "refstorage")) {
+		data->ref_storage = xstrdup(value);
+		return EXTENSION_OK;
 	}
-
 	return EXTENSION_UNKNOWN;
 }
 
@@ -642,6 +644,7 @@ void clear_repository_format(struct repository_format *format)
 	string_list_clear(&format->v1_only_extensions, 0);
 	free(format->work_tree);
 	free(format->partial_clone);
+	free(format->ref_storage);
 	init_repository_format(format);
 }
 
@@ -1299,8 +1302,11 @@ const char *setup_git_directory_gently(int *nongit_ok)
 				gitdir = DEFAULT_GIT_DIR_ENVIRONMENT;
 			setup_git_env(gitdir);
 		}
-		if (startup_info->have_repository)
+		if (startup_info->have_repository) {
 			repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
+			the_repository->ref_storage_format =
+				xstrdup_or_null(repo_fmt.ref_storage);
+		}
 	}
 
 	strbuf_release(&dir);
t/t0031-reftable.sh new file +173 -0

173 lines not on this page · raw message

Signature · 1 line
gitgitgadget
#406 lore raw

PATCH v20 18/21 vcxproj: adjust for the reftable changes

In reply to Han-Wen Nienhuys via GitGitGadget

From: Johannes Schindelin <[email protected]>

This allows Git to be compiled via Visual Studio again after integrating the hn/reftable branch.

---
 config.mak.uname                           |  2 +-
 contrib/buildsystems/Generators/Vcxproj.pm | 11 ++++++++++-
 2 files changed, 11 insertions(+), 2 deletions(-)
config.mak.uname +1 -1
index c7eba69e54..ae4e25a1a4 100644
@@ -709,7 +709,7 @@ vcxproj:
 	# Make .vcxproj files and add them
 	unset QUIET_GEN QUIET_BUILT_IN; \
 	perl contrib/buildsystems/generate -g Vcxproj
-	git add -f git.sln {*,*/lib,t/helper/*}/*.vcxproj
+	git add -f git.sln {*,*/lib,*/libreftable,t/helper/*}/*.vcxproj
 
 	# Generate the LinkOrCopyBuiltins.targets and LinkOrCopyRemoteHttp.targets file
 	(echo '<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">' && \
contrib/buildsystems/Generators/Vcxproj.pm +10 -1
index 5c666f9ac0..33a08d3165 100644
@@ -77,7 +77,7 @@ sub createProject {
     my $libs_release = "\n    ";
     my $libs_debug = "\n    ";
     if (!$static_library) {
-      $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}}));
+      $libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib|reftable\/libreftable\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}}));
       $libs_debug = $libs_release;
       $libs_debug =~ s/zlib\.lib/zlibd\.lib/g;
       $libs_debug =~ s/libcurl\.lib/libcurl-d\.lib/g;
@@ -231,6 +231,7 @@ sub createProject {
 EOM
     if (!$static_library || $target =~ 'vcs-svn' || $target =~ 'xdiff') {
       my $uuid_libgit = $$build_structure{"LIBS_libgit_GUID"};
+      my $uuid_libreftable = $$build_structure{"LIBS_reftable/libreftable_GUID"};
       my $uuid_xdiff_lib = $$build_structure{"LIBS_xdiff/lib_GUID"};
 
       print F << "EOM";
@@ -240,6 +241,14 @@ sub createProject {
       <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
     </ProjectReference>
 EOM
+      if (!($name =~ /xdiff|libreftable/)) {
+        print F << "EOM";
+    <ProjectReference Include="$cdup\\reftable\\libreftable\\libreftable.vcxproj">
+      <Project>$uuid_libreftable</Project>
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+    </ProjectReference>
+EOM
+      }
       if (!($name =~ 'xdiff')) {
         print F << "EOM";
     <ProjectReference Include="$cdup\\xdiff\\lib\\xdiff_lib.vcxproj">
Signature · 1 line
gitgitgadget
#407 lore raw

PATCH v20 19/21 git-prompt: prepare for reftable refs backend

In reply to Han-Wen Nienhuys via GitGitGadget

From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= <[email protected]>

In our git-prompt script we strive to use Bash builtins wherever possible, because fork()-ing subshells for command substitutions and fork()+exec()-ing Git commands are expensive on some platforms. We even read and parse '.git/HEAD' using Bash builtins to get the name of the current branch [1]. However, the upcoming reftable refs backend won't use '.git/HEAD' at all, but will write an invalid refname as placeholder for backwards compatibility instead, which will break our git-prompt script.

Update the git-prompt script to recognize the placeholder '.git/HEAD' written by the reftable backend (its content is specified in the reftable specs), and then fall back to use 'git symbolic-ref' to get the name of the current branch.

[1] 3a43c4b5bd (bash prompt: use bash builtins to find out current

    branch, 2011-03-31)
---
 contrib/completion/git-prompt.sh | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
contrib/completion/git-prompt.sh +6 -1
index 16260bab73..5086bbdbfe 100644
@@ -476,10 +476,15 @@ __git_ps1 ()
 			if ! __git_eread "$g/HEAD" head; then
 				return $exit
 			fi
-			# is it a symbolic ref?
 			b="${head#ref: }"
 			if [ "$head" = "$b" ]; then
 				detached=yes
+			elif [ "$b" = "refs/heads/.invalid" ]; then
+				# Reftable
+				b="$(git symbolic-ref HEAD 2>/dev/null)" ||
+				detached=yes
+			fi
+			if [ "$detached" = yes ]; then
 				b="$(
 				case "${GIT_PS1_DESCRIBE_STYLE-}" in
 				(contains)
Signature · 1 line
gitgitgadget
#409 lore raw

PATCH v20 21/21 Add "test-tool dump-reftable" command.

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

This command dumps individual tables or a stack of of tables.

---
 Makefile                 | 1 +
 t/helper/test-reftable.c | 5 +++++
 t/helper/test-tool.c     | 1 +
 t/helper/test-tool.h     | 1 +
 4 files changed, 8 insertions(+)
Makefile +1 -0
index 0948a3bb00..c22fd41662 100644
@@ -2375,6 +2375,7 @@ REFTABLE_OBJS += reftable/writer.o
 REFTABLE_OBJS += reftable/zlib-compat.o
 
 REFTABLE_TEST_OBJS += reftable/block_test.o
+REFTABLE_TEST_OBJS += reftable/dump.o
 REFTABLE_TEST_OBJS += reftable/merged_test.o
 REFTABLE_TEST_OBJS += reftable/record_test.o
 REFTABLE_TEST_OBJS += reftable/refname_test.o
t/helper/test-reftable.c +5 -0
index def8883439..aff4fbccda 100644
@@ -13,3 +13,8 @@ int cmd__reftable(int argc, const char **argv)
 	tree_test_main(argc, argv);
 	return 0;
 }
+
+int cmd__dump_reftable(int argc, const char **argv)
+{
+	return reftable_dump_main(argc, (char *const *)argv);
+}
t/helper/test-tool.c +1 -0
index 10366b7b76..9e689f9d2b 100644
@@ -53,6 +53,7 @@ static struct test_cmd cmds[] = {
 	{ "read-midx", cmd__read_midx },
 	{ "ref-store", cmd__ref_store },
 	{ "reftable", cmd__reftable },
+	{ "dump-reftable", cmd__dump_reftable },
 	{ "regex", cmd__regex },
 	{ "repository", cmd__repository },
 	{ "revision-walking", cmd__revision_walking },
t/helper/test-tool.h +1 -0
index d52ba2f5e5..bf833e01d4 100644
@@ -17,6 +17,7 @@ int cmd__dump_cache_tree(int argc, const char **argv);
 int cmd__dump_fsmonitor(int argc, const char **argv);
 int cmd__dump_split_index(int argc, const char **argv);
 int cmd__dump_untracked_cache(int argc, const char **argv);
+int cmd__dump_reftable(int argc, const char **argv);
 int cmd__example_decorate(int argc, const char **argv);
 int cmd__genrandom(int argc, const char **argv);
 int cmd__genzeros(int argc, const char **argv);
Signature · 1 line
gitgitgadget
#408 lore raw

PATCH v20 20/21 Add reftable testing infrastructure

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

* Add GIT_TEST_REFTABLE environment var to control default ref storage

* Add test_prerequisite REFTABLE.

* Skip some tests that are incompatible:

* t3210-pack-refs.sh - does not apply
* t1450-fsck.sh - manipulates .git/ directly to create invalid state

Major test failures:

* t1400-update-ref.sh - Reads from .git/{refs,logs} directly
* t1404-update-ref-errors.sh - Manipulates .git/refs/ directly
* t1405 - inspecs .git/ directly.

---
 t/t1409-avoid-packing-refs.sh | 6 ++++++
 t/t1450-fsck.sh               | 6 ++++++
 t/t3210-pack-refs.sh          | 6 ++++++
 t/test-lib.sh                 | 5 +++++
 4 files changed, 23 insertions(+)
t/t1409-avoid-packing-refs.sh +6 -0
index be12fb6350..c6f7832556 100755
@@ -4,6 +4,12 @@ test_description='avoid rewriting packed-refs unnecessarily'
 
 . ./test-lib.sh
 
+if test_have_prereq REFTABLE
+then
+  skip_all='skipping pack-refs tests; incompatible with reftable'
+  test_done
+fi
+
 # Add an identifying mark to the packed-refs file header line. This
 # shouldn't upset readers, and it should be omitted if the file is
 # ever rewritten.
t/t1450-fsck.sh +6 -0
index 344a2aad82..0966920324 100755
@@ -8,6 +8,12 @@ test_description='git fsck random collection of tests
 
 . ./test-lib.sh
 
+if test_have_prereq REFTABLE
+then
+  skip_all='skipping tests; incompatible with reftable'
+  test_done
+fi
+
 test_expect_success setup '
 	test_oid_init &&
 	git config gc.auto 0 &&
t/t3210-pack-refs.sh +6 -0
index f41b2afb99..edaef2c175 100755
@@ -11,6 +11,12 @@ semantic is still the same.
 '
 . ./test-lib.sh
 
+if test_have_prereq REFTABLE
+then
+  skip_all='skipping pack-refs tests; incompatible with reftable'
+  test_done
+fi
+
 test_expect_success 'enable reflogs' '
 	git config core.logallrefupdates true
 '
t/test-lib.sh +5 -0
index ba224c86f5..7a341c7bc5 100644
@@ -1508,6 +1508,11 @@ parisc* | hppa*)
 	;;
 esac
 
+if test -n "$GIT_TEST_REFTABLE"
+then
+  test_set_prereq REFTABLE
+fi
+
 ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1
 test -z "$NO_PERL" && test_set_prereq PERL
 test -z "$NO_PTHREADS" && test_set_prereq PTHREADS
Signature · 1 line
gitgitgadget
#400 lore raw

PATCH v20 12/21 Add reftable library

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

Reftable is a format for storing the ref database. The rationale and format layout is detailed in commit 35e6c47404 ("reftable: file format documentation", 2020-05-20).

This commits imports a fully functioning library for reading and writing reftables, along with unittests.

It is suggested to read the code in the following order:

* The specification under Documentation/technical/reftable.txt

* reftable.h - the public API

* record.{c,h} - reading and writing records

* block.{c,h} - reading and writing blocks.

* reader.{c,h} - reading a complete reftable file.

* writer.{c,h} - writing a complete reftable file.

* merged.{c,h} and pq.{c,h} - reading a stack of reftables

* stack.{c,h} - writing and compacting stacks of reftable on the filesystem.

---
 reftable/LICENSE          |   31 +
 reftable/VERSION          |    1 +
 reftable/basics.c         |  215 +++++++
 reftable/basics.h         |   53 ++
 reftable/block.c          |  432 +++++++++++++
 reftable/block.h          |  129 ++++
 reftable/block_test.c     |  157 +++++
 reftable/compat.c         |   98 +++
 reftable/compat.h         |   48 ++
 reftable/constants.h      |   21 +
 reftable/dump.c           |  212 +++++++
 reftable/file.c           |   95 +++
 reftable/iter.c           |  242 ++++++++
 reftable/iter.h           |   72 +++
 reftable/merged.c         |  317 ++++++++++
 reftable/merged.h         |   43 ++
 reftable/merged_test.c    |  286 +++++++++
 reftable/pq.c             |  115 ++++
 reftable/pq.h             |   34 +
 reftable/reader.c         |  744 ++++++++++++++++++++++
 reftable/reader.h         |   65 ++
 reftable/record.c         | 1126 ++++++++++++++++++++++++++++++++++
 reftable/record.h         |  143 +++++
 reftable/record_test.c    |  410 +++++++++++++
 reftable/refname.c        |  209 +++++++
 reftable/refname.h        |   38 ++
 reftable/refname_test.c   |   99 +++
 reftable/reftable-tests.h |   22 +
 reftable/reftable.c       |  154 +++++
 reftable/reftable.h       |  585 ++++++++++++++++++
 reftable/reftable_test.c  |  632 +++++++++++++++++++
 reftable/stack.c          | 1225 +++++++++++++++++++++++++++++++++++++
 reftable/stack.h          |   50 ++
 reftable/stack_test.c     |  787 ++++++++++++++++++++++++
 reftable/strbuf.c         |  206 +++++++
 reftable/strbuf.h         |   88 +++
 reftable/strbuf_test.c    |   39 ++
 reftable/system.h         |   53 ++
 reftable/test_framework.c |   69 +++
 reftable/test_framework.h |   64 ++
 reftable/tree.c           |   63 ++
 reftable/tree.h           |   34 +
 reftable/tree_test.c      |   62 ++
 reftable/update.sh        |   22 +
 reftable/writer.c         |  663 ++++++++++++++++++++
 reftable/writer.h         |   60 ++
 reftable/zlib-compat.c    |   92 +++
 47 files changed, 10405 insertions(+)
 create mode 100644 reftable/LICENSE
 create mode 100644 reftable/VERSION
 create mode 100644 reftable/basics.c
 create mode 100644 reftable/basics.h
 create mode 100644 reftable/block.c
 create mode 100644 reftable/block.h
 create mode 100644 reftable/block_test.c
 create mode 100644 reftable/compat.c
 create mode 100644 reftable/compat.h
 create mode 100644 reftable/constants.h
 create mode 100644 reftable/dump.c
 create mode 100644 reftable/file.c
 create mode 100644 reftable/iter.c
 create mode 100644 reftable/iter.h
 create mode 100644 reftable/merged.c
 create mode 100644 reftable/merged.h
 create mode 100644 reftable/merged_test.c
 create mode 100644 reftable/pq.c
 create mode 100644 reftable/pq.h
 create mode 100644 reftable/reader.c
 create mode 100644 reftable/reader.h
 create mode 100644 reftable/record.c
 create mode 100644 reftable/record.h
 create mode 100644 reftable/record_test.c
 create mode 100644 reftable/refname.c
 create mode 100644 reftable/refname.h
 create mode 100644 reftable/refname_test.c
 create mode 100644 reftable/reftable-tests.h
 create mode 100644 reftable/reftable.c
 create mode 100644 reftable/reftable.h
 create mode 100644 reftable/reftable_test.c
 create mode 100644 reftable/stack.c
 create mode 100644 reftable/stack.h
 create mode 100644 reftable/stack_test.c
 create mode 100644 reftable/strbuf.c
 create mode 100644 reftable/strbuf.h
 create mode 100644 reftable/strbuf_test.c
 create mode 100644 reftable/system.h
 create mode 100644 reftable/test_framework.c
 create mode 100644 reftable/test_framework.h
 create mode 100644 reftable/tree.c
 create mode 100644 reftable/tree.h
 create mode 100644 reftable/tree_test.c
 create mode 100755 reftable/update.sh
 create mode 100644 reftable/writer.c
 create mode 100644 reftable/writer.h
 create mode 100644 reftable/zlib-compat.c
reftable/LICENSE new file +31 -0
new file mode 100644
index 0000000000..402e0f9356
@@ -0,0 +1,31 @@
+BSD License
+
+Copyright (c) 2020, Google LLC
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+* Neither the name of Google LLC nor the names of its contributors may
+be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
reftable/VERSION new file +1 -0
new file mode 100644
index 0000000000..b0d1123483
@@ -0,0 +1 @@
+994cc2f626ff626ff04be5240413775f832110fb C: formatting tweaks
reftable/basics.c new file +215 -0

215 lines not on this page · raw message

reftable/basics.h new file +53 -0

53 lines not on this page · raw message

reftable/block.c new file +432 -0

432 lines not on this page · raw message

reftable/block.h new file +129 -0

129 lines not on this page · raw message

reftable/block_test.c new file +157 -0

157 lines not on this page · raw message

reftable/compat.c new file +98 -0

98 lines not on this page · raw message

reftable/compat.h new file +48 -0

48 lines not on this page · raw message

reftable/constants.h new file +21 -0
new file mode 100644
index 0000000000..5eee72c4c1
@@ -0,0 +1,21 @@
+/*
+Copyright 2020 Google LLC
+
+Use of this source code is governed by a BSD-style
+license that can be found in the LICENSE file or at
+https://developers.google.com/open-source/licenses/bsd
+*/
+
+#ifndef CONSTANTS_H
+#define CONSTANTS_H
+
+#define BLOCK_TYPE_LOG 'g'
+#define BLOCK_TYPE_INDEX 'i'
+#define BLOCK_TYPE_REF 'r'
+#define BLOCK_TYPE_OBJ 'o'
+#define BLOCK_TYPE_ANY 0
+
+#define MAX_RESTARTS ((1 << 16) - 1)
+#define DEFAULT_BLOCK_SIZE 4096
+
+#endif
reftable/dump.c new file +212 -0

212 lines not on this page · raw message

reftable/file.c new file +95 -0

95 lines not on this page · raw message

reftable/iter.c new file +242 -0

242 lines not on this page · raw message

reftable/iter.h new file +72 -0

72 lines not on this page · raw message

reftable/merged.c new file +317 -0

317 lines not on this page · raw message

reftable/merged.h new file +43 -0

43 lines not on this page · raw message

reftable/merged_test.c new file +286 -0

286 lines not on this page · raw message

reftable/pq.c new file +115 -0

115 lines not on this page · raw message

reftable/pq.h new file +34 -0
new file mode 100644
index 0000000000..1117f70306
@@ -0,0 +1,34 @@
+/*
+Copyright 2020 Google LLC
+
+Use of this source code is governed by a BSD-style
+license that can be found in the LICENSE file or at
+https://developers.google.com/open-source/licenses/bsd
+*/
+
+#ifndef PQ_H
+#define PQ_H
+
+#include "record.h"
+
+struct pq_entry {
+	int index;
+	struct reftable_record rec;
+};
+
+int pq_less(struct pq_entry a, struct pq_entry b);
+
+struct merged_iter_pqueue {
+	struct pq_entry *heap;
+	size_t len;
+	size_t cap;
+};
+
+struct pq_entry merged_iter_pqueue_top(struct merged_iter_pqueue pq);
+bool merged_iter_pqueue_is_empty(struct merged_iter_pqueue pq);
+void merged_iter_pqueue_check(struct merged_iter_pqueue pq);
+struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq);
+void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, struct pq_entry e);
+void merged_iter_pqueue_clear(struct merged_iter_pqueue *pq);
+
+#endif
reftable/reader.c new file +744 -0

399 lines not on this page · raw message

reftable/reader.h new file +65 -0

65 lines not on this page · raw message

reftable/record.c new file +1126 -0

1,126 lines not on this page · raw message

reftable/record.h new file +143 -0

143 lines not on this page · raw message

reftable/record_test.c new file +410 -0

410 lines not on this page · raw message

reftable/refname.c new file +209 -0

209 lines not on this page · raw message

reftable/refname.h new file +38 -0
new file mode 100644
index 0000000000..13bf1e6861

Lines of this file are not shown. View the raw message.

reftable/refname_test.c new file +99 -0

99 lines not on this page · raw message

reftable/reftable-tests.h new file +22 -0
new file mode 100644
index 0000000000..e38471888f

Lines of this file are not shown. View the raw message.

reftable/reftable.c new file +154 -0

154 lines not on this page · raw message

reftable/reftable.h new file +585 -0

585 lines not on this page · raw message

reftable/reftable_test.c new file +632 -0

632 lines not on this page · raw message

reftable/stack.c new file +1225 -0

1,225 lines not on this page · raw message

reftable/stack.h new file +50 -0

50 lines not on this page · raw message

reftable/stack_test.c new file +787 -0

787 lines not on this page · raw message

reftable/strbuf.c new file +206 -0

206 lines not on this page · raw message

reftable/strbuf.h new file +88 -0

88 lines not on this page · raw message

reftable/strbuf_test.c new file +39 -0
new file mode 100644
index 0000000000..862ed86791

Lines of this file are not shown. View the raw message.

reftable/system.h new file +53 -0

53 lines not on this page · raw message

reftable/test_framework.c new file +69 -0

69 lines not on this page · raw message

reftable/test_framework.h new file +64 -0

64 lines not on this page · raw message

reftable/tree.c new file +63 -0

63 lines not on this page · raw message

reftable/tree.h new file +34 -0
new file mode 100644
index 0000000000..954512e9a3

Lines of this file are not shown. View the raw message.

reftable/tree_test.c new file +62 -0

62 lines not on this page · raw message

reftable/update.sh new file +22 -0
new file mode 100755
index 0000000000..4dadd875f4

Lines of this file are not shown. View the raw message.

reftable/writer.c new file +663 -0

663 lines not on this page · raw message

reftable/writer.h new file +60 -0

60 lines not on this page · raw message

reftable/zlib-compat.c new file +92 -0

92 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

Signature · 1 line
gitgitgadget

Part of the diff is not shown here. View the raw message.

#405 lore raw

PATCH v20 17/21 Add GIT_DEBUG_REFS debugging mechanism

In reply to Han-Wen Nienhuys via GitGitGadget

From: Han-Wen Nienhuys <[email protected]>

When set in the environment, GIT_DEBUG_REFS makes git print operations and results as they flow through the ref storage backend. This helps debug discrepancies between different ref backends.

---
 Makefile              |   1 +
 builtin/worktree.c    |   4 +-
 refs.c                |   8 +
 refs.h                |   1 +
 refs/debug.c          | 387 ++++++++++++++++++++++++++++++++++++++++++
 refs/refs-internal.h  |   6 +
 t/t0033-debug-refs.sh |  18 ++
 7 files changed, 424 insertions(+), 1 deletion(-)
 create mode 100644 refs/debug.c
 create mode 100755 t/t0033-debug-refs.sh
Makefile +1 -0
index 5e69c16507..0948a3bb00 100644
@@ -961,6 +961,7 @@ LIB_OBJS += rebase.o
 LIB_OBJS += ref-filter.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
+LIB_OBJS += refs/debug.o
 LIB_OBJS += refs/files-backend.o
 LIB_OBJS += refs/reftable-backend.o
 LIB_OBJS += refs/iterator.o
builtin/worktree.c +3 -1
index 1935967072..12d2204239 100644
@@ -403,7 +403,9 @@ static int add_worktree(const char *path, const char *refname,
 	 * worktree.
 	 */
 	strbuf_reset(&sb);
-	if (get_main_ref_store(the_repository)->be == &refs_be_reftable) {
+
+	if (!strcmp(ref_store_backend_name(get_main_ref_store(the_repository)),
+		    "reftable")) {
 		/* XXX this is cut & paste from reftable_init_db. */
 		strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
 		write_file(sb.buf, "%s", "ref: refs/heads/.invalid\n");
refs.c +8 -0
index 3789bc1071..b37ccd4c53 100644
@@ -44,6 +44,11 @@ int ref_storage_backend_exists(const char *name)
 	return find_ref_storage_backend(name) != NULL;
 }
 
+const char *ref_store_backend_name(struct ref_store *refs)
+{
+	return refs->be->name;
+}
+
 /*
  * How to handle various characters in refnames:
  * 0: An acceptable character for refs
@@ -1758,6 +1763,9 @@ struct ref_store *get_main_ref_store(struct repository *r)
 						 r->ref_storage_format :
 						 default_ref_storage(),
 					 REF_STORE_ALL_CAPS);
+	if (getenv("GIT_DEBUG_REFS")) {
+		r->refs_private = debug_wrap(r->gitdir, r->refs_private);
+	}
 	return r->refs_private;
 }
 
refs.h +1 -0
index 581ec270dc..f6d619ea4b 100644
@@ -825,6 +825,7 @@ int reflog_expire(const char *refname, const struct object_id *oid,
 int ref_storage_backend_exists(const char *name);
 
 struct ref_store *get_main_ref_store(struct repository *r);
+const char *ref_store_backend_name(struct ref_store *refs);
 
 /**
  * Submodules
refs/debug.c new file +387 -0

387 lines not on this page · raw message

refs/refs-internal.h +6 -0
index b9b5c2e87c..e1da4b3a53 100644
@@ -695,4 +695,10 @@ int parse_loose_ref_contents(const char *buf, struct object_id *oid,
 void base_ref_store_init(struct ref_store *refs,
 			 const struct ref_storage_be *be);
 
+/*
+ * Print out ref operations as they occur. Useful for debugging alternate ref
+ * backends.
+ */
+struct ref_store *debug_wrap(const char *gitdir, struct ref_store *store);
+
 #endif /* REFS_REFS_INTERNAL_H */
t/t0033-debug-refs.sh new file +18 -0
new file mode 100755
index 0000000000..ed76c2c84a
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# Copyright (c) 2020 Google LLC
+#
+
+test_description='cross-check reftable with files, using GIT_DEBUG_REFS output'
+
+. ./test-lib.sh
+
+test_expect_success 'GIT_DEBUG_REFS' '
+	git init --ref-storage=files files &&
+	git init --ref-storage=reftable reftable &&
+	(cd files && GIT_DEBUG_REFS=1 test_commit message file) > files.txt &&
+	(cd reftable && GIT_DEBUG_REFS=1 test_commit message file) > reftable.txt &&
+	test_cmp files.txt reftable.txt
+'
+
+test_done
Signature · 1 line
gitgitgadget