Skip to content

Commit

Permalink
Fix git branch -m for symrefs.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Miklos Vajna authored and Junio C Hamano committed Oct 26, 2008
1 parent 031e6c8 commit eca35a2
Show file tree
Hide file tree
Showing 10 changed files with 55 additions and 31 deletions.
2 changes: 1 addition & 1 deletion builtin-branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
continue;
}

if (delete_ref(name, sha1)) {
if (delete_ref(name, sha1, 0)) {
error("Error deleting %sbranch '%s'", remote,
argv[i]);
ret = 1;
Expand Down
4 changes: 2 additions & 2 deletions builtin-remote.c
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ static int remove_branches(struct string_list *branches)
const char *refname = item->string;
unsigned char *sha1 = item->util;

if (delete_ref(refname, sha1))
if (delete_ref(refname, sha1, 0))
result |= error("Could not remove branch %s", refname);
}
return result;
Expand Down Expand Up @@ -570,7 +570,7 @@ static int prune(int argc, const char **argv)
const char *refname = states.stale.items[i].util;

if (!dry_run)
result |= delete_ref(refname, NULL);
result |= delete_ref(refname, NULL, 0);

printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
abbrev_ref(refname, "refs/remotes/"));
Expand Down
2 changes: 1 addition & 1 deletion builtin-reset.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
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));
update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR);

Expand Down
2 changes: 1 addition & 1 deletion builtin-send-pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
if (args.verbose)
fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
if (ref->deletion) {
delete_ref(rs.dst, NULL);
delete_ref(rs.dst, NULL, 0);
} else
update_ref("update by push", rs.dst,
ref->new_sha1, NULL, 0, 0);
Expand Down
2 changes: 1 addition & 1 deletion builtin-tag.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
const unsigned char *sha1)
{
if (delete_ref(ref, sha1))
if (delete_ref(ref, sha1, 0))
return 1;
printf("Deleted tag '%s'\n", name);
return 0;
Expand Down
2 changes: 1 addition & 1 deletion builtin-update-ref.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
die("%s: not a valid old SHA1", oldval);

if (delete)
return delete_ref(refname, oldval ? oldsha1 : NULL);
return delete_ref(refname, oldval ? oldsha1 : NULL, 0);
else
return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
no_deref ? REF_NODEREF : 0, DIE_ON_ERR);
Expand Down
2 changes: 1 addition & 1 deletion cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ extern int commit_locked_index(struct lock_file *);
extern void set_alternate_index_output(const char *);
extern int close_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 */
extern int trust_executable_bit;
Expand Down
2 changes: 1 addition & 1 deletion receive-pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ static const char *update(struct command *cmd)
warning ("Allowing deletion of corrupt ref.");
old_sha1 = NULL;
}
if (delete_ref(name, old_sha1)) {
if (delete_ref(name, old_sha1, 0)) {
error("failed to delete %s", name);
return "failed to delete";
}
Expand Down
59 changes: 37 additions & 22 deletions refs.c
Original file line number Diff line number Diff line change
Expand Up @@ -912,25 +912,33 @@ static int repack_without_ref(const char *refname)
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;
int err, i, ret = 0, flag = 0;
int err, i = 0, ret = 0, flag = 0;

lock = lock_ref_sha1_basic(refname, sha1, 0, &flag);
if (!lock)
return 1;
if (!(flag & REF_ISPACKED)) {
/* loose */
i = strlen(lock->lk->filename) - 5; /* .lock */
lock->lk->filename[i] = 0;
err = unlink(lock->lk->filename);
const char *path;

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) {
ret = 1;
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
* packed one. Also, if it was not loose we need to repack
Expand All @@ -955,11 +963,16 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
struct ref_lock *lock;
struct stat loginfo;
int log = !lstat(git_path("logs/%s", oldref), &loginfo);
const char *symref = NULL;
int is_symref = 0;

if (S_ISLNK(loginfo.st_mode))
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);

if (!is_refname_available(newref, oldref, get_packed_refs(), 0))
Expand All @@ -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",
oldref, strerror(errno));

if (delete_ref(oldref, orig_sha1)) {
if (delete_ref(oldref, orig_sha1, REF_NODEREF)) {
error("unable to delete old %s", oldref);
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 (remove_empty_directories(git_path("%s", newref))) {
error("Directory not empty: %s", newref);
Expand Down Expand Up @@ -1022,18 +1035,20 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
}
logmoved = log;

lock = lock_ref_sha1_basic(newref, NULL, 0, NULL);
if (!lock) {
error("unable to lock %s for update", newref);
goto rollback;
}

lock->force_write = 1;
hashcpy(lock->old_sha1, orig_sha1);
if (write_ref_sha1(lock, orig_sha1, logmsg)) {
error("unable to write current sha1 into %s", newref);
goto rollback;
}
if (!is_symref) {
lock = lock_ref_sha1_basic(newref, NULL, 0, NULL);
if (!lock) {
error("unable to lock %s for update", newref);
goto rollback;
}
lock->force_write = 1;
hashcpy(lock->old_sha1, orig_sha1);
if (write_ref_sha1(lock, orig_sha1, logmsg)) {
error("unable to write current sha1 into %s", newref);
goto rollback;
}
} else
create_symref(newref, symref, logmsg);

return 0;

Expand Down
9 changes: 9 additions & 0 deletions t/t3200-branch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ test_expect_success 'config information was renamed, too' \
"test $(git config branch.s.dummy) = Hello &&
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 \
'git branch -m u v should fail when the reflog for u is a symlink' '
git branch -l u &&
Expand Down

0 comments on commit eca35a2

Please sign in to comment.