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>
Miklos Vajna committed
Oct 26, 2008 at 03:33 UTC
eca35a25a92a1ad725af2a549fc9158488c4cc43
10 files changed
+55
-31
builtin-branch.c
+1
-1
@@ -160,7 +160,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
160
continue;
161
}
162
163
- if (delete_ref(name, sha1)) {
163
+ if (delete_ref(name, sha1, 0)) {
164
error("Error deleting %sbranch '%s'", remote,
165
argv[i]);
166
ret = 1;
builtin-remote.c
+2
-2
@@ -340,7 +340,7 @@ static int remove_branches(struct string_list *branches)
340
const char *refname = item->string;
341
unsigned char *sha1 = item->util;
342
343
- if (delete_ref(refname, sha1))
343
+ if (delete_ref(refname, sha1, 0))
344
result |= error("Could not remove branch %s", refname);
345
}
346
return result;
@@ -570,7 +570,7 @@ static int prune(int argc, const char **argv)
570
const char *refname = states.stale.items[i].util;
571
572
if (!dry_run)
573
- result |= delete_ref(refname, NULL);
573
+ result |= delete_ref(refname, NULL, 0);
574
575
printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
576
abbrev_ref(refname, "refs/remotes/"));
builtin-reset.c
+1
-1
@@ -279,7 +279,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
279
update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
280
}
281
else if (old_orig)
282
- delete_ref("ORIG_HEAD", old_orig);
282
+ delete_ref("ORIG_HEAD", old_orig, 0);
283
prepend_reflog_action("updating HEAD", msg, sizeof(msg));
284
update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR);
285
builtin-send-pack.c
+1
-1
@@ -226,7 +226,7 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
226
if (args.verbose)
227
fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
228
if (ref->deletion) {
229
- delete_ref(rs.dst, NULL);
229
+ delete_ref(rs.dst, NULL, 0);
230
} else
231
update_ref("update by push", rs.dst,
232
ref->new_sha1, NULL, 0, 0);
builtin-tag.c
+1
-1
@@ -125,7 +125,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
125
static int delete_tag(const char *name, const char *ref,
126
const unsigned char *sha1)
127
{
128
- if (delete_ref(ref, sha1))
128
+ if (delete_ref(ref, sha1, 0))
129
return 1;
130
printf("Deleted tag '%s'\n", name);
131
return 0;
builtin-update-ref.c
+1
-1
@@ -48,7 +48,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
48
die("%s: not a valid old SHA1", oldval);
49
50
if (delete)
51
- return delete_ref(refname, oldval ? oldsha1 : NULL);
51
+ return delete_ref(refname, oldval ? oldsha1 : NULL, 0);
52
else
53
return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
54
no_deref ? REF_NODEREF : 0, DIE_ON_ERR);
cache.h
+1
-1
@@ -420,7 +420,7 @@ extern int commit_locked_index(struct lock_file *);
420
extern void set_alternate_index_output(const char *);
421
extern int close_lock_file(struct lock_file *);
422
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);
424
425
/* Environment bits from configuration mechanism */
426
extern int trust_executable_bit;
receive-pack.c
+1
-1
@@ -222,7 +222,7 @@ static const char *update(struct command *cmd)
222
warning ("Allowing deletion of corrupt ref.");
223
old_sha1 = NULL;
224
}
225
- if (delete_ref(name, old_sha1)) {
225
+ if (delete_ref(name, old_sha1, 0)) {
226
error("failed to delete %s", name);
227
return "failed to delete";
228
}
refs.c
+37
-22
@@ -912,25 +912,33 @@ static int repack_without_ref(const char *refname)
912
return commit_lock_file(&packlock);
913
}
914
915
-int delete_ref(const char *refname, const unsigned char *sha1)
915
+int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
916
{
917
struct ref_lock *lock;
918
- int err, i, ret = 0, flag = 0;
918
+ int err, i = 0, ret = 0, flag = 0;
919
920
lock = lock_ref_sha1_basic(refname, sha1, 0, &flag);
921
if (!lock)
922
return 1;
923
if (!(flag & REF_ISPACKED)) {
924
/* 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);
935
if (err && errno != ENOENT) {
936
ret = 1;
937
error("unlink(%s) failed: %s",
931
- lock->lk->filename, strerror(errno));
938
+ path, strerror(errno));
939
}
933
- lock->lk->filename[i] = '.';
940
+ if (!(delopt & REF_NODEREF))
941
+ lock->lk->filename[i] = '.';
942
}
943
/* removing the loose one could have resurrected an earlier
944
* 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)
963
struct ref_lock *lock;
964
struct stat loginfo;
965
int log = !lstat(git_path("logs/%s", oldref), &loginfo);
966
+ const char *symref = NULL;
967
+ int is_symref = 0;
968
969
if (S_ISLNK(loginfo.st_mode))
970
return error("reflog for %s is a symlink", oldref);
971
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)
976
return error("refname %s not found", oldref);
977
978
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)
992
return error("unable to move logfile logs/%s to tmp-renamed-log: %s",
993
oldref, strerror(errno));
994
982
- if (delete_ref(oldref, orig_sha1)) {
995
+ if (delete_ref(oldref, orig_sha1, REF_NODEREF)) {
996
error("unable to delete old %s", oldref);
997
goto rollback;
998
}
999
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)) {
1001
if (errno==EISDIR) {
1002
if (remove_empty_directories(git_path("%s", newref))) {
1003
error("Directory not empty: %s", newref);
@@ -1022,18 +1035,20 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
1035
}
1036
logmoved = log;
1037
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);
1052
1053
return 0;
1054
t/t3200-branch.sh
+9
@@ -112,6 +112,15 @@ test_expect_success 'config information was renamed, too' \
112
"test $(git config branch.s.dummy) = Hello &&
113
test_must_fail git config branch.s/s/dummy"
114
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
+
124
test_expect_success \
125
'git branch -m u v should fail when the reflog for u is a symlink' '
126
git branch -l u &&