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.
15 static int find_subtree_in_current_level(
19 const char *annotated_object_sha
,
23 const git_tree_entry
*entry
;
30 for (i
= 0; i
< git_tree_entrycount(parent
); i
++) {
31 entry
= git_tree_entry_byindex(parent
, i
);
33 if (!git__ishex(git_tree_entry_name(entry
)))
36 if (S_ISDIR(git_tree_entry_attributes(entry
))
37 && strlen(git_tree_entry_name(entry
)) == 2
38 && !strncmp(git_tree_entry_name(entry
), annotated_object_sha
+ fanout
, 2))
39 return git_tree_lookup(out
, repo
, git_tree_entry_id(entry
));
41 /* Not a DIR, so do we have an already existing blob? */
42 if (!strcmp(git_tree_entry_name(entry
), annotated_object_sha
+ fanout
))
49 static int find_subtree_r(git_tree
**out
, git_tree
*root
,
50 git_repository
*repo
, const char *target
, int *fanout
)
53 git_tree
*subtree
= NULL
;
57 error
= find_subtree_in_current_level(&subtree
, repo
, root
, target
, *fanout
);
58 if (error
== GIT_EEXISTS
) {
59 return git_tree_lookup(out
, repo
, git_tree_id(root
));
66 error
= find_subtree_r(out
, subtree
, repo
, target
, fanout
);
67 git_tree_free(subtree
);
72 static int find_blob(git_oid
*blob
, git_tree
*tree
, const char *target
)
75 const git_tree_entry
*entry
;
77 for (i
=0; i
<git_tree_entrycount(tree
); i
++) {
78 entry
= git_tree_entry_byindex(tree
, i
);
80 if (!strcmp(git_tree_entry_name(entry
), target
)) {
81 /* found matching note object - return */
83 git_oid_cpy(blob
, git_tree_entry_id(entry
));
90 static int tree_write(
93 git_tree
*source_tree
,
94 const git_oid
*object_oid
,
95 const char *treeentry_name
,
96 unsigned int attributes
)
99 git_treebuilder
*tb
= NULL
;
100 const git_tree_entry
*entry
;
103 if ((error
= git_treebuilder_create(&tb
, source_tree
)) < 0)
107 if ((error
= git_treebuilder_insert(
108 &entry
, tb
, treeentry_name
, object_oid
, attributes
)) < 0)
111 if ((error
= git_treebuilder_remove(tb
, treeentry_name
)) < 0)
115 if ((error
= git_treebuilder_write(&tree_oid
, repo
, tb
)) < 0)
118 error
= git_tree_lookup(out
, repo
, &tree_oid
);
121 git_treebuilder_free(tb
);
125 static int manipulate_note_in_tree_r(
127 git_repository
*repo
,
130 const char *annotated_object_sha
,
132 int (*note_exists_cb
)(
134 git_repository
*repo
,
137 const char *annotated_object_sha
,
140 int (*note_notfound_cb
)(
142 git_repository
*repo
,
145 const char *annotated_object_sha
,
150 git_tree
*subtree
= NULL
, *new = NULL
;
151 char subtree_name
[3];
153 error
= find_subtree_in_current_level(
154 &subtree
, repo
, parent
, annotated_object_sha
, fanout
);
156 if (error
== GIT_EEXISTS
) {
157 error
= note_exists_cb(
158 out
, repo
, parent
, note_oid
, annotated_object_sha
, fanout
, error
);
162 if (error
== GIT_ENOTFOUND
) {
163 error
= note_notfound_cb(
164 out
, repo
, parent
, note_oid
, annotated_object_sha
, fanout
, error
);
171 /* An existing fanout has been found, let's dig deeper */
172 error
= manipulate_note_in_tree_r(
173 &new, repo
, subtree
, note_oid
, annotated_object_sha
,
174 fanout
+ 2, note_exists_cb
, note_notfound_cb
);
179 strncpy(subtree_name
, annotated_object_sha
+ fanout
, 2);
180 subtree_name
[2] = '\0';
182 error
= tree_write(out
, repo
, parent
, git_tree_id(new),
183 subtree_name
, 0040000);
188 git_tree_free(subtree
);
192 static int remove_note_in_tree_eexists_cb(
194 git_repository
*repo
,
197 const char *annotated_object_sha
,
201 GIT_UNUSED(note_oid
);
202 GIT_UNUSED(current_error
);
204 return tree_write(out
, repo
, parent
, NULL
, annotated_object_sha
+ fanout
, 0);
207 static int remove_note_in_tree_enotfound_cb(
209 git_repository
*repo
,
212 const char *annotated_object_sha
,
219 GIT_UNUSED(note_oid
);
222 giterr_set(GITERR_REPOSITORY
, "Object '%s' has no note", annotated_object_sha
);
223 return current_error
;
226 static int insert_note_in_tree_eexists_cb(git_tree
**out
,
227 git_repository
*repo
,
230 const char *annotated_object_sha
,
237 GIT_UNUSED(note_oid
);
240 giterr_set(GITERR_REPOSITORY
, "Note for '%s' exists already", annotated_object_sha
);
241 return current_error
;
244 static int insert_note_in_tree_enotfound_cb(git_tree
**out
,
245 git_repository
*repo
,
248 const char *annotated_object_sha
,
252 GIT_UNUSED(current_error
);
254 /* No existing fanout at this level, insert in place */
255 return tree_write(out
, repo
, parent
, note_oid
, annotated_object_sha
+ fanout
, 0100644);
258 static int note_write(git_oid
*out
,
259 git_repository
*repo
,
260 git_signature
*author
,
261 git_signature
*committer
,
262 const char *notes_ref
,
264 git_tree
*commit_tree
,
266 git_commit
**parents
)
270 git_tree
*tree
= NULL
;
272 // TODO: should we apply filters?
273 /* create note object */
274 if ((error
= git_blob_create_frombuffer(&oid
, repo
, note
, strlen(note
))) < 0)
277 if ((error
= manipulate_note_in_tree_r(
278 &tree
, repo
, commit_tree
, &oid
, target
, 0,
279 insert_note_in_tree_eexists_cb
, insert_note_in_tree_enotfound_cb
)) < 0)
283 git_oid_cpy(out
, &oid
);
285 error
= git_commit_create(&oid
, repo
, notes_ref
, author
, committer
,
286 NULL
, GIT_NOTES_DEFAULT_MSG_ADD
,
287 tree
, *parents
== NULL
? 0 : 1, (const git_commit
**) parents
);
294 static int note_new(git_note
**out
, git_oid
*note_oid
, git_blob
*blob
)
296 git_note
*note
= NULL
;
298 note
= (git_note
*)git__malloc(sizeof(git_note
));
299 GITERR_CHECK_ALLOC(note
);
301 git_oid_cpy(¬e
->oid
, note_oid
);
302 note
->message
= git__strdup((char *)git_blob_rawcontent(blob
));
303 GITERR_CHECK_ALLOC(note
->message
);
310 static int note_lookup(git_note
**out
, git_repository
*repo
,
311 git_tree
*tree
, const char *target
)
313 int error
, fanout
= 0;
315 git_blob
*blob
= NULL
;
316 git_note
*note
= NULL
;
317 git_tree
*subtree
= NULL
;
319 if ((error
= find_subtree_r(&subtree
, tree
, repo
, target
, &fanout
)) < 0)
322 if ((error
= find_blob(&oid
, subtree
, target
+ fanout
)) < 0)
325 if ((error
= git_blob_lookup(&blob
, repo
, &oid
)) < 0)
328 if ((error
= note_new(¬e
, &oid
, blob
)) < 0)
334 git_tree_free(subtree
);
339 static int note_remove(git_repository
*repo
,
340 git_signature
*author
, git_signature
*committer
,
341 const char *notes_ref
, git_tree
*tree
,
342 const char *target
, git_commit
**parents
)
345 git_tree
*tree_after_removal
= NULL
;
348 if ((error
= manipulate_note_in_tree_r(
349 &tree_after_removal
, repo
, tree
, NULL
, target
, 0,
350 remove_note_in_tree_eexists_cb
, remove_note_in_tree_enotfound_cb
)) < 0)
353 error
= git_commit_create(&oid
, repo
, notes_ref
, author
, committer
,
354 NULL
, GIT_NOTES_DEFAULT_MSG_RM
,
356 *parents
== NULL
? 0 : 1,
357 (const git_commit
**) parents
);
360 git_tree_free(tree_after_removal
);
364 static int note_get_default_ref(const char **out
, git_repository
*repo
)
371 if (git_repository_config__weakptr(&cfg
, repo
) < 0)
374 ret
= git_config_get_string(out
, cfg
, "core.notesRef");
375 if (ret
== GIT_ENOTFOUND
) {
376 *out
= GIT_NOTES_DEFAULT_REF
;
383 static int normalize_namespace(const char **notes_ref
, git_repository
*repo
)
388 return note_get_default_ref(notes_ref
, repo
);
391 static int retrieve_note_tree_and_commit(
393 git_commit
**commit_out
,
394 git_repository
*repo
,
395 const char **notes_ref
)
400 if ((error
= normalize_namespace(notes_ref
, repo
)) < 0)
403 if ((error
= git_reference_name_to_oid(&oid
, repo
, *notes_ref
)) < 0)
406 if (git_commit_lookup(commit_out
, repo
, &oid
) < 0)
409 if ((error
= git_commit_tree(tree_out
, *commit_out
)) < 0)
415 int git_note_read(git_note
**out
, git_repository
*repo
,
416 const char *notes_ref
, const git_oid
*oid
)
420 git_tree
*tree
= NULL
;
421 git_commit
*commit
= NULL
;
423 target
= git_oid_allocfmt(oid
);
424 GITERR_CHECK_ALLOC(target
);
426 if ((error
= retrieve_note_tree_and_commit(&tree
, &commit
, repo
, ¬es_ref
)) < 0)
429 error
= note_lookup(out
, repo
, tree
, target
);
434 git_commit_free(commit
);
439 git_oid
*out
, git_repository
*repo
,
440 git_signature
*author
, git_signature
*committer
,
441 const char *notes_ref
, const git_oid
*oid
,
446 git_commit
*commit
= NULL
;
447 git_tree
*tree
= NULL
;
449 target
= git_oid_allocfmt(oid
);
450 GITERR_CHECK_ALLOC(target
);
452 error
= retrieve_note_tree_and_commit(&tree
, &commit
, repo
, ¬es_ref
);
454 if (error
< 0 && error
!= GIT_ENOTFOUND
)
457 error
= note_write(out
, repo
, author
, committer
, notes_ref
,
458 note
, tree
, target
, &commit
);
462 git_commit_free(commit
);
467 int git_note_remove(git_repository
*repo
, const char *notes_ref
,
468 git_signature
*author
, git_signature
*committer
,
473 git_commit
*commit
= NULL
;
474 git_tree
*tree
= NULL
;
476 target
= git_oid_allocfmt(oid
);
477 GITERR_CHECK_ALLOC(target
);
479 if ((error
= retrieve_note_tree_and_commit(&tree
, &commit
, repo
, ¬es_ref
)) < 0)
482 error
= note_remove(repo
, author
, committer
, notes_ref
,
483 tree
, target
, &commit
);
487 git_commit_free(commit
);
492 int git_note_default_ref(const char **out
, git_repository
*repo
)
495 return note_get_default_ref(out
, repo
);
498 const char * git_note_message(git_note
*note
)
501 return note
->message
;
504 const git_oid
* git_note_oid(git_note
*note
)
510 void git_note_free(git_note
*note
)
515 git__free(note
->message
);
519 static int process_entry_path(
520 const char* entry_path
,
521 const git_oid
*note_oid
,
522 int (*note_cb
)(git_note_data
*note_data
, void *payload
),
526 size_t i
= 0, j
= 0, len
;
527 git_buf buf
= GIT_BUF_INIT
;
528 git_note_data note_data
;
530 if (git_buf_puts(&buf
, entry_path
) < 0)
533 len
= git_buf_len(&buf
);
536 if (buf
.ptr
[i
] == '/') {
541 if (git__fromhex(buf
.ptr
[i
]) < 0) {
542 /* This is not a note entry */
548 buf
.ptr
[j
] = buf
.ptr
[i
];
557 if (j
!= GIT_OID_HEXSZ
) {
558 /* This is not a note entry */
563 if (git_oid_fromstr(¬e_data
.annotated_object_oid
, buf
.ptr
) < 0)
566 git_oid_cpy(¬e_data
.blob_oid
, note_oid
);
568 error
= note_cb(¬e_data
, payload
);
575 int git_note_foreach(
576 git_repository
*repo
,
577 const char *notes_ref
,
578 int (*note_cb
)(git_note_data
*note_data
, void *payload
),
582 git_iterator
*iter
= NULL
;
583 git_tree
*tree
= NULL
;
584 git_commit
*commit
= NULL
;
585 const git_index_entry
*item
;
587 if ((error
= retrieve_note_tree_and_commit(&tree
, &commit
, repo
, ¬es_ref
)) < 0)
590 if (git_iterator_for_tree(&iter
, repo
, tree
) < 0)
593 if (git_iterator_current(iter
, &item
) < 0)
597 if (process_entry_path(item
->path
, &item
->oid
, note_cb
, payload
) < 0)
600 if (git_iterator_advance(iter
, &item
) < 0)
607 git_iterator_free(iter
);
609 git_commit_free(commit
);