summaryrefslogtreecommitdiff
path: root/refs/files-backend.c
diff options
context:
space:
mode:
Diffstat (limited to 'refs/files-backend.c')
-rw-r--r--refs/files-backend.c438
1 files changed, 313 insertions, 125 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 0824c0b8a9..64f51f0da9 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1,6 +1,7 @@
#define USE_THE_REPOSITORY_VARIABLE
#include "../git-compat-util.h"
+#include "../abspath.h"
#include "../config.h"
#include "../copy.h"
#include "../environment.h"
@@ -23,6 +24,7 @@
#include "../dir.h"
#include "../chdir-notify.h"
#include "../setup.h"
+#include "../worktree.h"
#include "../wrapper.h"
#include "../write-or-die.h"
#include "../revision.h"
@@ -568,7 +570,7 @@ stat_ref:
buf = sb_contents.buf;
ret = parse_loose_ref_contents(ref_store->repo->hash_algo, buf,
- oid, referent, type, &myerr);
+ oid, referent, type, NULL, &myerr);
out:
if (ret && !myerr)
@@ -605,7 +607,7 @@ static int files_read_symbolic_ref(struct ref_store *ref_store, const char *refn
int parse_loose_ref_contents(const struct git_hash_algo *algop,
const char *buf, struct object_id *oid,
struct strbuf *referent, unsigned int *type,
- int *failure_errno)
+ const char **trailing, int *failure_errno)
{
const char *p;
if (skip_prefix(buf, "ref:", &buf)) {
@@ -627,6 +629,10 @@ int parse_loose_ref_contents(const struct git_hash_algo *algop,
*failure_errno = EINVAL;
return -1;
}
+
+ if (trailing)
+ *trailing = p;
+
return 0;
}
@@ -706,7 +712,7 @@ retry:
* reason to expect this error to be transitory.
*/
if (refs_verify_refname_available(&refs->base, refname,
- extras, NULL, err)) {
+ extras, NULL, 0, err)) {
if (mustexist) {
/*
* To the user the relevant error is
@@ -813,7 +819,7 @@ retry:
REMOVE_DIR_EMPTY_ONLY)) {
if (refs_verify_refname_available(
&refs->base, refname,
- extras, NULL, err)) {
+ extras, NULL, 0, err)) {
/*
* The error message set by
* verify_refname_available() is OK.
@@ -850,7 +856,7 @@ retry:
*/
if (refs_verify_refname_available(
refs->packed_ref_store, refname,
- extras, NULL, err)) {
+ extras, NULL, 0, err)) {
ret = TRANSACTION_NAME_CONFLICT;
goto error_return;
}
@@ -1159,7 +1165,7 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
*/
if (is_null_oid(&lock->old_oid) &&
refs_verify_refname_available(refs->packed_ref_store, refname,
- NULL, NULL, err))
+ NULL, NULL, 0, err))
goto error_return;
lock->ref_name = xstrdup(refname);
@@ -1252,7 +1258,7 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)
if (check_refname_format(r->name, 0))
return;
- transaction = ref_store_transaction_begin(&refs->base, &err);
+ transaction = ref_store_transaction_begin(&refs->base, 0, &err);
if (!transaction)
goto cleanup;
ref_transaction_add_update(
@@ -1396,7 +1402,8 @@ static int files_pack_refs(struct ref_store *ref_store,
if (!should_pack_refs(refs, opts))
return 0;
- transaction = ref_store_transaction_begin(refs->packed_ref_store, &err);
+ transaction = ref_store_transaction_begin(refs->packed_ref_store,
+ 0, &err);
if (!transaction)
return -1;
@@ -1537,7 +1544,7 @@ static int refs_rename_ref_available(struct ref_store *refs,
string_list_insert(&skip, old_refname);
ok = !refs_verify_refname_available(refs, new_refname,
- NULL, &skip, &err);
+ NULL, &skip, 0, &err);
if (!ok)
error("%s", err.buf);
@@ -2780,6 +2787,8 @@ static int files_transaction_prepare(struct ref_store *ref_store,
assert(err);
+ if (transaction->flags & REF_TRANSACTION_FLAG_INITIAL)
+ goto cleanup;
if (!transaction->nr)
goto cleanup;
@@ -2867,7 +2876,8 @@ static int files_transaction_prepare(struct ref_store *ref_store,
*/
if (!packed_transaction) {
packed_transaction = ref_store_transaction_begin(
- refs->packed_ref_store, err);
+ refs->packed_ref_store,
+ transaction->flags, err);
if (!packed_transaction) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
@@ -2973,6 +2983,127 @@ static int parse_and_write_reflog(struct files_ref_store *refs,
return 0;
}
+static int ref_present(const char *refname, const char *referent UNUSED,
+ const struct object_id *oid UNUSED,
+ int flags UNUSED,
+ void *cb_data)
+{
+ struct string_list *affected_refnames = cb_data;
+
+ return string_list_has_string(affected_refnames, refname);
+}
+
+static int files_transaction_finish_initial(struct files_ref_store *refs,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ size_t i;
+ int ret = 0;
+ struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+ struct ref_transaction *packed_transaction = NULL;
+ struct ref_transaction *loose_transaction = NULL;
+
+ assert(err);
+
+ if (transaction->state != REF_TRANSACTION_PREPARED)
+ BUG("commit called for transaction that is not prepared");
+
+ /* Fail if a refname appears more than once in the transaction: */
+ for (i = 0; i < transaction->nr; i++)
+ string_list_append(&affected_refnames,
+ transaction->updates[i]->refname);
+ string_list_sort(&affected_refnames);
+ if (ref_update_reject_duplicates(&affected_refnames, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+
+ /*
+ * It's really undefined to call this function in an active
+ * repository or when there are existing references: we are
+ * only locking and changing packed-refs, so (1) any
+ * simultaneous processes might try to change a reference at
+ * the same time we do, and (2) any existing loose versions of
+ * the references that we are setting would have precedence
+ * over our values. But some remote helpers create the remote
+ * "HEAD" and "master" branches before calling this function,
+ * so here we really only check that none of the references
+ * that we are creating already exists.
+ */
+ if (refs_for_each_rawref(&refs->base, ref_present,
+ &affected_refnames))
+ BUG("initial ref transaction called with existing refs");
+
+ packed_transaction = ref_store_transaction_begin(refs->packed_ref_store,
+ transaction->flags, err);
+ if (!packed_transaction) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+
+ for (i = 0; i < transaction->nr; i++) {
+ struct ref_update *update = transaction->updates[i];
+
+ if ((update->flags & REF_HAVE_OLD) &&
+ !is_null_oid(&update->old_oid))
+ BUG("initial ref transaction with old_sha1 set");
+
+ if (refs_verify_refname_available(&refs->base, update->refname,
+ &affected_refnames, NULL, 1, err)) {
+ ret = TRANSACTION_NAME_CONFLICT;
+ goto cleanup;
+ }
+
+ /*
+ * packed-refs don't support symbolic refs and root refs, so we
+ * have to queue these references via the loose transaction.
+ */
+ if (update->new_target || is_root_ref(update->refname)) {
+ if (!loose_transaction) {
+ loose_transaction = ref_store_transaction_begin(&refs->base, 0, err);
+ if (!loose_transaction) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+ }
+
+ ref_transaction_add_update(loose_transaction, update->refname,
+ update->flags & ~REF_HAVE_OLD,
+ update->new_target ? NULL : &update->new_oid, NULL,
+ update->new_target, NULL, NULL);
+ } else {
+ ref_transaction_add_update(packed_transaction, update->refname,
+ update->flags & ~REF_HAVE_OLD,
+ &update->new_oid, &update->old_oid,
+ NULL, NULL, NULL);
+ }
+ }
+
+ if (packed_refs_lock(refs->packed_ref_store, 0, err) ||
+ ref_transaction_commit(packed_transaction, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+ packed_refs_unlock(refs->packed_ref_store);
+
+ if (loose_transaction) {
+ if (ref_transaction_prepare(loose_transaction, err) ||
+ ref_transaction_commit(loose_transaction, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ if (loose_transaction)
+ ref_transaction_free(loose_transaction);
+ if (packed_transaction)
+ ref_transaction_free(packed_transaction);
+ transaction->state = REF_TRANSACTION_CLOSED;
+ string_list_clear(&affected_refnames, 0);
+ return ret;
+}
+
static int files_transaction_finish(struct ref_store *ref_store,
struct ref_transaction *transaction,
struct strbuf *err)
@@ -2988,6 +3119,8 @@ static int files_transaction_finish(struct ref_store *ref_store,
assert(err);
+ if (transaction->flags & REF_TRANSACTION_FLAG_INITIAL)
+ return files_transaction_finish_initial(refs, transaction, err);
if (!transaction->nr) {
transaction->state = REF_TRANSACTION_CLOSED;
return 0;
@@ -3121,106 +3254,6 @@ static int files_transaction_abort(struct ref_store *ref_store,
return 0;
}
-static int ref_present(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flags UNUSED,
- void *cb_data)
-{
- struct string_list *affected_refnames = cb_data;
-
- return string_list_has_string(affected_refnames, refname);
-}
-
-static int files_initial_transaction_commit(struct ref_store *ref_store,
- struct ref_transaction *transaction,
- struct strbuf *err)
-{
- struct files_ref_store *refs =
- files_downcast(ref_store, REF_STORE_WRITE,
- "initial_ref_transaction_commit");
- size_t i;
- int ret = 0;
- struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
- struct ref_transaction *packed_transaction = NULL;
-
- assert(err);
-
- if (transaction->state != REF_TRANSACTION_OPEN)
- BUG("commit called for transaction that is not open");
-
- /* Fail if a refname appears more than once in the transaction: */
- for (i = 0; i < transaction->nr; i++)
- string_list_append(&affected_refnames,
- transaction->updates[i]->refname);
- string_list_sort(&affected_refnames);
- if (ref_update_reject_duplicates(&affected_refnames, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
- goto cleanup;
- }
-
- /*
- * It's really undefined to call this function in an active
- * repository or when there are existing references: we are
- * only locking and changing packed-refs, so (1) any
- * simultaneous processes might try to change a reference at
- * the same time we do, and (2) any existing loose versions of
- * the references that we are setting would have precedence
- * over our values. But some remote helpers create the remote
- * "HEAD" and "master" branches before calling this function,
- * so here we really only check that none of the references
- * that we are creating already exists.
- */
- if (refs_for_each_rawref(&refs->base, ref_present,
- &affected_refnames))
- BUG("initial ref transaction called with existing refs");
-
- packed_transaction = ref_store_transaction_begin(refs->packed_ref_store, err);
- if (!packed_transaction) {
- ret = TRANSACTION_GENERIC_ERROR;
- goto cleanup;
- }
-
- for (i = 0; i < transaction->nr; i++) {
- struct ref_update *update = transaction->updates[i];
-
- if ((update->flags & REF_HAVE_OLD) &&
- !is_null_oid(&update->old_oid))
- BUG("initial ref transaction with old_sha1 set");
- if (refs_verify_refname_available(&refs->base, update->refname,
- &affected_refnames, NULL,
- err)) {
- ret = TRANSACTION_NAME_CONFLICT;
- goto cleanup;
- }
-
- /*
- * Add a reference creation for this reference to the
- * packed-refs transaction:
- */
- ref_transaction_add_update(packed_transaction, update->refname,
- update->flags & ~REF_HAVE_OLD,
- &update->new_oid, &update->old_oid,
- NULL, NULL, NULL);
- }
-
- if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
- goto cleanup;
- }
-
- if (initial_ref_transaction_commit(packed_transaction, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
- }
-
- packed_refs_unlock(refs->packed_ref_store);
-cleanup:
- if (packed_transaction)
- ref_transaction_free(packed_transaction);
- transaction->state = REF_TRANSACTION_CLOSED;
- string_list_clear(&affected_refnames, 0);
- return ret;
-}
-
struct expire_reflog_cb {
reflog_expiry_should_prune_fn *should_prune_fn;
void *policy_cb;
@@ -3501,12 +3534,153 @@ static int files_ref_store_remove_on_disk(struct ref_store *ref_store,
*/
typedef int (*files_fsck_refs_fn)(struct ref_store *ref_store,
struct fsck_options *o,
- const char *refs_check_dir,
+ const char *refname,
struct dir_iterator *iter);
+static int files_fsck_symref_target(struct fsck_options *o,
+ struct fsck_ref_report *report,
+ struct strbuf *referent,
+ unsigned int symbolic_link)
+{
+ int is_referent_root;
+ char orig_last_byte;
+ size_t orig_len;
+ int ret = 0;
+
+ orig_len = referent->len;
+ orig_last_byte = referent->buf[orig_len - 1];
+ if (!symbolic_link)
+ strbuf_rtrim(referent);
+
+ is_referent_root = is_root_ref(referent->buf);
+ if (!is_referent_root &&
+ !starts_with(referent->buf, "refs/") &&
+ !starts_with(referent->buf, "worktrees/")) {
+ ret = fsck_report_ref(o, report,
+ FSCK_MSG_SYMREF_TARGET_IS_NOT_A_REF,
+ "points to non-ref target '%s'", referent->buf);
+
+ }
+
+ if (!is_referent_root && check_refname_format(referent->buf, 0)) {
+ ret = fsck_report_ref(o, report,
+ FSCK_MSG_BAD_REFERENT_NAME,
+ "points to invalid refname '%s'", referent->buf);
+ goto out;
+ }
+
+ if (symbolic_link)
+ goto out;
+
+ if (referent->len == orig_len ||
+ (referent->len < orig_len && orig_last_byte != '\n')) {
+ ret = fsck_report_ref(o, report,
+ FSCK_MSG_REF_MISSING_NEWLINE,
+ "misses LF at the end");
+ }
+
+ if (referent->len != orig_len && referent->len != orig_len - 1) {
+ ret = fsck_report_ref(o, report,
+ FSCK_MSG_TRAILING_REF_CONTENT,
+ "has trailing whitespaces or newlines");
+ }
+
+out:
+ return ret;
+}
+
+static int files_fsck_refs_content(struct ref_store *ref_store,
+ struct fsck_options *o,
+ const char *target_name,
+ struct dir_iterator *iter)
+{
+ struct strbuf ref_content = STRBUF_INIT;
+ struct strbuf abs_gitdir = STRBUF_INIT;
+ struct strbuf referent = STRBUF_INIT;
+ struct fsck_ref_report report = { 0 };
+ const char *trailing = NULL;
+ unsigned int type = 0;
+ int failure_errno = 0;
+ struct object_id oid;
+ int ret = 0;
+
+ report.path = target_name;
+
+ if (S_ISLNK(iter->st.st_mode)) {
+ const char *relative_referent_path = NULL;
+
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_SYMLINK_REF,
+ "use deprecated symbolic link for symref");
+
+ strbuf_add_absolute_path(&abs_gitdir, ref_store->repo->gitdir);
+ strbuf_normalize_path(&abs_gitdir);
+ if (!is_dir_sep(abs_gitdir.buf[abs_gitdir.len - 1]))
+ strbuf_addch(&abs_gitdir, '/');
+
+ strbuf_add_real_path(&ref_content, iter->path.buf);
+ skip_prefix(ref_content.buf, abs_gitdir.buf,
+ &relative_referent_path);
+
+ if (relative_referent_path)
+ strbuf_addstr(&referent, relative_referent_path);
+ else
+ strbuf_addbuf(&referent, &ref_content);
+
+ ret |= files_fsck_symref_target(o, &report, &referent, 1);
+ goto cleanup;
+ }
+
+ if (strbuf_read_file(&ref_content, iter->path.buf, 0) < 0) {
+ /*
+ * Ref file could be removed by another concurrent process. We should
+ * ignore this error and continue to the next ref.
+ */
+ if (errno == ENOENT)
+ goto cleanup;
+
+ ret = error_errno(_("cannot read ref file '%s'"), iter->path.buf);
+ goto cleanup;
+ }
+
+ if (parse_loose_ref_contents(ref_store->repo->hash_algo,
+ ref_content.buf, &oid, &referent,
+ &type, &trailing, &failure_errno)) {
+ strbuf_rtrim(&ref_content);
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_BAD_REF_CONTENT,
+ "%s", ref_content.buf);
+ goto cleanup;
+ }
+
+ if (!(type & REF_ISSYMREF)) {
+ if (!*trailing) {
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_REF_MISSING_NEWLINE,
+ "misses LF at the end");
+ goto cleanup;
+ }
+ if (*trailing != '\n' || *(trailing + 1)) {
+ ret = fsck_report_ref(o, &report,
+ FSCK_MSG_TRAILING_REF_CONTENT,
+ "has trailing garbage: '%s'", trailing);
+ goto cleanup;
+ }
+ } else {
+ ret = files_fsck_symref_target(o, &report, &referent, 0);
+ goto cleanup;
+ }
+
+cleanup:
+ strbuf_release(&ref_content);
+ strbuf_release(&referent);
+ strbuf_release(&abs_gitdir);
+ return ret;
+}
+
static int files_fsck_refs_name(struct ref_store *ref_store UNUSED,
struct fsck_options *o,
- const char *refs_check_dir,
+ const char *refname,
struct dir_iterator *iter)
{
struct strbuf sb = STRBUF_INIT;
@@ -3519,11 +3693,13 @@ static int files_fsck_refs_name(struct ref_store *ref_store UNUSED,
if (iter->basename[0] != '.' && ends_with(iter->basename, ".lock"))
goto cleanup;
- if (check_refname_format(iter->basename, REFNAME_ALLOW_ONELEVEL)) {
- struct fsck_ref_report report = { .path = NULL };
+ /*
+ * This works right now because we never check the root refs.
+ */
+ if (check_refname_format(refname, 0)) {
+ struct fsck_ref_report report = { 0 };
- strbuf_addf(&sb, "%s/%s", refs_check_dir, iter->relative_path);
- report.path = sb.buf;
+ report.path = refname;
ret = fsck_report_ref(o, &report,
FSCK_MSG_BAD_REF_NAME,
"invalid refname format");
@@ -3537,8 +3713,10 @@ cleanup:
static int files_fsck_refs_dir(struct ref_store *ref_store,
struct fsck_options *o,
const char *refs_check_dir,
+ struct worktree *wt,
files_fsck_refs_fn *fsck_refs_fn)
{
+ struct strbuf refname = STRBUF_INIT;
struct strbuf sb = STRBUF_INIT;
struct dir_iterator *iter;
int iter_status;
@@ -3557,11 +3735,18 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
continue;
} else if (S_ISREG(iter->st.st_mode) ||
S_ISLNK(iter->st.st_mode)) {
+ strbuf_reset(&refname);
+
+ if (!is_main_worktree(wt))
+ strbuf_addf(&refname, "worktrees/%s/", wt->id);
+ strbuf_addf(&refname, "%s/%s", refs_check_dir,
+ iter->relative_path);
+
if (o->verbose)
- fprintf_ln(stderr, "Checking %s/%s",
- refs_check_dir, iter->relative_path);
+ fprintf_ln(stderr, "Checking %s", refname.buf);
+
for (size_t i = 0; fsck_refs_fn[i]; i++) {
- if (fsck_refs_fn[i](ref_store, o, refs_check_dir, iter))
+ if (fsck_refs_fn[i](ref_store, o, refname.buf, iter))
ret = -1;
}
} else {
@@ -3578,30 +3763,34 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
out:
strbuf_release(&sb);
+ strbuf_release(&refname);
return ret;
}
static int files_fsck_refs(struct ref_store *ref_store,
- struct fsck_options *o)
+ struct fsck_options *o,
+ struct worktree *wt)
{
files_fsck_refs_fn fsck_refs_fn[]= {
files_fsck_refs_name,
+ files_fsck_refs_content,
NULL,
};
if (o->verbose)
fprintf_ln(stderr, _("Checking references consistency"));
- return files_fsck_refs_dir(ref_store, o, "refs", fsck_refs_fn);
+ return files_fsck_refs_dir(ref_store, o, "refs", wt, fsck_refs_fn);
}
static int files_fsck(struct ref_store *ref_store,
- struct fsck_options *o)
+ struct fsck_options *o,
+ struct worktree *wt)
{
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_READ, "fsck");
- return files_fsck_refs(ref_store, o) |
- refs->packed_ref_store->be->fsck(refs->packed_ref_store, o);
+ return files_fsck_refs(ref_store, o, wt) |
+ refs->packed_ref_store->be->fsck(refs->packed_ref_store, o, wt);
}
struct ref_storage_be refs_be_files = {
@@ -3614,7 +3803,6 @@ struct ref_storage_be refs_be_files = {
.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,
.rename_ref = files_rename_ref,