Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Packed-refs v2 Part II: create extensions.refFormat #24

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Documentation/config/extensions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,31 @@ Note that this setting should only be set by linkgit:git-init[1] or
linkgit:git-clone[1]. Trying to change it after initialization will not
work and will produce hard-to-diagnose issues.

extensions.refFormat::
Specify the reference storage mechanisms used by the repoitory as a
multi-valued list. The acceptable values are `files` and `packed`.
If not specified, the list of `files` and `packed` is assumed. It
is an error to specify this key unless `core.repositoryFormatVersion`
is 1.
+
As new ref formats are added, Git commands may modify this list before and
after upgrading the on-disk reference storage files. The specific values
indicate the existence of different layers:
+
* `files`: When present, references may be stored as "loose" reference files
in the `$GIT_DIR/refs/` directory. The name of the reference corresponds
to the filename after `$GIT_DIR` and the file contains an object ID as
a hexadecimal string. If a loose reference file exists, then its value
takes precedence over all other formats.
+
* `packed`: When present, references may be stored as a group in a
`packed-refs` file in its version 1 format. When grouped with `"files"`
or provided on its own, this file is located at `$GIT_DIR/packed-refs`.
This file contains a list of distinct reference names, paired with their
object IDs. When combined with `files`, the `packed` format will only be
used to group multiple loose object files upon request via the
`git pack-refs` command or via the `pack-refs` maintenance task.

extensions.worktreeConfig::
If enabled, then worktrees will load config settings from the
`$GIT_DIR/config.worktree` file in addition to the
Expand Down
8 changes: 8 additions & 0 deletions Documentation/config/index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,11 @@ index.version::
Specify the version with which new index files should be
initialized. This does not affect existing repositories.
If `feature.manyFiles` is enabled, then the default is 4.

index.computeHash::
When enabled, compute the hash of the index file as it is written
and store the hash at the end of the content. This is enabled by
default.
+
If you disable `index.computHash`, then older Git clients may report that
your index is corrupt during `git fsck`.
2 changes: 2 additions & 0 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,8 @@ struct repository_format {
int hash_algo;
int sparse_index;
char *work_tree;
int ref_format_count;
enum ref_format_flags ref_format;
struct string_list unknown_extensions;
struct string_list v1_only_extensions;
};
Expand Down
14 changes: 11 additions & 3 deletions csum-file.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ void hashflush(struct hashfile *f)
unsigned offset = f->offset;

if (offset) {
the_hash_algo->update_fn(&f->ctx, f->buffer, offset);
if (!f->skip_hash)
the_hash_algo->update_fn(&f->ctx, f->buffer, offset);
flush(f, f->buffer, offset);
f->offset = 0;
}
Expand All @@ -64,7 +65,12 @@ int finalize_hashfile(struct hashfile *f, unsigned char *result,
int fd;

hashflush(f);
the_hash_algo->final_fn(f->buffer, &f->ctx);

if (f->skip_hash)
memset(f->buffer, 0, the_hash_algo->rawsz);
else
the_hash_algo->final_fn(f->buffer, &f->ctx);

if (result)
hashcpy(result, f->buffer);
if (flags & CSUM_HASH_IN_STREAM)
Expand Down Expand Up @@ -108,7 +114,8 @@ void hashwrite(struct hashfile *f, const void *buf, unsigned int count)
* the hashfile's buffer. In this block,
* f->offset is necessarily zero.
*/
the_hash_algo->update_fn(&f->ctx, buf, nr);
if (!f->skip_hash)
the_hash_algo->update_fn(&f->ctx, buf, nr);
flush(f, buf, nr);
} else {
/*
Expand Down Expand Up @@ -153,6 +160,7 @@ static struct hashfile *hashfd_internal(int fd, const char *name,
f->tp = tp;
f->name = name;
f->do_crc = 0;
f->skip_hash = 0;
the_hash_algo->init_fn(&f->ctx);

f->buffer_len = buffer_len;
Expand Down
7 changes: 7 additions & 0 deletions csum-file.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ struct hashfile {
size_t buffer_len;
unsigned char *buffer;
unsigned char *check_buffer;

/**
* If set to 1, skip_hash indicates that we should
* not actually compute the hash for this hashfile and
* instead only use it as a buffered write.
*/
unsigned int skip_hash;
};

