diff options
Diffstat (limited to 'refs/files-backend.c')
| -rw-r--r-- | refs/files-backend.c | 267 |
1 files changed, 148 insertions, 119 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c index 6f9a631592..73380d7e99 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -89,9 +89,9 @@ static void clear_loose_ref_cache(struct files_ref_store *refs) * Create a new submodule ref cache and add it to the internal * set of caches. */ -static struct ref_store *files_ref_store_create(struct repository *repo, - const char *gitdir, - unsigned int flags) +static struct ref_store *files_ref_store_init(struct repository *repo, + const char *gitdir, + unsigned int flags) { struct files_ref_store *refs = xcalloc(1, sizeof(*refs)); struct ref_store *ref_store = (struct ref_store *)refs; @@ -102,7 +102,7 @@ static struct ref_store *files_ref_store_create(struct repository *repo, get_common_dir_noenv(&sb, gitdir); refs->gitcommondir = strbuf_detach(&sb, NULL); refs->packed_ref_store = - packed_ref_store_create(repo, refs->gitcommondir, flags); + packed_ref_store_init(repo, refs->gitcommondir, flags); chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir); chdir_notify_reparent("files-backend $GIT_COMMONDIR", @@ -149,6 +149,14 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store, return refs; } +static void files_ref_store_release(struct ref_store *ref_store) +{ + struct files_ref_store *refs = files_downcast(ref_store, 0, "release"); + free_ref_cache(refs->loose); + free(refs->gitcommondir); + ref_store_release(refs->packed_ref_store); +} + static void files_reflog_path(struct files_ref_store *refs, struct strbuf *sb, const char *refname) @@ -1197,7 +1205,7 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r) ref_transaction_add_update( transaction, r->name, REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING, - null_oid(), &r->oid, NULL); + null_oid(), &r->oid, NULL, NULL, NULL); if (ref_transaction_commit(transaction, &err)) goto cleanup; @@ -1228,7 +1236,8 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_ /* * Return true if the specified reference should be packed. */ -static int should_pack_ref(const char *refname, +static int should_pack_ref(struct files_ref_store *refs, + const char *refname, const struct object_id *oid, unsigned int ref_flags, struct pack_refs_opts *opts) { @@ -1244,7 +1253,7 @@ static int should_pack_ref(const char *refname, return 0; /* Do not pack broken refs: */ - if (!ref_resolves_to_object(refname, the_repository, oid, ref_flags)) + if (!ref_resolves_to_object(refname, refs->base.repo, oid, ref_flags)) return 0; if (ref_excluded(opts->exclusions, refname)) @@ -1276,14 +1285,14 @@ static int files_pack_refs(struct ref_store *ref_store, packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err); iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, 0), NULL, - the_repository, 0); + refs->base.repo, 0); while ((ok = ref_iterator_advance(iter)) == ITER_OK) { /* * If the loose reference can be packed, add an entry * in the packed ref cache. If the reference should be * pruned, also add it to refs_to_prune. */ - if (!should_pack_ref(iter->refname, iter->oid, iter->flags, opts)) + if (!should_pack_ref(refs, iter->refname, iter->oid, iter->flags, opts)) continue; /* @@ -1291,7 +1300,7 @@ static int files_pack_refs(struct ref_store *ref_store, * packed-refs transaction: */ if (ref_transaction_update(transaction, iter->refname, - iter->oid, NULL, + iter->oid, NULL, NULL, NULL, REF_NO_DEREF, NULL, &err)) die("failure preparing to create packed reference %s: %s", iter->refname, err.buf); @@ -1380,7 +1389,8 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname) return ret; } -static int write_ref_to_lockfile(struct ref_lock *lock, +static int write_ref_to_lockfile(struct files_ref_store *refs, + struct ref_lock *lock, const struct object_id *oid, int skip_oid_verification, struct strbuf *err); static int commit_ref_update(struct files_ref_store *refs, @@ -1528,7 +1538,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store, } oidcpy(&lock->old_oid, &orig_oid); - if (write_ref_to_lockfile(lock, &orig_oid, 0, &err) || + if (write_ref_to_lockfile(refs, lock, &orig_oid, 0, &err) || commit_ref_update(refs, lock, &orig_oid, logmsg, &err)) { error("unable to write current sha1 into %s: %s", newrefname, err.buf); strbuf_release(&err); @@ -1548,7 +1558,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store, flag = log_all_ref_updates; log_all_ref_updates = LOG_REFS_NONE; - if (write_ref_to_lockfile(lock, &orig_oid, 0, &err) || + if (write_ref_to_lockfile(refs, lock, &orig_oid, 0, &err) || commit_ref_update(refs, lock, &orig_oid, NULL, &err)) { error("unable to write current sha1 into %s: %s", oldrefname, err.buf); strbuf_release(&err); @@ -1782,7 +1792,8 @@ static int files_log_ref_write(struct files_ref_store *refs, * Write oid into the open lockfile, then close the lockfile. On * errors, rollback the lockfile, fill in *err and return -1. */ -static int write_ref_to_lockfile(struct ref_lock *lock, +static int write_ref_to_lockfile(struct files_ref_store *refs, + struct ref_lock *lock, const struct object_id *oid, int skip_oid_verification, struct strbuf *err) { @@ -1791,7 +1802,7 @@ static int write_ref_to_lockfile(struct ref_lock *lock, int fd; if (!skip_oid_verification) { - o = parse_object(the_repository, oid); + o = parse_object(refs->base.repo, oid); if (!o) { strbuf_addf( err, @@ -1810,7 +1821,7 @@ static int write_ref_to_lockfile(struct ref_lock *lock, } } fd = get_lock_file_fd(&lock->lk); - if (write_in_full(fd, oid_to_hex(oid), the_hash_algo->hexsz) < 0 || + if (write_in_full(fd, oid_to_hex(oid), refs->base.repo->hash_algo->hexsz) < 0 || write_in_full(fd, &term, 1) < 0 || fsync_component(FSYNC_COMPONENT_REFERENCE, get_lock_file_fd(&lock->lk)) < 0 || close_ref_gently(lock) < 0) { @@ -1902,66 +1913,23 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target) return ret; } -static void update_symref_reflog(struct files_ref_store *refs, - struct ref_lock *lock, const char *refname, - const char *target, const char *logmsg) -{ - struct strbuf err = STRBUF_INIT; - struct object_id new_oid; - - if (logmsg && - refs_resolve_ref_unsafe(&refs->base, target, - RESOLVE_REF_READING, &new_oid, NULL) && - files_log_ref_write(refs, refname, &lock->old_oid, - &new_oid, logmsg, 0, &err)) { - error("%s", err.buf); - strbuf_release(&err); - } -} - -static int create_symref_locked(struct files_ref_store *refs, - struct ref_lock *lock, const char *refname, - const char *target, const char *logmsg) +static int create_symref_lock(struct files_ref_store *refs, + struct ref_lock *lock, const char *refname, + const char *target, struct strbuf *err) { - if (prefer_symlink_refs && !create_ref_symlink(lock, target)) { - update_symref_reflog(refs, lock, refname, target, logmsg); - return 0; + if (!fdopen_lock_file(&lock->lk, "w")) { + strbuf_addf(err, "unable to fdopen %s: %s", + get_lock_file_path(&lock->lk), strerror(errno)); + return -1; } - if (!fdopen_lock_file(&lock->lk, "w")) - return error("unable to fdopen %s: %s", + if (fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target) < 0) { + strbuf_addf(err, "unable to write to %s: %s", get_lock_file_path(&lock->lk), strerror(errno)); - - update_symref_reflog(refs, lock, refname, target, logmsg); - - /* no error check; commit_ref will check ferror */ - fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target); - if (commit_ref(lock) < 0) - return error("unable to write symref for %s: %s", refname, - strerror(errno)); - return 0; -} - -static int files_create_symref(struct ref_store *ref_store, - const char *refname, const char *target, - const char *logmsg) -{ - struct files_ref_store *refs = - files_downcast(ref_store, REF_STORE_WRITE, "create_symref"); - struct strbuf err = STRBUF_INIT; - struct ref_lock *lock; - int ret; - - lock = lock_ref_oid_basic(refs, refname, &err); - if (!lock) { - error("%s", err.buf); - strbuf_release(&err); return -1; } - ret = create_symref_locked(refs, lock, refname, target, logmsg); - unlock_ref(lock); - return ret; + return 0; } static int files_reflog_exists(struct ref_store *ref_store, @@ -2308,7 +2276,7 @@ static int split_head_update(struct ref_update *update, transaction, "HEAD", update->flags | REF_LOG_ONLY | REF_NO_DEREF, &update->new_oid, &update->old_oid, - update->msg); + NULL, NULL, update->msg); /* * Add "HEAD". This insertion is O(N) in the transaction @@ -2370,8 +2338,9 @@ static int split_symref_update(struct ref_update *update, new_update = ref_transaction_add_update( transaction, referent, new_flags, - &update->new_oid, &update->old_oid, - update->msg); + update->new_target ? NULL : &update->new_oid, + update->old_target ? NULL : &update->old_oid, + update->new_target, update->old_target, update->msg); new_update->parent_update = update; @@ -2400,17 +2369,6 @@ static int split_symref_update(struct ref_update *update, } /* - * Return the refname under which update was originally requested. - */ -static const char *original_update_refname(struct ref_update *update) -{ - while (update->parent_update) - update = update->parent_update; - - return update->refname; -} - -/* * Check whether the REF_HAVE_OLD and old_oid values stored in update * are consistent with oid, which is the reference's current value. If * everything is OK, return 0; otherwise, write an error message to @@ -2426,16 +2384,16 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid, if (is_null_oid(&update->old_oid)) strbuf_addf(err, "cannot lock ref '%s': " "reference already exists", - original_update_refname(update)); + ref_update_original_update_refname(update)); else if (is_null_oid(oid)) strbuf_addf(err, "cannot lock ref '%s': " "reference is missing but expected %s", - original_update_refname(update), + ref_update_original_update_refname(update), oid_to_hex(&update->old_oid)); else strbuf_addf(err, "cannot lock ref '%s': " "is at %s but expected %s", - original_update_refname(update), + ref_update_original_update_refname(update), oid_to_hex(oid), oid_to_hex(&update->old_oid)); @@ -2470,7 +2428,7 @@ static int lock_ref_for_update(struct files_ref_store *refs, files_assert_main_repository(refs, "lock_ref_for_update"); - if ((update->flags & REF_HAVE_NEW) && is_null_oid(&update->new_oid)) + if ((update->flags & REF_HAVE_NEW) && ref_update_has_null_new_value(update)) update->flags |= REF_DELETING; if (head_ref) { @@ -2489,7 +2447,7 @@ static int lock_ref_for_update(struct files_ref_store *refs, reason = strbuf_detach(err, NULL); strbuf_addf(err, "cannot lock ref '%s': %s", - original_update_refname(update), reason); + ref_update_original_update_refname(update), reason); free(reason); goto out; } @@ -2509,11 +2467,18 @@ static int lock_ref_for_update(struct files_ref_store *refs, if (update->flags & REF_HAVE_OLD) { strbuf_addf(err, "cannot lock ref '%s': " "error reading reference", - original_update_refname(update)); + ref_update_original_update_refname(update)); ret = TRANSACTION_GENERIC_ERROR; goto out; } - } else if (check_old_oid(update, &lock->old_oid, err)) { + } + + if (update->old_target) { + if (ref_update_check_old_target(referent.buf, update, err)) { + ret = TRANSACTION_GENERIC_ERROR; + goto out; + } + } else if (check_old_oid(update, &lock->old_oid, err)) { ret = TRANSACTION_GENERIC_ERROR; goto out; } @@ -2534,7 +2499,17 @@ static int lock_ref_for_update(struct files_ref_store *refs, } else { struct ref_update *parent_update; - if (check_old_oid(update, &lock->old_oid, err)) { + /* + * Even if the ref is a regular ref, if `old_target` is set, we + * check the referent value. Ideally `old_target` should only + * be set for symrefs, but we're strict about its usage. + */ + if (update->old_target) { + if (ref_update_check_old_target(referent.buf, update, err)) { + ret = TRANSACTION_GENERIC_ERROR; + goto out; + } + } else if (check_old_oid(update, &lock->old_oid, err)) { ret = TRANSACTION_GENERIC_ERROR; goto out; } @@ -2552,9 +2527,28 @@ static int lock_ref_for_update(struct files_ref_store *refs, } } - if ((update->flags & REF_HAVE_NEW) && - !(update->flags & REF_DELETING) && - !(update->flags & REF_LOG_ONLY)) { + if (update->new_target && !(update->flags & REF_LOG_ONLY)) { + if (create_symref_lock(refs, lock, update->refname, + update->new_target, err)) { + ret = TRANSACTION_GENERIC_ERROR; + goto out; + } + + if (close_ref_gently(lock)) { + strbuf_addf(err, "couldn't close '%s.lock'", + update->refname); + ret = TRANSACTION_GENERIC_ERROR; + goto out; + } + + /* + * Once we have created the symref lock, the commit + * phase of the transaction only needs to commit the lock. + */ + update->flags |= REF_NEEDS_COMMIT; + } else if ((update->flags & REF_HAVE_NEW) && + !(update->flags & REF_DELETING) && + !(update->flags & REF_LOG_ONLY)) { if (!(update->type & REF_ISSYMREF) && oideq(&lock->old_oid, &update->new_oid)) { /* @@ -2562,7 +2556,7 @@ static int lock_ref_for_update(struct files_ref_store *refs, * value, so we don't need to write it. */ } else if (write_ref_to_lockfile( - lock, &update->new_oid, + refs, lock, &update->new_oid, update->flags & REF_SKIP_OID_VERIFICATION, err)) { char *write_err = strbuf_detach(err, NULL); @@ -2762,7 +2756,7 @@ static int files_transaction_prepare(struct ref_store *ref_store, packed_transaction, update->refname, REF_HAVE_NEW | REF_NO_DEREF, &update->new_oid, NULL, - NULL); + NULL, NULL, NULL); } } @@ -2817,6 +2811,43 @@ cleanup: return ret; } +static int parse_and_write_reflog(struct files_ref_store *refs, + struct ref_update *update, + struct ref_lock *lock, + struct strbuf *err) +{ + if (update->new_target) { + /* + * We want to get the resolved OID for the target, to ensure + * that the correct value is added to the reflog. + */ + if (!refs_resolve_ref_unsafe(&refs->base, update->new_target, + RESOLVE_REF_READING, + &update->new_oid, NULL)) { + /* + * TODO: currently we skip creating reflogs for dangling + * symref updates. It would be nice to capture this as + * zero oid updates however. + */ + return 0; + } + } + + if (files_log_ref_write(refs, lock->ref_name, &lock->old_oid, + &update->new_oid, update->msg, update->flags, err)) { + char *old_msg = strbuf_detach(err, NULL); + + strbuf_addf(err, "cannot update the ref '%s': %s", + lock->ref_name, old_msg); + free(old_msg); + unlock_ref(lock); + update->backend_data = NULL; + return -1; + } + + return 0; +} + static int files_transaction_finish(struct ref_store *ref_store, struct ref_transaction *transaction, struct strbuf *err) @@ -2847,23 +2878,20 @@ static int files_transaction_finish(struct ref_store *ref_store, if (update->flags & REF_NEEDS_COMMIT || update->flags & REF_LOG_ONLY) { - if (files_log_ref_write(refs, - lock->ref_name, - &lock->old_oid, - &update->new_oid, - update->msg, update->flags, - err)) { - char *old_msg = strbuf_detach(err, NULL); - - strbuf_addf(err, "cannot update the ref '%s': %s", - lock->ref_name, old_msg); - free(old_msg); - unlock_ref(lock); - update->backend_data = NULL; + if (parse_and_write_reflog(refs, update, lock, err)) { ret = TRANSACTION_GENERIC_ERROR; goto cleanup; } } + + /* + * We try creating a symlink, if that succeeds we continue to the + * next update. If not, we try and create a regular symref. + */ + if (update->new_target && prefer_symlink_refs) + if (!create_ref_symlink(lock, update->new_target)) + continue; + if (update->flags & REF_NEEDS_COMMIT) { clear_loose_ref_cache(refs); if (commit_ref(lock)) { @@ -3047,7 +3075,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store, ref_transaction_add_update(packed_transaction, update->refname, update->flags & ~REF_HAVE_OLD, &update->new_oid, &update->old_oid, - NULL); + NULL, NULL, NULL); } if (packed_refs_lock(refs->packed_ref_store, 0, err)) { @@ -3211,7 +3239,7 @@ static int files_reflog_expire(struct ref_store *ref_store, rollback_lock_file(&reflog_lock); } else if (update && (write_in_full(get_lock_file_fd(&lock->lk), - oid_to_hex(&cb.last_kept_oid), the_hash_algo->hexsz) < 0 || + oid_to_hex(&cb.last_kept_oid), refs->base.repo->hash_algo->hexsz) < 0 || write_str_in_full(get_lock_file_fd(&lock->lk), "\n") < 0 || close_ref_gently(lock) < 0)) { status |= error("couldn't write %s", @@ -3235,12 +3263,12 @@ static int files_reflog_expire(struct ref_store *ref_store, return -1; } -static int files_init_db(struct ref_store *ref_store, - int flags, - struct strbuf *err UNUSED) +static int files_ref_store_create_on_disk(struct ref_store *ref_store, + int flags, + struct strbuf *err UNUSED) { struct files_ref_store *refs = - files_downcast(ref_store, REF_STORE_WRITE, "init_db"); + files_downcast(ref_store, REF_STORE_WRITE, "create"); struct strbuf sb = STRBUF_INIT; /* @@ -3263,7 +3291,7 @@ static int files_init_db(struct ref_store *ref_store, * There is no need to create directories for common refs when creating * a worktree ref store. */ - if (!(flags & REFS_INIT_DB_IS_WORKTREE)) { + if (!(flags & REF_STORE_CREATE_ON_DISK_IS_WORKTREE)) { /* * Create .git/refs/{heads,tags} */ @@ -3282,15 +3310,16 @@ static int files_init_db(struct ref_store *ref_store, struct ref_storage_be refs_be_files = { .name = "files", - .init = files_ref_store_create, - .init_db = files_init_db, + .init = files_ref_store_init, + .release = files_ref_store_release, + .create_on_disk = files_ref_store_create_on_disk, + .transaction_prepare = files_transaction_prepare, .transaction_finish = files_transaction_finish, .transaction_abort = files_transaction_abort, .initial_transaction_commit = files_initial_transaction_commit, .pack_refs = files_pack_refs, - .create_symref = files_create_symref, .rename_ref = files_rename_ref, .copy_ref = files_copy_ref, |
