Fix git branch -m for symrefs.

This had two problems with symrefs. First, it copied the actual sha1
instead of the "pointer", second it failed to remove the old ref after a
successful rename.

Given that till now delete_ref() always dereferenced symrefs, a new
parameters has been introduced to delete_ref() to allow deleting refs
without a dereference.

Signed-off-by: Miklos Vajna <vmiklos@frugalware.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Miklos Vajna 2008-10-26 03:33:56 +01:00 committed by Junio C Hamano
parent 031e6c898f
commit eca35a25a9
10 changed files with 55 additions and 31 deletions

View File

@ -160,7 +160,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
continue; continue;
} }
if (delete_ref(name, sha1)) { if (delete_ref(name, sha1, 0)) {
error("Error deleting %sbranch '%s'", remote, error("Error deleting %sbranch '%s'", remote,
argv[i]); argv[i]);
ret = 1; ret = 1;

View File

@ -340,7 +340,7 @@ static int remove_branches(struct string_list *branches)
const char *refname = item->string; const char *refname = item->string;
unsigned char *sha1 = item->util; unsigned char *sha1 = item->util;
if (delete_ref(refname, sha1)) if (delete_ref(refname, sha1, 0))
result |= error("Could not remove branch %s", refname); result |= error("Could not remove branch %s", refname);
} }
return result; return result;
@ -570,7 +570,7 @@ static int prune(int argc, const char **argv)
const char *refname = states.stale.items[i].util; const char *refname = states.stale.items[i].util;
if (!dry_run) if (!dry_run)
result |= delete_ref(refname, NULL); result |= delete_ref(refname, NULL, 0);
printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned", printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
abbrev_ref(refname, "refs/remotes/")); abbrev_ref(refname, "refs/remotes/"));

View File

@ -279,7 +279,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR); update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
} }
else if (old_orig) else if (old_orig)
delete_ref("ORIG_HEAD", old_orig); delete_ref("ORIG_HEAD", old_orig, 0);
prepend_reflog_action("updating HEAD", msg, sizeof(msg)); prepend_reflog_action("updating HEAD", msg, sizeof(msg));
update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR); update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR);

View File

@ -226,7 +226,7 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
if (args.verbose) if (args.verbose)
fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst); fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
if (ref->deletion) { if (ref->deletion) {
delete_ref(rs.dst, NULL); delete_ref(rs.dst, NULL, 0);
} else } else
update_ref("update by push", rs.dst, update_ref("update by push", rs.dst,
ref->new_sha1, NULL, 0, 0); ref->new_sha1, NULL, 0, 0);

View File

@ -125,7 +125,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
static int delete_tag(const char *name, const char *ref, static int delete_tag(const char *name, const char *ref,
const unsigned char *sha1) const unsigned char *sha1)
{ {
if (delete_ref(ref, sha1)) if (delete_ref(ref, sha1, 0))
return 1; return 1;
printf("Deleted tag '%s'\n", name); printf("Deleted tag '%s'\n", name);
return 0; return 0;

View File

@ -48,7 +48,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
die("%s: not a valid old SHA1", oldval); die("%s: not a valid old SHA1", oldval);
if (delete) if (delete)
return delete_ref(refname, oldval ? oldsha1 : NULL); return delete_ref(refname, oldval ? oldsha1 : NULL, 0);
else else
return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL, return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
no_deref ? REF_NODEREF : 0, DIE_ON_ERR); no_deref ? REF_NODEREF : 0, DIE_ON_ERR);

View File

@ -420,7 +420,7 @@ extern int commit_locked_index(struct lock_file *);
extern void set_alternate_index_output(const char *); extern void set_alternate_index_output(const char *);
extern int close_lock_file(struct lock_file *); extern int close_lock_file(struct lock_file *);
extern void rollback_lock_file(struct lock_file *); extern void rollback_lock_file(struct lock_file *);
extern int delete_ref(const char *, const unsigned char *sha1); extern int delete_ref(const char *, const unsigned char *sha1, int delopt);
/* Environment bits from configuration mechanism */ /* Environment bits from configuration mechanism */
extern int trust_executable_bit; extern int trust_executable_bit;

View File

@ -222,7 +222,7 @@ static const char *update(struct command *cmd)
warning ("Allowing deletion of corrupt ref."); warning ("Allowing deletion of corrupt ref.");
old_sha1 = NULL; old_sha1 = NULL;
} }
if (delete_ref(name, old_sha1)) { if (delete_ref(name, old_sha1, 0)) {
error("failed to delete %s", name); error("failed to delete %s", name);
return "failed to delete"; return "failed to delete";
} }

59
refs.c
View File

