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_create(&tb
, 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
, repo
, 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
;
316 git_buf note_contents
= GIT_BUF_INIT
;
318 note
= (git_note
*)git__malloc(sizeof(git_note
));
319 GITERR_CHECK_ALLOC(note
);
321 git_oid_cpy(¬e
->id
, note_oid
);
323 if (git_signature_dup(¬e
->author
, git_commit_author(commit
)) < 0 ||
324 git_signature_dup(¬e
->committer
, git_commit_committer(commit
)) < 0)
327 git_buf_put(¬e_contents
, git_blob_rawcontent(blob
), git_blob_rawsize(blob
));
328 note
->message
= git_buf_detach(¬e_contents
);
334 static int note_lookup(
336 git_repository
*repo
,
341 int error
, fanout
= 0;
343 git_blob
*blob
= NULL
;
344 git_note
*note
= NULL
;
345 git_tree
*subtree
= NULL
;
347 if ((error
= find_subtree_r(&subtree
, tree
, repo
, target
, &fanout
)) < 0)
350 if ((error
= find_blob(&oid
, subtree
, target
+ fanout
)) < 0)
353 if ((error
= git_blob_lookup(&blob
, repo
, &oid
)) < 0)
356 if ((error
= note_new(¬e
, &oid
, commit
, blob
)) < 0)
362 git_tree_free(subtree
);
367 static int note_remove(git_repository
*repo
,
368 const git_signature
*author
, const git_signature
*committer
,
369 const char *notes_ref
, git_tree
*tree
,
370 const char *target
, git_commit
**parents
)
373 git_tree
*tree_after_removal
= NULL
;
376 if ((error
= manipulate_note_in_tree_r(
377 &tree_after_removal
, repo
, tree
, NULL
, target
, 0,
378 remove_note_in_tree_eexists_cb
, remove_note_in_tree_enotfound_cb
)) < 0)
381 error
= git_commit_create(&oid
, repo
, notes_ref
, author
, committer
,
382 NULL
, GIT_NOTES_DEFAULT_MSG_RM
,
384 *parents
== NULL
? 0 : 1,
385 (const git_commit
**) parents
);
388 git_tree_free(tree_after_removal
);
392 static int note_get_default_ref(const char **out
, git_repository
*repo
)
395 int ret
= git_repository_config__weakptr(&cfg
, repo
);
397 *out
= (ret
!= 0) ? NULL
: git_config__get_string_force(
398 cfg
, "core.notesref", GIT_NOTES_DEFAULT_REF
);
403 static int normalize_namespace(const char **notes_ref
, git_repository
*repo
)
408 return note_get_default_ref(notes_ref
, repo
);
411 static int retrieve_note_tree_and_commit(
413 git_commit
**commit_out
,
414 git_repository
*repo
,
415 const char **notes_ref
)
420 if ((error
= normalize_namespace(notes_ref
, repo
)) < 0)
423 if ((error
= git_reference_name_to_id(&oid
, repo
, *notes_ref
)) < 0)
426 if (git_commit_lookup(commit_out
, repo
, &oid
) < 0)
429 if ((error
= git_commit_tree(tree_out
, *commit_out
)) < 0)
435 int git_note_read(git_note
**out
, git_repository
*repo
,
436 const char *notes_ref
, const git_oid
*oid
)
440 git_tree
*tree
= NULL
;
441 git_commit
*commit
= NULL
;
443 target
= git_oid_allocfmt(oid
);
444 GITERR_CHECK_ALLOC(target
);
446 if (!(error
= retrieve_note_tree_and_commit(
447 &tree
, &commit
, repo
, ¬es_ref
)))
448 error
= note_lookup(out
, repo
, commit
, tree
, target
);
452 git_commit_free(commit
);
458 git_repository
*repo
,
459 const git_signature
*author
,
460 const git_signature
*committer
,
461 const char *notes_ref
,
464 int allow_note_overwrite
)
468 git_commit
*commit
= NULL
;
469 git_tree
*tree
= NULL
;
471 target
= git_oid_allocfmt(oid
);
472 GITERR_CHECK_ALLOC(target
);
474 error
= retrieve_note_tree_and_commit(&tree
, &commit
, repo
, ¬es_ref
);
476 if (error
< 0 && error
!= GIT_ENOTFOUND
)
479 error
= note_write(out
, repo
, author
, committer
, notes_ref
,
480 note
, tree
, target
, &commit
, allow_note_overwrite
);
484 git_commit_free(commit
);
489 int git_note_remove(git_repository
*repo
, const char *notes_ref
,
490 const git_signature
*author
, const git_signature
*committer
,
495 git_commit
*commit
= NULL
;
496 git_tree
*tree
= NULL
;
498 target
= git_oid_allocfmt(oid
);
499 GITERR_CHECK_ALLOC(target
);
501 if (!(error
= retrieve_note_tree_and_commit(
502 &tree
, &commit
, repo
, ¬es_ref
)))
504 repo
, author
, committer
, notes_ref
, tree
, target
, &commit
);
507 git_commit_free(commit
);
512 int git_note_default_ref(const char **out
, git_repository
*repo
)
515 return note_get_default_ref(out
, repo
);
518 const git_signature
*git_note_committer(const git_note
*note
)
521 return note
->committer
;
524 const git_signature
*git_note_author(const git_note
*note
)
530 const char * git_note_message(const git_note
*note
)
533 return note
->message
;
536 const git_oid
* git_note_id(const git_note
*note
)
542 void git_note_free(git_note
*note
)
547 git_signature_free(note
->committer
);
548 git_signature_free(note
->author
);
549 git__free(note
->message
);
553 static int process_entry_path(
554 const char* entry_path
,
555 git_oid
*annotated_object_id
)
558 size_t i
= 0, j
= 0, len
;
559 git_buf buf
= GIT_BUF_INIT
;
561 if ((error
= git_buf_puts(&buf
, entry_path
)) < 0)
564 len
= git_buf_len(&buf
);
567 if (buf
.ptr
[i
] == '/') {
572 if (git__fromhex(buf
.ptr
[i
]) < 0) {
573 /* This is not a note entry */
578 buf
.ptr
[j
] = buf
.ptr
[i
];
587 if (j
!= GIT_OID_HEXSZ
) {
588 /* This is not a note entry */
592 error
= git_oid_fromstr(annotated_object_id
, buf
.ptr
);
599 int git_note_foreach(
600 git_repository
*repo
,
601 const char *notes_ref
,
602 git_note_foreach_cb note_cb
,
606 git_note_iterator
*iter
= NULL
;
607 git_oid note_id
, annotated_id
;
609 if ((error
= git_note_iterator_new(&iter
, repo
, notes_ref
)) < 0)
612 while (!(error
= git_note_next(¬e_id
, &annotated_id
, iter
))) {
613 if ((error
= note_cb(¬e_id
, &annotated_id
, payload
)) != 0) {
614 giterr_set_after_callback(error
);
619 if (error
== GIT_ITEROVER
)
622 git_note_iterator_free(iter
);
627 void git_note_iterator_free(git_note_iterator
*it
)
632 git_iterator_free(it
);
636 int git_note_iterator_new(
637 git_note_iterator
**it
,
638 git_repository
*repo
,
639 const char *notes_ref
)
642 git_commit
*commit
= NULL
;
643 git_tree
*tree
= NULL
;
645 error
= retrieve_note_tree_and_commit(&tree
, &commit
, repo
, ¬es_ref
);
649 if ((error
= git_iterator_for_tree(it
, tree
, 0, NULL
, NULL
)) < 0)
650 git_iterator_free(*it
);
654 git_commit_free(commit
);
661 git_oid
* annotated_id
,
662 git_note_iterator
*it
)
665 const git_index_entry
*item
;
667 if ((error
= git_iterator_current(&item
, it
)) < 0)
670 git_oid_cpy(note_id
, &item
->id
);
672 if (!(error
= process_entry_path(item
->path
, annotated_id
)))
673 git_iterator_advance(NULL
, it
);