2 * Copyright (C) 2009-2012 the libgit2 contributors
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 find_subtree_in_current_level(
20 const char *annotated_object_sha
,
24 const git_tree_entry
*entry
;
31 for (i
= 0; i
< git_tree_entrycount(parent
); i
++) {
32 entry
= git_tree_entry_byindex(parent
, i
);
34 if (!git__ishex(git_tree_entry_name(entry
)))
37 if (S_ISDIR(git_tree_entry_filemode(entry
))
38 && strlen(git_tree_entry_name(entry
)) == 2
39 && !strncmp(git_tree_entry_name(entry
), annotated_object_sha
+ fanout
, 2))
40 return git_tree_lookup(out
, repo
, git_tree_entry_id(entry
));
42 /* Not a DIR, so do we have an already existing blob? */
43 if (!strcmp(git_tree_entry_name(entry
), annotated_object_sha
+ fanout
))
50 static int find_subtree_r(git_tree
**out
, git_tree
*root
,
51 git_repository
*repo
, const char *target
, int *fanout
)
54 git_tree
*subtree
= NULL
;
58 error
= find_subtree_in_current_level(&subtree
, repo
, root
, target
, *fanout
);
59 if (error
== GIT_EEXISTS
) {
60 return git_tree_lookup(out
, repo
, git_tree_id(root
));
67 error
= find_subtree_r(out
, subtree
, repo
, target
, fanout
);
68 git_tree_free(subtree
);
73 static int find_blob(git_oid
*blob
, git_tree
*tree
, const char *target
)
76 const git_tree_entry
*entry
;
78 for (i
=0; i
<git_tree_entrycount(tree
); i
++) {
79 entry
= git_tree_entry_byindex(tree
, i
);
81 if (!strcmp(git_tree_entry_name(entry
), target
)) {
82 /* found matching note object - return */
84 git_oid_cpy(blob
, git_tree_entry_id(entry
));
91 static int tree_write(
94 git_tree
*source_tree
,
95 const git_oid
*object_oid
,
96 const char *treeentry_name
,
97 unsigned int attributes
)
100 git_treebuilder
*tb
= NULL
;
101 const git_tree_entry
*entry
;
104 if ((error
= git_treebuilder_create(&tb
, source_tree
)) < 0)
108 if ((error
= git_treebuilder_insert(
109 &entry
, tb
, treeentry_name
, object_oid
, attributes
)) < 0)
112 if ((error
= git_treebuilder_remove(tb
, treeentry_name
)) < 0)
116 if ((error
= git_treebuilder_write(&tree_oid
, repo
, tb
)) < 0)
119 error
= git_tree_lookup(out
, repo
, &tree_oid
);
122 git_treebuilder_free(tb
);
126 static int manipulate_note_in_tree_r(
128 git_repository
*repo
,
131 const char *annotated_object_sha
,
133 int (*note_exists_cb
)(
135 git_repository
*repo
,
138 const char *annotated_object_sha
,
141 int (*note_notfound_cb
)(
143 git_repository
*repo
,
146 const char *annotated_object_sha
,
151 git_tree
*subtree
= NULL
, *new = NULL
;
152 char subtree_name
[3];
154 error
= find_subtree_in_current_level(
155 &subtree
, repo
, parent
, annotated_object_sha
, fanout
);
157 if (error
== GIT_EEXISTS
) {
158 error
= note_exists_cb(
159 out
, repo
, parent
, note_oid
, annotated_object_sha
, fanout
, error
);
163 if (error
== GIT_ENOTFOUND
) {
164 error
= note_notfound_cb(
165 out
, repo
, parent
, note_oid
, annotated_object_sha
, fanout
, error
);
172 /* An existing fanout has been found, let's dig deeper */
173 error
= manipulate_note_in_tree_r(
174 &new, repo
, subtree
, note_oid
, annotated_object_sha
,
175 fanout
+ 2, note_exists_cb
, note_notfound_cb
);
180 strncpy(subtree_name
, annotated_object_sha
+ fanout
, 2);
181 subtree_name
[2] = '\0';
183 error
= tree_write(out
, repo
, parent
, git_tree_id(new),
184 subtree_name
, GIT_FILEMODE_TREE
);
189 git_tree_free(subtree
);
193 static int remove_note_in_tree_eexists_cb(
195 git_repository
*repo
,
198 const char *annotated_object_sha
,
202 GIT_UNUSED(note_oid
);
203 GIT_UNUSED(current_error
);
205 return tree_write(out
, repo
, parent
, NULL
, annotated_object_sha
+ fanout
, 0);
208 static int remove_note_in_tree_enotfound_cb(
210 git_repository
*repo
,
213 const char *annotated_object_sha
,
220 GIT_UNUSED(note_oid
);
223 giterr_set(GITERR_REPOSITORY
, "Object '%s' has no note", annotated_object_sha
);
224 return current_error
;
227 static int insert_note_in_tree_eexists_cb(git_tree
**out
,
228 git_repository
*repo
,
231 const char *annotated_object_sha
,
238 GIT_UNUSED(note_oid
);
241 giterr_set(GITERR_REPOSITORY
, "Note for '%s' exists already", annotated_object_sha
);
242 return current_error
;
245 static int insert_note_in_tree_enotfound_cb(git_tree
**out
,
246 git_repository
*repo
,
249 const char *annotated_object_sha
,
253 GIT_UNUSED(current_error
);
255 /* No existing fanout at this level, insert in place */
261 annotated_object_sha
+ fanout
,
265 static int note_write(git_oid
*out
,
266 git_repository
*repo
,
267 const git_signature
*author
,
268 const git_signature
*committer
,
269 const char *notes_ref
,
271 git_tree
*commit_tree
,
273 git_commit
**parents
,
274 int allow_note_overwrite
)
278 git_tree
*tree
= NULL
;
280 // TODO: should we apply filters?
281 /* create note object */
282 if ((error
= git_blob_create_frombuffer(&oid
, repo
, note
, strlen(note
))) < 0)
285 if ((error
= manipulate_note_in_tree_r(
286 &tree
, repo
, commit_tree
, &oid
, target
, 0,
287 allow_note_overwrite
? insert_note_in_tree_enotfound_cb
: insert_note_in_tree_eexists_cb
,
288 insert_note_in_tree_enotfound_cb
)) < 0)
292 git_oid_cpy(out
, &oid
);
294 error
= git_commit_create(&oid
, repo
, notes_ref
, author
, committer
,
295 NULL
, GIT_NOTES_DEFAULT_MSG_ADD
,
296 tree
, *parents
== NULL
? 0 : 1, (const git_commit
**) parents
);
303 static int note_new(git_note
**out
, git_oid
*note_oid
, git_blob
*blob
)
305 git_note
*note
= NULL
;
307 note
= (git_note
*)git__malloc(sizeof(git_note
));
308 GITERR_CHECK_ALLOC(note
);
310 git_oid_cpy(¬e
->oid
, note_oid
);
311 note
->message
= git__strdup((char *)git_blob_rawcontent(blob
));
312 GITERR_CHECK_ALLOC(note
->message
);
319 static int note_lookup(git_note
**out
, git_repository
*repo
,
320 git_tree
*tree
, const char *target
)
322 int error
, fanout
= 0;
324 git_blob
*blob
= NULL
;
325 git_note
*note
= NULL
;
326 git_tree
*subtree
= NULL
;
328 if ((error
= find_subtree_r(&subtree
, tree
, repo
, target
, &fanout
)) < 0)
331 if ((error
= find_blob(&oid
, subtree
, target
+ fanout
)) < 0)
334 if ((error
= git_blob_lookup(&blob
, repo
, &oid
)) < 0)
337 if ((error
= note_new(¬e
, &oid
, blob
)) < 0)
343 git_tree_free(subtree
);
348 static int note_remove(git_repository
*repo
,
349 const git_signature
*author
, const git_signature
*committer
,
350 const char *notes_ref
, git_tree
*tree
,
351 const char *target
, git_commit
**parents
)
354 git_tree
*tree_after_removal
= NULL
;
357 if ((error
= manipulate_note_in_tree_r(
358 &tree_after_removal
, repo
, tree
, NULL
, target
, 0,
359 remove_note_in_tree_eexists_cb
, remove_note_in_tree_enotfound_cb
)) < 0)
362 error
= git_commit_create(&oid
, repo
, notes_ref
, author
, committer
,
363 NULL
, GIT_NOTES_DEFAULT_MSG_RM
,
365 *parents
== NULL
? 0 : 1,
366 (const git_commit
**) parents
);
369 git_tree_free(tree_after_removal
);
373 static int note_get_default_ref(const char **out
, git_repository
*repo
)
380 if (git_repository_config__weakptr(&cfg
, repo
) < 0)
383 ret
= git_config_get_string(out
, cfg
, "core.notesRef");
384 if (ret
== GIT_ENOTFOUND
) {
385 *out
= GIT_NOTES_DEFAULT_REF
;
392 static int normalize_namespace(const char **notes_ref
, git_repository
*repo
)
397 return note_get_default_ref(notes_ref
, repo
);
400 static int retrieve_note_tree_and_commit(
402 git_commit
**commit_out
,
403 git_repository
*repo
,
404 const char **notes_ref
)
409 if ((error
= normalize_namespace(notes_ref
, repo
)) < 0)
412 if ((error
= git_reference_name_to_id(&oid
, repo
, *notes_ref
)) < 0)
415 if (git_commit_lookup(commit_out
, repo
, &oid
) < 0)
418 if ((error
= git_commit_tree(tree_out
, *commit_out
)) < 0)
424 int git_note_read(git_note
**out
, git_repository
*repo
,
425 const char *notes_ref
, const git_oid
*oid
)
429 git_tree
*tree
= NULL
;
430 git_commit
*commit
= NULL
;
432 target
= git_oid_allocfmt(oid
);
433 GITERR_CHECK_ALLOC(target
);
435 if ((error
= retrieve_note_tree_and_commit(&tree
, &commit
, repo
, ¬es_ref
)) < 0)
438 error
= note_lookup(out
, repo
, tree
, target
);
443 git_commit_free(commit
);
449 git_repository
*repo
,
450 const git_signature
*author
,
451 const git_signature
*committer
,
452 const char *notes_ref
,
455 int allow_note_overwrite
)
459 git_commit
*commit
= NULL
;
460 git_tree
*tree
= NULL
;
462 target
= git_oid_allocfmt(oid
);
463 GITERR_CHECK_ALLOC(target
);
465 error
= retrieve_note_tree_and_commit(&tree
, &commit
, repo
, ¬es_ref
);
467 if (error
< 0 && error
!= GIT_ENOTFOUND
)
470 error
= note_write(out
, repo
, author
, committer
, notes_ref
,
471 note
, tree
, target
, &commit
, allow_note_overwrite
);
475 git_commit_free(commit
);
480 int git_note_remove(git_repository
*repo
, const char *notes_ref
,
481 const git_signature
*author
, const git_signature
*committer
,
486 git_commit
*commit
= NULL
;
487 git_tree
*tree
= NULL
;
489 target
= git_oid_allocfmt(oid
);
490 GITERR_CHECK_ALLOC(target
);
492 if ((error
= retrieve_note_tree_and_commit(&tree
, &commit
, repo
, ¬es_ref
)) < 0)
495 error
= note_remove(repo
, author
, committer
, notes_ref
,
496 tree
, target
, &commit
);
500 git_commit_free(commit
);
505 int git_note_default_ref(const char **out
, git_repository
*repo
)
508 return note_get_default_ref(out
, repo
);
511 const char * git_note_message(const git_note
*note
)
514 return note
->message
;
517 const git_oid
* git_note_oid(const git_note
*note
)
523 void git_note_free(git_note
*note
)
528 git__free(note
->message
);
532 static int process_entry_path(
533 const char* entry_path
,
534 const git_oid
*note_oid
,
535 git_note_foreach_cb note_cb
,
539 size_t i
= 0, j
= 0, len
;
540 git_buf buf
= GIT_BUF_INIT
;
541 git_oid annotated_object_id
;
543 if ((error
= git_buf_puts(&buf
, entry_path
)) < 0)
546 len
= git_buf_len(&buf
);
549 if (buf
.ptr
[i
] == '/') {
554 if (git__fromhex(buf
.ptr
[i
]) < 0) {
555 /* This is not a note entry */
560 buf
.ptr
[j
] = buf
.ptr
[i
];
569 if (j
!= GIT_OID_HEXSZ
) {
570 /* This is not a note entry */
574 if ((error
= git_oid_fromstr(&annotated_object_id
, buf
.ptr
)) < 0)
577 if (note_cb(note_oid
, &annotated_object_id
, payload
))
585 int git_note_foreach(
586 git_repository
*repo
,
587 const char *notes_ref
,
588 git_note_foreach_cb note_cb
,
592 git_iterator
*iter
= NULL
;
593 git_tree
*tree
= NULL
;
594 git_commit
*commit
= NULL
;
595 const git_index_entry
*item
;
597 if (!(error
= retrieve_note_tree_and_commit(
598 &tree
, &commit
, repo
, ¬es_ref
)) &&
599 !(error
= git_iterator_for_tree(&iter
, tree
)))
600 error
= git_iterator_current(iter
, &item
);
602 while (!error
&& item
) {
603 error
= process_entry_path(item
->path
, &item
->oid
, note_cb
, payload
);
606 error
= git_iterator_advance(iter
, &item
);
609 git_iterator_free(iter
);
611 git_commit_free(commit
);