Git fork

Merge branch 'jp/get-ref-dir-unsorted'

* jp/get-ref-dir-unsorted:
refs.c: free duplicate entries in the ref array instead of leaking them
refs.c: abort ref search if ref array is empty
refs.c: ensure struct whose member may be passed to realloc is initialized
refs: Use binary search to lookup refs faster
Don't sort ref_list too early

Conflicts:
refs.c

+152 -199
+151 -198
refs.c
··· 8 8 #define REF_KNOWS_PEELED 04 9 9 #define REF_BROKEN 010 10 10 11 - struct ref_list { 12 - struct ref_list *next; 11 + struct ref_entry { 13 12 unsigned char flag; /* ISSYMREF? ISPACKED? */ 14 13 unsigned char sha1[20]; 15 14 unsigned char peeled[20]; 16 15 char name[FLEX_ARRAY]; 16 + }; 17 + 18 + struct ref_array { 19 + int nr, alloc; 20 + struct ref_entry **refs; 17 21 }; 18 22 19 23 static const char *parse_ref_line(char *line, unsigned char *sha1) ··· 44 48 return line; 45 49 } 46 50 47 - static struct ref_list *add_ref(const char *name, const unsigned char *sha1, 48 - int flag, struct ref_list *list, 49 - struct ref_list **new_entry) 51 + static void add_ref(const char *name, const unsigned char *sha1, 52 + int flag, struct ref_array *refs, 53 + struct ref_entry **new_entry) 50 54 { 51 55 int len; 52 - struct ref_list *entry; 56 + struct ref_entry *entry; 53 57 54 58 /* Allocate it and add it in.. */ 55 59 len = strlen(name) + 1; 56 - entry = xmalloc(sizeof(struct ref_list) + len); 60 + entry = xmalloc(sizeof(struct ref_entry) + len); 57 61 hashcpy(entry->sha1, sha1); 58 62 hashclr(entry->peeled); 59 63 if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT)) 60 64 die("Reference has invalid format: '%s'", name); 61 65 memcpy(entry->name, name, len); 62 66 entry->flag = flag; 63 - entry->next = list; 64 67 if (new_entry) 65 68 *new_entry = entry; 66 - return entry; 69 + ALLOC_GROW(refs->refs, refs->nr + 1, refs->alloc); 70 + refs->refs[refs->nr++] = entry; 67 71 } 68 72 69 - /* merge sort the ref list */ 70 - static struct ref_list *sort_ref_list(struct ref_list *list) 73 + static int ref_entry_cmp(const void *a, const void *b) 71 74 { 72 - int psize, qsize, last_merge_count, cmp; 73 - struct ref_list *p, *q, *l, *e; 74 - struct ref_list *new_list = list; 75 - int k = 1; 76 - int merge_count = 0; 75 + struct ref_entry *one = *(struct ref_entry **)a; 76 + struct ref_entry *two = *(struct ref_entry **)b; 77 + return strcmp(one->name, two->name); 78 + } 77 79 78 - if (!list) 79 - return list; 80 + static void sort_ref_array(struct ref_array *array) 81 + { 82 + int i = 0, j = 1; 80 83 81 - do { 82 - last_merge_count = merge_count; 83 - merge_count = 0; 84 + /* Nothing to sort unless there are at least two entries */ 85 + if (array->nr < 2) 86 + return; 84 87 85 - psize = 0; 88 + qsort(array->refs, array->nr, sizeof(*array->refs), ref_entry_cmp); 86 89 87 - p = new_list; 88 - q = new_list; 89 - new_list = NULL; 90 - l = NULL; 90 + /* Remove any duplicates from the ref_array */ 91 + for (; j < array->nr; j++) { 92 + struct ref_entry *a = array->refs[i]; 93 + struct ref_entry *b = array->refs[j]; 94 + if (!strcmp(a->name, b->name)) { 95 + if (hashcmp(a->sha1, b->sha1)) 96 + die("Duplicated ref, and SHA1s don't match: %s", 97 + a->name); 98 + warning("Duplicated ref: %s", a->name); 99 + free(b); 100 + continue; 101 + } 102 + i++; 103 + array->refs[i] = array->refs[j]; 104 + } 105 + array->nr = i + 1; 106 + } 91 107 92 - while (p) { 93 - merge_count++; 108 + static struct ref_entry *search_ref_array(struct ref_array *array, const char *name) 109 + { 110 + struct ref_entry *e, **r; 111 + int len; 94 112 95 - while (psize < k && q->next) { 96 - q = q->next; 97 - psize++; 98 - } 99 - qsize = k; 113 + if (name == NULL) 114 + return NULL; 100 115 101 - while ((psize > 0) || (qsize > 0 && q)) { 102 - if (qsize == 0 || !q) { 103 - e = p; 104 - p = p->next; 105 - psize--; 106 - } else if (psize == 0) { 107 - e = q; 108 - q = q->next; 109 - qsize--; 110 - } else { 111 - cmp = strcmp(q->name, p->name); 112 - if (cmp < 0) { 113 - e = q; 114 - q = q->next; 115 - qsize--; 116 - } else if (cmp > 0) { 117 - e = p; 118 - p = p->next; 119 - psize--; 120 - } else { 121 - if (hashcmp(q->sha1, p->sha1)) 122 - die("Duplicated ref, and SHA1s don't match: %s", 123 - q->name); 124 - warning("Duplicated ref: %s", q->name); 125 - e = q; 126 - q = q->next; 127 - qsize--; 128 - free(e); 129 - e = p; 130 - p = p->next; 131 - psize--; 132 - } 133 - } 116 + if (!array->nr) 117 + return NULL; 134 118 135 - e->next = NULL; 119 + len = strlen(name) + 1; 120 + e = xmalloc(sizeof(struct ref_entry) + len); 121 + memcpy(e->name, name, len); 136 122 137 - if (l) 138 - l->next = e; 139 - if (!new_list) 140 - new_list = e; 141 - l = e; 142 - } 123 + r = bsearch(&e, array->refs, array->nr, sizeof(*array->refs), ref_entry_cmp); 143 124 144 - p = q; 145 - }; 125 + free(e); 146 126 147 - k = k * 2; 148 - } while ((last_merge_count != merge_count) || (last_merge_count != 1)); 127 + if (r == NULL) 128 + return NULL; 149 129 150 - return new_list; 130 + return *r; 151 131 } 152 132 153 133 /* ··· 158 138 struct cached_refs *next; 159 139 char did_loose; 160 140 char did_packed; 161 - struct ref_list *loose; 162 - struct ref_list *packed; 141 + struct ref_array loose; 142 + struct ref_array packed; 163 143 /* The submodule name, or "" for the main repo. */ 164 144 char name[FLEX_ARRAY]; 165 145 } *cached_refs; 166 146 167 - static struct ref_list *current_ref; 147 + static struct ref_entry *current_ref; 168 148 169 - static struct ref_list *extra_refs; 149 + static struct ref_array extra_refs; 170 150 171 - static void free_ref_list(struct ref_list *list) 151 + static void free_ref_array(struct ref_array *array) 172 152 { 173 - struct ref_list *next; 174 - for ( ; list; list = next) { 175 - next = list->next; 176 - free(list); 177 - } 153 + int i; 154 + for (i = 0; i < array->nr; i++) 155 + free(array->refs[i]); 156 + free(array->refs); 157 + array->nr = array->alloc = 0; 158 + array->refs = NULL; 178 159 } 179 160 180 161 static void clear_cached_refs(struct cached_refs *ca) 181 162 { 182 - if (ca->did_loose && ca->loose) 183 - free_ref_list(ca->loose); 184 - if (ca->did_packed && ca->packed) 185 - free_ref_list(ca->packed); 186 - ca->loose = ca->packed = NULL; 163 + if (ca->did_loose) 164 + free_ref_array(&ca->loose); 165 + if (ca->did_packed) 166 + free_ref_array(&ca->packed); 187 167 ca->did_loose = ca->did_packed = 0; 188 168 } 189 169 ··· 194 174 if (!submodule) 195 175 submodule = ""; 196 176 len = strlen(submodule) + 1; 197 - refs = xmalloc(sizeof(struct cached_refs) + len); 198 - refs->next = NULL; 199 - refs->did_loose = refs->did_packed = 0; 200 - refs->loose = refs->packed = NULL; 177 + refs = xcalloc(1, sizeof(struct cached_refs) + len); 201 178 memcpy(refs->name, submodule, len); 202 179 return refs; 203 180 } ··· 234 211 } 235 212 } 236 213 237 - static struct ref_list *read_packed_refs(FILE *f) 214 + static void read_packed_refs(FILE *f, struct ref_array *array) 238 215 { 239 - struct ref_list *list = NULL; 240 - struct ref_list *last = NULL; 216 + struct ref_entry *last = NULL; 241 217 char refline[PATH_MAX]; 242 218 int flag = REF_ISPACKED; 243 219 ··· 256 232 257 233 name = parse_ref_line(refline, sha1); 258 234 if (name) { 259 - list = add_ref(name, sha1, flag, list, &last); 235 + add_ref(name, sha1, flag, array, &last); 260 236 continue; 261 237 } 262 238 if (last && ··· 266 242 !get_sha1_hex(refline + 1, sha1)) 267 243 hashcpy(last->peeled, sha1); 268 244 } 269 - return sort_ref_list(list); 245 + sort_ref_array(array); 270 246 } 271 247 272 248 void add_extra_ref(const char *name, const unsigned char *sha1, int flag) 273 249 { 274 - extra_refs = add_ref(name, sha1, flag, extra_refs, NULL); 250 + add_ref(name, sha1, flag, &extra_refs, NULL); 275 251 } 276 252 277 253 void clear_extra_refs(void) 278 254 { 279 - free_ref_list(extra_refs); 280 - extra_refs = NULL; 255 + free_ref_array(&extra_refs); 281 256 } 282 257 283 - static struct ref_list *get_packed_refs(const char *submodule) 258 + static struct ref_array *get_packed_refs(const char *submodule) 284 259 { 285 260 struct cached_refs *refs = get_cached_refs(submodule); 286 261 ··· 293 268 else 294 269 packed_refs_file = git_path("packed-refs"); 295 270 f = fopen(packed_refs_file, "r"); 296 - refs->packed = NULL; 297 271 if (f) { 298 - refs->packed = read_packed_refs(f); 272 + read_packed_refs(f, &refs->packed); 299 273 fclose(f); 300 274 } 301 275 refs->did_packed = 1; 302 276 } 303 - return refs->packed; 277 + return &refs->packed; 304 278 } 305 279 306 - static struct ref_list *get_ref_dir(const char *submodule, const char *base, 307 - struct ref_list *list) 280 + static void get_ref_dir(const char *submodule, const char *base, 281 + struct ref_array *array) 308 282 { 309 283 DIR *dir; 310 284 const char *path; ··· 347 321 if (stat(refdir, &st) < 0) 348 322 continue; 349 323 if (S_ISDIR(st.st_mode)) { 350 - list = get_ref_dir(submodule, ref, list); 324 + get_ref_dir(submodule, ref, array); 351 325 continue; 352 326 } 353 327 if (submodule) { ··· 362 336 hashclr(sha1); 363 337 flag |= REF_BROKEN; 364 338 } 365 - list = add_ref(ref, sha1, flag, list, NULL); 339 + add_ref(ref, sha1, flag, array, NULL); 366 340 } 367 341 free(ref); 368 342 closedir(dir); 369 343 } 370 - return sort_ref_list(list); 371 344 } 372 345 373 346 struct warn_if_dangling_data { ··· 404 377 for_each_rawref(warn_if_dangling_symref, &data); 405 378 } 406 379 407 - static struct ref_list *get_loose_refs(const char *submodule) 380 + static struct ref_array *get_loose_refs(const char *submodule) 408 381 { 409 382 struct cached_refs *refs = get_cached_refs(submodule); 410 383 411 384 if (!refs->did_loose) { 412 - refs->loose = get_ref_dir(submodule, "refs", NULL); 385 + get_ref_dir(submodule, "refs", &refs->loose); 386 + sort_ref_array(&refs->loose); 413 387 refs->did_loose = 1; 414 388 } 415 - return refs->loose; 389 + return &refs->loose; 416 390 } 417 391 418 392 /* We allow "recursive" symbolic refs. Only within reason, though */ ··· 421 395 422 396 static int resolve_gitlink_packed_ref(char *name, int pathlen, const char *refname, unsigned char *result) 423 397 { 424 - FILE *f; 425 - struct ref_list *packed_refs; 426 - struct ref_list *ref; 427 - int retval; 398 + int retval = -1; 399 + struct ref_entry *ref; 400 + struct ref_array *array = get_packed_refs(name); 428 401 429 - strcpy(name + pathlen, "packed-refs"); 430 - f = fopen(name, "r"); 431 - if (!f) 432 - return -1; 433 - packed_refs = read_packed_refs(f); 434 - fclose(f); 435 - ref = packed_refs; 436 - retval = -1; 437 - while (ref) { 438 - if (!strcmp(ref->name, refname)) { 439 - retval = 0; 440 - memcpy(result, ref->sha1, 20); 441 - break; 442 - } 443 - ref = ref->next; 402 + ref = search_ref_array(array, refname); 403 + if (ref != NULL) { 404 + memcpy(result, ref->sha1, 20); 405 + retval = 0; 444 406 } 445 - free_ref_list(packed_refs); 446 407 return retval; 447 408 } 448 409 ··· 515 476 */ 516 477 static int get_packed_ref(const char *ref, unsigned char *sha1) 517 478 { 518 - struct ref_list *list = get_packed_refs(NULL); 519 - while (list) { 520 - if (!strcmp(ref, list->name)) { 521 - hashcpy(sha1, list->sha1); 522 - return 0; 523 - } 524 - list = list->next; 479 + struct ref_array *packed = get_packed_refs(NULL); 480 + struct ref_entry *entry = search_ref_array(packed, ref); 481 + if (entry) { 482 + hashcpy(sha1, entry->sha1); 483 + return 0; 525 484 } 526 485 return -1; 527 486 } ··· 649 608 650 609 #define DO_FOR_EACH_INCLUDE_BROKEN 01 651 610 static int do_one_ref(const char *base, each_ref_fn fn, int trim, 652 - int flags, void *cb_data, struct ref_list *entry) 611 + int flags, void *cb_data, struct ref_entry *entry) 653 612 { 654 613 if (prefixcmp(entry->name, base)) 655 614 return 0; ··· 695 654 return -1; 696 655 697 656 if ((flag & REF_ISPACKED)) { 698 - struct ref_list *list = get_packed_refs(NULL); 657 + struct ref_array *array = get_packed_refs(NULL); 658 + struct ref_entry *r = search_ref_array(array, ref); 699 659 700 - while (list) { 701 - if (!strcmp(list->name, ref)) { 702 - if (list->flag & REF_KNOWS_PEELED) { 703 - hashcpy(sha1, list->peeled); 704 - return 0; 705 - } 706 - /* older pack-refs did not leave peeled ones */ 707 - break; 708 - } 709 - list = list->next; 660 + if (r != NULL && r->flag & REF_KNOWS_PEELED) { 661 + hashcpy(sha1, r->peeled); 662 + return 0; 710 663 } 711 664 } 712 665 ··· 725 678 static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn, 726 679 int trim, int flags, void *cb_data) 727 680 { 728 - int retval = 0; 729 - struct ref_list *packed = get_packed_refs(submodule); 730 - struct ref_list *loose = get_loose_refs(submodule); 681 + int retval = 0, i, p = 0, l = 0; 682 + struct ref_array *packed = get_packed_refs(submodule); 683 + struct ref_array *loose = get_loose_refs(submodule); 731 684 732 - struct ref_list *extra; 685 + struct ref_array *extra = &extra_refs; 733 686 734 - for (extra = extra_refs; extra; extra = extra->next) 735 - retval = do_one_ref(base, fn, trim, flags, cb_data, extra); 687 + for (i = 0; i < extra->nr; i++) 688 + retval = do_one_ref(base, fn, trim, flags, cb_data, extra->refs[i]); 736 689 737 - while (packed && loose) { 738 - struct ref_list *entry; 739 - int cmp = strcmp(packed->name, loose->name); 690 + while (p < packed->nr && l < loose->nr) { 691 + struct ref_entry *entry; 692 + int cmp = strcmp(packed->refs[p]->name, loose->refs[l]->name); 740 693 if (!cmp) { 741 - packed = packed->next; 694 + p++; 742 695 continue; 743 696 } 744 697 if (cmp > 0) { 745 - entry = loose; 746 - loose = loose->next; 698 + entry = loose->refs[l++]; 747 699 } else { 748 - entry = packed; 749 - packed = packed->next; 700 + entry = packed->refs[p++]; 750 701 } 751 702 retval = do_one_ref(base, fn, trim, flags, cb_data, entry); 752 703 if (retval) 753 704 goto end_each; 754 705 } 755 706 756 - for (packed = packed ? packed : loose; packed; packed = packed->next) { 757 - retval = do_one_ref(base, fn, trim, flags, cb_data, packed); 707 + if (l < loose->nr) { 708 + p = l; 709 + packed = loose; 710 + } 711 + 712 + for (; p < packed->nr; p++) { 713 + retval = do_one_ref(base, fn, trim, flags, cb_data, packed->refs[p]); 758 714 if (retval) 759 715 goto end_each; 760 716 } ··· 1087 1043 } 1088 1044 1089 1045 static int is_refname_available(const char *ref, const char *oldref, 1090 - struct ref_list *list, int quiet) 1046 + struct ref_array *array, int quiet) 1091 1047 { 1092 - int namlen = strlen(ref); /* e.g. 'foo/bar' */ 1093 - while (list) { 1094 - /* list->name could be 'foo' or 'foo/bar/baz' */ 1095 - if (!oldref || strcmp(oldref, list->name)) { 1096 - int len = strlen(list->name); 1048 + int i, namlen = strlen(ref); /* e.g. 'foo/bar' */ 1049 + for (i = 0; i < array->nr; i++ ) { 1050 + struct ref_entry *entry = array->refs[i]; 1051 + /* entry->name could be 'foo' or 'foo/bar/baz' */ 1052 + if (!oldref || strcmp(oldref, entry->name)) { 1053 + int len = strlen(entry->name); 1097 1054 int cmplen = (namlen < len) ? namlen : len; 1098 - const char *lead = (namlen < len) ? list->name : ref; 1099 - if (!strncmp(ref, list->name, cmplen) && 1055 + const char *lead = (namlen < len) ? entry->name : ref; 1056 + if (!strncmp(ref, entry->name, cmplen) && 1100 1057 lead[cmplen] == '/') { 1101 1058 if (!quiet) 1102 1059 error("'%s' exists; cannot create '%s'", 1103 - list->name, ref); 1060 + entry->name, ref); 1104 1061 return 0; 1105 1062 } 1106 1063 } 1107 - list = list->next; 1108 1064 } 1109 1065 return 1; 1110 1066 } ··· 1207 1163 1208 1164 static int repack_without_ref(const char *refname) 1209 1165 { 1210 - struct ref_list *list, *packed_ref_list; 1211 - int fd; 1212 - int found = 0; 1166 + struct ref_array *packed; 1167 + struct ref_entry *ref; 1168 + int fd, i; 1213 1169 1214 - packed_ref_list = get_packed_refs(NULL); 1215 - for (list = packed_ref_list; list; list = list->next) { 1216 - if (!strcmp(refname, list->name)) { 1217 - found = 1; 1218 - break; 1219 - } 1220 - } 1221 - if (!found) 1170 + packed = get_packed_refs(NULL); 1171 + ref = search_ref_array(packed, refname); 1172 + if (ref == NULL) 1222 1173 return 0; 1223 1174 fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0); 1224 1175 if (fd < 0) { ··· 1226 1177 return error("cannot delete '%s' from packed refs", refname); 1227 1178 } 1228 1179 1229 - for (list = packed_ref_list; list; list = list->next) { 1180 + for (i = 0; i < packed->nr; i++) { 1230 1181 char line[PATH_MAX + 100]; 1231 1182 int len; 1232 1183 1233 - if (!strcmp(refname, list->name)) 1184 + ref = packed->refs[i]; 1185 + 1186 + if (!strcmp(refname, ref->name)) 1234 1187 continue; 1235 1188 len = snprintf(line, sizeof(line), "%s %s\n", 1236 - sha1_to_hex(list->sha1), list->name); 1189 + sha1_to_hex(ref->sha1), ref->name); 1237 1190 /* this should not happen but just being defensive */ 1238 1191 if (len > sizeof(line)) 1239 - die("too long a refname '%s'", list->name); 1192 + die("too long a refname '%s'", ref->name); 1240 1193 write_or_die(fd, line, len); 1241 1194 } 1242 1195 return commit_lock_file(&packlock); ··· 1931 1884 return 0; 1932 1885 } 1933 1886 1934 - int ref_exists(char *refname) 1887 + int ref_exists(const char *refname) 1935 1888 { 1936 1889 unsigned char sha1[20]; 1937 1890 return !!resolve_ref(refname, sha1, 1, NULL);
+1 -1
refs.h
··· 57 57 */ 58 58 extern void add_extra_ref(const char *refname, const unsigned char *sha1, int flags); 59 59 extern void clear_extra_refs(void); 60 - extern int ref_exists(char *); 60 + extern int ref_exists(const char *); 61 61 62 62 extern int peel_ref(const char *, unsigned char *); 63 63