Skip to content

Commit c3b0dec

Browse files
torvaldsgitster
authored andcommitted
Be more careful about updating refs
This makes write_ref_sha1() more careful: it actually checks the SHA1 of the ref it is updating, and refuses to update a ref with an object that it cannot find. Perhaps more importantly, it also refuses to update a branch head with a non-commit object. I don't quite know *how* the stable series maintainers were able to corrupt their repository to have a HEAD that pointed to a tag rather than a commit object, but they did. Which results in a totally broken repository that cannot be cloned or committed on. So make it harder for people to shoot themselves in the foot like that. The test t1400-update-ref.sh is fixed at the same time, as it assumed that the commands involved in the particular test would not care about corrupted repositories whose refs point at nonexistant bogus objects. That assumption does not hold true anymore. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 9f6fe82 commit c3b0dec

File tree

2 files changed

+32
-6
lines changed

2 files changed

+32
-6
lines changed

refs.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,17 +1119,36 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
11191119
return 0;
11201120
}
11211121

1122+
static int is_branch(const char *refname)
1123+
{
1124+
return !strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/");
1125+
}
1126+
11221127
int write_ref_sha1(struct ref_lock *lock,
11231128
const unsigned char *sha1, const char *logmsg)
11241129
{
11251130
static char term = '\n';
1131+
struct object *o;
11261132

11271133
if (!lock)
11281134
return -1;
11291135
if (!lock->force_write && !hashcmp(lock->old_sha1, sha1)) {
11301136
unlock_ref(lock);
11311137
return 0;
11321138
}
1139+
o = parse_object(sha1);
1140+
if (!o) {
1141+
error("Trying to write ref %s with nonexistant object %s",
1142+
lock->ref_name, sha1_to_hex(sha1));
1143+
unlock_ref(lock);
1144+
return -1;
1145+
}
1146+
if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
1147+
error("Trying to write non-commit object %s to branch %s",
1148+
sha1_to_hex(sha1), lock->ref_name);
1149+
unlock_ref(lock);
1150+
return -1;
1151+
}
11331152
if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
11341153
write_in_full(lock->lock_fd, &term, 1) != 1
11351154
|| close(lock->lock_fd) < 0) {

t/t1400-update-ref.sh

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,19 @@ test_description='Test git update-ref and basic ref logging'
77
. ./test-lib.sh
88

99
Z=0000000000000000000000000000000000000000
10-
A=1111111111111111111111111111111111111111
11-
B=2222222222222222222222222222222222222222
12-
C=3333333333333333333333333333333333333333
13-
D=4444444444444444444444444444444444444444
14-
E=5555555555555555555555555555555555555555
15-
F=6666666666666666666666666666666666666666
10+
11+
test_expect_success setup '
12+
13+
for name in A B C D E F
14+
do
15+
test_tick &&
16+
T=$(git write-tree) &&
17+
sha1=$(echo $name | git commit-tree $T) &&
18+
eval $name=$sha1
19+
done
20+
21+
'
22+
1623
m=refs/heads/master
1724
n_dir=refs/heads/gu
1825
n=$n_dir/fixes

0 commit comments

Comments
 (0)