2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
14 #include "signature.h"
17 static int note_error_notfound(void)
19 git_error_set(GIT_ERROR_INVALID
, "note could not be found");
23 static int find_subtree_in_current_level(
27 const char *annotated_object_sha
,
31 const git_tree_entry
*entry
;
36 return note_error_notfound();
38 for (i
= 0; i
< git_tree_entrycount(parent
); i
++) {
39 entry
= git_tree_entry_byindex(parent
, i
);
41 if (!git__ishex(git_tree_entry_name(entry
)))
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
));
49 /* Not a DIR, so do we have an already existing blob? */
50 if (!strcmp(git_tree_entry_name(entry
), annotated_object_sha
+ fanout
))
54 return note_error_notfound();
57 static int find_subtree_r(git_tree
**out
, git_tree
*root
,
58 git_repository
*repo
, const char *target
, int *fanout
)
61 git_tree
*subtree
= NULL
;
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
));
73 error
= find_subtree_r(out
, subtree
, repo
, target
, fanout
);
74 git_tree_free(subtree
);
79 static int find_blob(git_oid
*blob
, git_tree
*tree
, const char *target
)
82 const git_tree_entry
*entry
;
84 for (i
=0; i
<git_tree_entrycount(tree
); i
++) {
85 entry
= git_tree_entry_byindex(tree
, i
);
87 if (!strcmp(git_tree_entry_name(entry
), target
)) {
88 /* found matching note object - return */
90 git_oid_cpy(blob
, git_tree_entry_id(entry
));
95 return note_error_notfound();
98 static int tree_write(
100 git_repository
*repo
,
101 git_tree
*source_tree
,
102 const git_oid
*object_oid
,
103 const char *treeentry_name
,
104 unsigned int attributes
)
107 git_treebuilder
*tb
= NULL
;
108 const git_tree_entry
*entry
;
111 if ((error
= git_treebuilder_new(&tb
, repo
, source_tree
)) < 0)
115 if ((error
= git_treebuilder_insert(
116 &entry
, tb
, treeentry_name
, object_oid
, attributes
)) < 0)
119 if ((error
= git_treebuilder_remove(tb
, treeentry_name
)) < 0)
123 if ((error
= git_treebuilder_write(&tree_oid
, tb
)) < 0)
126 error
= git_tree_lookup(out
, repo
, &tree_oid
);
129 git_treebuilder_free(tb
);
133 static int manipulate_note_in_tree_r(
135 git_repository
*repo
,
138 const char *annotated_object_sha
,
140 int (*note_exists_cb
)(
142 git_repository
*repo
,
145 const char *annotated_object_sha
,
148 int (*note_notfound_cb
)(
150 git_repository
*repo
,
153 const char *annotated_object_sha
,
158 git_tree
*subtree
= NULL
, *new = NULL
;
159 char subtree_name
[3];
161 error
= find_subtree_in_current_level(
162 &subtree
, repo
, parent
, annotated_object_sha
, fanout
);
164 if (error
== GIT_EEXISTS
) {
165 error
= note_exists_cb(
166 out
, repo
, parent
, note_oid
, annotated_object_sha
, fanout
, error
);
170 if (error
== GIT_ENOTFOUND
) {
171 error
= note_notfound_cb(
172 out
, repo
, parent
, note_oid
, annotated_object_sha
, fanout
, error
);
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
);
187 strncpy(subtree_name
, annotated_object_sha
+ fanout
, 2);
188 subtree_name
[2] = '\0';
190 error
= tree_write(out
, repo
, parent
, git_tree_id(new),
191 subtree_name
, GIT_FILEMODE_TREE
);
196 git_tree_free(subtree
);
200 static int remove_note_in_tree_eexists_cb(
202 git_repository
*repo
,
205 const char *annotated_object_sha
,
209 GIT_UNUSED(note_oid
);
210 GIT_UNUSED(current_error
);
212 return tree_write(out
, repo
, parent
, NULL
, annotated_object_sha
+ fanout
, 0);
215 static int remove_note_in_tree_enotfound_cb(
217 git_repository
*repo
,
220 const char *annotated_object_sha
,
227 GIT_UNUSED(note_oid
);
230 git_error_set(GIT_ERROR_REPOSITORY
, "object '%s' has no note", annotated_object_sha
);
231 return current_error
;
234 static int insert_note_in_tree_eexists_cb(git_tree
**out
,
235 git_repository
*repo
,
238 const char *annotated_object_sha
,
245 GIT_UNUSED(note_oid
);
248 git_error_set(GIT_ERROR_REPOSITORY
, "note for '%s' exists already", annotated_object_sha
);
249 return current_error
;
252 static int insert_note_in_tree_enotfound_cb(git_tree
**out
,
253 git_repository
*repo
,
256 const char *annotated_object_sha
,
260 GIT_UNUSED(current_error
);
262 /* No existing fanout at this level, insert in place */
268 annotated_object_sha
+ fanout
,
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
,
280 git_tree
*commit_tree
,
282 git_commit
**parents
,
283 int allow_note_overwrite
)
287 git_tree
*tree
= NULL
;
289 /* TODO: should we apply filters? */
290 /* create note object */
291 if ((error
= git_blob_create_from_buffer(&oid
, repo
, note
, strlen(note
))) < 0)
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)
301 git_oid_cpy(notes_blob_out
, &oid
);
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
);
308 if (notes_commit_out
)
309 git_oid_cpy(notes_commit_out
, &oid
);
322 git_note
*note
= NULL
;
323 git_object_size_t blobsize
;
325 note
= git__malloc(sizeof(git_note
));
326 GIT_ERROR_CHECK_ALLOC(note
);
328 git_oid_cpy(¬e
->id
, note_oid
);
330 if (git_signature_dup(¬e
->author
, git_commit_author(commit
)) < 0 ||
331 git_signature_dup(¬e
->committer
, git_commit_committer(commit
)) < 0)
334 blobsize
= git_blob_rawsize(blob
);
335 GIT_ERROR_CHECK_BLOBSIZE(blobsize
);
337 note
->message
= git__strndup(git_blob_rawcontent(blob
), (size_t)blobsize
);
338 GIT_ERROR_CHECK_ALLOC(note
->message
);
344 static int note_lookup(
346 git_repository
*repo
,
351 int error
, fanout
= 0;
353 git_blob
*blob
= NULL
;
354 git_note
*note
= NULL
;
355 git_tree
*subtree
= NULL
;
357 if ((error
= find_subtree_r(&subtree
, tree
, repo
, target
, &fanout
)) < 0)
360 if ((error
= find_blob(&oid
, subtree
, target
+ fanout
)) < 0)
363 if ((error
= git_blob_lookup(&blob
, repo
, &oid
)) < 0)
366 if ((error
= note_new(¬e
, &oid
, commit
, blob
)) < 0)
372 git_tree_free(subtree
);
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
)
385 git_tree
*tree_after_removal
= NULL
;
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)
393 error
= git_commit_create(&oid
, repo
, notes_ref
, author
, committer
,
394 NULL
, GIT_NOTES_DEFAULT_MSG_RM
,
396 *parents
== NULL
? 0 : 1,
397 (const git_commit
**) parents
);
402 if (notes_commit_out
)
403 git_oid_cpy(notes_commit_out
, &oid
);
406 git_tree_free(tree_after_removal
);
410 static int note_get_default_ref(char **out
, git_repository
*repo
)
413 int ret
= git_repository_config__weakptr(&cfg
, repo
);
415 *out
= (ret
!= 0) ? NULL
: git_config__get_string_force(
416 cfg
, "core.notesref", GIT_NOTES_DEFAULT_REF
);
421 static int normalize_namespace(char **out
, git_repository
*repo
, const char *notes_ref
)
424 *out
= git__strdup(notes_ref
);
425 GIT_ERROR_CHECK_ALLOC(*out
);
429 return note_get_default_ref(out
, repo
);
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
)
441 if ((error
= normalize_namespace(notes_ref_out
, repo
, notes_ref
)) < 0)
444 if ((error
= git_reference_name_to_id(&oid
, repo
, *notes_ref_out
)) < 0)
447 if (git_commit_lookup(commit_out
, repo
, &oid
) < 0)
453 int git_note_commit_read(
455 git_repository
*repo
,
456 git_commit
*notes_commit
,
460 git_tree
*tree
= NULL
;
461 char target
[GIT_OID_HEXSZ
+ 1];
463 git_oid_tostr(target
, sizeof(target
), oid
);
465 if ((error
= git_commit_tree(&tree
, notes_commit
)) < 0)
468 error
= note_lookup(out
, repo
, notes_commit
, tree
, target
);
475 int git_note_read(git_note
**out
, git_repository
*repo
,
476 const char *notes_ref_in
, const git_oid
*oid
)
479 char *notes_ref
= NULL
;
480 git_commit
*commit
= NULL
;
482 error
= retrieve_note_commit(&commit
, ¬es_ref
, repo
, notes_ref_in
);
487 error
= git_note_commit_read(out
, repo
, commit
, oid
);
490 git__free(notes_ref
);
491 git_commit_free(commit
);
495 int git_note_commit_create(
496 git_oid
*notes_commit_out
,
497 git_oid
*notes_blob_out
,
498 git_repository
*repo
,
500 const git_signature
*author
,
501 const git_signature
*committer
,
504 int allow_note_overwrite
)
507 git_tree
*tree
= NULL
;
508 char target
[GIT_OID_HEXSZ
+ 1];
510 git_oid_tostr(target
, sizeof(target
), oid
);
512 if (parent
!= NULL
&& (error
= git_commit_tree(&tree
, parent
)) < 0)
515 error
= note_write(notes_commit_out
, notes_blob_out
, repo
, author
,
516 committer
, NULL
, note
, tree
, target
, &parent
, allow_note_overwrite
);
528 git_repository
*repo
,
529 const char *notes_ref_in
,
530 const git_signature
*author
,
531 const git_signature
*committer
,
534 int allow_note_overwrite
)
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
;
542 error
= retrieve_note_commit(&existing_notes_commit
, ¬es_ref
,
545 if (error
< 0 && error
!= GIT_ENOTFOUND
)
548 error
= git_note_commit_create(¬es_commit_oid
,
550 repo
, existing_notes_commit
, author
,
551 committer
, oid
, note
,
552 allow_note_overwrite
);
556 error
= git_reference_create(&ref
, repo
, notes_ref
,
557 ¬es_commit_oid
, 1, NULL
);
560 git_oid_cpy(out
, ¬es_blob_oid
);
563 git__free(notes_ref
);
564 git_commit_free(existing_notes_commit
);
565 git_reference_free(ref
);
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
,
578 git_tree
*tree
= NULL
;
579 char target
[GIT_OID_HEXSZ
+ 1];
581 git_oid_tostr(target
, sizeof(target
), oid
);
583 if ((error
= git_commit_tree(&tree
, notes_commit
)) < 0)
586 error
= note_remove(notes_commit_out
,
587 repo
, author
, committer
, NULL
, tree
, target
, ¬es_commit
);
594 int git_note_remove(git_repository
*repo
, const char *notes_ref_in
,
595 const git_signature
*author
, const git_signature
*committer
,
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
;
604 error
= retrieve_note_commit(&existing_notes_commit
, ¬es_ref_target
,
610 error
= git_note_commit_remove(&new_notes_commit
, repo
,
611 existing_notes_commit
, author
, committer
, oid
);
615 error
= git_reference_create(¬es_ref
, repo
, notes_ref_target
,
616 &new_notes_commit
, 1, NULL
);
619 git__free(notes_ref_target
);
620 git_reference_free(notes_ref
);
621 git_commit_free(existing_notes_commit
);
625 int git_note_default_ref(git_buf
*out
, git_repository
*repo
)
632 git_buf_sanitize(out
);
634 if ((error
= note_get_default_ref(&default_ref
, repo
)) < 0)
637 git_buf_attach(out
, default_ref
, strlen(default_ref
));
641 const git_signature
*git_note_committer(const git_note
*note
)
644 return note
->committer
;
647 const git_signature
*git_note_author(const git_note
*note
)
653 const char * git_note_message(const git_note
*note
)
656 return note
->message
;
659 const git_oid
* git_note_id(const git_note
*note
)
665 void git_note_free(git_note
*note
)
670 git_signature_free(note
->committer
);
671 git_signature_free(note
->author
);
672 git__free(note
->message
);
676 static int process_entry_path(
677 const char* entry_path
,
678 git_oid
*annotated_object_id
)
681 size_t i
= 0, j
= 0, len
;
682 git_buf buf
= GIT_BUF_INIT
;
684 if ((error
= git_buf_puts(&buf
, entry_path
)) < 0)
687 len
= git_buf_len(&buf
);
690 if (buf
.ptr
[i
] == '/') {
695 if (git__fromhex(buf
.ptr
[i
]) < 0) {
696 /* This is not a note entry */
701 buf
.ptr
[j
] = buf
.ptr
[i
];
710 if (j
!= GIT_OID_HEXSZ
) {
711 /* This is not a note entry */
715 error
= git_oid_fromstr(annotated_object_id
, buf
.ptr
);
718 git_buf_dispose(&buf
);
722 int git_note_foreach(
723 git_repository
*repo
,
724 const char *notes_ref
,
725 git_note_foreach_cb note_cb
,
729 git_note_iterator
*iter
= NULL
;
730 git_oid note_id
, annotated_id
;
732 if ((error
= git_note_iterator_new(&iter
, repo
, notes_ref
)) < 0)
735 while (!(error
= git_note_next(¬e_id
, &annotated_id
, iter
))) {
736 if ((error
= note_cb(¬e_id
, &annotated_id
, payload
)) != 0) {
737 git_error_set_after_callback(error
);
742 if (error
== GIT_ITEROVER
)
745 git_note_iterator_free(iter
);
749 void git_note_iterator_free(git_note_iterator
*it
)
754 git_iterator_free(it
);
757 int git_note_commit_iterator_new(
758 git_note_iterator
**it
,
759 git_commit
*notes_commit
)
764 if ((error
= git_commit_tree(&tree
, notes_commit
)) < 0)
767 if ((error
= git_iterator_for_tree(it
, tree
, NULL
)) < 0)
768 git_iterator_free(*it
);
776 int git_note_iterator_new(
777 git_note_iterator
**it
,
778 git_repository
*repo
,
779 const char *notes_ref_in
)
782 git_commit
*commit
= NULL
;
785 error
= retrieve_note_commit(&commit
, ¬es_ref
, repo
, notes_ref_in
);
789 error
= git_note_commit_iterator_new(it
, commit
);
792 git__free(notes_ref
);
793 git_commit_free(commit
);
800 git_oid
* annotated_id
,
801 git_note_iterator
*it
)
804 const git_index_entry
*item
;
806 if ((error
= git_iterator_current(&item
, it
)) < 0)
809 git_oid_cpy(note_id
, &item
->id
);
811 if ((error
= process_entry_path(item
->path
, annotated_id
)) < 0)
814 if ((error
= git_iterator_advance(NULL
, it
)) < 0 && error
!= GIT_ITEROVER
)