/* Checkpoint */
Expand Down
22 changes: 21 additions & 1 deletion read-cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -1817,6 +1817,8 @@ static int verify_hdr(const struct cache_header *hdr, unsigned long size)
git_hash_ctx c;
unsigned char hash[GIT_MAX_RAWSZ];
int hdr_version;
int all_zeroes = 1;
unsigned char *start, *end;

if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
return error(_("bad signature 0x%08x"), hdr->hdr_signature);
Expand All @@ -1827,10 +1829,23 @@ static int verify_hdr(const struct cache_header *hdr, unsigned long size)
if (!verify_index_checksum)
return 0;

end = (unsigned char *)hdr + size;
start = end - the_hash_algo->rawsz;
while (start < end) {
if (*start != 0) {
all_zeroes = 0;
break;
}
start++;
}

if (all_zeroes)
return 0;

the_hash_algo->init_fn(&c);
the_hash_algo->update_fn(&c, hdr, size - the_hash_algo->rawsz);
the_hash_algo->final_fn(hash, &c);
if (!hasheq(hash, (unsigned char *)hdr + size - the_hash_algo->rawsz))
if (!hasheq(hash, end - the_hash_algo->rawsz))
return error(_("bad index file sha1 signature"));
return 0;
}
Expand Down Expand Up @@ -2917,9 +2932,14 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
int ieot_entries = 1;
struct index_entry_offset_table *ieot = NULL;
int nr, nr_threads;
int compute_hash;

f = hashfd(tempfile->fd, tempfile->filename.buf);

if (!git_config_get_maybe_bool("index.computehash", &compute_hash) &&
!compute_hash)
f->skip_hash = 1;

