Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 997c67d

Browse files
author
Edward Thomson
committed
Merge pull request libgit2#3670 from libgit2/vmg/expand-fixes
Fixes for `gid_odb_expand_ids`
2 parents 084f2c4 + 1bbcb2b commit 997c67d

File tree

2 files changed

+163
-70
lines changed

2 files changed

+163
-70
lines changed

src/odb.c

Lines changed: 154 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,37 @@ static git_cache *odb_cache(git_odb *odb)
4949
return &odb->own_cache;
5050
}
5151

52+
static int odb_otype_fast(git_otype *type_p, git_odb *db, const git_oid *id);
5253
static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth);
5354

55+
static git_otype odb_hardcoded_type(const git_oid *id)
56+
{
57+
static git_oid empty_blob = {{ 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, 0xd6, 0x43, 0x4b, 0x8b,
58+
0x29, 0xae, 0x77, 0x5a, 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91 }};
59+
static git_oid empty_tree = {{ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60,
60+
0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04 }};
61+
62+
if (!git_oid_cmp(id, &empty_blob))
63+
return GIT_OBJ_BLOB;
64+
65+
if (!git_oid_cmp(id, &empty_tree))
66+
return GIT_OBJ_TREE;
67+
68+
return GIT_OBJ_BAD;
69+
}
70+
71+
static int odb_read_hardcoded(git_rawobj *raw, const git_oid *id)
72+
{
73+
git_otype type = odb_hardcoded_type(id);
74+
if (type == GIT_OBJ_BAD)
75+
return -1;
76+
77+
raw->type = type;
78+
raw->len = 0;
79+
raw->data = git__calloc(1, sizeof(uint8_t));
80+
return 0;
81+
}
82+
5483
int git_odb__format_object_header(char *hdr, size_t n, git_off_t obj_len, git_otype obj_type)
5584
{
5685
const char *type_str = git_object_type2string(obj_type);
@@ -747,55 +776,65 @@ int git_odb_expand_ids(
747776
git_odb_expand_id *ids,
748777
size_t count)
749778
{
750-
size_t len, i;
751-
int error;
779+
size_t i;
752780

753781
assert(db && ids);
754782

755783
for (i = 0; i < count; i++) {
756784
git_odb_expand_id *query = &ids[i];
757-
git_oid *actual_id = NULL, tmp;
758-
git_otype query_type = (query->type == GIT_OBJ_ANY) ? 0 : query->type;
759-
git_otype actual_type = 0;
785+
int error = GIT_EAMBIGUOUS;
760786

761-
/* if we were given a full object ID, simply look it up */
762-
if (query->length >= GIT_OID_HEXSZ) {
763-
error = git_odb_read_header(&len, &actual_type, db, &query->id);
787+
if (!query->type)
788+
query->type = GIT_OBJ_ANY;
789+
790+
/* if we have a short OID, expand it first */
791+
if (query->length >= GIT_OID_MINPREFIXLEN && query->length < GIT_OID_HEXSZ) {
792+
git_oid actual_id;
793+
794+
error = odb_exists_prefix_1(&actual_id, db, &query->id, query->length, false);
795+
if (!error) {
796+
git_oid_cpy(&query->id, &actual_id);
797+
query->length = GIT_OID_HEXSZ;
798+
}
764799
}
765800

766-
/* otherwise, resolve the short id to full, then (optionally)
767-
* read the header.
801+
/*
802+
* now we ought to have a 40-char OID, either because we've expanded it
803+
* or because the user passed a full OID. Ensure its type is right.
768804
*/
769-
else if (query->length >= GIT_OID_MINPREFIXLEN) {
770-
error = odb_exists_prefix_1(&tmp,
771-
db, &query->id, query->length, false);
805+
if (query->length >= GIT_OID_HEXSZ) {
806+
git_otype actual_type;
772807

808+
error = odb_otype_fast(&actual_type, db, &query->id);
773809
if (!error) {
774-
actual_id = &tmp;
775-
error = git_odb_read_header(&len, &actual_type, db, &tmp);
810+
if (query->type != GIT_OBJ_ANY && query->type != actual_type)
811+
error = GIT_ENOTFOUND;
812+
else
813+
query->type = actual_type;
776814
}
777815
}
778816

779-
if (error < 0 && error != GIT_ENOTFOUND && error != GIT_EAMBIGUOUS)
780-
break;
781-
782-
if (error == 0 && (query_type == actual_type || !query_type)) {
783-
if (actual_id)
784-
git_oid_cpy(&query->id, actual_id);
817+
switch (error) {
818+
/* no errors, so we've successfully expanded the OID */
819+
case 0:
820+
continue;
785821

786-
query->length = GIT_OID_HEXSZ;
787-
query->type = actual_type;
788-
} else {
822+
/* the object is missing or ambiguous */
823+
case GIT_ENOTFOUND:
824+
case GIT_EAMBIGUOUS:
789825
memset(&query->id, 0, sizeof(git_oid));
790826
query->length = 0;
791827
query->type = 0;
828+
break;
829+
830+
/* something went very wrong with the ODB; bail hard */
831+
default:
832+
return error;
792833
}
793834
}
794835

795-
if (!error)
796-
giterr_clear();
797-
798-
return error;
836+
giterr_clear();
837+
return 0;
799838
}
800839

801840
int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
@@ -811,11 +850,53 @@ int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git
811850
return error;
812851
}
813852

853+
static int odb_read_header_1(
854+
size_t *len_p, git_otype *type_p, git_odb *db,
855+
const git_oid *id, bool only_refreshed)
856+
{
857+
size_t i;
858+
git_otype ht;
859+
bool passthrough = false;
860+
int error;
861+
862+
if (!only_refreshed && (ht = odb_hardcoded_type(id)) != GIT_OBJ_BAD) {
863+
*type_p = ht;
864+
*len_p = 0;
865+
return 0;
866+
}
867+
868+
for (i = 0; i < db->backends.length; ++i) {
869+
backend_internal *internal = git_vector_get(&db->backends, i);
870+
git_odb_backend *b = internal->backend;
871+
872+
if (only_refreshed && !b->refresh)
873+
continue;
874+
875+
if (!b->read_header) {
876+
passthrough = true;
877+
continue;
878+
}
879+
880+
error = b->read_header(len_p, type_p, b, id);
881+
882+
switch (error) {
883+
case GIT_PASSTHROUGH:
884+
passthrough = true;
885+
break;
886+
case GIT_ENOTFOUND:
887+
break;
888+
default:
889+
return error;
890+
}
891+
}
892+
893+
return passthrough ? GIT_PASSTHROUGH : GIT_ENOTFOUND;
894+
}
895+
814896
int git_odb__read_header_or_object(
815897
git_odb_object **out, size_t *len_p, git_otype *type_p,
816898
git_odb *db, const git_oid *id)
817899
{
818-
size_t i;
819900
int error = GIT_ENOTFOUND;
820901
git_odb_object *object;
821902

@@ -829,52 +910,32 @@ int git_odb__read_header_or_object(
829910
}
830911

831912
*out = NULL;
913+
error = odb_read_header_1(len_p, type_p, db, id, false);
832914

833-
for (i = 0; i < db->backends.length && error < 0; ++i) {
834-
backend_internal *internal = git_vector_get(&db->backends, i);
835-
git_odb_backend *b = internal->backend;
915+
if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
916+
error = odb_read_header_1(len_p, type_p, db, id, true);
836917

837-
if (b->read_header != NULL)
838-
error = b->read_header(len_p, type_p, b, id);
839-
}
918+
if (error == GIT_ENOTFOUND)
919+
return git_odb__error_notfound("cannot read header for", id, GIT_OID_HEXSZ);
840920

841-
if (!error || error == GIT_PASSTHROUGH)
921+
/* we found the header; return early */
922+
if (!error)
842923
return 0;
843924

844-
/*
845-
* no backend could read only the header.
846-
* try reading the whole object and freeing the contents
847-
*/
848-
if ((error = git_odb_read(&object, db, id)) < 0)
849-
return error; /* error already set - pass along */
850-
851-
*len_p = object->cached.size;
852-
*type_p = object->cached.type;
853-
*out = object;
854-
855-
return 0;
856-
}
857-
858-
static git_oid empty_blob = {{ 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, 0xd6, 0x43, 0x4b, 0x8b,
859-
0x29, 0xae, 0x77, 0x5a, 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91 }};
860-
static git_oid empty_tree = {{ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60,
861-
0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04 }};
862-
863-
static int hardcoded_objects(git_rawobj *raw, const git_oid *id)
864-
{
865-
if (!git_oid_cmp(id, &empty_blob)) {
866-
raw->type = GIT_OBJ_BLOB;
867-
raw->len = 0;
868-
raw->data = git__calloc(1, sizeof(uint8_t));
869-
return 0;
870-
} else if (!git_oid_cmp(id, &empty_tree)) {
871-
raw->type = GIT_OBJ_TREE;
872-
raw->len = 0;
873-
raw->data = git__calloc(1, sizeof(uint8_t));
874-
return 0;
875-
} else {
876-
return GIT_ENOTFOUND;
925+
if (error == GIT_PASSTHROUGH) {
926+
/*
927+
* no backend has header-reading functionality
928+
* so try using `git_odb_read` instead
929+
*/
930+
error = git_odb_read(&object, db, id);
931+
if (!error) {
932+
*len_p = object->cached.size;
933+
*type_p = object->cached.type;
934+
*out = object;
935+
}
877936
}
937+
938+
return error;
878939
}
879940

880941
static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id,
@@ -885,7 +946,7 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id,
885946
git_odb_object *object;
886947
bool found = false;
887948

888-
if (!hardcoded_objects(&raw, id))
949+
if (!only_refreshed && odb_read_hardcoded(&raw, id) == 0)
889950
found = true;
890951

891952
for (i = 0; i < db->backends.length && !found; ++i) {
@@ -939,6 +1000,29 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
9391000
return error;
9401001
}
9411002

1003+
static int odb_otype_fast(git_otype *type_p, git_odb *db, const git_oid *id)
1004+
{
1005+
git_odb_object *object;
1006+
size_t _unused;
1007+
int error;
1008+
1009+
if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
1010+
*type_p = object->cached.type;
1011+
return 0;
1012+
}
1013+
1014+
error = odb_read_header_1(&_unused, type_p, db, id, false);
1015+
1016+
if (error == GIT_PASSTHROUGH) {
1017+
error = odb_read_1(&object, db, id, false);
1018+
if (!error)
1019+
*type_p = object->cached.type;
1020+
git_odb_object_free(object);
1021+
}
1022+
1023+
return error;
1024+
}
1025+
9421026
static int read_prefix_1(git_odb_object **out, git_odb *db,
9431027
const git_oid *key, size_t len, bool only_refreshed)
9441028
{

tests/odb/mixed.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ struct expand_id_test_data expand_id_test_data[] = {
130130
{ "0ddeaded9", "0ddeaded9502971eefe1e41e34d0e536853ae20f", GIT_OBJ_BLOB },
131131
{ "f00b4e", NULL, GIT_OBJ_ANY },
132132

133+
/* this OID is too short and should be ambiguous! */
134+
{ "f00", NULL, GIT_OBJ_ANY },
135+
133136
/* some full-length object ids */
134137
{ "0000000000000000000000000000000000000000", NULL, GIT_OBJ_ANY },
135138
{
@@ -143,6 +146,12 @@ struct expand_id_test_data expand_id_test_data[] = {
143146
"4d5979b468252190cb572ae758aca36928e8a91e",
144147
GIT_OBJ_TREE
145148
},
149+
150+
/*
151+
* ensure we're not leaking the return error code for the
152+
* last lookup if the last object is invalid
153+
*/
154+
{ "0ddeadedfff", NULL, GIT_OBJ_ANY },
146155
};
147156

148157
static void setup_prefix_query(

0 commit comments

Comments
 (0)