Merge branch 'hn/reftable-prep-part-2'

Further preliminary change to refs API.

* hn/reftable-prep-part-2:
  Make HEAD a PSEUDOREF rather than PER_WORKTREE.
  Modify pseudo refs through ref backend storage
  t1400: use git rev-parse for testing PSEUDOREF existence
maint
Junio C Hamano 2020-08-17 17:02:42 -07:00
commit 95c687bf85
4 changed files with 36 additions and 139 deletions

View File

@ -148,12 +148,13 @@ still see a subset of the modifications.


LOGGING UPDATES LOGGING UPDATES
--------------- ---------------
If config parameter "core.logAllRefUpdates" is true and the ref is one under If config parameter "core.logAllRefUpdates" is true and the ref is one
"refs/heads/", "refs/remotes/", "refs/notes/", or the symbolic ref HEAD; or under "refs/heads/", "refs/remotes/", "refs/notes/", or a pseudoref
the file "$GIT_DIR/logs/<ref>" exists then `git update-ref` will append like HEAD or ORIG_HEAD; or the file "$GIT_DIR/logs/<ref>" exists then
a line to the log file "$GIT_DIR/logs/<ref>" (dereferencing all `git update-ref` will append a line to the log file
symbolic refs before creating the log name) describing the change "$GIT_DIR/logs/<ref>" (dereferencing all symbolic refs before creating
in ref value. Log lines are formatted as: the log name) describing the change in ref value. Log lines are
formatted as:


oldsha1 SP newsha1 SP committer LF oldsha1 SP newsha1 SP committer LF



113
refs.c
View File

@ -708,8 +708,7 @@ int dwim_log(const char *str, int len, struct object_id *oid, char **log)


static int is_per_worktree_ref(const char *refname) static int is_per_worktree_ref(const char *refname)
{ {
return !strcmp(refname, "HEAD") || return starts_with(refname, "refs/worktree/") ||
starts_with(refname, "refs/worktree/") ||
starts_with(refname, "refs/bisect/") || starts_with(refname, "refs/bisect/") ||
starts_with(refname, "refs/rewritten/"); starts_with(refname, "refs/rewritten/");
} }
@ -771,102 +770,6 @@ long get_files_ref_lock_timeout_ms(void)
return timeout_ms; return timeout_ms;
} }


static int write_pseudoref(const char *pseudoref, const struct object_id *oid,
const struct object_id *old_oid, struct strbuf *err)
{
const char *filename;
int fd;
struct lock_file lock = LOCK_INIT;
struct strbuf buf = STRBUF_INIT;
int ret = -1;

if (!oid)
return 0;

strbuf_addf(&buf, "%s\n", oid_to_hex(oid));

filename = git_path("%s", pseudoref);
fd = hold_lock_file_for_update_timeout(&lock, filename, 0,
get_files_ref_lock_timeout_ms());
if (fd < 0) {
strbuf_addf(err, _("could not open '%s' for writing: %s"),
filename, strerror(errno));
goto done;
}

if (old_oid) {
struct object_id actual_old_oid;

if (read_ref(pseudoref, &actual_old_oid)) {
if (!is_null_oid(old_oid)) {
strbuf_addf(err, _("could not read ref '%s'"),
pseudoref);
rollback_lock_file(&lock);
goto done;
}
} else if (is_null_oid(old_oid)) {
strbuf_addf(err, _("ref '%s' already exists"),
pseudoref);
rollback_lock_file(&lock);
goto done;
} else if (!oideq(&actual_old_oid, old_oid)) {
strbuf_addf(err, _("unexpected object ID when writing '%s'"),
pseudoref);
rollback_lock_file(&lock);
goto done;
}
}

if (write_in_full(fd, buf.buf, buf.len) < 0) {
strbuf_addf(err, _("could not write to '%s'"), filename);
rollback_lock_file(&lock);
goto done;
}

commit_lock_file(&lock);
ret = 0;
done:
strbuf_release(&buf);
return ret;
}

