Skip to content

Commit eca35a2

Browse files
Miklos Vajnagitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 031e6c8 commit eca35a2

10 files changed

+55
-31
lines changed

builtin-branch.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
160160
continue;
161161
}
162162

163-
if (delete_ref(name, sha1)) {
163+
if (delete_ref(name, sha1, 0)) {
164164
error("Error deleting %sbranch '%s'", remote,
165165
argv[i]);
166166
ret = 1;

builtin-remote.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ static int remove_branches(struct string_list *branches)
340340
const char *refname = item->string;
341341
unsigned char *sha1 = item->util;
342342

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

572572
if (!dry_run)
573-
result |= delete_ref(refname, NULL);
573+
result |= delete_ref(refname, NULL, 0);
574574

575575
printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
576576
abbrev_ref(refname, "refs/remotes/"));

builtin-reset.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
279279
update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
280280
}
281281
else if (old_orig)
282-
delete_ref("ORIG_HEAD", old_orig);
282+
delete_ref("ORIG_HEAD", old_orig, 0);
283283
prepend_reflog_action("updating HEAD", msg, sizeof(msg));
284284
update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR);
285285

builtin-send-pack.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
226226
if (args.verbose)
227227
fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
228228
if (ref->deletion) {
229-
delete_ref(rs.dst, NULL);
229+
delete_ref(rs.dst, NULL, 0);
230230
} else
231231
update_ref("update by push", rs.dst,
232232
ref->new_sha1, NULL, 0, 0);

builtin-tag.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
125125
static int delete_tag(const char *name, const char *ref,
126126
const unsigned char *sha1)
127127
{
128-
if (delete_ref(ref, sha1))
128+
if (delete_ref(ref, sha1, 0))
129129
return 1;
130130
printf("Deleted tag '%s'\n", name);
131131
return 0;

builtin-update-ref.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
4848
die("%s: not a valid old SHA1", oldval);
4949

5050
if (delete)
51-
return delete_ref(refname, oldval ? oldsha1 : NULL);
51+
return delete_ref(refname, oldval ? oldsha1 : NULL, 0);
5252
else
5353
return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
5454
no_deref ? REF_NODEREF : 0, DIE_ON_ERR);

cache.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ extern int commit_locked_index(struct lock_file *);
420420
extern void set_alternate_index_output(const char *);
421421
extern int close_lock_file(struct lock_file *);
422422
extern void rollback_lock_file(struct lock_file *);
423-
extern int delete_ref(const char *, const unsigned char *sha1);
423+
extern int delete_ref(const char *, const unsigned char *sha1, int delopt);
424424

425425
/* Environment bits from configuration mechanism */
426426
extern int trust_executable_bit;

receive-pack.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ static const char *update(struct command *cmd)
222222
warning ("Allowing deletion of corrupt ref.");
223223
old_sha1 = NULL;
224224
}
225-
if (delete_ref(name, old_sha1)) {
225+
if (delete_ref(name, old_sha1, 0)) {
226226
error("failed to delete %s", name);
227227
return "failed to delete";
228228
}

refs.c

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -912,25 +912,33 @@ static int repack_without_ref(const char *refname)
912912
return commit_lock_file(&packlock);
913913
}
914914

