]> git.proxmox.com Git - libgit2.git/blob - src/notes.c
New upstream version 1.1.0+dfsg.1
[libgit2.git] / src / notes.c
1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8 #include "notes.h"
9
10 #include "git2.h"
11 #include "refs.h"
12 #include "config.h"
13 #include "iterator.h"
14 #include "signature.h"
15 #include "blob.h"
16
17 static int note_error_notfound(void)
18 {
19 git_error_set(GIT_ERROR_INVALID, "note could not be found");
20 return GIT_ENOTFOUND;
21 }
22
23 static int find_subtree_in_current_level(
24 git_tree **out,
25 git_repository *repo,
26 git_tree *parent,
27 const char *annotated_object_sha,
28 int fanout)
29 {
30 size_t i;
31 const git_tree_entry *entry;
32
33 *out = NULL;
34
35 if (parent == NULL)
36 return note_error_notfound();
37
38 for (i = 0; i < git_tree_entrycount(parent); i++) {
39 entry = git_tree_entry_byindex(parent, i);
40
41 if (!git__ishex(git_tree_entry_name(entry)))
42 continue;
43
44 if (S_ISDIR(git_tree_entry_filemode(entry))
45 && strlen(git_tree_entry_name(entry)) == 2
46 && !strncmp(git_tree_entry_name(entry), annotated_object_sha + fanout, 2))
47 return git_tree_lookup(out, repo, git_tree_entry_id(entry));
48
49 /* Not a DIR, so do we have an already existing blob? */
50 if (!strcmp(git_tree_entry_name(entry), annotated_object_sha + fanout))
51 return GIT_EEXISTS;
52 }
53
54 return note_error_notfound();
55 }
56
57 static int find_subtree_r(git_tree **out, git_tree *root,
58 git_repository *repo, const char *target, int *fanout)
59 {
60 int error;
61 git_tree *subtree = NULL;
62
63 *out = NULL;
64
65 error = find_subtree_in_current_level(&subtree, repo, root, target, *fanout);
66 if (error == GIT_EEXISTS)
67 return git_tree_lookup(out, repo, git_tree_id(root));
68
69 if (error < 0)
70 return error;
71
72 *fanout += 2;
73 error = find_subtree_r(out, subtree, repo, target, fanout);
74 git_tree_free(subtree);
75
76 return error;
77 }
78
79 static int find_blob(git_oid *blob, git_tree *tree, const char *target)
80 {
81 size_t i;
82 const git_tree_entry *entry;
83
84 for (i=0; i<git_tree_entrycount(tree); i++) {
85 entry = git_tree_entry_byindex(tree, i);
86
87 if (!strcmp(git_tree_entry_name(entry), target)) {
88 /* found matching note object - return */
89
90 git_oid_cpy(blob, git_tree_entry_id(entry));
91 return 0;
92 }
93 }
94
95 return note_error_notfound();
96 }
97
98 static int tree_write(
99 git_tree **out,
100 git_repository *repo,
101 git_tree *source_tree,
102 const git_oid *object_oid,
103 const char *treeentry_name,
104 unsigned int attributes)
105 {
106 int error;
107 git_treebuilder *tb = NULL;
108 const git_tree_entry *entry;
109 git_oid tree_oid;
110
111 if ((error = git_treebuilder_new(&tb, repo, source_tree)) < 0)
112 goto cleanup;
113
114 if (object_oid) {
115 if ((error = git_treebuilder_insert(
116 &entry, tb, treeentry_name, object_oid, attributes)) < 0)
117 goto cleanup;
118 } else {
119 if ((error = git_treebuilder_remove(tb, treeentry_name)) < 0)
120 goto cleanup;
121 }
122
123 if ((error = git_treebuilder_write(&tree_oid, tb)) < 0)
124 goto cleanup;
125
126 error = git_tree_lookup(out, repo, &tree_oid);
127
128 cleanup:
129 git_treebuilder_free(tb);
130 return error;
131 }
132
133 static int manipulate_note_in_tree_r(
134 git_tree **out,
135 git_repository *repo,
136 git_tree *parent,
137 git_oid *note_oid,
138 const char *annotated_object_sha,
139 int fanout,
140 int (*note_exists_cb)(
141 git_tree **out,
142 git_repository *repo,
143 git_tree *parent,
144 git_oid *note_oid,
145 const char *annotated_object_sha,
146 int fanout,
147 int current_error),
148 int (*note_notfound_cb)(
149 git_tree **out,
150 git_repository *repo,
151 git_tree *parent,
152 git_oid *note_oid,
153 const char *annotated_object_sha,
154 int fanout,
155 int current_error))
156 {
157 int error;
158 git_tree *subtree = NULL, *new = NULL;
159 char subtree_name[3];
160
161 error = find_subtree_in_current_level(
162 &subtree, repo, parent, annotated_object_sha, fanout);
163
164 if (error == GIT_EEXISTS) {
165 error = note_exists_cb(
166 out, repo, parent, note_oid, annotated_object_sha, fanout, error);
167 goto cleanup;
168 }
169
170 if (error == GIT_ENOTFOUND) {
171 error = note_notfound_cb(
172 out, repo, parent, note_oid, annotated_object_sha, fanout, error);
173 goto cleanup;
174 }
175
176 if (error < 0)
177 goto cleanup;
178
179 /* An existing fanout has been found, let's dig deeper */
180 error = manipulate_note_in_tree_r(
181 &new, repo, subtree, note_oid, annotated_object_sha,
182 fanout + 2, note_exists_cb, note_notfound_cb);
183
184 if (error < 0)
185 goto cleanup;
186
187 strncpy(subtree_name, annotated_object_sha + fanout, 2);
188 subtree_name[2] = '\0';
189
190 error = tree_write(out, repo, parent, git_tree_id(new),
191 subtree_name, GIT_FILEMODE_TREE);
192
193
194 cleanup:
195 git_tree_free(new);
196 git_tree_free(subtree);
197 return error;
198 }
199
200 static int remove_note_in_tree_eexists_cb(
201 git_tree **out,
202 git_repository *repo,
203 git_tree *parent,
204 git_oid *note_oid,
205 const char *annotated_object_sha,
206 int fanout,
207 int current_error)
208 {
209 GIT_UNUSED(note_oid);
210 GIT_UNUSED(current_error);
211
212 return tree_write(out, repo, parent, NULL, annotated_object_sha + fanout, 0);
213 }
214
215 static int remove_note_in_tree_enotfound_cb(
216 git_tree **out,
217 git_repository *repo,
218 git_tree *parent,
219 git_oid *note_oid,
220 const char *annotated_object_sha,
221 int fanout,
222 int current_error)
223 {
224 GIT_UNUSED(out);
225 GIT_UNUSED(repo);
226 GIT_UNUSED(parent);
227 GIT_UNUSED(note_oid);
228 GIT_UNUSED(fanout);
229
230 git_error_set(GIT_ERROR_REPOSITORY, "object '%s' has no note", annotated_object_sha);
231 return current_error;
232 }
233
234 static int insert_note_in_tree_eexists_cb(git_tree **out,
235 git_repository *repo,
236 git_tree *parent,
237 git_oid *note_oid,
238 const char *annotated_object_sha,
239 int fanout,
240 int current_error)
241 {
242 GIT_UNUSED(out);
243 GIT_UNUSED(repo);
244 GIT_UNUSED(parent);
245 GIT_UNUSED(note_oid);
246 GIT_UNUSED(fanout);
247
248 git_error_set(GIT_ERROR_REPOSITORY, "note for '%s' exists already", annotated_object_sha);
249 return current_error;
250 }
251
252 static int insert_note_in_tree_enotfound_cb(git_tree **out,
253 git_repository *repo,
254 git_tree *parent,
255 git_oid *note_oid,
256 const char *annotated_object_sha,
257 int fanout,
258 int current_error)
259 {
260 GIT_UNUSED(current_error);
261
262 /* No existing fanout at this level, insert in place */
263 return tree_write(
264 out,
265 repo,
266 parent,
267 note_oid,
268 annotated_object_sha + fanout,
269 GIT_FILEMODE_BLOB);
270 }
271
272 static int note_write(
273 git_oid *notes_commit_out,
274 git_oid *notes_blob_out,
275 git_repository *repo,
276 const git_signature *author,
277 const git_signature *committer,
278 const char *notes_ref,
279 const char *note,
280 git_tree *commit_tree,
281 const char *target,
282 git_commit **parents,
283 int allow_note_overwrite)
284 {
285 int error;
286 git_oid oid;
287 git_tree *tree = NULL;
288
289 /* TODO: should we apply filters? */
290 /* create note object */
291 if ((error = git_blob_create_from_buffer(&oid, repo, note, strlen(note))) < 0)
292 goto cleanup;
293
294 if ((error = manipulate_note_in_tree_r(
295 &tree, repo, commit_tree, &oid, target, 0,
296 allow_note_overwrite ? insert_note_in_tree_enotfound_cb : insert_note_in_tree_eexists_cb,
297 insert_note_in_tree_enotfound_cb)) < 0)
298 goto cleanup;
299
300 if (notes_blob_out)
301 git_oid_cpy(notes_blob_out, &oid);
302
303
304 error = git_commit_create(&oid, repo, notes_ref, author, committer,
305 NULL, GIT_NOTES_DEFAULT_MSG_ADD,
306 tree, *parents == NULL ? 0 : 1, (const git_commit **) parents);
307
308 if (notes_commit_out)
309 git_oid_cpy(notes_commit_out, &oid);
310
311 cleanup:
312 git_tree_free(tree);
313 return error;
314 }
315
316 static int note_new(
317 git_note **out,
318 git_oid *note_oid,
319 git_commit *commit,
320 git_blob *blob)
321 {
322 git_note *note = NULL;
323 git_object_size_t blobsize;
324
325 note = git__malloc(sizeof(git_note));
326 GIT_ERROR_CHECK_ALLOC(note);
327
328 git_oid_cpy(&note->id, note_oid);
329
330 if (git_signature_dup(&note->author, git_commit_author(commit)) < 0 ||
331 git_signature_dup(&note->committer, git_commit_committer(commit)) < 0)
332 return -1;
333
334 blobsize = git_blob_rawsize(blob);
335 GIT_ERROR_CHECK_BLOBSIZE(blobsize);
336
337 note->message = git__strndup(git_blob_rawcontent(blob), (size_t)blobsize);
338 GIT_ERROR_CHECK_ALLOC(note->message);
339
340 *out = note;
341 return 0;
342 }
343
344 static int note_lookup(
345 git_note **out,
346 git_repository *repo,
347 git_commit *commit,
348 git_tree *tree,
349 const char *target)
350 {
351 int error, fanout = 0;
352 git_oid oid;
353 git_blob *blob = NULL;
354 git_note *note = NULL;
355 git_tree *subtree = NULL;
356
357 if ((error = find_subtree_r(&subtree, tree, repo, target, &fanout)) < 0)
358 goto cleanup;
359
360 if ((error = find_blob(&oid, subtree, target + fanout)) < 0)
361 goto cleanup;
362
363 if ((error = git_blob_lookup(&blob, repo, &oid)) < 0)
364 goto cleanup;
365
366 if ((error = note_new(&note, &oid, commit, blob)) < 0)
367 goto cleanup;
368
369 *out = note;
370
371 cleanup:
372 git_tree_free(subtree);
373 git_blob_free(blob);
374 return error;
375 }
376
377 static int note_remove(
378 git_oid *notes_commit_out,
379 git_repository *repo,
380 const git_signature *author, const git_signature *committer,
381 const char *notes_ref, git_tree *tree,
382 const char *target, git_commit **parents)
383 {
384 int error;
385 git_tree *tree_after_removal = NULL;
386 git_oid oid;
387
388 if ((error = manipulate_note_in_tree_r(
389 &tree_after_removal, repo, tree, NULL, target, 0,
390 remove_note_in_tree_eexists_cb, remove_note_in_tree_enotfound_cb)) < 0)
391 goto cleanup;
392
393 error = git_commit_create(&oid, repo, notes_ref, author, committer,
394 NULL, GIT_NOTES_DEFAULT_MSG_RM,
395 tree_after_removal,
396 *parents == NULL ? 0 : 1,
397 (const git_commit **) parents);
398
399 if (error < 0)
400 goto cleanup;
401
402 if (notes_commit_out)
403 git_oid_cpy(notes_commit_out, &oid);
404
405 cleanup:
406 git_tree_free(tree_after_removal);
407 return error;
408 }
409
410 static int note_get_default_ref(char **out, git_repository *repo)
411 {
412 git_config *cfg;
413 int ret = git_repository_config__weakptr(&cfg, repo);
414
415 *out = (ret != 0) ? NULL : git_config__get_string_force(
416 cfg, "core.notesref", GIT_NOTES_DEFAULT_REF);
417
418 return ret;
419 }
420
421 static int normalize_namespace(char **out, git_repository *repo, const char *notes_ref)
422 {
423 if (notes_ref) {
424 *out = git__strdup(notes_ref);
425 GIT_ERROR_CHECK_ALLOC(*out);
426 return 0;
427 }
428
429 return note_get_default_ref(out, repo);
430 }
431
432 static int retrieve_note_commit(
433 git_commit **commit_out,
434 char **notes_ref_out,
435 git_repository *repo,
436 const char *notes_ref)
437 {
438 int error;
439 git_oid oid;
440
441 if ((error = normalize_namespace(notes_ref_out, repo, notes_ref)) < 0)
442 return error;
443
444 if ((error = git_reference_name_to_id(&oid, repo, *notes_ref_out)) < 0)
445 return error;
446
447 if (git_commit_lookup(commit_out, repo, &oid) < 0)
448 return error;
449
450 return 0;
451 }
452
453 int git_note_commit_read(
454 git_note **out,
455 git_repository *repo,
456 git_commit *notes_commit,
457 const git_oid *oid)
458 {
459 int error;
460 git_tree *tree = NULL;
461 char target[GIT_OID_HEXSZ + 1];
462
463 git_oid_tostr(target, sizeof(target), oid);
464
465 if ((error = git_commit_tree(&tree, notes_commit)) < 0)
466 goto cleanup;
467
468 error = note_lookup(out, repo, notes_commit, tree, target);
469
470 cleanup:
471 git_tree_free(tree);
472 return error;
473 }
474
475 int git_note_read(git_note **out, git_repository *repo,
476 const char *notes_ref_in, const git_oid *oid)
477 {
478 int error;
479 char *notes_ref = NULL;
480 git_commit *commit = NULL;
481
482 error = retrieve_note_commit(&commit, &notes_ref, repo, notes_ref_in);
483
484 if (error < 0)
485 goto cleanup;
486
487 error = git_note_commit_read(out, repo, commit, oid);
488
489 cleanup:
490 git__free(notes_ref);
491 git_commit_free(commit);
492 return error;
493 }
494
495 int git_note_commit_create(
496 git_oid *notes_commit_out,
497 git_oid *notes_blob_out,
498 git_repository *repo,
499 git_commit *parent,
500 const git_signature *author,
501 const git_signature *committer,
502 const git_oid *oid,
503 const char *note,
504 int allow_note_overwrite)
505 {
506 int error;
507 git_tree *tree = NULL;
508 char target[GIT_OID_HEXSZ + 1];
509
510 git_oid_tostr(target, sizeof(target), oid);
511
512 if (parent != NULL && (error = git_commit_tree(&tree, parent)) < 0)
513 goto cleanup;
514
515 error = note_write(notes_commit_out, notes_blob_out, repo, author,
516 committer, NULL, note, tree, target, &parent, allow_note_overwrite);
517
518 if (error < 0)
519 goto cleanup;
520
521 cleanup:
522 git_tree_free(tree);
523 return error;
524 }
525
526 int git_note_create(
527 git_oid *out,
528 git_repository *repo,
529 const char *notes_ref_in,
530 const git_signature *author,
531 const git_signature *committer,
532 const git_oid *oid,
533 const char *note,
534 int allow_note_overwrite)
535 {
536 int error;
537 char *notes_ref = NULL;
538 git_commit *existing_notes_commit = NULL;
539 git_reference *ref = NULL;
540 git_oid notes_blob_oid, notes_commit_oid;
541
542 error = retrieve_note_commit(&existing_notes_commit, &notes_ref,
543 repo, notes_ref_in);
544
545 if (error < 0 && error != GIT_ENOTFOUND)
546 goto cleanup;
547
548 error = git_note_commit_create(&notes_commit_oid,
549 &notes_blob_oid,
550 repo, existing_notes_commit, author,
551 committer, oid, note,
552 allow_note_overwrite);
553 if (error < 0)
554 goto cleanup;
555
556 error = git_reference_create(&ref, repo, notes_ref,
557 &notes_commit_oid, 1, NULL);
558
559 if (out != NULL)
560 git_oid_cpy(out, &notes_blob_oid);
561
562 cleanup:
563 git__free(notes_ref);
564 git_commit_free(existing_notes_commit);
565 git_reference_free(ref);
566 return error;
567 }
568
569 int git_note_commit_remove(
570 git_oid *notes_commit_out,
571 git_repository *repo,
572 git_commit *notes_commit,
573 const git_signature *author,
574 const git_signature *committer,
575 const git_oid *oid)
576 {
577 int error;
578 git_tree *tree = NULL;
579 char target[GIT_OID_HEXSZ + 1];
580
581 git_oid_tostr(target, sizeof(target), oid);
582
583 if ((error = git_commit_tree(&tree, notes_commit)) < 0)
584 goto cleanup;
585
586 error = note_remove(notes_commit_out,
587 repo, author, committer, NULL, tree, target, &notes_commit);
588
589 cleanup:
590 git_tree_free(tree);
591 return error;
592 }
593
594 int git_note_remove(git_repository *repo, const char *notes_ref_in,
595 const git_signature *author, const git_signature *committer,
596 const git_oid *oid)
597 {
598 int error;
599 char *notes_ref_target = NULL;
600 git_commit *existing_notes_commit = NULL;
601 git_oid new_notes_commit;
602 git_reference *notes_ref = NULL;
603
604 error = retrieve_note_commit(&existing_notes_commit, &notes_ref_target,
605 repo, notes_ref_in);
606
607 if (error < 0)
608 goto cleanup;
609
610 error = git_note_commit_remove(&new_notes_commit, repo,
611 existing_notes_commit, author, committer, oid);
612 if (error < 0)
613 goto cleanup;
614
615 error = git_reference_create(&notes_ref, repo, notes_ref_target,
616 &new_notes_commit, 1, NULL);
617
618 cleanup:
619 git__free(notes_ref_target);
620 git_reference_free(notes_ref);
621 git_commit_free(existing_notes_commit);
622 return error;
623 }
624
625 int git_note_default_ref(git_buf *out, git_repository *repo)
626 {
627 char *default_ref;
628 int error;
629
630 assert(out && repo);
631
632 git_buf_sanitize(out);
633
634 if ((error = note_get_default_ref(&default_ref, repo)) < 0)
635 return error;
636
637 git_buf_attach(out, default_ref, strlen(default_ref));
638 return 0;
639 }
640
641 const git_signature *git_note_committer(const git_note *note)
642 {
643 assert(note);
644 return note->committer;
645 }
646
647 const git_signature *git_note_author(const git_note *note)
648 {
649 assert(note);
650 return note->author;
651 }
652
653 const char * git_note_message(const git_note *note)
654 {
655 assert(note);
656 return note->message;
657 }
658
659 const git_oid * git_note_id(const git_note *note)
660 {
661 assert(note);
662 return &note->id;
663 }
664
665 void git_note_free(git_note *note)
666 {
667 if (note == NULL)
668 return;
669
670 git_signature_free(note->committer);
671 git_signature_free(note->author);
672 git__free(note->message);
673 git__free(note);
674 }
675
676 static int process_entry_path(
677 const char* entry_path,
678 git_oid *annotated_object_id)
679 {
680 int error = 0;
681 size_t i = 0, j = 0, len;
682 git_buf buf = GIT_BUF_INIT;
683
684 if ((error = git_buf_puts(&buf, entry_path)) < 0)
685 goto cleanup;
686
687 len = git_buf_len(&buf);
688
689 while (i < len) {
690 if (buf.ptr[i] == '/') {
691 i++;
692 continue;
693 }
694
695 if (git__fromhex(buf.ptr[i]) < 0) {
696 /* This is not a note entry */
697 goto cleanup;
698 }
699
700 if (i != j)
701 buf.ptr[j] = buf.ptr[i];
702
703 i++;
704 j++;
705 }
706
707 buf.ptr[j] = '\0';
708 buf.size = j;
709
710 if (j != GIT_OID_HEXSZ) {
711 /* This is not a note entry */
712 goto cleanup;
713 }
714
715 error = git_oid_fromstr(annotated_object_id, buf.ptr);
716
717 cleanup:
718 git_buf_dispose(&buf);
719 return error;
720 }
721
722 int git_note_foreach(
723 git_repository *repo,
724 const char *notes_ref,
725 git_note_foreach_cb note_cb,
726 void *payload)
727 {
728 int error;
729 git_note_iterator *iter = NULL;
730 git_oid note_id, annotated_id;
731
732 if ((error = git_note_iterator_new(&iter, repo, notes_ref)) < 0)
733 return error;
734
735 while (!(error = git_note_next(&note_id, &annotated_id, iter))) {
736 if ((error = note_cb(&note_id, &annotated_id, payload)) != 0) {
737 git_error_set_after_callback(error);
738 break;
739 }
740 }
741
742 if (error == GIT_ITEROVER)
743 error = 0;
744
745 git_note_iterator_free(iter);
746 return error;
747 }
748
749 void git_note_iterator_free(git_note_iterator *it)
750 {
751 if (it == NULL)
752 return;
753
754 git_iterator_free(it);
755 }
756
757 int git_note_commit_iterator_new(
758 git_note_iterator **it,
759 git_commit *notes_commit)
760 {
761 int error;
762 git_tree *tree;
763
764 if ((error = git_commit_tree(&tree, notes_commit)) < 0)
765 goto cleanup;
766
767 if ((error = git_iterator_for_tree(it, tree, NULL)) < 0)
768 git_iterator_free(*it);
769
770 cleanup:
771 git_tree_free(tree);
772
773 return error;
774 }
775
776 int git_note_iterator_new(
777 git_note_iterator **it,
778 git_repository *repo,
779 const char *notes_ref_in)
780 {
781 int error;
782 git_commit *commit = NULL;
783 char *notes_ref;
784
785 error = retrieve_note_commit(&commit, &notes_ref, repo, notes_ref_in);
786 if (error < 0)
787 goto cleanup;
788
789 error = git_note_commit_iterator_new(it, commit);
790
791 cleanup:
792 git__free(notes_ref);
793 git_commit_free(commit);
794
795 return error;
796 }
797
798 int git_note_next(
799 git_oid* note_id,
800 git_oid* annotated_id,
801 git_note_iterator *it)
802 {
803 int error;
804 const git_index_entry *item;
805
806 if ((error = git_iterator_current(&item, it)) < 0)
807 return error;
808
809 git_oid_cpy(note_id, &item->id);
810
811 if ((error = process_entry_path(item->path, annotated_id)) < 0)
812 return error;
813
814 if ((error = git_iterator_advance(NULL, it)) < 0 && error != GIT_ITEROVER)
815 return error;
816
817 return 0;
818 }