static int delete_pseudoref(const char *pseudoref, const struct object_id *old_oid)
{
const char *filename;

filename = git_path("%s", pseudoref);

if (old_oid && !is_null_oid(old_oid)) {
struct lock_file lock = LOCK_INIT;
int fd;
struct object_id actual_old_oid;

fd = hold_lock_file_for_update_timeout(
&lock, filename, 0,
get_files_ref_lock_timeout_ms());
if (fd < 0) {
error_errno(_("could not open '%s' for writing"),
filename);
return -1;
}
if (read_ref(pseudoref, &actual_old_oid))
die(_("could not read ref '%s'"), pseudoref);
if (!oideq(&actual_old_oid, old_oid)) {
error(_("unexpected object ID when deleting '%s'"),
pseudoref);
rollback_lock_file(&lock);
return -1;
}

unlink(filename);
rollback_lock_file(&lock);
} else {
unlink(filename);
}

return 0;
}

int refs_delete_ref(struct ref_store *refs, const char *msg, int refs_delete_ref(struct ref_store *refs, const char *msg,
const char *refname, const char *refname,
const struct object_id *old_oid, const struct object_id *old_oid,
@ -875,11 +778,6 @@ int refs_delete_ref(struct ref_store *refs, const char *msg,
struct ref_transaction *transaction; struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT; struct strbuf err = STRBUF_INIT;


if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
assert(refs == get_main_ref_store(the_repository));
return delete_pseudoref(refname, old_oid);
}

transaction = ref_store_transaction_begin(refs, &err); transaction = ref_store_transaction_begin(refs, &err);
if (!transaction || if (!transaction ||
ref_transaction_delete(transaction, refname, old_oid, ref_transaction_delete(transaction, refname, old_oid,
@ -1210,19 +1108,14 @@ int refs_update_ref(struct ref_store *refs, const char *msg,
struct strbuf err = STRBUF_INIT; struct strbuf err = STRBUF_INIT;
int ret = 0; int ret = 0;


if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
assert(refs == get_main_ref_store(the_repository));
ret = write_pseudoref(refname, new_oid, old_oid, &err);
} else {
t = ref_store_transaction_begin(refs, &err); t = ref_store_transaction_begin(refs, &err);
if (!t || if (!t ||
ref_transaction_update(t, refname, new_oid, old_oid, ref_transaction_update(t, refname, new_oid, old_oid, flags, msg,
flags, msg, &err) || &err) ||
ref_transaction_commit(t, &err)) { ref_transaction_commit(t, &err)) {
ret = 1; ret = 1;
ref_transaction_free(t); ref_transaction_free(t);
} }
}
if (ret) { if (ret) {
const char *str = _("update_ref failed for ref '%s': %s"); const char *str = _("update_ref failed for ref '%s': %s");



View File

@ -160,10 +160,10 @@ test_expect_success 'core.logAllRefUpdates=always creates reflog by default' '
git reflog exists $outside git reflog exists $outside
' '


test_expect_success 'core.logAllRefUpdates=always creates no reflog for ORIG_HEAD' ' test_expect_success 'core.logAllRefUpdates=always creates reflog for ORIG_HEAD' '
test_config core.logAllRefUpdates always && test_config core.logAllRefUpdates always &&
git update-ref ORIG_HEAD $A && git update-ref ORIG_HEAD $A &&
test_must_fail git reflog exists ORIG_HEAD git reflog exists ORIG_HEAD
' '


test_expect_success '--no-create-reflog overrides core.logAllRefUpdates=always' ' test_expect_success '--no-create-reflog overrides core.logAllRefUpdates=always' '
@ -475,57 +475,57 @@ test_expect_success 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER


test_expect_success 'given old value for missing pseudoref, do not create' ' test_expect_success 'given old value for missing pseudoref, do not create' '
test_must_fail git update-ref PSEUDOREF $A $B 2>err && test_must_fail git update-ref PSEUDOREF $A $B 2>err &&
test_path_is_missing .git/PSEUDOREF && test_must_fail git rev-parse PSEUDOREF &&
test_i18ngrep "could not read ref" err test_i18ngrep "unable to resolve reference" err
' '


test_expect_success 'create pseudoref' ' test_expect_success 'create pseudoref' '
git update-ref PSEUDOREF $A && git update-ref PSEUDOREF $A &&
test $A = $(cat .git/PSEUDOREF) test $A = $(git rev-parse PSEUDOREF)
' '


test_expect_success 'overwrite pseudoref with no old value given' ' test_expect_success 'overwrite pseudoref with no old value given' '
git update-ref PSEUDOREF $B && git update-ref PSEUDOREF $B &&
test $B = $(cat .git/PSEUDOREF) test $B = $(git rev-parse PSEUDOREF)
' '


test_expect_success 'overwrite pseudoref with correct old value' ' test_expect_success 'overwrite pseudoref with correct old value' '
git update-ref PSEUDOREF $C $B && git update-ref PSEUDOREF $C $B &&
test $C = $(cat .git/PSEUDOREF) test $C = $(git rev-parse PSEUDOREF)
' '


test_expect_success 'do not overwrite pseudoref with wrong old value' ' test_expect_success 'do not overwrite pseudoref with wrong old value' '
test_must_fail git update-ref PSEUDOREF $D $E 2>err && test_must_fail git update-ref PSEUDOREF $D $E 2>err &&
test $C = $(cat .git/PSEUDOREF) && test $C = $(git rev-parse PSEUDOREF) &&
test_i18ngrep "unexpected object ID" err test_i18ngrep "cannot lock ref.*expected" err
' '


test_expect_success 'delete pseudoref' ' test_expect_success 'delete pseudoref' '
git update-ref -d PSEUDOREF && git update-ref -d PSEUDOREF &&
test_path_is_missing .git/PSEUDOREF test_must_fail git rev-parse PSEUDOREF
' '


test_expect_success 'do not delete pseudoref with wrong old value' ' test_expect_success 'do not delete pseudoref with wrong old value' '
git update-ref PSEUDOREF $A && git update-ref PSEUDOREF $A &&
test_must_fail git update-ref -d PSEUDOREF $B 2>err && test_must_fail git update-ref -d PSEUDOREF $B 2>err &&
test $A = $(cat .git/PSEUDOREF) && test $A = $(git rev-parse PSEUDOREF) &&
test_i18ngrep "unexpected object ID" err test_i18ngrep "cannot lock ref.*expected" err
' '


test_expect_success 'delete pseudoref with correct old value' ' test_expect_success 'delete pseudoref with correct old value' '
git update-ref -d PSEUDOREF $A && git update-ref -d PSEUDOREF $A &&
test_path_is_missing .git/PSEUDOREF test_must_fail git rev-parse PSEUDOREF
' '


test_expect_success 'create pseudoref with old OID zero' ' test_expect_success 'create pseudoref with old OID zero' '
git update-ref PSEUDOREF $A $Z && git update-ref PSEUDOREF $A $Z &&
test $A = $(cat .git/PSEUDOREF) test $A = $(git rev-parse PSEUDOREF)
' '


test_expect_success 'do not overwrite pseudoref with old OID zero' ' test_expect_success 'do not overwrite pseudoref with old OID zero' '
test_when_finished git update-ref -d PSEUDOREF && test_when_finished git update-ref -d PSEUDOREF &&
test_must_fail git update-ref PSEUDOREF $B $Z 2>err && test_must_fail git update-ref PSEUDOREF $B $Z 2>err &&
test $A = $(cat .git/PSEUDOREF) && test $A = $(git rev-parse PSEUDOREF) &&
test_i18ngrep "already exists" err test_i18ngrep "already exists" err
' '



View File

@ -31,7 +31,10 @@ test_expect_success 'create_symref(FOO, refs/heads/master)' '
test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' ' test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '
git rev-parse FOO -- && git rev-parse FOO -- &&
git rev-parse refs/tags/new-tag -- && git rev-parse refs/tags/new-tag -- &&
$RUN delete-refs 0 nothing FOO refs/tags/new-tag && m=$(git rev-parse master) &&
REF_NO_DEREF=1 &&
$RUN delete-refs $REF_NO_DEREF nothing FOO refs/tags/new-tag &&
test_must_fail git rev-parse --symbolic-full-name FOO &&
test_must_fail git rev-parse FOO -- && test_must_fail git rev-parse FOO -- &&
test_must_fail git rev-parse refs/tags/new-tag -- test_must_fail git rev-parse refs/tags/new-tag --
' '