]> git.proxmox.com Git - libgit2.git/blame - src/refs.c
config: Fix unmatched parameters in docs
[libgit2.git] / src / refs.c
CommitLineData
9282e921 1/*
2 * This file is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2,
4 * as published by the Free Software Foundation.
5 *
6 * In addition to the permissions in the GNU General Public License,
7 * the authors give you unlimited permission to link the compiled
8 * version of this file into combinations with other programs,
9 * and to distribute those combinations without any restriction
10 * coming from the use of this file. (The General Public License
11 * restrictions do apply in other respects; for example, they cover
12 * modification of the file, and distribution when not linked into
13 * a combined executable.)
14 *
15 * This file is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; see the file COPYING. If not, write to
22 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26#include "refs.h"
27#include "hash.h"
28#include "repository.h"
29#include "fileops.h"
30
87d3acf4
VM
31#include <git2/tag.h>
32#include <git2/object.h>
33
9282e921 34#define MAX_NESTING_LEVEL 5
35
86194b24
VM
36typedef struct {
37 git_reference ref;
38 git_oid oid;
39 git_oid peel_target;
40} reference_oid;
41
42typedef struct {
43 git_reference ref;
44 char *target;
45} reference_symbolic;
46
9282e921 47static const int default_table_size = 32;
48
fc658755 49static uint32_t reftable_hash(const void *key, int hash_id)
9282e921 50{
fc658755
VM
51 static uint32_t hash_seeds[GIT_HASHTABLE_HASHES] = {
52 2147483647,
53 0x5d20bb23,
54 0x7daaab3c
55 };
9282e921 56
fc658755 57 return git__hash(key, strlen((const char *)key), hash_seeds[hash_id]);
9282e921 58}
59
87d3acf4
VM
60static void reference_free(git_reference *reference);
61static int reference_create(git_reference **ref_out, git_repository *repo, const char *name, git_rtype type);
7341bf87 62static int reference_read(gitfo_buf *file_content, time_t *mtime, const char *repo_path, const char *ref_name);
87d3acf4
VM
63
64/* loose refs */
65static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content);
66static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content);
7341bf87 67static int loose_lookup(git_reference **ref_out, git_repository *repo, const char *name, int skip_symbolic);
87d3acf4 68static int loose_write(git_reference *ref);
7341bf87 69static int loose_update(git_reference *ref);
87d3acf4
VM
70
71/* packed refs */
87d3acf4
VM
72static int packed_parse_peel(reference_oid *tag_ref, const char **buffer_out, const char *buffer_end);
73static int packed_parse_oid(reference_oid **ref_out, git_repository *repo, const char **buffer_out, const char *buffer_end);
74static int packed_load(git_repository *repo);
75static int packed_loadloose(git_repository *repository);
76static int packed_write_ref(reference_oid *ref, git_filebuf *file);
77static int packed_find_peel(reference_oid *ref);
78static int packed_remove_loose(git_repository *repo, git_vector *packing_list);
79static int packed_sort(const void *a, const void *b);
80static int packed_write(git_repository *repo);
81
95cde17c 82/* internal helpers */
1b6d8163 83static int reference_available(git_repository *repo, const char *ref, const char *old_ref);
95cde17c 84
87d3acf4
VM
85/* name normalization */
86static int check_valid_ref_char(char ch);
3101a3e5 87static int normalize_name(char *buffer_out, size_t out_size, const char *name, int is_oid_ref);
87d3acf4
VM
88
89/*****************************************
90 * Internal methods - Constructor/destructor
91 *****************************************/
2f8a8ab2 92static void reference_free(git_reference *reference)
9282e921 93{
2f8a8ab2
VM
94 if (reference == NULL)
95 return;
9282e921 96
2f8a8ab2
VM
97 if (reference->name)
98 free(reference->name);
9282e921 99
2f8a8ab2 100 if (reference->type == GIT_REF_SYMBOLIC)
86194b24 101 free(((reference_symbolic *)reference)->target);
9282e921 102
2f8a8ab2 103 free(reference);
9282e921 104}
105
87d3acf4
VM
106static int reference_create(
107 git_reference **ref_out,
108 git_repository *repo,
109 const char *name,
110 git_rtype type)
111{
3101a3e5 112 char normalized[GIT_REFNAME_MAX];
86194b24 113 int error = GIT_SUCCESS, size;
9282e921 114 git_reference *reference = NULL;
115
1d8cc731 116 assert(ref_out && repo && name);
117
86194b24
VM
118 if (type == GIT_REF_SYMBOLIC)
119 size = sizeof(reference_symbolic);
120 else if (type == GIT_REF_OID)
121 size = sizeof(reference_oid);
122 else
fa59f18d
VM
123 return git__throw(GIT_EINVALIDARGS,
124 "Invalid reference type. Use either GIT_REF_OID or GIT_REF_SYMBOLIC as type specifier");
9282e921 125
86194b24 126 reference = git__malloc(size);
9282e921 127 if (reference == NULL)
128 return GIT_ENOMEM;
129
86194b24 130 memset(reference, 0x0, size);
2f8a8ab2 131 reference->owner = repo;
1d8cc731 132 reference->type = type;
133
3101a3e5 134 error = normalize_name(normalized, sizeof(normalized), name, (type & GIT_REF_OID));
1d8cc731 135 if (error < GIT_SUCCESS)
136 goto cleanup;
137
138 reference->name = git__strdup(normalized);
139 if (reference->name == NULL) {
140 error = GIT_ENOMEM;
141 goto cleanup;
142 }
9282e921 143
2f8a8ab2 144 *ref_out = reference;
1d8cc731 145
5bdf7b9f 146 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference");
1d8cc731 147
148cleanup:
149 reference_free(reference);
5bdf7b9f 150 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference");
1d8cc731 151}
152
7341bf87
VM
153static int reference_read(gitfo_buf *file_content, time_t *mtime, const char *repo_path, const char *ref_name)
154{
155 struct stat st;
156 char path[GIT_PATH_MAX];
157
158 /* Determine the full path of the file */
159 git__joinpath(path, repo_path, ref_name);
160
fa59f18d
VM
161 if (gitfo_stat(path, &st) < 0 || S_ISDIR(st.st_mode))
162 return git__throw(GIT_ENOTFOUND,
163 "Cannot read reference file '%s'", ref_name);
7341bf87
VM
164
165 if (mtime)
166 *mtime = st.st_mtime;
167
168 if (file_content)
169 return gitfo_read_file(file_content, path);
170
171 return GIT_SUCCESS;
172}
173
1d8cc731 174
1d8cc731 175
9282e921 176
87d3acf4
VM
177/*****************************************
178 * Internal methods - Loose references
179 *****************************************/
7341bf87
VM
180static int loose_update(git_reference *ref)
181{
182 int error;
183 time_t ref_time;
184 gitfo_buf ref_file = GITFO_BUF_INIT;
185
186 if (ref->type & GIT_REF_PACKED)
187 return packed_load(ref->owner);
188
189 error = reference_read(NULL, &ref_time, ref->owner->path_repository, ref->name);
190 if (error < GIT_SUCCESS)
191 goto cleanup;
192
193 if (ref_time == ref->mtime)
194 return GIT_SUCCESS;
195
196 error = reference_read(&ref_file, &ref->mtime, ref->owner->path_repository, ref->name);
197 if (error < GIT_SUCCESS)
198 goto cleanup;
199
200 if (ref->type == GIT_REF_SYMBOLIC)
201 error = loose_parse_symbolic(ref, &ref_file);
202 else if (ref->type == GIT_REF_OID)
203 error = loose_parse_oid(ref, &ref_file);
204 else
fa59f18d
VM
205 error = git__throw(GIT_EOBJCORRUPTED,
206 "Invalid reference type (%d) for loose reference", ref->type);
7341bf87
VM
207
208 gitfo_free_buf(&ref_file);
209
210cleanup:
211 if (error != GIT_SUCCESS) {
212 reference_free(ref);
213 git_hashtable_remove(ref->owner->references.loose_cache, ref->name);
214 }
215
5bdf7b9f 216 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to update loose reference");
7341bf87
VM
217}
218
87d3acf4 219static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content)
2f8a8ab2 220{
ddc9e79a 221 const unsigned int header_len = strlen(GIT_SYMREF);
2f8a8ab2
VM
222 const char *refname_start;
223 char *eol;
86194b24 224 reference_symbolic *ref_sym;
9282e921 225
2f8a8ab2 226 refname_start = (const char *)file_content->data;
86194b24 227 ref_sym = (reference_symbolic *)ref;
9282e921 228
2f8a8ab2 229 if (file_content->len < (header_len + 1))
fa59f18d
VM
230 return git__throw(GIT_EOBJCORRUPTED,
231 "Failed to parse loose reference. Object too short");
9282e921 232
2f8a8ab2
VM
233 /*
234 * Assume we have already checked for the header
235 * before calling this function
236 */
9282e921 237
2f8a8ab2 238 refname_start += header_len;
9282e921 239
7341bf87 240 free(ref_sym->target);
86194b24
VM
241 ref_sym->target = git__strdup(refname_start);
242 if (ref_sym->target == NULL)
32054c24 243 return GIT_ENOMEM;
9282e921 244
2f8a8ab2 245 /* remove newline at the end of file */
86194b24 246 eol = strchr(ref_sym->target, '\n');
ff5873ad 247 if (eol == NULL)
fa59f18d
VM
248 return git__throw(GIT_EOBJCORRUPTED,
249 "Failed to parse loose reference. Missing EOL");
ff5873ad
VM
250
251 *eol = '\0';
252 if (eol[-1] == '\r')
253 eol[-1] = '\0';
9282e921 254
2f8a8ab2 255 return GIT_SUCCESS;
9282e921 256}
257
87d3acf4 258static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content)
2f8a8ab2 259{
fa59f18d 260 int error;
86194b24 261 reference_oid *ref_oid;
2f8a8ab2 262 char *buffer;
86194b24 263
2f8a8ab2 264 buffer = (char *)file_content->data;
86194b24 265 ref_oid = (reference_oid *)ref;
9282e921 266
2f8a8ab2 267 /* File format: 40 chars (OID) + newline */
ff5873ad 268 if (file_content->len < GIT_OID_HEXSZ + 1)
fa59f18d
VM
269 return git__throw(GIT_EOBJCORRUPTED,
270 "Failed to parse loose reference. Reference too short");
9282e921 271
fa48608e 272 if ((error = git_oid_fromstr(&ref_oid->oid, buffer)) < GIT_SUCCESS)
fa59f18d 273 return git__rethrow(GIT_EOBJCORRUPTED, "Failed to parse loose reference.");
9282e921 274
ff5873ad
VM
275 buffer = buffer + GIT_OID_HEXSZ;
276 if (*buffer == '\r')
277 buffer++;
278
279 if (*buffer != '\n')
fa59f18d
VM
280 return git__throw(GIT_EOBJCORRUPTED,
281 "Failed to parse loose reference. Missing EOL");
9282e921 282
2f8a8ab2 283 return GIT_SUCCESS;
9282e921 284}
285
9282e921 286
00571828
VM
287static git_rtype loose_guess_rtype(const char *full_path)
288{
289 gitfo_buf ref_file = GITFO_BUF_INIT;
290 git_rtype type;
291
292 type = GIT_REF_INVALID;
293
294 if (gitfo_read_file(&ref_file, full_path) == GIT_SUCCESS) {
295 if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0)
296 type = GIT_REF_SYMBOLIC;
297 else
298 type = GIT_REF_OID;
299 }
300
301 gitfo_free_buf(&ref_file);
302 return type;
303}
304
87d3acf4 305static int loose_lookup(
2f8a8ab2
VM
306 git_reference **ref_out,
307 git_repository *repo,
87d3acf4
VM
308 const char *name,
309 int skip_symbolic)
9282e921 310{
311 int error = GIT_SUCCESS;
2f8a8ab2
VM
312 gitfo_buf ref_file = GITFO_BUF_INIT;
313 git_reference *ref = NULL;
7341bf87 314 time_t ref_time;
9282e921 315
2f8a8ab2 316 *ref_out = NULL;
9282e921 317
7341bf87 318 error = reference_read(&ref_file, &ref_time, repo->path_repository, name);
9282e921 319 if (error < GIT_SUCCESS)
320 goto cleanup;
321
1d8cc731 322 if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0) {
87d3acf4
VM
323 if (skip_symbolic)
324 return GIT_SUCCESS;
325
32054c24 326 error = reference_create(&ref, repo, name, GIT_REF_SYMBOLIC);
1d8cc731 327 if (error < GIT_SUCCESS)
328 goto cleanup;
9282e921 329
87d3acf4 330 error = loose_parse_symbolic(ref, &ref_file);
1d8cc731 331 } else {
32054c24 332 error = reference_create(&ref, repo, name, GIT_REF_OID);
1d8cc731 333 if (error < GIT_SUCCESS)
334 goto cleanup;
335
87d3acf4 336 error = loose_parse_oid(ref, &ref_file);
1d8cc731 337 }
338
ff5873ad
VM
339 if (error < GIT_SUCCESS)
340 goto cleanup;
341
7341bf87 342 ref->mtime = ref_time;
2f8a8ab2 343 *ref_out = ref;
567fc1d2 344 gitfo_free_buf(&ref_file);
2f8a8ab2 345 return GIT_SUCCESS;
9282e921 346
347cleanup:
2f8a8ab2
VM
348 gitfo_free_buf(&ref_file);
349 reference_free(ref);
5bdf7b9f 350 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to lookup loose reference");
9282e921 351}
352
87d3acf4
VM
353static int loose_write(git_reference *ref)
354{
355 git_filebuf file;
356 char ref_path[GIT_PATH_MAX];
e7e0e20f 357 int error;
7341bf87 358 struct stat st;
87d3acf4 359
76f7cf70 360 assert((ref->type & GIT_REF_PACKED) == 0);
361
87d3acf4
VM
362 git__joinpath(ref_path, ref->owner->path_repository, ref->name);
363
55ffebe3 364 if ((error = git_filebuf_open(&file, ref_path, GIT_FILEBUF_FORCE)) < GIT_SUCCESS)
5bdf7b9f 365 return git__rethrow(error, "Failed to write loose reference");
87d3acf4
VM
366
367 if (ref->type & GIT_REF_OID) {
368 reference_oid *ref_oid = (reference_oid *)ref;
e7e0e20f 369 char oid[GIT_OID_HEXSZ + 1];
87d3acf4 370
e7e0e20f 371 memset(oid, 0x0, sizeof(oid));
87d3acf4 372
e7e0e20f
CMN
373 git_oid_fmt(oid, &ref_oid->oid);
374 error = git_filebuf_printf(&file, "%s\n", oid);
375 if (error < GIT_SUCCESS)
376 goto unlock;
87d3acf4
VM
377
378 } else if (ref->type & GIT_REF_SYMBOLIC) { /* GIT_REF_SYMBOLIC */
379 reference_symbolic *ref_sym = (reference_symbolic *)ref;
380
e7e0e20f 381 error = git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref_sym->target);
87d3acf4 382 } else {
fa59f18d 383 error = git__throw(GIT_EOBJCORRUPTED, "Failed to write reference. Invalid reference type");
87d3acf4
VM
384 goto unlock;
385 }
386
87d3acf4
VM
387 error = git_filebuf_commit(&file);
388
7341bf87
VM
389 if (gitfo_stat(ref_path, &st) == GIT_SUCCESS)
390 ref->mtime = st.st_mtime;
391
5bdf7b9f 392 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write loose reference");
87d3acf4
VM
393
394unlock:
395 git_filebuf_cleanup(&file);
5bdf7b9f 396 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write loose reference");
87d3acf4
VM
397}
398
399
400
401
402
2f8a8ab2 403
87d3acf4
VM
404/*****************************************
405 * Internal methods - Packed references
406 *****************************************/
9282e921 407
87d3acf4 408static int packed_parse_peel(
86194b24 409 reference_oid *tag_ref,
2f8a8ab2
VM
410 const char **buffer_out,
411 const char *buffer_end)
9282e921 412{
2f8a8ab2
VM
413 const char *buffer = *buffer_out + 1;
414
415 assert(buffer[-1] == '^');
9282e921 416
417 /* Ensure it's not the first entry of the file */
2f8a8ab2 418 if (tag_ref == NULL)
5bdf7b9f 419 return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Reference is the first entry of the file");
9282e921 420
421 /* Ensure reference is a tag */
86194b24 422 if (git__prefixcmp(tag_ref->ref.name, GIT_REFS_TAGS_DIR) != 0)
5bdf7b9f 423 return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Reference is not a tag");
9282e921 424
2f8a8ab2 425 if (buffer + GIT_OID_HEXSZ >= buffer_end)
5bdf7b9f 426 return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Buffer too small");
9282e921 427
2f8a8ab2 428 /* Is this a valid object id? */
fa48608e 429 if (git_oid_fromstr(&tag_ref->peel_target, buffer) < GIT_SUCCESS)
5bdf7b9f 430 return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Not a valid object ID");
2f8a8ab2 431
ff5873ad
VM
432 buffer = buffer + GIT_OID_HEXSZ;
433 if (*buffer == '\r')
434 buffer++;
435
436 if (*buffer != '\n')
5bdf7b9f 437 return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Buffer not terminated correctly");
ff5873ad
VM
438
439 *buffer_out = buffer + 1;
86194b24 440 tag_ref->ref.type |= GIT_REF_HAS_PEEL;
2f8a8ab2 441
2f8a8ab2 442 return GIT_SUCCESS;
9282e921 443}
444
87d3acf4 445static int packed_parse_oid(
86194b24 446 reference_oid **ref_out,
2f8a8ab2
VM
447 git_repository *repo,
448 const char **buffer_out,
449 const char *buffer_end)
9282e921 450{
e06dd9b6 451 reference_oid *ref = NULL;
2f8a8ab2
VM
452
453 const char *buffer = *buffer_out;
454 const char *refname_begin, *refname_end;
455
9282e921 456 int error = GIT_SUCCESS;
9282e921 457 int refname_len;
3101a3e5 458 char refname[GIT_REFNAME_MAX];
1d8cc731 459 git_oid id;
9282e921 460
2f8a8ab2
VM
461 refname_begin = (buffer + GIT_OID_HEXSZ + 1);
462 if (refname_begin >= buffer_end ||
463 refname_begin[-1] != ' ') {
464 error = GIT_EPACKEDREFSCORRUPTED;
465 goto cleanup;
466 }
9282e921 467
2f8a8ab2 468 /* Is this a valid object id? */
fa48608e 469 if ((error = git_oid_fromstr(&id, buffer)) < GIT_SUCCESS)
2f8a8ab2 470 goto cleanup;
9282e921 471
2f8a8ab2
VM
472 refname_end = memchr(refname_begin, '\n', buffer_end - refname_begin);
473 if (refname_end == NULL) {
474 error = GIT_EPACKEDREFSCORRUPTED;
475 goto cleanup;
476 }
9282e921 477
2f8a8ab2 478 refname_len = refname_end - refname_begin;
9282e921 479
1d8cc731 480 memcpy(refname, refname_begin, refname_len);
481 refname[refname_len] = 0;
9282e921 482
1d8cc731 483 if (refname[refname_len - 1] == '\r')
484 refname[refname_len - 1] = 0;
9282e921 485
86194b24 486 error = reference_create((git_reference **)&ref, repo, refname, GIT_REF_OID);
1d8cc731 487 if (error < GIT_SUCCESS)
488 goto cleanup;
489
86194b24
VM
490 git_oid_cpy(&ref->oid, &id);
491 ref->ref.type |= GIT_REF_PACKED;
9282e921 492
2f8a8ab2 493 *ref_out = ref;
9282e921 494 *buffer_out = refname_end + 1;
495
2f8a8ab2
VM
496 return GIT_SUCCESS;
497
498cleanup:
86194b24 499 reference_free((git_reference *)ref);
5bdf7b9f 500 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse OID of packed reference");
9282e921 501}
502
87d3acf4 503static int packed_load(git_repository *repo)
9282e921 504{
505 int error = GIT_SUCCESS;
2f8a8ab2
VM
506 gitfo_buf packfile = GITFO_BUF_INIT;
507 const char *buffer_start, *buffer_end;
87d3acf4
VM
508 git_refcache *ref_cache = &repo->references;
509
510 /* already loaded */
7341bf87
VM
511 if (repo->references.packfile != NULL) {
512 time_t packed_time;
87d3acf4 513
7341bf87
VM
514 /* check if we can read the time of the index;
515 * if we can read it and it matches the time of the
516 * index we had previously loaded, we don't need to do
517 * anything else.
518 *
519 * if we cannot load the time (e.g. the packfile
520 * has disappeared) or the time is different, we
521 * have to reload the packfile */
87d3acf4 522
7341bf87
VM
523 if (!reference_read(NULL, &packed_time, repo->path_repository, GIT_PACKEDREFS_FILE) &&
524 packed_time == ref_cache->packfile_time)
525 return GIT_SUCCESS;
87d3acf4 526
7341bf87
VM
527 git_hashtable_clear(repo->references.packfile);
528 } else {
529 ref_cache->packfile = git_hashtable_alloc(
530 default_table_size,
531 reftable_hash,
532 (git_hash_keyeq_ptr)strcmp);
533
534 if (ref_cache->packfile == NULL)
535 return GIT_ENOMEM;
536 }
537
538 /* read the packfile from disk;
539 * store its modification time to check for future reloads */
540 error = reference_read(
541 &packfile,
542 &ref_cache->packfile_time,
543 repo->path_repository,
544 GIT_PACKEDREFS_FILE);
87d3acf4
VM
545
546 /* there is no packfile on disk; that's ok */
547 if (error == GIT_ENOTFOUND)
548 return GIT_SUCCESS;
2f8a8ab2 549
9282e921 550 if (error < GIT_SUCCESS)
551 goto cleanup;
552
2f8a8ab2
VM
553 buffer_start = (const char *)packfile.data;
554 buffer_end = (const char *)(buffer_start) + packfile.len;
9282e921 555
7c8a7b91
VM
556 while (buffer_start < buffer_end && buffer_start[0] == '#') {
557 buffer_start = strchr(buffer_start, '\n');
558 if (buffer_start == NULL) {
559 error = GIT_EPACKEDREFSCORRUPTED;
560 goto cleanup;
561 }
ddc9e79a 562 buffer_start++;
7c8a7b91 563 }
ddc9e79a 564
9282e921 565 while (buffer_start < buffer_end) {
86194b24 566 reference_oid *ref = NULL;
2f8a8ab2 567
87d3acf4 568 error = packed_parse_oid(&ref, repo, &buffer_start, buffer_end);
2f8a8ab2
VM
569 if (error < GIT_SUCCESS)
570 goto cleanup;
571
9282e921 572 if (buffer_start[0] == '^') {
87d3acf4 573 error = packed_parse_peel(ref, &buffer_start, buffer_end);
9282e921 574 if (error < GIT_SUCCESS)
575 goto cleanup;
2f8a8ab2 576 }
9282e921 577
87d3acf4 578 error = git_hashtable_insert(ref_cache->packfile, ref->ref.name, ref);
2f8a8ab2 579 if (error < GIT_SUCCESS) {
86194b24 580 reference_free((git_reference *)ref);
9282e921 581 goto cleanup;
2f8a8ab2
VM
582 }
583 }
9282e921 584
7341bf87
VM
585 gitfo_free_buf(&packfile);
586 return GIT_SUCCESS;
587
2f8a8ab2 588cleanup:
7341bf87
VM
589 git_hashtable_free(ref_cache->packfile);
590 ref_cache->packfile = NULL;
2f8a8ab2 591 gitfo_free_buf(&packfile);
5bdf7b9f 592 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to load packed references");
2f8a8ab2 593}
9282e921 594
00571828
VM
595
596
597
598struct dirent_list_data {
7ad96e51 599 git_repository *repo;
00571828
VM
600 size_t repo_path_len;
601 unsigned int list_flags;
09e8de0f
VM
602
603 int (*callback)(const char *, void *);
604 void *callback_payload;
00571828
VM
605};
606
607static int _dirent_loose_listall(void *_data, char *full_path)
608{
609 struct dirent_list_data *data = (struct dirent_list_data *)_data;
7ad96e51 610 char *file_path = full_path + data->repo_path_len;
00571828
VM
611
612 if (gitfo_isdir(full_path) == GIT_SUCCESS)
613 return gitfo_dirent(full_path, GIT_PATH_MAX, _dirent_loose_listall, _data);
614
7ad96e51 615 /* do not add twice a reference that exists already in the packfile */
b5abb881
VM
616 if ((data->list_flags & GIT_REF_PACKED) != 0 &&
617 git_hashtable_lookup(data->repo->references.packfile, file_path) != NULL)
7ad96e51
VM
618 return GIT_SUCCESS;
619
09e8de0f
VM
620 if (data->list_flags != GIT_REF_LISTALL) {
621 if ((data->list_flags & loose_guess_rtype(full_path)) == 0)
622 return GIT_SUCCESS; /* we are filtering out this reference */
623 }
00571828 624
09e8de0f 625 return data->callback(file_path, data->callback_payload);
00571828
VM
626}
627
87d3acf4 628static int _dirent_loose_load(void *data, char *full_path)
2f8a8ab2 629{
87d3acf4
VM
630 git_repository *repository = (git_repository *)data;
631 git_reference *reference, *old_ref;
632 char *file_path;
633 int error;
86194b24 634
87d3acf4 635 if (gitfo_isdir(full_path) == GIT_SUCCESS)
0594e3ef 636 return gitfo_dirent(full_path, GIT_PATH_MAX, _dirent_loose_load, repository);
9282e921 637
87d3acf4
VM
638 file_path = full_path + strlen(repository->path_repository);
639 error = loose_lookup(&reference, repository, file_path, 1);
640 if (error == GIT_SUCCESS && reference != NULL) {
641 reference->type |= GIT_REF_PACKED;
86194b24 642
87d3acf4
VM
643 if (git_hashtable_insert2(repository->references.packfile, reference->name, reference, (void **)&old_ref) < GIT_SUCCESS) {
644 reference_free(reference);
645 return GIT_ENOMEM;
646 }
647
648 if (old_ref != NULL)
649 reference_free(old_ref);
650 }
9282e921 651
5bdf7b9f 652 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to load loose dirent");
2f8a8ab2
VM
653}
654
87d3acf4
VM
655/*
656 * Load all the loose references from the repository
657 * into the in-memory Packfile, and build a vector with
658 * all the references so it can be written back to
659 * disk.
660 */
661static int packed_loadloose(git_repository *repository)
2f8a8ab2 662{
87d3acf4 663 char refs_path[GIT_PATH_MAX];
86194b24 664
87d3acf4
VM
665 /* the packfile must have been previously loaded! */
666 assert(repository->references.packfile);
9282e921 667
87d3acf4 668 git__joinpath(refs_path, repository->path_repository, GIT_REFS_DIR);
86194b24 669
87d3acf4
VM
670 /* Remove any loose references from the cache */
671 {
402a47a7 672 const void *GIT_UNUSED(_unused);
87d3acf4 673 git_reference *reference;
9282e921 674
87d3acf4
VM
675 GIT_HASHTABLE_FOREACH(repository->references.loose_cache, _unused, reference,
676 reference_free(reference);
677 );
678 }
679
680 git_hashtable_clear(repository->references.loose_cache);
681
682 /*
683 * Load all the loose files from disk into the Packfile table.
684 * This will overwrite any old packed entries with their
685 * updated loose versions
686 */
687 return gitfo_dirent(refs_path, GIT_PATH_MAX, _dirent_loose_load, repository);
2f8a8ab2 688}
9282e921 689
87d3acf4
VM
690/*
691 * Write a single reference into a packfile
692 */
693static int packed_write_ref(reference_oid *ref, git_filebuf *file)
2f8a8ab2 694{
87d3acf4
VM
695 int error;
696 char oid[GIT_OID_HEXSZ + 1];
2f8a8ab2 697
87d3acf4
VM
698 git_oid_fmt(oid, &ref->oid);
699 oid[GIT_OID_HEXSZ] = 0;
2f8a8ab2 700
87d3acf4
VM
701 /*
702 * For references that peel to an object in the repo, we must
703 * write the resulting peel on a separate line, e.g.
704 *
705 * 6fa8a902cc1d18527e1355773c86721945475d37 refs/tags/libgit2-0.4
706 * ^2ec0cb7959b0bf965d54f95453f5b4b34e8d3100
707 *
708 * This obviously only applies to tags.
709 * The required peels have already been loaded into `ref->peel_target`.
710 */
711 if (ref->ref.type & GIT_REF_HAS_PEEL) {
712 char peel[GIT_OID_HEXSZ + 1];
713 git_oid_fmt(peel, &ref->peel_target);
714 peel[GIT_OID_HEXSZ] = 0;
715
716 error = git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->ref.name, peel);
717 } else {
718 error = git_filebuf_printf(file, "%s %s\n", oid, ref->ref.name);
719 }
720
5bdf7b9f 721 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write packed reference");
9282e921 722}
723
87d3acf4
VM
724/*
725 * Find out what object this reference resolves to.
726 *
727 * For references that point to a 'big' tag (e.g. an
728 * actual tag object on the repository), we need to
729 * cache on the packfile the OID of the object to
730 * which that 'big tag' is pointing to.
731 */
732static int packed_find_peel(reference_oid *ref)
9282e921 733{
d79f1da6 734 git_object *object;
87d3acf4 735 int error;
32054c24 736
87d3acf4
VM
737 if (ref->ref.type & GIT_REF_HAS_PEEL)
738 return GIT_SUCCESS;
9282e921 739
87d3acf4
VM
740 /*
741 * Only applies to tags, i.e. references
742 * in the /refs/tags folder
743 */
744 if (git__prefixcmp(ref->ref.name, GIT_REFS_TAGS_DIR) != 0)
745 return GIT_SUCCESS;
9282e921 746
87d3acf4 747 /*
d79f1da6 748 * Find the tagged object in the repository
87d3acf4 749 */
d79f1da6 750 error = git_object_lookup(&object, ref->ref.owner, &ref->oid, GIT_OBJ_ANY);
87d3acf4 751 if (error < GIT_SUCCESS)
5bdf7b9f 752 return git__throw(GIT_EOBJCORRUPTED, "Failed to find packed reference");
86194b24 753
87d3acf4 754 /*
d79f1da6
VM
755 * If the tagged object is a Tag object, we need to resolve it;
756 * if the ref is actually a 'weak' ref, we don't need to resolve
757 * anything.
87d3acf4 758 */
d79f1da6
VM
759 if (git_object_type(object) == GIT_OBJ_TAG) {
760 git_tag *tag = (git_tag *)object;
86194b24 761
d79f1da6
VM
762 /*
763 * Find the object pointed at by this tag
764 */
765 git_oid_cpy(&ref->peel_target, git_tag_target_oid(tag));
766 ref->ref.type |= GIT_REF_HAS_PEEL;
767
768 /*
769 * The reference has now cached the resolved OID, and is
770 * marked at such. When written to the packfile, it'll be
771 * accompanied by this resolved oid
772 */
773 }
87d3acf4 774
1648fbd3
VM
775 git_object_close(object);
776
87d3acf4 777 return GIT_SUCCESS;
2f8a8ab2 778}
9282e921 779
87d3acf4
VM
780/*
781 * Remove all loose references
782 *
783 * Once we have successfully written a packfile,
784 * all the loose references that were packed must be
785 * removed from disk.
786 *
787 * This is a dangerous method; make sure the packfile
788 * is well-written, because we are destructing references
789 * here otherwise.
790 */
791static int packed_remove_loose(git_repository *repo, git_vector *packing_list)
2f8a8ab2 792{
87d3acf4
VM
793 unsigned int i;
794 char full_path[GIT_PATH_MAX];
795 int error = GIT_SUCCESS;
8f90ced5 796 git_reference *reference;
87d3acf4
VM
797
798 for (i = 0; i < packing_list->length; ++i) {
799 git_reference *ref = git_vector_get(packing_list, i);
8f90ced5 800
801 /* Ensure the packed reference doesn't exist
802 * in a (more up-to-date?) state as a loose reference
803 */
804 reference = git_hashtable_lookup(ref->owner->references.loose_cache, ref->name);
805 if (reference != NULL)
806 continue;
807
87d3acf4
VM
808 git__joinpath(full_path, repo->path_repository, ref->name);
809
810 if (gitfo_exists(full_path) == GIT_SUCCESS &&
811 gitfo_unlink(full_path) < GIT_SUCCESS)
812 error = GIT_EOSERR;
813
814 /*
815 * if we fail to remove a single file, this is *not* good,
816 * but we should keep going and remove as many as possible.
817 * After we've removed as many files as possible, we return
818 * the error code anyway.
819 *
820 * TODO: mark this with a very special error code?
821 * GIT_EFAILTORMLOOSE
822 */
823 }
824
5bdf7b9f 825 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to remove loose packed reference");
2f8a8ab2 826}
9282e921 827
87d3acf4 828static int packed_sort(const void *a, const void *b)
2f8a8ab2 829{
87d3acf4
VM
830 const git_reference *ref_a = *(const git_reference **)a;
831 const git_reference *ref_b = *(const git_reference **)b;
832
833 return strcmp(ref_a->name, ref_b->name);
2f8a8ab2
VM
834}
835
87d3acf4
VM
836/*
837 * Write all the contents in the in-memory packfile to disk.
838 */
839static int packed_write(git_repository *repo)
2f8a8ab2 840{
87d3acf4
VM
841 git_filebuf pack_file;
842 int error;
843 unsigned int i;
844 char pack_file_path[GIT_PATH_MAX];
2f8a8ab2 845
87d3acf4
VM
846 git_vector packing_list;
847 size_t total_refs;
2f8a8ab2 848
87d3acf4 849 assert(repo && repo->references.packfile);
9282e921 850
87d3acf4 851 total_refs = repo->references.packfile->key_count;
86d7e1ca 852 if ((error = git_vector_init(&packing_list, total_refs, packed_sort)) < GIT_SUCCESS)
5bdf7b9f 853 return git__rethrow(error, "Failed to write packed reference");
9282e921 854
87d3acf4
VM
855 /* Load all the packfile into a vector */
856 {
857 git_reference *reference;
402a47a7 858 const void *GIT_UNUSED(_unused);
87d3acf4
VM
859
860 GIT_HASHTABLE_FOREACH(repo->references.packfile, _unused, reference,
861 git_vector_insert(&packing_list, reference); /* cannot fail: vector already has the right size */
862 );
9282e921 863 }
864
87d3acf4
VM
865 /* sort the vector so the entries appear sorted on the packfile */
866 git_vector_sort(&packing_list);
9282e921 867
87d3acf4
VM
868 /* Now we can open the file! */
869 git__joinpath(pack_file_path, repo->path_repository, GIT_PACKEDREFS_FILE);
870 if ((error = git_filebuf_open(&pack_file, pack_file_path, 0)) < GIT_SUCCESS)
5bdf7b9f 871 return git__rethrow(error, "Failed to write packed reference");
9282e921 872
7c8a7b91
VM
873 /* Packfiles have a header... apparently
874 * This is in fact not required, but we might as well print it
875 * just for kicks */
87d3acf4 876 if ((error = git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < GIT_SUCCESS)
5bdf7b9f 877 return git__rethrow(error, "Failed to write packed reference");
2f8a8ab2 878
87d3acf4
VM
879 for (i = 0; i < packing_list.length; ++i) {
880 reference_oid *ref = (reference_oid *)git_vector_get(&packing_list, i);
2f8a8ab2 881
87d3acf4
VM
882 /* only direct references go to the packfile; otherwise
883 * this is a disaster */
884 assert(ref->ref.type & GIT_REF_OID);
2f8a8ab2 885
2b397327
VM
886 if ((error = packed_find_peel(ref)) < GIT_SUCCESS) {
887 error = git__throw(GIT_EOBJCORRUPTED, "A reference cannot be peeled");
87d3acf4 888 goto cleanup;
2b397327
VM
889 }
890
2f8a8ab2 891
87d3acf4
VM
892 if ((error = packed_write_ref(ref, &pack_file)) < GIT_SUCCESS)
893 goto cleanup;
894 }
2f8a8ab2 895
87d3acf4
VM
896cleanup:
897 /* if we've written all the references properly, we can commit
898 * the packfile to make the changes effective */
899 if (error == GIT_SUCCESS) {
900 error = git_filebuf_commit(&pack_file);
901
902 /* when and only when the packfile has been properly written,
903 * we can go ahead and remove the loose refs */
7341bf87
VM
904 if (error == GIT_SUCCESS) {
905 struct stat st;
906
87d3acf4 907 error = packed_remove_loose(repo, &packing_list);
7341bf87
VM
908
909 if (gitfo_stat(pack_file_path, &st) == GIT_SUCCESS)
910 repo->references.packfile_time = st.st_mtime;
911 }
9282e921 912 }
87d3acf4 913 else git_filebuf_cleanup(&pack_file);
9282e921 914
87d3acf4 915 git_vector_free(&packing_list);
1d8cc731 916
5bdf7b9f 917 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write packed reference");
87d3acf4 918}
2f8a8ab2 919
1b6d8163
MS
920static int _reference_available_cb(const char *ref, void *data)
921{
f120e92b 922 const char *new, *old;
923 git_vector *refs;
924
1b6d8163
MS
925 assert(ref && data);
926
f120e92b 927 refs = (git_vector *)data;
1b6d8163 928
f120e92b 929 new = (const char *)git_vector_get(refs, 0);
930 old = (const char *)git_vector_get(refs, 1);
1b6d8163
MS
931
932 if (!old || strcmp(old, ref)) {
933 int reflen = strlen(ref);
934 int newlen = strlen(new);
935 int cmplen = reflen < newlen ? reflen : newlen;
936 const char *lead = reflen < newlen ? new : ref;
937
938 if (!strncmp(new, ref, cmplen) &&
939 lead[cmplen] == '/')
940 return GIT_EEXISTS;
941 }
942
943 return GIT_SUCCESS;
944}
945
946static int reference_available(git_repository *repo, const char *ref, const char* old_ref)
947{
948 int error;
949 git_vector refs;
950
951 if (git_vector_init(&refs, 2, NULL) < GIT_SUCCESS)
952 return GIT_ENOMEM;
953
954 git_vector_insert(&refs, (void *)ref);
955 git_vector_insert(&refs, (void *)old_ref);
956
43521d06 957 error = git_reference_foreach(repo, GIT_REF_LISTALL, _reference_available_cb, (void *)&refs);
1b6d8163
MS
958
959 git_vector_free(&refs);
960
76b15cb1 961 return error == GIT_SUCCESS ? GIT_SUCCESS : git__throw(GIT_EEXISTS, "Reference name `%s` conflicts with existing reference", ref);
1b6d8163
MS
962}
963
87d3acf4
VM
964/*****************************************
965 * External Library API
966 *****************************************/
967
968/**
969 * Constructors
970 */
5de079b8 971int git_reference_lookup(git_reference **ref_out, git_repository *repo, const char *name)
2f8a8ab2
VM
972{
973 int error;
3101a3e5 974 char normalized_name[GIT_REFNAME_MAX];
2f8a8ab2
VM
975
976 assert(ref_out && repo && name);
977
978 *ref_out = NULL;
979
3101a3e5 980 error = normalize_name(normalized_name, sizeof(normalized_name), name, 0);
2f8a8ab2 981 if (error < GIT_SUCCESS)
5bdf7b9f 982 return git__rethrow(error, "Failed to lookup reference");
2f8a8ab2 983
87d3acf4
VM
984 /* First, check has been previously loaded and cached */
985 *ref_out = git_hashtable_lookup(repo->references.loose_cache, normalized_name);
2f8a8ab2 986 if (*ref_out != NULL)
7341bf87 987 return loose_update(*ref_out);
2f8a8ab2 988
87d3acf4
VM
989 /* Then check if there is a loose file for that reference.*/
990 error = loose_lookup(ref_out, repo, normalized_name, 0);
2f8a8ab2 991
87d3acf4 992 /* If the file exists, we store it on the cache */
2f8a8ab2 993 if (error == GIT_SUCCESS)
87d3acf4 994 return git_hashtable_insert(repo->references.loose_cache, (*ref_out)->name, (*ref_out));
2f8a8ab2 995
87d3acf4
VM
996 /* The loose lookup has failed, but not because the reference wasn't found;
997 * probably the loose reference is corrupted. this is bad. */
9282e921 998 if (error != GIT_ENOTFOUND)
5bdf7b9f 999 return git__rethrow(error, "Failed to lookup reference");
9282e921 1000
87d3acf4
VM
1001 /*
1002 * If we cannot find a loose reference, we look into the packfile
1003 * Load the packfile first if it hasn't been loaded
1004 */
7341bf87
VM
1005 /* load all the packed references */
1006 error = packed_load(repo);
1007 if (error < GIT_SUCCESS)
5bdf7b9f 1008 return git__rethrow(error, "Failed to lookup reference");
9282e921 1009
87d3acf4
VM
1010 /* Look up on the packfile */
1011 *ref_out = git_hashtable_lookup(repo->references.packfile, normalized_name);
86194b24
VM
1012 if (*ref_out != NULL)
1013 return GIT_SUCCESS;
1014
2f8a8ab2 1015 /* The reference doesn't exist anywhere */
5bdf7b9f 1016 return git__throw(GIT_ENOTFOUND, "Failed to lookup reference. Reference doesn't exist");
2f8a8ab2 1017}
9282e921 1018
87d3acf4
VM
1019/**
1020 * Getters
1021 */
1022git_rtype git_reference_type(git_reference *ref)
1023{
1024 assert(ref);
1025
1026 if (ref->type & GIT_REF_OID)
1027 return GIT_REF_OID;
1028
1029 if (ref->type & GIT_REF_SYMBOLIC)
1030 return GIT_REF_SYMBOLIC;
1031
1032 return GIT_REF_INVALID;
1033}
1034
1035const char *git_reference_name(git_reference *ref)
1036{
1037 assert(ref);
1038 return ref->name;
1039}
1040
1041git_repository *git_reference_owner(git_reference *ref)
1042{
1043 assert(ref);
1044 return ref->owner;
1045}
1046
1047const git_oid *git_reference_oid(git_reference *ref)
1048{
1049 assert(ref);
1050
1051 if ((ref->type & GIT_REF_OID) == 0)
1052 return NULL;
1053
7341bf87
VM
1054 if (loose_update(ref) < GIT_SUCCESS)
1055 return NULL;
1056
87d3acf4
VM
1057 return &((reference_oid *)ref)->oid;
1058}
1059
1060const char *git_reference_target(git_reference *ref)
1061{
1062 assert(ref);
1063
1064 if ((ref->type & GIT_REF_SYMBOLIC) == 0)
1065 return NULL;
1066
7341bf87
VM
1067 if (loose_update(ref) < GIT_SUCCESS)
1068 return NULL;
1069
87d3acf4
VM
1070 return ((reference_symbolic *)ref)->target;
1071}
1072
d5afc039
VM
1073int git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force)
1074{
1075 char normalized[GIT_REFNAME_MAX];
1076 int error = GIT_SUCCESS, updated = 0;
1077 git_reference *ref = NULL, *old_ref = NULL;
1078
1079 if (git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force)
1080 return git__throw(GIT_EEXISTS, "Failed to create symbolic reference. Reference already exists");
1081
1082 /*
1083 * If they old ref was of the same type, then we can just update
1084 * it (once we've checked that the target is valid). Otherwise we
1085 * need a new reference because we can't make a symbolic ref out
1086 * of an oid one.
1087 * If if didn't exist, then we need to create a new one anyway.
1088 */
1089 if (ref && ref->type & GIT_REF_SYMBOLIC){
1090 updated = 1;
1091 } else {
1092 ref = NULL;
1093 error = reference_create(&ref, repo, name, GIT_REF_SYMBOLIC);
1094 if (error < GIT_SUCCESS)
1095 goto cleanup;
1096 }
1097
1098 /* The target can aither be the name of an object id reference or the name of another symbolic reference */
1099 error = normalize_name(normalized, sizeof(normalized), target, 0);
1100 if (error < GIT_SUCCESS)
1101 goto cleanup;
1102
1103 /* set the target; this will write the reference on disk */
1104 error = git_reference_set_target(ref, normalized);
1105 if (error < GIT_SUCCESS)
1106 goto cleanup;
1107
1108 /*
1109 * If we didn't update the ref, then we need to insert or replace
1110 * it in the loose cache. If we replaced a ref, free it.
1111 */
1112 if (!updated){
1113 error = git_hashtable_insert2(repo->references.loose_cache, ref->name, ref, (void **) &old_ref);
1114 if (error < GIT_SUCCESS)
1115 goto cleanup;
1116
1117 if(old_ref)
1118 reference_free(old_ref);
1119 }
1120
1121 *ref_out = ref;
1122
1123 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create symbolic reference");
1124
1125cleanup:
1126 reference_free(ref);
1127 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create symbolic reference");
1128}
1129
1130int git_reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force)
1131{
1132 int error = GIT_SUCCESS, updated = 0;
1133 git_reference *ref = NULL, *old_ref = NULL;
1134
1135 if(git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force)
1136 return git__throw(GIT_EEXISTS, "Failed to create reference OID. Reference already exists");
1137
1138 if ((error = reference_available(repo, name, NULL)) < GIT_SUCCESS)
1139 return git__rethrow(error, "Failed to create reference");
1140
1141 /*
1142 * If they old ref was of the same type, then we can just update
1143 * it (once we've checked that the target is valid). Otherwise we
1144 * need a new reference because we can't make a symbolic ref out
1145 * of an oid one.
1146 * If if didn't exist, then we need to create a new one anyway.
1147 */
1148 if (ref && ref-> type & GIT_REF_OID){
1149 updated = 1;
1150 } else {
1151 ref = NULL;
1152 error = reference_create(&ref, repo, name, GIT_REF_OID);
1153 if (error < GIT_SUCCESS)
1154 goto cleanup;
1155 }
1156
1157 /* set the oid; this will write the reference on disk */
1158 error = git_reference_set_oid(ref, id);
1159 if (error < GIT_SUCCESS)
1160 goto cleanup;
1161
1162 if(!updated){
1163 error = git_hashtable_insert2(repo->references.loose_cache, ref->name, ref, (void **) &old_ref);
1164 if (error < GIT_SUCCESS)
1165 goto cleanup;
1166
1167 if(old_ref)
1168 reference_free(old_ref);
1169 }
1170
1171 *ref_out = ref;
1172
1173 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference OID");
1174
1175cleanup:
1176 reference_free(ref);
1177 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference OID");
1178}
1179
87d3acf4
VM
1180/**
1181 * Setters
1182 */
1183
1184/*
1185 * Change the OID target of a reference.
1186 *
1187 * For loose references, just change the oid in memory
1188 * and overwrite the file in disk.
1189 *
1190 * For packed files, this is not pretty:
1191 * For performance reasons, we write the new reference
1192 * loose on disk (it replaces the old on the packfile),
1193 * but we cannot invalidate the pointer to the reference,
1194 * and most importantly, the `packfile` object must stay
1195 * consistent with the representation of the packfile
1196 * on disk. This is what we need to:
1197 *
1198 * 1. Copy the reference
1199 * 2. Change the oid on the original
1200 * 3. Write the original to disk
1201 * 4. Write the original to the loose cache
1202 * 5. Replace the original with the copy (old reference) in the packfile cache
1203 */
1204int git_reference_set_oid(git_reference *ref, const git_oid *id)
1205{
1206 reference_oid *ref_oid;
1207 reference_oid *ref_old = NULL;
1208 int error = GIT_SUCCESS;
1209
1210 if ((ref->type & GIT_REF_OID) == 0)
5bdf7b9f 1211 return git__throw(GIT_EINVALIDREFSTATE, "Failed to set OID target of reference. Not an OID reference");
87d3acf4
VM
1212
1213 ref_oid = (reference_oid *)ref;
1214
9a53df7e
VM
1215 assert(ref->owner);
1216
1217 /* Don't let the user create references to OIDs that
1218 * don't exist in the ODB */
1219 if (!git_odb_exists(git_repository_database(ref->owner), id))
5bdf7b9f 1220 return git__throw(GIT_ENOTFOUND, "Failed to set OID target of reference. OID doesn't exist in ODB");
9a53df7e 1221
87d3acf4
VM
1222 /* duplicate the reference;
1223 * this copy will stay on the packfile cache */
1224 if (ref->type & GIT_REF_PACKED) {
1225 ref_old = git__malloc(sizeof(reference_oid));
1226 if (ref_old == NULL)
1227 return GIT_ENOMEM;
1228
1229 ref_old->ref.name = git__strdup(ref->name);
1230 if (ref_old->ref.name == NULL) {
1231 free(ref_old);
1232 return GIT_ENOMEM;
1233 }
1234 }
1235
1236 git_oid_cpy(&ref_oid->oid, id);
1237 ref->type &= ~GIT_REF_HAS_PEEL;
1238
1239 error = loose_write(ref);
1240 if (error < GIT_SUCCESS)
1241 goto cleanup;
1242
1243 if (ref->type & GIT_REF_PACKED) {
1244 /* insert the original on the loose cache */
1245 error = git_hashtable_insert(ref->owner->references.loose_cache, ref->name, ref);
1246 if (error < GIT_SUCCESS)
1247 goto cleanup;
1248
1249 ref->type &= ~GIT_REF_PACKED;
1250
1251 /* replace the original in the packfile with the copy */
1252 error = git_hashtable_insert(ref->owner->references.packfile, ref_old->ref.name, ref_old);
1253 if (error < GIT_SUCCESS)
1254 goto cleanup;
1255 }
1256
1257 return GIT_SUCCESS;
1258
1259cleanup:
1260 reference_free((git_reference *)ref_old);
5bdf7b9f 1261 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to set OID target of reference");
87d3acf4
VM
1262}
1263
1264/*
1265 * Change the target of a symbolic reference.
1266 *
1267 * This is easy because symrefs cannot be inside
1268 * a pack. We just change the target in memory
1269 * and overwrite the file on disk.
1270 */
1271int git_reference_set_target(git_reference *ref, const char *target)
1272{
1273 reference_symbolic *ref_sym;
1274
1275 if ((ref->type & GIT_REF_SYMBOLIC) == 0)
5bdf7b9f 1276 return git__throw(GIT_EINVALIDREFSTATE, "Failed to set reference target. Not a symbolic reference");
87d3acf4
VM
1277
1278 ref_sym = (reference_symbolic *)ref;
1279
1280 free(ref_sym->target);
1281 ref_sym->target = git__strdup(target);
1282 if (ref_sym->target == NULL)
1283 return GIT_ENOMEM;
1284
1285 return loose_write(ref);
1286}
1287
1288/**
1289 * Other
1290 */
1291
7376ad99
VM
1292/*
1293 * Rename a reference
1294 *
1295 * If the reference is packed, we need to rewrite the
1296 * packfile to remove the reference from it and create
1297 * the reference back as a loose one.
1298 *
1299 * If the reference is loose, we just rename it on
1300 * the filesystem.
1301 *
1302 * We also need to re-insert the reference on its corresponding
1303 * in-memory cache, since the caches are indexed by refname.
1304 */
1305int git_reference_rename(git_reference *ref, const char *new_name, int force)
1306{
1307 int error;
1308 char *old_name;
1309 char old_path[GIT_PATH_MAX], new_path[GIT_PATH_MAX], normalized_name[GIT_REFNAME_MAX];
1310 git_reference *looked_up_ref, *old_ref = NULL;
1311
1312 assert(ref);
1313
1314 /* Ensure the name is valid */
1315 error = normalize_name(normalized_name, sizeof(normalized_name), new_name, ref->type & GIT_REF_OID);
1316 if (error < GIT_SUCCESS)
1317 return git__rethrow(error, "Failed to rename reference");
1318
1319 new_name = normalized_name;
1320
1321 /* Ensure we're not going to overwrite an existing reference
1322 unless the user has allowed us */
1323 error = git_reference_lookup(&looked_up_ref, ref->owner, new_name);
1324 if (error == GIT_SUCCESS && !force)
1325 return git__throw(GIT_EEXISTS, "Failed to rename reference. Reference already exists");
1326
1327 if (error < GIT_SUCCESS &&
1328 error != GIT_ENOTFOUND)
1329 return git__rethrow(error, "Failed to rename reference");
1330
1331 if ((error = reference_available(ref->owner, new_name, ref->name)) < GIT_SUCCESS)
1332 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference. Reference already exists");
1333
1334 old_name = ref->name;
1335 ref->name = git__strdup(new_name);
1336
1337 if (ref->name == NULL) {
1338 ref->name = old_name;
1339 return GIT_ENOMEM;
1340 }
1341
1342 if (ref->type & GIT_REF_PACKED) {
1343 /* write the packfile to disk; note
1344 * that the state of the in-memory cache is not
1345 * consistent, because the reference is indexed
1346 * by its old name but it already has the new one.
1347 * This doesn't affect writing, though, and allows
1348 * us to rollback if writing fails
1349 */
1350
1351 ref->type &= ~GIT_REF_PACKED;
1352
1353 /* Create the loose ref under its new name */
1354 error = loose_write(ref);
1355 if (error < GIT_SUCCESS) {
1356 ref->type |= GIT_REF_PACKED;
1357 goto cleanup;
1358 }
1359
1360 /* Remove from the packfile cache in order to avoid packing it back
1361 * Note : we do not rely on git_reference_delete() because this would
1362 * invalidate the reference.
1363 */
1364 git_hashtable_remove(ref->owner->references.packfile, old_name);
1365
1366 /* Recreate the packed-refs file without the reference */
1367 error = packed_write(ref->owner);
1368 if (error < GIT_SUCCESS)
1369 goto rename_loose_to_old_name;
1370
1371 } else {
1372 git__joinpath(old_path, ref->owner->path_repository, old_name);
1373 git__joinpath(new_path, ref->owner->path_repository, ref->name);
1374
1375 error = gitfo_mv_force(old_path, new_path);
1376 if (error < GIT_SUCCESS)
1377 goto cleanup;
1378
1379 /* Once succesfully renamed, remove from the cache the reference known by its old name*/
1380 git_hashtable_remove(ref->owner->references.loose_cache, old_name);
1381 }
1382
1383 /* Store the renamed reference into the loose ref cache */
1384 error = git_hashtable_insert2(ref->owner->references.loose_cache, ref->name, ref, (void **) &old_ref);
1385
1386 /* If we force-replaced, we need to free the old reference */
1387 if(old_ref)
1388 reference_free(old_ref);
1389
1390 free(old_name);
1391 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference");
1392
1393cleanup:
1394 /* restore the old name if this failed */
1395 free(ref->name);
1396 ref->name = old_name;
1397 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference");
1398
1399rename_loose_to_old_name:
1400 /* If we hit this point. Something *bad* happened! Think "Ghostbusters
1401 * crossing the streams" definition of bad.
1402 * Either the packed-refs has been correctly generated and something else
1403 * has gone wrong, or the writing of the new packed-refs has failed, and
1404 * we're stuck with the old one. As a loose ref always takes priority over
1405 * a packed ref, we'll eventually try and rename the generated loose ref to
1406 * its former name. It even that fails, well... we might have lost the reference
1407 * for good. :-/
1408 */
1409
1410 git__joinpath(old_path, ref->owner->path_repository, ref->name);
1411 git__joinpath(new_path, ref->owner->path_repository, old_name);
1412
1413 /* No error checking. We'll return the initial error */
1414 gitfo_mv_force(old_path, new_path);
1415
1416 /* restore the old name */
1417 free(ref->name);
1418 ref->name = old_name;
1419
1420 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference");
1421}
1422
1423
87d3acf4
VM
1424/*
1425 * Delete a reference.
1426 *
1427 * If the reference is packed, this is an expensive
1428 * operation. We need to remove the reference from
1429 * the memory cache and then rewrite the whole pack
1430 *
5ad0351d 1431 * If the reference is loose, we remove it on
87d3acf4 1432 * the filesystem and update the in-memory cache
5ad0351d 1433 * accordingly. We also make sure that an older version
1434 * of it doesn't exist as a packed reference. If this
1435 * is the case, this packed reference is removed as well.
87d3acf4
VM
1436 *
1437 * This obviously invalidates the `ref` pointer.
1438 */
1439int git_reference_delete(git_reference *ref)
1440{
1441 int error;
5ad0351d 1442 git_reference *reference;
87d3acf4
VM
1443
1444 assert(ref);
1445
1446 if (ref->type & GIT_REF_PACKED) {
d79f1da6
VM
1447 /* load the existing packfile */
1448 if ((error = packed_load(ref->owner)) < GIT_SUCCESS)
5bdf7b9f 1449 return git__rethrow(error, "Failed to delete reference");
d79f1da6 1450
2b397327
VM
1451 if (git_hashtable_remove(ref->owner->references.packfile, ref->name) < GIT_SUCCESS)
1452 return git__throw(GIT_ENOTFOUND, "Reference not found");
1453
87d3acf4
VM
1454 error = packed_write(ref->owner);
1455 } else {
1456 char full_path[GIT_PATH_MAX];
1457 git__joinpath(full_path, ref->owner->path_repository, ref->name);
1458 git_hashtable_remove(ref->owner->references.loose_cache, ref->name);
1459 error = gitfo_unlink(full_path);
5ad0351d 1460 if (error < GIT_SUCCESS)
1461 goto cleanup;
1462
1463 /* When deleting a loose reference, we have to ensure that an older
1464 * packed version of it doesn't exist
1465 */
5de079b8 1466 if (!git_reference_lookup(&reference, ref->owner, ref->name)) {
5ad0351d 1467 assert((reference->type & GIT_REF_PACKED) != 0);
1468 error = git_reference_delete(reference);
1469 }
87d3acf4
VM
1470 }
1471
5ad0351d 1472cleanup:
87d3acf4 1473 reference_free(ref);
5bdf7b9f 1474 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to delete reference");
87d3acf4
VM
1475}
1476
87d3acf4
VM
1477int git_reference_resolve(git_reference **resolved_ref, git_reference *ref)
1478{
1479 git_repository *repo;
1480 int error, i;
1481
1482 assert(resolved_ref && ref);
1483 *resolved_ref = NULL;
7341bf87
VM
1484
1485 if ((error = loose_update(ref)) < GIT_SUCCESS)
5bdf7b9f 1486 return git__rethrow(error, "Failed to resolve reference");
87d3acf4
VM
1487
1488 repo = ref->owner;
1489
1490 for (i = 0; i < MAX_NESTING_LEVEL; ++i) {
1491 reference_symbolic *ref_sym;
1492
68a146c1
CMN
1493 *resolved_ref = ref;
1494
0d5d5190 1495 if (ref->type & GIT_REF_OID)
87d3acf4 1496 return GIT_SUCCESS;
87d3acf4
VM
1497
1498 ref_sym = (reference_symbolic *)ref;
5de079b8 1499 if ((error = git_reference_lookup(&ref, repo, ref_sym->target)) < GIT_SUCCESS)
0d5d5190 1500 return error;
87d3acf4
VM
1501 }
1502
0d5d5190 1503 return git__throw(GIT_ENOMEM, "Failed to resolve reference. Reference is too nested");
87d3acf4
VM
1504}
1505
1506int git_reference_packall(git_repository *repo)
1507{
1508 int error;
1509
1510 /* load the existing packfile */
1511 if ((error = packed_load(repo)) < GIT_SUCCESS)
5bdf7b9f 1512 return git__rethrow(error, "Failed to pack references");
87d3acf4
VM
1513
1514 /* update it in-memory with all the loose references */
1515 if ((error = packed_loadloose(repo)) < GIT_SUCCESS)
5bdf7b9f 1516 return git__rethrow(error, "Failed to pack references");
87d3acf4
VM
1517
1518 /* write it back to disk */
1519 return packed_write(repo);
1520}
1521
43521d06 1522int git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload)
00571828
VM
1523{
1524 int error;
1525 struct dirent_list_data data;
1526 char refs_path[GIT_PATH_MAX];
1527
7ad96e51 1528 /* list all the packed references first */
00571828
VM
1529 if (list_flags & GIT_REF_PACKED) {
1530 const char *ref_name;
402a47a7 1531 void *GIT_UNUSED(_unused);
00571828 1532
09e8de0f 1533 if ((error = packed_load(repo)) < GIT_SUCCESS)
5bdf7b9f 1534 return git__rethrow(error, "Failed to list references");
00571828
VM
1535
1536 GIT_HASHTABLE_FOREACH(repo->references.packfile, ref_name, _unused,
09e8de0f 1537 if ((error = callback(ref_name, payload)) < GIT_SUCCESS)
0d5d5190 1538 return git__throw(error, "Failed to list references. User callback failed");
00571828
VM
1539 );
1540 }
1541
7ad96e51
VM
1542 /* now list the loose references, trying not to
1543 * duplicate the ref names already in the packed-refs file */
09e8de0f
VM
1544
1545 data.repo_path_len = strlen(repo->path_repository);
1546 data.list_flags = list_flags;
1547 data.repo = repo;
1548 data.callback = callback;
1549 data.callback_payload = payload;
1550
1551
7ad96e51 1552 git__joinpath(refs_path, repo->path_repository, GIT_REFS_DIR);
09e8de0f
VM
1553 return gitfo_dirent(refs_path, GIT_PATH_MAX, _dirent_loose_listall, &data);
1554}
1555
1556int cb__reflist_add(const char *ref, void *data)
1557{
1558 return git_vector_insert((git_vector *)data, git__strdup(ref));
1559}
1560
1561int git_reference_listall(git_strarray *array, git_repository *repo, unsigned int list_flags)
1562{
1563 int error;
1564 git_vector ref_list;
1565
1566 assert(array && repo);
1567
1568 array->strings = NULL;
1569 array->count = 0;
1570
1571 if (git_vector_init(&ref_list, 8, NULL) < GIT_SUCCESS)
1572 return GIT_ENOMEM;
1573
43521d06 1574 error = git_reference_foreach(repo, list_flags, &cb__reflist_add, (void *)&ref_list);
7ad96e51
VM
1575
1576 if (error < GIT_SUCCESS) {
09e8de0f 1577 git_vector_free(&ref_list);
0d5d5190 1578 return error;
7ad96e51
VM
1579 }
1580
09e8de0f
VM
1581 array->strings = (char **)ref_list.contents;
1582 array->count = ref_list.length;
00571828
VM
1583 return GIT_SUCCESS;
1584}
87d3acf4
VM
1585
1586
1587
1588
1589/*****************************************
1590 * Init/free (repository API)
1591 *****************************************/
2f8a8ab2
VM
1592int git_repository__refcache_init(git_refcache *refs)
1593{
1594 assert(refs);
9282e921 1595
87d3acf4 1596 refs->loose_cache = git_hashtable_alloc(
86194b24
VM
1597 default_table_size,
1598 reftable_hash,
1599 (git_hash_keyeq_ptr)strcmp);
1600
87d3acf4
VM
1601 /* packfile loaded lazily */
1602 refs->packfile = NULL;
9282e921 1603
87d3acf4 1604 return (refs->loose_cache) ? GIT_SUCCESS : GIT_ENOMEM;
2f8a8ab2 1605}
9282e921 1606
2f8a8ab2
VM
1607void git_repository__refcache_free(git_refcache *refs)
1608{
2f8a8ab2 1609 git_reference *reference;
402a47a7 1610 const void *GIT_UNUSED(_unused);
9282e921 1611
2f8a8ab2
VM
1612 assert(refs);
1613
87d3acf4 1614 GIT_HASHTABLE_FOREACH(refs->loose_cache, _unused, reference,
86194b24
VM
1615 reference_free(reference);
1616 );
1617
87d3acf4 1618 git_hashtable_free(refs->loose_cache);
2f8a8ab2 1619
87d3acf4
VM
1620 if (refs->packfile) {
1621 GIT_HASHTABLE_FOREACH(refs->packfile, _unused, reference,
1622 reference_free(reference);
1623 );
1624
1625 git_hashtable_free(refs->packfile);
1626 }
9282e921 1627}
2f8a8ab2 1628
87d3acf4
VM
1629
1630
1631/*****************************************
1632 * Name normalization
1633 *****************************************/
aa2120e9 1634static int check_valid_ref_char(char ch)
1635{
1636 if (ch <= ' ')
0d5d5190 1637 return GIT_ERROR;
aa2120e9 1638
1639 switch (ch) {
1640 case '~':
1641 case '^':
1642 case ':':
1643 case '\\':
1644 case '?':
1645 case '[':
e1be1028 1646 case '*':
0d5d5190 1647 return GIT_ERROR;
aa2120e9 1648 break;
1649
1650 default:
1651 return GIT_SUCCESS;
1652 }
1653}
1654
3101a3e5 1655static int normalize_name(char *buffer_out, size_t out_size, const char *name, int is_oid_ref)
aa2120e9 1656{
aa2120e9 1657 const char *name_end, *buffer_out_start;
1658 char *current;
1659 int contains_a_slash = 0;
1660
1661 assert(name && buffer_out);
1662
1663 buffer_out_start = buffer_out;
1664 current = (char *)name;
1665 name_end = name + strlen(name);
1666
3101a3e5
VM
1667 /* Terminating null byte */
1668 out_size--;
1669
aa2120e9 1670 /* A refname can not be empty */
1671 if (name_end == name)
5bdf7b9f 1672 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name is empty");
aa2120e9 1673
1674 /* A refname can not end with a dot or a slash */
1675 if (*(name_end - 1) == '.' || *(name_end - 1) == '/')
5bdf7b9f 1676 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name ends with dot or slash");
aa2120e9 1677
3101a3e5 1678 while (current < name_end && out_size) {
aa2120e9 1679 if (check_valid_ref_char(*current))
0d5d5190 1680 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name contains invalid characters");
aa2120e9 1681
1682 if (buffer_out > buffer_out_start) {
1683 char prev = *(buffer_out - 1);
1684
1685 /* A refname can not start with a dot nor contain a double dot */
1686 if (*current == '.' && ((prev == '.') || (prev == '/')))
5bdf7b9f 1687 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name starts with a dot or contains a double dot");
aa2120e9 1688
1689 /* '@{' is forbidden within a refname */
1690 if (*current == '{' && prev == '@')
5bdf7b9f 1691 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name contains '@{'");
aa2120e9 1692
1693 /* Prevent multiple slashes from being added to the output */
1694 if (*current == '/' && prev == '/') {
1695 current++;
1696 continue;
1697 }
1698 }
1699
e1be1028 1700 if (*current == '/')
aa2120e9 1701 contains_a_slash = 1;
aa2120e9 1702
1703 *buffer_out++ = *current++;
3101a3e5 1704 out_size--;
aa2120e9 1705 }
1706
3101a3e5
VM
1707 if (!out_size)
1708 return git__throw(GIT_EINVALIDREFNAME, "Reference name is too long");
1709
83c95128 1710 /* Object id refname have to contain at least one slash, except
df30eac1
JP
1711 * for HEAD in a detached state or MERGE_HEAD if we're in the
1712 * middle of a merge */
1713 if (is_oid_ref && !contains_a_slash && (strcmp(name, GIT_HEAD_FILE) && strcmp(name, GIT_MERGE_HEAD_FILE)))
0d5d5190 1714 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name contains no slashes");
aa2120e9 1715
1716 /* A refname can not end with ".lock" */
1717 if (!git__suffixcmp(name, GIT_FILELOCK_EXTENSION))
0d5d5190 1718 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name ends with '.lock'");
aa2120e9 1719
1720 *buffer_out = '\0';
1721
83c95128
CMN
1722 /*
1723 * For object id references, name has to start with refs/. Again,
1724 * we need to allow HEAD to be in a detached state.
1725 */
0d5d5190
VM
1726 if (is_oid_ref && !(git__prefixcmp(buffer_out_start, GIT_REFS_DIR) ||
1727 strcmp(buffer_out_start, GIT_HEAD_FILE)))
5bdf7b9f 1728 return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name does not start with 'refs/'");
aa2120e9 1729
0d5d5190 1730 return GIT_SUCCESS;
aa2120e9 1731}
2f8a8ab2 1732
3101a3e5 1733int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name)
86194b24 1734{
3101a3e5 1735 return normalize_name(buffer_out, out_size, name, 0);
86194b24
VM
1736}
1737
3101a3e5 1738int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name)
86194b24 1739{
3101a3e5 1740 return normalize_name(buffer_out, out_size, name, 1);
86194b24
VM
1741}
1742
1743