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"
16 static int note_error_notfound(void)
18 giterr_set(GITERR_INVALID
, "Note could not be found");
22 static int find_subtree_in_current_level(
26 const char *annotated_object_sha
,
30 const git_tree_entry
*entry
;
35 return note_error_notfound();
37 for (i
= 0; i
< git_tree_entrycount(parent
); i
++) {
38 entry
= git_tree_entry_byindex(parent
, i
);
40 if (!git__ishex(git_tree_entry_name(entry
)))
43 if (S_ISDIR(git_tree_entry_filemode(entry
))
44 && strlen(git_tree_entry_name(entry
)) == 2
45 && !strncmp(git_tree_entry_name(entry
), annotated_object_sha
+ fanout
, 2))
46 return git_tree_lookup(out
, repo
, git_tree_entry_id(entry
));
48 /* Not a DIR, so do we have an already existing blob? */
49 if (!strcmp(git_tree_entry_name(entry
), annotated_object_sha
+ fanout
))
53 return note_error_notfound();
56 static int find_subtree_r(git_tree
**out
, git_tree
*root
,
57 git_repository
*repo
, const char *target
, int *fanout
)
60 git_tree
*subtree
= NULL
;
64 error
= find_subtree_in_current_level(&subtree
, repo
, root
, target
, *fanout
);
65 if (error
== GIT_EEXISTS
)
66 return git_tree_lookup(out
, repo
, git_tree_id(root
));
72 error
= find_subtree_r(out
, subtree
, repo
, target
, fanout
);
73 git_tree_free(subtree
);
78 static int find_blob(git_oid
*blob
, git_tree
*tree
, const char *target
)
81 const git_tree_entry
*entry
;
83 for (i
=0; i
<git_tree_entrycount(tree
); i
++) {
84 entry
= git_tree_entry_byindex(tree
, i
);
86 if (!strcmp(git_tree_entry_name(entry
), target
)) {
87 /* found matching note object - return */
89 git_oid_cpy(blob
, git_tree_entry_id(entry
));
94 return note_error_notfound();
97 static int tree_write(
100 git_tree
*source_tree
,
101 const git_oid
*object_oid
,
102 const char *treeentry_name
,
103 unsigned int attributes
)
106 git_treebuilder
*tb
= NULL
;
107 const git_tree_entry
*entry
;
110 if ((error
= git_treebuilder_new(&tb
, repo
, source_tree
)) < 0)
114 if ((error
= git_treebuilder_insert(
115 &entry
, tb
, treeentry_name
, object_oid
, attributes
)) < 0)
118 if ((error
= git_treebuilder_remove(tb
, treeentry_name
)) < 0)
122 if ((error
= git_treebuilder_write(&tree_oid
, tb
)) < 0)
125 error
= git_tree_lookup(out
, repo
, &tree_oid
);
128 git_treebuilder_free(tb
);
132 static int manipulate_note_in_tree_r(
134 git_repository
*repo
,
137 const char *annotated_object_sha
,
139 int (*note_exists_cb
)(
141 git_repository
*repo
,
144 const char *annotated_object_sha
,
147 int (*note_notfound_cb
)(
149 git_repository
*repo
,
152 const char *annotated_object_sha
,
157 git_tree
*subtree
= NULL
, *new = NULL
;
158 char subtree_name
[3];
160 error
= find_subtree_in_current_level(
161 &subtree
, repo
, parent
, annotated_object_sha
, fanout
);
163 if (error
== GIT_EEXISTS
) {
164 error
= note_exists_cb(
165 out
, repo
, parent
, note_oid
, annotated_object_sha
, fanout
, error
);
169 if (error
== GIT_ENOTFOUND
) {
170 error
= note_notfound_cb(
171 out
, repo
, parent
, note_oid
, annotated_object_sha
, fanout
, error
);
178 /* An existing fanout has been found, let's dig deeper */
179 error
= manipulate_note_in_tree_r(
180 &new, repo
, subtree
, note_oid
, annotated_object_sha
,
181 fanout
+ 2, note_exists_cb
, note_notfound_cb
);
186 strncpy(subtree_name
, annotated_object_sha
+ fanout
, 2);
187 subtree_name
[2] = '\0';
189 error
= tree_write(out
, repo
, parent
, git_tree_id(new),
190 subtree_name
, GIT_FILEMODE_TREE
);
195 git_tree_free(subtree
);
199 static int remove_note_in_tree_eexists_cb(
201 git_repository
*repo
,
204 const char *annotated_object_sha
,
208 GIT_UNUSED(note_oid
);
209 GIT_UNUSED(current_error
);
211 return tree_write(out
, repo
, parent
, NULL
, annotated_object_sha
+ fanout
, 0);
214 static int remove_note_in_tree_enotfound_cb(
216 git_repository
*repo
,
219 const char *annotated_object_sha
,
226 GIT_UNUSED(note_oid
);
229 giterr_set(GITERR_REPOSITORY
, "Object '%s' has no note", annotated_object_sha
);
230 return current_error
;
233 static int insert_note_in_tree_eexists_cb(git_tree
**out
,
234 git_repository
*repo
,
237 const char *annotated_object_sha
,
244 GIT_UNUSED(note_oid
);
247 giterr_set(GITERR_REPOSITORY
, "Note for '%s' exists already", annotated_object_sha
);
248 return current_error
;
251 static int insert_note_in_tree_enotfound_cb(git_tree
**out
,
252 git_repository
*repo
,
255 const char *annotated_object_sha
,
259 GIT_UNUSED(current_error
);
261 /* No existing fanout at this level, insert in place */
267 annotated_object_sha
+ fanout
,
271 static int note_write(git_oid
*out
,
272 git_repository
*repo
,
273 const git_signature
*author
,
274 const git_signature
*committer
,
275 const char *notes_ref
,
277 git_tree
*commit_tree
,
279 git_commit
**parents
,
280 int allow_note_overwrite
)
284 git_tree
*tree
= NULL
;
286 // TODO: should we apply filters?
287 /* create note object */
288 if ((error
= git_blob_create_frombuffer(&oid
, repo
, note
, strlen(note
))) < 0)
291 if ((error
= manipulate_note_in_tree_r(
292 &tree
, repo
, commit_tree
, &oid
, target
, 0,
293 allow_note_overwrite
? insert_note_in_tree_enotfound_cb
: insert_note_in_tree_eexists_cb
,
294 insert_note_in_tree_enotfound_cb
)) < 0)
298 git_oid_cpy(out
, &oid
);
300 error
= git_commit_create(&oid
, repo
, notes_ref
, author
, committer
,
301 NULL
, GIT_NOTES_DEFAULT_MSG_ADD
,
302 tree
, *parents
== NULL
? 0 : 1, (const git_commit
**) parents
);
315 git_note
*note
= NULL
;
317 note
= (git_note
*)git__malloc(sizeof(git_note
));
318 GITERR_CHECK_ALLOC(note
);
320 git_oid_cpy(¬e
->id
, note_oid
);
322 if (git_signature_dup(¬e
->author
, git_commit_author(commit
)) < 0 ||
323 git_signature_dup(¬e
->committer
, git_commit_committer(commit
)) < 0)
326 note
->message
= git__strndup(git_blob_rawcontent(blob
), git_blob_rawsize(blob
));
327 GITERR_CHECK_ALLOC(note
->message
);
333 static int note_lookup(
335 git_repository
*repo
,
340 int error
, fanout
= 0;
342 git_blob
*blob
= NULL
;
343 git_note
*note
= NULL
;
344 git_tree
*subtree
= NULL
;
346 if ((error
= find_subtree_r(&subtree
, tree
, repo
, target
, &fanout
)) < 0)
349 if ((error
= find_blob(&oid
, subtree
, target
+ fanout
)) < 0)
352 if ((error
= git_blob_lookup(&blob
, repo
, &oid
)) < 0)
355 if ((error
= note_new(¬e
, &oid
, commit
, blob
)) < 0)
361 git_tree_free(subtree
);
366 static int note_remove(git_repository
*repo
,
367 const git_signature
*author
, const git_signature
*committer
,
368 const char *notes_ref
, git_tree
*tree
,
369 const char *target
, git_commit
**parents
)
372 git_tree
*tree_after_removal
= NULL
;
375 if ((error
= manipulate_note_in_tree_r(
376 &tree_after_removal
, repo
, tree
, NULL
, target
, 0,
377 remove_note_in_tree_eexists_cb
, remove_note_in_tree_enotfound_cb
)) < 0)
380 error
= git_commit_create(&oid
, repo
, notes_ref
, author
, committer
,
381 NULL
, GIT_NOTES_DEFAULT_MSG_RM
,
383 *parents
== NULL
? 0 : 1,
384 (const git_commit
**) parents
);
387 git_tree_free(tree_after_removal
);
391 static int note_get_default_ref(const char **out
, git_repository
*repo
)
394 int ret
= git_repository_config__weakptr(&cfg
, repo
);
396 *out
= (ret
!= 0) ? NULL
: git_config__get_string_force(
397 cfg
, "core.notesref", GIT_NOTES_DEFAULT_REF
);
402 static int normalize_namespace(const char **notes_ref
, git_repository
*repo
)
407 return note_get_default_ref(notes_ref
, repo
);
410 static int retrieve_note_tree_and_commit(
412 git_commit
**commit_out
,
413 git_repository
*repo
,
414 const char **notes_ref
)
419 if ((error
= normalize_namespace(notes_ref
, repo
)) < 0)
422 if ((error
= git_reference_name_to_id(&oid
, repo
, *notes_ref
)) < 0)
425 if (git_commit_lookup(commit_out
, repo
, &oid
) < 0)
428 if ((error
= git_commit_tree(tree_out
, *commit_out
)) < 0)
434 int git_note_read(git_note
**out
, git_repository
*repo
,
435 const char *notes_ref
, const git_oid
*oid
)
439 git_tree
*tree
= NULL
;
440 git_commit
*commit
= NULL
;
442 target
= git_oid_allocfmt(oid
);
443 GITERR_CHECK_ALLOC(target
);
445 if (!(error
= retrieve_note_tree_and_commit(
446 &tree
, &commit
, repo
, ¬es_ref
)))
447 error
= note_lookup(out
, repo
, commit
, tree
, target
);
451 git_commit_free(commit
);
457 git_repository
*repo
,
458 const char *notes_ref
,
459 const git_signature
*author
,
460 const git_signature
*committer
,
463 int allow_note_overwrite
)
467 git_commit
*commit
= NULL
;
468 git_tree
*tree
= NULL
;
470 target
= git_oid_allocfmt(oid
);
471 GITERR_CHECK_ALLOC(target
);
473 error
= retrieve_note_tree_and_commit(&tree
, &commit
, repo
, ¬es_ref
);
475 if (error
< 0 && error
!= GIT_ENOTFOUND
)
478 error
= note_write(out
, repo
, author
, committer
, notes_ref
,
479 note
, tree
, target
, &commit
, allow_note_overwrite
);
483 git_commit_free(commit
);
488 int git_note_remove(git_repository
*repo
, const char *notes_ref
,
489 const git_signature
*author
, const git_signature
*committer
,
494 git_commit
*commit
= NULL
;
495 git_tree
*tree
= NULL
;
497 target
= git_oid_allocfmt(oid
);
498 GITERR_CHECK_ALLOC(target
);
500 if (!(error
= retrieve_note_tree_and_commit(
501 &tree
, &commit
, repo
, ¬es_ref
)))
503 repo
, author
, committer
, notes_ref
, tree
, target
, &commit
);
506 git_commit_free(commit
);
511 int git_note_default_ref(const char **out
, git_repository
*repo
)
514 return note_get_default_ref(out
, repo
);
517 const git_signature
*git_note_committer(const git_note
*note
)
520 return note
->committer
;
523 const git_signature
*git_note_author(const git_note
*note
)
529 const char * git_note_message(const git_note
*note
)
532 return note
->message
;
535 const git_oid
* git_note_id(const git_note
*note
)
541 void git_note_free(git_note
*note
)
546 git_signature_free(note
->committer
);
547 git_signature_free(note
->author
);
548 git__free(note
->message
);
552 static int process_entry_path(
553 const char* entry_path
,
554 git_oid
*annotated_object_id
)
557 size_t i
= 0, j
= 0, len
;
558 git_buf buf
= GIT_BUF_INIT
;
560 if ((error
= git_buf_puts(&buf
, entry_path
)) < 0)
563 len
= git_buf_len(&buf
);
566 if (buf
.ptr
[i
] == '/') {
571 if (git__fromhex(buf
.ptr
[i
]) < 0) {
572 /* This is not a note entry */
577 buf
.ptr
[j
] = buf
.ptr
[i
];
586 if (j
!= GIT_OID_HEXSZ
) {
587 /* This is not a note entry */
591 error
= git_oid_fromstr(annotated_object_id
, buf
.ptr
);
598 int git_note_foreach(
599 git_repository
*repo
,
600 const char *notes_ref
,
601 git_note_foreach_cb note_cb
,
605 git_note_iterator
*iter
= NULL
;
606 git_oid note_id
, annotated_id
;
608 if ((error
= git_note_iterator_new(&iter
, repo
, notes_ref
)) < 0)
611 while (!(error
= git_note_next(¬e_id
, &annotated_id
, iter
))) {
612 if ((error
= note_cb(¬e_id
, &annotated_id
, payload
)) != 0) {
613 giterr_set_after_callback(error
);
618 if (error
== GIT_ITEROVER
)
621 git_note_iterator_free(iter
);
626 void git_note_iterator_free(git_note_iterator
*it
)
631 git_iterator_free(it
);
635 int git_note_iterator_new(
636 git_note_iterator
**it
,
637 git_repository
*repo
,
638 const char *notes_ref
)
641 git_commit
*commit
= NULL
;
642 git_tree
*tree
= NULL
;
644 error
= retrieve_note_tree_and_commit(&tree
, &commit
, repo
, ¬es_ref
);
648 if ((error
= git_iterator_for_tree(it
, tree
, 0, NULL
, NULL
)) < 0)
649 git_iterator_free(*it
);
653 git_commit_free(commit
);
660 git_oid
* annotated_id
,
661 git_note_iterator
*it
)
664 const git_index_entry
*item
;
666 if ((error
= git_iterator_current(&item
, it
)) < 0)
669 git_oid_cpy(note_id
, &item
->id
);
671 if (!(error
= process_entry_path(item
->path
, annotated_id
)))
672 git_iterator_advance(NULL
, it
);