915-
int delete_ref(const char *refname, const unsigned char *sha1)
915+
int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
916916
{
917917
struct ref_lock *lock;
918-
int err, i, ret = 0, flag = 0;
918+
int err, i = 0, ret = 0, flag = 0;
919919

920920
lock = lock_ref_sha1_basic(refname, sha1, 0, &flag);
921921
if (!lock)
922922
return 1;
923923
if (!(flag & REF_ISPACKED)) {
924924
/* loose */
925-
i = strlen(lock->lk->filename) - 5; /* .lock */
926-
lock->lk->filename[i] = 0;
927-
err = unlink(lock->lk->filename);
925+
const char *path;
926+
927+
if (!(delopt & REF_NODEREF)) {
928+
i = strlen(lock->lk->filename) - 5; /* .lock */
929+
lock->lk->filename[i] = 0;
930+
path = lock->lk->filename;
931+
} else {
932+
path = git_path(refname);
933+
}
934+
err = unlink(path);
928935
if (err && errno != ENOENT) {
929936
ret = 1;
930937
error("unlink(%s) failed: %s",
931-
lock->lk->filename, strerror(errno));
938+
path, strerror(errno));
932939
}
933-
lock->lk->filename[i] = '.';
940+
if (!(delopt & REF_NODEREF))
941+
lock->lk->filename[i] = '.';
934942
}
935943
/* removing the loose one could have resurrected an earlier
936944
* 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)
955963
struct ref_lock *lock;
956964
struct stat loginfo;
957965
int log = !lstat(git_path("logs/%s", oldref), &loginfo);
966+
const char *symref = NULL;
967+
int is_symref = 0;
958968

959969
if (S_ISLNK(loginfo.st_mode))
960970
return error("reflog for %s is a symlink", oldref);
961971

962-
if (!resolve_ref(oldref, orig_sha1, 1, &flag))
972+
symref = resolve_ref(oldref, orig_sha1, 1, &flag);
973+
if (flag & REF_ISSYMREF)
974+
is_symref = 1;
975+
if (!symref)
963976
return error("refname %s not found", oldref);
964977

965978
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)
979992
return error("unable to move logfile logs/%s to tmp-renamed-log: %s",
980993
oldref, strerror(errno));
981994

982-
if (delete_ref(oldref, orig_sha1)) {
995+
if (delete_ref(oldref, orig_sha1, REF_NODEREF)) {
983996
error("unable to delete old %s", oldref);
984997
goto rollback;
985998
}
986999

987-
if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1)) {
1000+
if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1, REF_NODEREF)) {
9881001
if (errno==EISDIR) {
9891002
if (remove_empty_directories(git_path("%s", newref))) {
9901003
error("Directory not empty: %s", newref);
@@ -1022,18 +1035,20 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
10221035
}
10231036
logmoved = log;
10241037

1025-
lock = lock_ref_sha1_basic(newref, NULL, 0, NULL);
1026-
if (!lock) {
1027-
error("unable to lock %s for update", newref);
1028-
goto rollback;
1029-
}
1030-
1031-
lock->force_write = 1;
1032-
hashcpy(lock->old_sha1, orig_sha1);
1033-
if (write_ref_sha1(lock, orig_sha1, logmsg)) {
1034-
error("unable to write current sha1 into %s", newref);
1035-
goto rollback;
1036-
}
1038+
if (!is_symref) {
1039+
lock = lock_ref_sha1_basic(newref, NULL, 0, NULL);
1040+
if (!lock) {
1041+
error("unable to lock %s for update", newref);
1042+
goto rollback;
1043+
}
1044+
lock->force_write = 1;
1045+
hashcpy(lock->old_sha1, orig_sha1);
1046+
if (write_ref_sha1(lock, orig_sha1, logmsg)) {
1047+
error("unable to write current sha1 into %s", newref);
1048+
goto rollback;
1049+
}
1050+
} else
1051+
create_symref(newref, symref, logmsg);
10371052

10381053
return 0;
10391054

t/t3200-branch.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,15 @@ test_expect_success 'config information was renamed, too' \
112112
"test $(git config branch.s.dummy) = Hello &&
113113
test_must_fail git config branch.s/s/dummy"
114114

115+
test_expect_success 'renaming a symref' \
116+
'
117+
git symbolic-ref refs/heads/master2 refs/heads/master &&
118+
git branch -m master2 master3 &&
119+
git symbolic-ref refs/heads/master3 &&
120+
test -f .git/refs/heads/master &&
121+
! test -f .git/refs/heads/master2
122+
'
123+
115124
test_expect_success \
116125
'git branch -m u v should fail when the reflog for u is a symlink' '
117126
git branch -l u &&

0 commit comments

Comments
 (0)