1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-05-27 05:16:17 +02:00

Merge branch 'jk/symbolic-ref'

The low-level code that is used to create symbolic references has
been updated to share more code with the code that deals with
normal references.

* jk/symbolic-ref:
  lock_ref_sha1_basic: handle REF_NODEREF with invalid refs
  lock_ref_sha1_basic: always fill old_oid while holding lock
  checkout,clone: check return value of create_symref
  create_symref: write reflog while holding lock
  create_symref: use existing ref-lock code
  create_symref: modernize variable names
This commit is contained in:
Junio C Hamano 2016-01-26 15:40:30 -08:00
commit 90ce285a42
6 changed files with 141 additions and 78 deletions

View File

@ -661,7 +661,8 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
describe_detached_head(_("HEAD is now at"), new->commit);
}
} else if (new->path) { /* Switch branches. */
create_symref("HEAD", new->path, msg.buf);
if (create_symref("HEAD", new->path, msg.buf) < 0)
die("unable to update HEAD");
if (!opts->quiet) {
if (old->path && !strcmp(new->path, old->path)) {
if (opts->new_branch_force)

View File

@ -636,9 +636,11 @@ static void update_remote_refs(const struct ref *refs,
struct strbuf head_ref = STRBUF_INIT;
strbuf_addstr(&head_ref, branch_top);
strbuf_addstr(&head_ref, "HEAD");
create_symref(head_ref.buf,
remote_head_points_at->peer_ref->name,
msg);
if (create_symref(head_ref.buf,
remote_head_points_at->peer_ref->name,
msg) < 0)
die("unable to update %s", head_ref.buf);
strbuf_release(&head_ref);
}
}
@ -648,7 +650,8 @@ static void update_head(const struct ref *our, const struct ref *remote,
const char *head;
if (our && skip_prefix(our->name, "refs/heads/", &head)) {
/* Local default branch link */
create_symref("HEAD", our->name, NULL);
if (create_symref("HEAD", our->name, NULL) < 0)
die("unable to update HEAD");
if (!option_bare) {
update_ref(msg, "HEAD", our->old_oid.hash, NULL, 0,
UPDATE_REFS_DIE_ON_ERR);

2
refs.h
View File

@ -292,7 +292,7 @@ extern char *shorten_unambiguous_ref(const char *refname, int strict);
/** rename ref, return 0 on success **/
extern int rename_ref(const char *oldref, const char *newref, const char *logmsg);
extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg);
extern int create_symref(const char *refname, const char *target, const char *logmsg);
enum action_on_err {
UPDATE_REFS_MSG_ON_ERR,

View File

@ -1840,12 +1840,17 @@ static int verify_lock(struct ref_lock *lock,
if (read_ref_full(lock->ref_name,
mustexist ? RESOLVE_REF_READING : 0,
lock->old_oid.hash, NULL)) {
int save_errno = errno;
strbuf_addf(err, "can't verify ref %s", lock->ref_name);
errno = save_errno;
return -1;
if (old_sha1) {
int save_errno = errno;
strbuf_addf(err, "can't verify ref %s", lock->ref_name);
errno = save_errno;
return -1;
} else {
hashclr(lock->old_oid.hash);
return 0;
}
}
if (hashcmp(lock->old_oid.hash, old_sha1)) {
if (old_sha1 && hashcmp(lock->old_oid.hash, old_sha1)) {
strbuf_addf(err, "ref %s is at %s but expected %s",
lock->ref_name,
sha1_to_hex(lock->old_oid.hash),
@ -1882,7 +1887,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
const char *orig_refname = refname;
struct ref_lock *lock;
int last_errno = 0;
int type, lflags;
int type;
int lflags = 0;
int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
int resolve_flags = 0;
int attempts_remaining = 3;
@ -1893,10 +1899,11 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
if (mustexist)
resolve_flags |= RESOLVE_REF_READING;
if (flags & REF_DELETING) {
if (flags & REF_DELETING)
resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
if (flags & REF_NODEREF)
resolve_flags |= RESOLVE_REF_NO_RECURSE;
if (flags & REF_NODEREF) {
resolve_flags |= RESOLVE_REF_NO_RECURSE;
lflags |= LOCK_NO_DEREF;
}
refname = resolve_ref_unsafe(refname, resolve_flags,
@ -1932,6 +1939,10 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
goto error_return;
}
if (flags & REF_NODEREF)
refname = orig_refname;
/*
* If the ref did not exist and we are creating it, make sure
* there is no existing packed ref whose name begins with our
@ -1947,11 +1958,6 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
lock->lk = xcalloc(1, sizeof(struct lock_file));
lflags = 0;
if (flags & REF_NODEREF) {
refname = orig_refname;
lflags |= LOCK_NO_DEREF;
}
lock->ref_name = xstrdup(refname);
lock->orig_ref_name = xstrdup(orig_refname);
strbuf_git_path(&ref_file, "%s", refname);
@ -1985,7 +1991,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
goto error_return;
}
}
if (old_sha1 && verify_lock(lock, old_sha1, mustexist, err)) {
if (verify_lock(lock, old_sha1, mustexist, err)) {
last_errno = errno;
goto error_return;
}
@ -2811,75 +2817,74 @@ static int commit_ref_update(struct ref_lock *lock,
return 0;
}
int create_symref(const char *ref_target, const char *refs_heads_master,
const char *logmsg)
static int create_ref_symlink(struct ref_lock *lock, const char *target)
{
char *lockpath = NULL;
char ref[1000];
int fd, len, written;
char *git_HEAD = git_pathdup("%s", ref_target);
unsigned char old_sha1[20], new_sha1[20];
struct strbuf err = STRBUF_INIT;
if (logmsg && read_ref(ref_target, old_sha1))
hashclr(old_sha1);
if (safe_create_leading_directories(git_HEAD) < 0)
return error("unable to create directory for %s", git_HEAD);
int ret = -1;
#ifndef NO_SYMLINK_HEAD
if (prefer_symlink_refs) {
unlink(git_HEAD);
if (!symlink(refs_heads_master, git_HEAD))
goto done;
char *ref_path = get_locked_file_path(lock->lk);
unlink(ref_path);
ret = symlink(target, ref_path);
free(ref_path);
if (ret)
fprintf(stderr, "no symlink - falling back to symbolic ref\n");
}
#endif
return ret;
}
len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master);
if (sizeof(ref) <= len) {
error("refname too long: %s", refs_heads_master);
goto error_free_return;
}
lockpath = mkpathdup("%s.lock", git_HEAD);
fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
if (fd < 0) {
error("Unable to open %s for writing", lockpath);
goto error_free_return;
}
written = write_in_full(fd, ref, len);
if (close(fd) != 0 || written != len) {
error("Unable to write to %s", lockpath);
goto error_unlink_return;
}
if (rename(lockpath, git_HEAD) < 0) {
error("Unable to create %s", git_HEAD);
goto error_unlink_return;
}
if (adjust_shared_perm(git_HEAD)) {
error("Unable to fix permissions on %s", lockpath);
error_unlink_return:
unlink_or_warn(lockpath);
error_free_return:
free(lockpath);
free(git_HEAD);
return -1;
}
free(lockpath);
#ifndef NO_SYMLINK_HEAD
done:
#endif
if (logmsg && !read_ref(refs_heads_master, new_sha1) &&
log_ref_write(ref_target, old_sha1, new_sha1, logmsg, 0, &err)) {
static void update_symref_reflog(struct ref_lock *lock, const char *refname,
const char *target, const char *logmsg)
{
struct strbuf err = STRBUF_INIT;
unsigned char new_sha1[20];
if (logmsg && !read_ref(target, new_sha1) &&
log_ref_write(refname, lock->old_oid.hash, new_sha1, logmsg, 0, &err)) {
error("%s", err.buf);
strbuf_release(&err);
}
}
free(git_HEAD);
static int create_symref_locked(struct ref_lock *lock, const char *refname,
const char *target, const char *logmsg)
{
if (prefer_symlink_refs && !create_ref_symlink(lock, target)) {
update_symref_reflog(lock, refname, target, logmsg);
return 0;
}
if (!fdopen_lock_file(lock->lk, "w"))
return error("unable to fdopen %s: %s",
lock->lk->tempfile.filename.buf, strerror(errno));
update_symref_reflog(lock, refname, target, logmsg);
/* no error check; commit_ref will check ferror */
fprintf(lock->lk->tempfile.fp, "ref: %s\n", target);
if (commit_ref(lock) < 0)
return error("unable to write symref for %s: %s", refname,
strerror(errno));
return 0;
}
int create_symref(const char *refname, const char *target, const char *logmsg)
{
struct strbuf err = STRBUF_INIT;
struct ref_lock *lock;
int ret;
lock = lock_ref_sha1_basic(refname, NULL, NULL, NULL, REF_NODEREF, NULL,
&err);
if (!lock) {
error("%s", err.buf);
strbuf_release(&err);
return -1;
}
ret = create_symref_locked(lock, refname, target, logmsg);
unlock_ref(lock);
return ret;
}
int reflog_exists(const char *refname)
{
struct stat st;

View File

@ -114,4 +114,19 @@ test_expect_success 'symbolic-ref writes reflog entry' '
test_cmp expect actual
'
test_expect_success 'symbolic-ref does not create ref d/f conflicts' '
git checkout -b df &&
test_commit df &&
test_must_fail git symbolic-ref refs/heads/df/conflict refs/heads/df &&
git pack-refs --all --prune &&
test_must_fail git symbolic-ref refs/heads/df/conflict refs/heads/df
'
test_expect_success 'symbolic-ref handles existing pointer to invalid name' '
head=$(git rev-parse HEAD) &&
git symbolic-ref HEAD refs/heads/outer &&
git update-ref refs/heads/outer/inner $head &&
git symbolic-ref HEAD refs/heads/unrelated
'
test_done

View File

@ -19,4 +19,43 @@ test_expect_success 'checkout master from invalid HEAD' '
git checkout master --
'
test_expect_success 'checkout notices failure to lock HEAD' '
test_when_finished "rm -f .git/HEAD.lock" &&
>.git/HEAD.lock &&
test_must_fail git checkout -b other
'
test_expect_success 'create ref directory/file conflict scenario' '
git update-ref refs/heads/outer/inner master &&
# do not rely on symbolic-ref to get a known state,
# as it may use the same code we are testing
reset_to_df () {
echo "ref: refs/heads/outer" >.git/HEAD
}
'
test_expect_success 'checkout away from d/f HEAD (unpacked, to branch)' '
reset_to_df &&
git checkout master
'
test_expect_success 'checkout away from d/f HEAD (unpacked, to detached)' '
reset_to_df &&
git checkout --detach master
'
test_expect_success 'pack refs' '
git pack-refs --all --prune
'
test_expect_success 'checkout away from d/f HEAD (packed, to branch)' '
reset_to_df &&
git checkout master
'
test_expect_success 'checkout away from d/f HEAD (packed, to detached)' '
reset_to_df &&
git checkout --detach master
'
test_done