@ -912,25 +912,33 @@ static int repack_without_ref(const char *refname)
return commit_lock_file(&packlock); return commit_lock_file(&packlock);
} }
int delete_ref(const char *refname, const unsigned char *sha1) int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
{ {
struct ref_lock *lock; struct ref_lock *lock;
int err, i, ret = 0, flag = 0; int err, i = 0, ret = 0, flag = 0;
lock = lock_ref_sha1_basic(refname, sha1, 0, &flag); lock = lock_ref_sha1_basic(refname, sha1, 0, &flag);
if (!lock) if (!lock)
return 1; return 1;
if (!(flag & REF_ISPACKED)) { if (!(flag & REF_ISPACKED)) {
/* loose */ /* loose */
i = strlen(lock->lk->filename) - 5; /* .lock */ const char *path;
lock->lk->filename[i] = 0;
err = unlink(lock->lk->filename); if (!(delopt & REF_NODEREF)) {
i = strlen(lock->lk->filename) - 5; /* .lock */
lock->lk->filename[i] = 0;
path = lock->lk->filename;
} else {
path = git_path(refname);
}
err = unlink(path);
if (err && errno != ENOENT) { if (err && errno != ENOENT) {
ret = 1; ret = 1;
error("unlink(%s) failed: %s", error("unlink(%s) failed: %s",
lock->lk->filename, strerror(errno)); path, strerror(errno));
} }
lock->lk->filename[i] = '.'; if (!(delopt & REF_NODEREF))
lock->lk->filename[i] = '.';
} }
/* removing the loose one could have resurrected an earlier /* removing the loose one could have resurrected an earlier
* packed one. Also, if it was not loose we need to repack * packed one. Also, if it was not loose we need to repack
@ -955,11 +963,16 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
struct ref_lock *lock; struct ref_lock *lock;
struct stat loginfo; struct stat loginfo;
int log = !lstat(git_path("logs/%s", oldref), &loginfo); int log = !lstat(git_path("logs/%s", oldref), &loginfo);
const char *symref = NULL;
int is_symref = 0;
if (S_ISLNK(loginfo.st_mode)) if (S_ISLNK(loginfo.st_mode))
return error("reflog for %s is a symlink", oldref); return error("reflog for %s is a symlink", oldref);
if (!resolve_ref(oldref, orig_sha1, 1, &flag)) symref = resolve_ref(oldref, orig_sha1, 1, &flag);
if (flag & REF_ISSYMREF)
is_symref = 1;
if (!symref)
return error("refname %s not found", oldref); return error("refname %s not found", oldref);
if (!is_refname_available(newref, oldref, get_packed_refs(), 0)) if (!is_refname_available(newref, oldref, get_packed_refs(), 0))
@ -979,12 +992,12 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
return error("unable to move logfile logs/%s to tmp-renamed-log: %s", return error("unable to move logfile logs/%s to tmp-renamed-log: %s",
oldref, strerror(errno)); oldref, strerror(errno));
if (delete_ref(oldref, orig_sha1)) { if (delete_ref(oldref, orig_sha1, REF_NODEREF)) {
error("unable to delete old %s", oldref); error("unable to delete old %s", oldref);
goto rollback; goto rollback;
} }
if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1)) { if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1, REF_NODEREF)) {
if (errno==EISDIR) { if (errno==EISDIR) {
if (remove_empty_directories(git_path("%s", newref))) { if (remove_empty_directories(git_path("%s", newref))) {
error("Directory not empty: %s", newref); error("Directory not empty: %s", newref);
@ -1022,18 +1035,20 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
} }
logmoved = log; logmoved = log;
lock = lock_ref_sha1_basic(newref, NULL, 0, NULL); if (!is_symref) {
if (!lock) { lock = lock_ref_sha1_basic(newref, NULL, 0, NULL);
error("unable to lock %s for update", newref); if (!lock) {
goto rollback; error("unable to lock %s for update", newref);
} goto rollback;
}
lock->force_write = 1; lock->force_write = 1;
hashcpy(lock->old_sha1, orig_sha1); hashcpy(lock->old_sha1, orig_sha1);
if (write_ref_sha1(lock, orig_sha1, logmsg)) { if (write_ref_sha1(lock, orig_sha1, logmsg)) {
error("unable to write current sha1 into %s", newref); error("unable to write current sha1 into %s", newref);
goto rollback; goto rollback;
} }
} else
create_symref(newref, symref, logmsg);
return 0; return 0;

View File

@ -112,6 +112,15 @@ test_expect_success 'config information was renamed, too' \
"test $(git config branch.s.dummy) = Hello && "test $(git config branch.s.dummy) = Hello &&
test_must_fail git config branch.s/s/dummy" test_must_fail git config branch.s/s/dummy"
test_expect_success 'renaming a symref' \
'
git symbolic-ref refs/heads/master2 refs/heads/master &&
git branch -m master2 master3 &&
git symbolic-ref refs/heads/master3 &&
test -f .git/refs/heads/master &&
! test -f .git/refs/heads/master2
'
test_expect_success \ test_expect_success \
'git branch -m u v should fail when the reflog for u is a symlink' ' 'git branch -m u v should fail when the reflog for u is a symlink' '
git branch -l u && git branch -l u &&