#define USE_THE_REPOSITORY_VARIABLE #include "../git-compat-util.h" #include "../config.h" #include "../dir.h" #include "../gettext.h" #include "../hash.h" #include "../hex.h" #include "../refs.h" #include "refs-internal.h" #include "packed-backend.h" #include "../iterator.h" #include "../lockfile.h" #include "../chdir-notify.h" #include "../statinfo.h" #include "../wrapper.h" #include "../write-or-die.h" #include "../trace2.h" enum mmap_strategy { … }; #if defined(NO_MMAP) static enum mmap_strategy mmap_strategy = MMAP_NONE; #elif defined(MMAP_PREVENTS_DELETE) static enum mmap_strategy mmap_strategy = MMAP_TEMPORARY; #else static enum mmap_strategy mmap_strategy = …; #endif 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 `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 { … }; /* * Increment the reference count of `*snapshot`. */ static void acquire_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. */ static void clear_snapshot_buffer(struct snapshot *snapshot) { … } /* * Decrease the reference count of `*snapshot`. If it goes to zero, * free `*snapshot` and return true; otherwise return false. */ static int release_snapshot(struct snapshot *snapshot) { … } static size_t snapshot_hexsz(const struct snapshot *snapshot) { … } struct ref_store *packed_ref_store_init(struct repository *repo, const char *gitdir, unsigned int store_flags) { … } /* * Downcast `ref_store` to `packed_ref_store`. Die if `ref_store` is * not a `packed_ref_store`. Also die if `packed_ref_store` doesn't * support at least the flags specified in `required_flags`. `caller` * is used in any necessary error messages. */ static struct packed_ref_store *packed_downcast(struct ref_store *ref_store, unsigned int required_flags, const char *caller) { … } static void clear_snapshot(struct packed_ref_store *refs) { … } static void packed_ref_store_release(struct ref_store *ref_store) { … } static NORETURN void die_unterminated_line(const char *path, const char *p, size_t len) { … } static NORETURN void die_invalid_line(const char *path, const char *p, size_t len) { … } struct snapshot_record { … }; static int cmp_packed_ref_records(const void *v1, const void *v2, void *cb_data) { … } /* * Compare a snapshot record at `rec` to the specified NUL-terminated * refname. */ static int cmp_record_to_refname(const char *rec, const char *refname, int start, const struct snapshot *snapshot) { … } /* * `snapshot->buf` is not known to be sorted. Check whether it is, and * if not, sort it into new memory and munmap/free the old storage. */ static void sort_snapshot(struct snapshot *snapshot) { … } /* * Return a pointer to the start of the record that contains the * character `*p` (which must be within the buffer). If no other * record start is found, return `buf`. */ static const char *find_start_of_record(const char *buf, const char *p) { … } /* * Return a pointer to the start of the record following the record * that contains `*p`. If none is found before `end`, return `end`. */ static const char *find_end_of_record(const char *p, const char *end) { … } /* * We want to be able to compare mmapped reference records quickly, * without totally parsing them. We can do so because the records are * LF-terminated, and the refname should start exactly (GIT_SHA1_HEXSZ * + 1) bytes past the beginning of the record. * * But what if the `packed-refs` file contains garbage? We're willing * to tolerate not detecting the problem, as long as we don't produce * totally garbled output (we can't afford to check the integrity of * the whole file during every Git invocation). But we do want to be * sure that we never read past the end of the buffer in memory and * perform an illegal memory access. * * Guarantee that minimum level of safety by verifying that the last * record in the file is LF-terminated, and that it has at least * (GIT_SHA1_HEXSZ + 1) characters before the LF. Die if either of * these checks fails. */ static void verify_buffer_safe(struct snapshot *snapshot) { … } #define SMALL_FILE_SIZE … /* * Depending on `mmap_strategy`, either mmap or read the contents of * the `packed-refs` file into the snapshot. Return 1 if the file * existed and was read, or 0 if the file was absent or empty. Die on * errors. */ static int load_contents(struct snapshot *snapshot) { … } static const char *find_reference_location_1(struct snapshot *snapshot, const char *refname, int mustexist, int start) { … } /* * 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. */ static const char *find_reference_location(struct snapshot *snapshot, const char *refname, int mustexist) { … } /* * Find the place in `snapshot->buf` after the end of the record for * `refname`. In other words, find the location of first thing *after* * `refname`. * * Other semantics are identical to the ones in * `find_reference_location()`. */ static const char *find_reference_location_end(struct snapshot *snapshot, const char *refname, int mustexist) { … } /* * Create a newly-allocated `snapshot` of the `packed-refs` file in * its current state and return it. The return value will already have * its reference count incremented. * * A comment line of the form "# pack-refs with: " may contain zero or * more traits. We interpret the traits as follows: * * Neither `peeled` nor `fully-peeled`: * * Probably no references are peeled. But if the file contains a * peeled value for a reference, we will use it. * * `peeled`: * * References under "refs/tags/", if they *can* be peeled, *are* * peeled in this file. References outside of "refs/tags/" are * probably not peeled even if they could have been, but if we find * a peeled value for such a reference we will use it. * * `fully-peeled`: * * All references in the file that can be peeled are peeled. * Inversely (and this is more important), any references in the * file for which no peeled value is recorded is not peelable. This * trait should typically be written alongside "peeled" for * compatibility with older clients, but we do not require it * (i.e., "peeled" is a no-op if "fully-peeled" is set). * * `sorted`: * * The references in this file are known to be sorted by refname. */ static struct snapshot *create_snapshot(struct packed_ref_store *refs) { … } /* * Check that `refs->snapshot` (if present) still reflects the * contents of the `packed-refs` file. If not, clear the snapshot. */ static void validate_snapshot(struct packed_ref_store *refs) { … } /* * Get the `snapshot` for the specified packed_ref_store, creating and * populating it if it hasn't been read before or if the file has been * changed (according to its `validity` field) since it was last read. * On the other hand, if we hold the lock, then assume that the file * hasn't been changed out from under us, so skip the extra `stat()` * call in `stat_validity_check()`. This function does *not* increase * the snapshot's reference count on behalf of the caller. */ static struct snapshot *get_snapshot(struct packed_ref_store *refs) { … } static int packed_read_raw_ref(struct ref_store *ref_store, const char *refname, struct object_id *oid, struct strbuf *referent UNUSED, unsigned int *type, int *failure_errno) { … } /* * 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 … /* * An iterator over a snapshot of a `packed-refs` file. */ struct packed_ref_iterator { … }; /* * Move the iterator to the next record in the snapshot, without * respect for whether the record is actually required by the current * iteration. Adjust the fields in `iter` and return `ITER_OK` or * `ITER_DONE`. This function does not free the iterator in the case * of `ITER_DONE`. */ static int next_record(struct packed_ref_iterator *iter) { … } static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator) { … } static int packed_ref_iterator_peel(struct ref_iterator *ref_iterator, struct object_id *peeled) { … } static int packed_ref_iterator_abort(struct ref_iterator *ref_iterator) { … } static struct ref_iterator_vtable packed_ref_iterator_vtable = …; static int jump_list_entry_cmp(const void *va, const void *vb) { … } static int has_glob_special(const char *str) { … } static void populate_excluded_jump_list(struct packed_ref_iterator *iter, struct snapshot *snapshot, const char **excluded_patterns) { … } static struct ref_iterator *packed_ref_iterator_begin( struct ref_store *ref_store, const char *prefix, const char **exclude_patterns, unsigned int flags) { … } /* * Write an entry to the packed-refs file for the specified refname. * If peeled is non-NULL, write it as the entry's peeled value. On * error, return a nonzero value and leave errno set at the value left * by the failing call to `fprintf()`. */ static int write_packed_entry(FILE *fh, const char *refname, const struct object_id *oid, const struct object_id *peeled) { … } int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err) { … } void packed_refs_unlock(struct ref_store *ref_store) { … } int packed_refs_is_locked(struct ref_store *ref_store) { … } int packed_refs_size(struct ref_store *ref_store, size_t *out) { … } /* * The packed-refs header line that we write out. Perhaps other traits * will be added later. * * Note that earlier versions of Git used to parse these traits by * looking for " trait " in the line. For this reason, the space after * the colon and the trailing space are required. */ static const char PACKED_REFS_HEADER[] = …; static int packed_ref_store_create_on_disk(struct ref_store *ref_store UNUSED, int flags UNUSED, struct strbuf *err UNUSED) { … } static int packed_ref_store_remove_on_disk(struct ref_store *ref_store, struct strbuf *err) { … } /* * Write the packed refs from the current snapshot to the packed-refs * tempfile, incorporating any changes from `updates`. `updates` must * be a sorted string list whose keys are the refnames and whose util * values are `struct ref_update *`. On error, rollback the tempfile, * write an error message to `err`, and return a nonzero value. * * The packfile must be locked before calling this function and will * remain locked when it is done. */ static int write_with_updates(struct packed_ref_store *refs, struct string_list *updates, struct strbuf *err) { … } int is_packed_transaction_needed(struct ref_store *ref_store, struct ref_transaction *transaction) { … } struct packed_transaction_backend_data { … }; static void packed_transaction_cleanup(struct packed_ref_store *refs, struct ref_transaction *transaction) { … } static int packed_transaction_prepare(struct ref_store *ref_store, struct ref_transaction *transaction, struct strbuf *err) { … } static int packed_transaction_abort(struct ref_store *ref_store, struct ref_transaction *transaction, struct strbuf *err UNUSED) { … } static int packed_transaction_finish(struct ref_store *ref_store, struct ref_transaction *transaction, struct strbuf *err) { … } static int packed_initial_transaction_commit(struct ref_store *ref_store UNUSED, struct ref_transaction *transaction, struct strbuf *err) { … } static int packed_pack_refs(struct ref_store *ref_store UNUSED, struct pack_refs_opts *pack_opts UNUSED) { … } static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_store UNUSED) { … } static int packed_fsck(struct ref_store *ref_store UNUSED, struct fsck_options *o UNUSED) { … } struct ref_storage_be refs_be_packed = …;