for (i = removed = extended = 0; i < entries; i++) {
if (cache[i]->ce_flags & CE_REMOVE)
removed++;
Expand Down
22 changes: 20 additions & 2 deletions refs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1956,6 +1956,15 @@ static struct ref_store *lookup_ref_store_map(struct hashmap *map,
return entry ? entry->refs : NULL;
}

static int add_ref_format_flags(enum ref_format_flags flags, int caps) {
if (flags & REF_FORMAT_FILES)
caps |= REF_STORE_FORMAT_FILES;
if (flags & REF_FORMAT_PACKED)
caps |= REF_STORE_FORMAT_PACKED;

return caps;
}

/*
* Create, record, and return a ref_store instance for the specified
* gitdir.
Expand All @@ -1965,9 +1974,17 @@ static struct ref_store *ref_store_init(struct repository *repo,
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;

flags = add_ref_format_flags(repo->ref_format, flags);

if (!(flags & REF_STORE_FORMAT_FILES) &&
(flags & REF_STORE_FORMAT_PACKED))
be_name = "packed";

be = find_ref_storage_backend(be_name);

if (!be)
BUG("reference backend %s is unknown", be_name);

Expand All @@ -1983,7 +2000,8 @@ 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, r->gitdir, REF_STORE_ALL_CAPS);
r->refs_private = ref_store_init(r, r->gitdir,
REF_STORE_ALL_CAPS);
r->refs_private = maybe_debug_wrap_ref_store(r->gitdir, r->refs_private);
return r->refs_private;
}
Expand Down
8 changes: 7 additions & 1 deletion refs/files-backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,12 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
struct strbuf err = STRBUF_INIT;
struct ref_transaction *transaction;

if (!packed_refs_enabled(refs->store_flags)) {
warning(_("refusing to create '%s' file because '%s' is not set"),
"packed-refs", "extensions.refFormat=packed");
return -1;
}

transaction = ref_store_transaction_begin(refs->packed_ref_store, &err);
if (!transaction)
return -1;
Expand Down Expand Up @@ -3282,7 +3288,7 @@ static int files_init_db(struct ref_store *ref_store, struct strbuf *err UNUSED)
}

struct ref_storage_be refs_be_files = {
.next = NULL,
.next = &refs_be_packed,
.name = "files",
.init = files_ref_store_create,
.init_db = files_init_db,
Expand Down
3 changes: 3 additions & 0 deletions refs/packed-backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,9 @@ static int load_contents(struct snapshot *snapshot)
size_t size;
ssize_t bytes_read;

if (!packed_refs_enabled(snapshot->refs->store_flags))
return 0;

fd = open(snapshot->refs->path, O_RDONLY);
if (fd < 0) {
if (errno == ENOENT) {
Expand Down
8 changes: 8 additions & 0 deletions refs/refs-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,14 @@ struct ref_store;
REF_STORE_ODB | \
REF_STORE_MAIN)

#define REF_STORE_FORMAT_FILES (1 << 8) /* can use loose ref files */
#define REF_STORE_FORMAT_PACKED (1 << 9) /* can use packed-refs file */

static inline int packed_refs_enabled(int flags)
{
return flags & REF_STORE_FORMAT_PACKED;
}

/*
* Initialize the ref_store for the specified gitdir. These functions
* should call base_ref_store_init() to initialize the shared part of
Expand Down
2 changes: 2 additions & 0 deletions repository.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ int repo_init(struct repository *repo,
repo->repository_format_partial_clone = format.partial_clone;
format.partial_clone = NULL;

repo->ref_format = format.ref_format;

if (worktree)
repo_set_worktree(repo, worktree);

Expand Down
6 changes: 6 additions & 0 deletions repository.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ struct repo_path_cache {
char *shallow;
};

enum ref_format_flags {
REF_FORMAT_FILES = (1 << 0),
REF_FORMAT_PACKED = (1 << 1),
};

struct repository {
/* Environment */
/*
Expand Down Expand Up @@ -95,6 +100,7 @@ struct repository {
* the ref object.
*/
struct ref_store *refs_private;
enum ref_format_flags ref_format;

/*
* Contains path to often used file names.
Expand Down
21 changes: 21 additions & 0 deletions setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,16 @@ static enum extension_result handle_extension(const char *var,
"extensions.objectformat", value);
data->hash_algo = format;
return EXTENSION_OK;
} else if (!strcmp(ext, "refformat")) {
if (!strcmp(value, "files"))
data->ref_format |= REF_FORMAT_FILES;
else if (!strcmp(value, "packed"))
data->ref_format |= REF_FORMAT_PACKED;
else
return error(_("invalid value for '%s': '%s'"),
"extensions.refFormat", value);
data->ref_format_count++;
return EXTENSION_OK;
}
return EXTENSION_UNKNOWN;
}
Expand Down Expand Up @@ -718,6 +728,11 @@ int read_repository_format(struct repository_format *format, const char *path)
git_config_from_file(check_repo_format, path, format);
if (format->version == -1)
clear_repository_format(format);

/* Set default ref_format if no extensions.refFormat exists. */
if (!format->ref_format_count)
format->ref_format = REF_FORMAT_FILES | REF_FORMAT_PACKED;

return format->version;
}

Expand Down Expand Up @@ -1420,6 +1435,9 @@ int discover_git_directory(struct strbuf *commondir,
candidate.partial_clone;
candidate.partial_clone = NULL;

/* take ownership of candidate.ref_format */
the_repository->ref_format = candidate.ref_format;

clear_repository_format(&candidate);
return 0;
}
Expand Down Expand Up @@ -1556,6 +1574,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
the_repository->repository_format_partial_clone =
repo_fmt.partial_clone;
repo_fmt.partial_clone = NULL;

the_repository->ref_format = repo_fmt.ref_format;
}
}
/*
Expand Down Expand Up @@ -1645,6 +1665,7 @@ void check_repository_format(struct repository_format *fmt)
repo_set_hash_algo(the_repository, fmt->hash_algo);
the_repository->repository_format_partial_clone =
xstrdup_or_null(fmt->partial_clone);
the_repository->ref_format = fmt->ref_format;
clear_repository_format(&repo_fmt);
}

Expand Down
8 changes: 8 additions & 0 deletions t/t1600-index.sh
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,12 @@ test_expect_success 'index version config precedence' '
test_index_version 0 true 2 2
'

test_expect_success 'index.computeHash config option' '
(
rm -f .git/index &&
git -c index.computeHash=false add a &&
git fsck
)
'

test_done
Loading