diff options
Diffstat (limited to 'refs/packed-backend.h')
-rw-r--r-- | refs/packed-backend.h | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/refs/packed-backend.h b/refs/packed-backend.h index 9dd8a344c3..1936bb5c76 100644 --- a/refs/packed-backend.h +++ b/refs/packed-backend.h @@ -1,6 +1,10 @@ #ifndef REFS_PACKED_BACKEND_H #define REFS_PACKED_BACKEND_H +#include "../cache.h" +#include "refs-internal.h" +#include "../lockfile.h" + struct repository; struct ref_transaction; @@ -36,4 +40,281 @@ int packed_refs_is_locked(struct ref_store *ref_store); int is_packed_transaction_needed(struct ref_store *ref_store, struct ref_transaction *transaction); +struct packed_ref_store; + +/* + * A `snapshot` represents one snapshot of a `packed-refs` file. + * + * Normally, this will be a mmapped view of the contents of the + * `packed-refs` file at the time the snapshot was created. However, + * if the `packed-refs` file was not sorted, this might point at heap + * memory holding the contents of the `packed-refs` file with its + * records sorted by refname. + * + * `snapshot` instances are reference counted (via + * `acquire_snapshot()` and `release_snapshot()`). This is to prevent + * an instance from disappearing while an iterator is still iterating + * over it. Instances are garbage collected when their `referrers` + * count goes to zero. + * + * The most recent `snapshot`, if available, is referenced by the + * `packed_ref_store`. Its freshness is checked whenever + * `get_snapshot()` is called; if the existing snapshot is obsolete, a + * new snapshot is taken. + */ +struct snapshot { + /* + * A back-pointer to the packed_ref_store with which this + * snapshot is associated: + */ + struct packed_ref_store *refs; + + /* Is the `packed-refs` file currently mmapped? */ + int mmapped; + + /* which file format version is this file? */ + int version; + + /* + * The contents of the `packed-refs` file: + * + * - buf -- a pointer to the start of the memory + * - start -- a pointer to the first byte of actual references + * (i.e., after the header line, if one is present) + * - eof -- a pointer just past the end of the reference + * contents + * + * If the `packed-refs` file was already sorted, `buf` points + * at the mmapped contents of the file. If not, it points at + * heap-allocated memory containing the contents, sorted. If + * there were no contents (e.g., because the file didn't + * exist), `buf`, `start`, and `eof` are all NULL. + */ + char *buf, *start, *eof; + + /* + * What is the peeled state of the `packed-refs` file that + * this snapshot represents? (This is usually determined from + * the file's header.) + */ + enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled; + + /************************* + * packed-refs v2 values * + *************************/ + size_t nr; + size_t prefixes_nr; + size_t buflen; + const unsigned char *offset_chunk; + const char *refs_chunk; + const unsigned char *prefix_offsets_chunk; + const char *prefix_chunk; + + /* + * Count of references to this instance, including the pointer + * from `packed_ref_store::snapshot`, if any. The instance + * will not be freed as long as the reference count is + * nonzero. + */ + unsigned int referrers; + + /* + * The metadata of the `packed-refs` file from which this + * snapshot was created, used to tell if the file has been + * replaced since we read it. + */ + struct stat_validity validity; +}; + +int release_snapshot(struct snapshot *snapshot); + +/* + * If the buffer in `snapshot` is active, then either munmap the + * memory and close the file, or free the memory. Then set the buffer + * pointers to NULL. + */ +void clear_snapshot_buffer(struct snapshot *snapshot); + +/* + * A `ref_store` representing references stored in a `packed-refs` + * file. It implements the `ref_store` interface, though it has some + * limitations: + * + * - It cannot store symbolic references. + * + * - It cannot store reflogs. + * + * - It does not support reference renaming (though it could). + * + * On the other hand, it can be locked outside of a reference + * transaction. In that case, it remains locked even after the + * transaction is done and the new `packed-refs` file is activated. + */ +struct packed_ref_store { + struct ref_store base; + + unsigned int store_flags; + + /* The path of the "packed-refs" file: */ + char *path; + + /* + * A snapshot of the values read from the `packed-refs` file, + * if it might still be current; otherwise, NULL. + */ + struct snapshot *snapshot; + + /* + * Lock used for the "packed-refs" file. Note that this (and + * thus the enclosing `packed_ref_store`) must not be freed. + */ + struct lock_file lock; + + /* + * Temporary file used when rewriting new contents to the + * "packed-refs" file. Note that this (and thus the enclosing + * `packed_ref_store`) must not be freed. + */ + struct tempfile *tempfile; +}; + +/* + * This value is set in `base.flags` if the peeled value of the + * current reference is known. In that case, `peeled` contains the + * correct peeled value for the reference, which might be `null_oid` + * if the reference is not a tag or if it is broken. + */ +#define REF_KNOWS_PEELED 0x40 + +/* + * An iterator over a snapshot of a `packed-refs` file. + */ +struct packed_ref_iterator { + struct ref_iterator base; + struct snapshot *snapshot; + struct repository *repo; + unsigned int flags; + int version; + + /* Scratch space for current values: */ + struct object_id oid, peeled; + struct strbuf refname_buf; + + /* The current position in the snapshot's buffer: */ + const char *pos; + + /*********************************** + * packed-refs v1 iterator values. * + ***********************************/ + + /* The end of the part of the buffer that will be iterated over: */ + const char *eof; + + /*********************************** + * packed-refs v2 iterator values. * + ***********************************/ + size_t nr; + size_t row; + size_t prefix_row_end; + size_t prefix_i; + const char *cur_prefix; +}; + +typedef int (*write_ref_fn)(const char *refname, + const struct object_id *oid, + const struct object_id *peeled, + void *write_data); + +int merge_iterator_and_updates(struct packed_ref_store *refs, + struct string_list *updates, + struct strbuf *err, + write_ref_fn write_fn, + void *write_data); + +/** + * Parse the buffer at the given snapshot to verify that it is a + * packed-refs file in version 1 format. Update the snapshot->peeled + * value according to the header information. Update the given + * 'sorted' value with whether or not the packed-refs file is sorted. + */ +int parse_packed_format_v1_header(struct packed_ref_store *refs, + struct snapshot *snapshot, + int *sorted); + +/* + * Find the place in `snapshot->buf` where the start of the record for + * `refname` starts. If `mustexist` is true and the reference doesn't + * exist, then return NULL. If `mustexist` is false and the reference + * doesn't exist, then return the point where that reference would be + * inserted, or `snapshot->eof` (which might be NULL) if it would be + * inserted at the end of the file. In the latter mode, `refname` + * doesn't have to be a proper reference name; for example, one could + * search for "refs/replace/" to find the start of any replace + * references. + * + * The record is sought using a binary search, so `snapshot->buf` must + * be sorted. + */ +const char *find_reference_location_v1(struct snapshot *snapshot, + const char *refname, int mustexist); + +int packed_read_raw_ref_v1(struct packed_ref_store *refs, struct snapshot *snapshot, + const char *refname, struct object_id *oid, + unsigned int *type, int *failure_errno); + +void verify_buffer_safe_v1(struct snapshot *snapshot); +void sort_snapshot_v1(struct snapshot *snapshot); +int write_packed_file_header_v1(FILE *out); +int next_record_v1(struct packed_ref_iterator *iter); +int write_packed_entry_v1(const char *refname, + const struct object_id *oid, + const struct object_id *peeled, + void *write_data); + +/** + * Parse the buffer at the given snapshot to verify that it is a + * packed-refs file in version 1 format. Update the snapshot->peeled + * value according to the header information. Update the given + * 'sorted' value with whether or not the packed-refs file is sorted. + */ +int parse_packed_format_v1_header(struct packed_ref_store *refs, + struct snapshot *snapshot, + int *sorted); + +int detect_packed_format_v2_header(struct packed_ref_store *refs, + struct snapshot *snapshot); +/* + * Find the place in `snapshot->buf` where the start of the record for + * `refname` starts. If `mustexist` is true and the reference doesn't + * exist, then return NULL. If `mustexist` is false and the reference + * doesn't exist, then return the point where that reference would be + * inserted, or `snapshot->eof` (which might be NULL) if it would be + * inserted at the end of the file. In the latter mode, `refname` + * doesn't have to be a proper reference name; for example, one could + * search for "refs/replace/" to find the start of any replace + * references. + * + * The record is sought using a binary search, so `snapshot->buf` must + * be sorted. + */ +const char *find_reference_location_v2(struct snapshot *snapshot, + const char *refname, int mustexist, + size_t *pos); + +int packed_read_raw_ref_v2(struct packed_ref_store *refs, struct snapshot *snapshot, + const char *refname, struct object_id *oid, + unsigned int *type, int *failure_errno); +int next_record_v2(struct packed_ref_iterator *iter); +void fill_snapshot_v2(struct snapshot *snapshot); + +struct write_packed_refs_v2_context; +struct write_packed_refs_v2_context *create_v2_context(struct packed_ref_store *refs, + struct string_list *updates, + struct strbuf *err); +int write_packed_refs_v2(struct write_packed_refs_v2_context *ctx); +void free_v2_context(struct write_packed_refs_v2_context *ctx); + +void init_iterator_prefix_info(const char *prefix, + struct packed_ref_iterator *iter); + #endif /* REFS_PACKED_BACKEND_H */ |