]>
Commit | Line | Data |
---|---|---|
bf477ed4 | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
bf477ed4 MS |
3 | * |
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. | |
6 | */ | |
7 | ||
8 | #include "notes.h" | |
9 | ||
10 | #include "git2.h" | |
11 | #include "refs.h" | |
caea5e54 | 12 | #include "config.h" |
86ecd844 | 13 | #include "iterator.h" |
4ec197f3 | 14 | #include "signature.h" |
ac3d33df | 15 | #include "blob.h" |
bf477ed4 | 16 | |
734c6fc1 RB |
17 | static int note_error_notfound(void) |
18 | { | |
ac3d33df | 19 | git_error_set(GIT_ERROR_INVALID, "note could not be found"); |
734c6fc1 RB |
20 | return GIT_ENOTFOUND; |
21 | } | |
22 | ||
b93688d0 VM |
23 | static int find_subtree_in_current_level( |
24 | git_tree **out, | |
25 | git_repository *repo, | |
26 | git_tree *parent, | |
27 | const char *annotated_object_sha, | |
28 | int fanout) | |
bf477ed4 | 29 | { |
e120123e | 30 | size_t i; |
bf477ed4 MS |
31 | const git_tree_entry *entry; |
32 | ||
a02e7249 | 33 | *out = NULL; |
e120123e | 34 | |
a02e7249 | 35 | if (parent == NULL) |
734c6fc1 | 36 | return note_error_notfound(); |
bf477ed4 | 37 | |
a02e7249 | 38 | for (i = 0; i < git_tree_entrycount(parent); i++) { |
39 | entry = git_tree_entry_byindex(parent, i); | |
bf477ed4 MS |
40 | |
41 | if (!git__ishex(git_tree_entry_name(entry))) | |
42 | continue; | |
43 | ||
9d7ac675 | 44 | if (S_ISDIR(git_tree_entry_filemode(entry)) |
e120123e | 45 | && strlen(git_tree_entry_name(entry)) == 2 |
a02e7249 | 46 | && !strncmp(git_tree_entry_name(entry), annotated_object_sha + fanout, 2)) |
47 | return git_tree_lookup(out, repo, git_tree_entry_id(entry)); | |
bf477ed4 | 48 | |
a02e7249 | 49 | /* Not a DIR, so do we have an already existing blob? */ |
e120123e | 50 | if (!strcmp(git_tree_entry_name(entry), annotated_object_sha + fanout)) |
a02e7249 | 51 | return GIT_EEXISTS; |
52 | } | |
bf477ed4 | 53 | |
734c6fc1 | 54 | return note_error_notfound(); |
a02e7249 | 55 | } |
bf477ed4 | 56 | |
a02e7249 | 57 | static int find_subtree_r(git_tree **out, git_tree *root, |
58 | git_repository *repo, const char *target, int *fanout) | |
59 | { | |
60 | int error; | |
61 | git_tree *subtree = NULL; | |
bf477ed4 | 62 | |
a02e7249 | 63 | *out = NULL; |
bf477ed4 | 64 | |
a02e7249 | 65 | error = find_subtree_in_current_level(&subtree, repo, root, target, *fanout); |
734c6fc1 | 66 | if (error == GIT_EEXISTS) |
dca6b228 | 67 | return git_tree_lookup(out, repo, git_tree_id(root)); |
bf477ed4 | 68 | |
a02e7249 | 69 | if (error < 0) |
b93688d0 | 70 | return error; |
a02e7249 | 71 | |
72 | *fanout += 2; | |
73 | error = find_subtree_r(out, subtree, repo, target, fanout); | |
dca6b228 | 74 | git_tree_free(subtree); |
a02e7249 | 75 | |
a02e7249 | 76 | return error; |
bf477ed4 MS |
77 | } |
78 | ||
79 | static int find_blob(git_oid *blob, git_tree *tree, const char *target) | |
80 | { | |
e120123e | 81 | size_t i; |
bf477ed4 MS |
82 | const git_tree_entry *entry; |
83 | ||
84 | for (i=0; i<git_tree_entrycount(tree); i++) { | |
85 | entry = git_tree_entry_byindex(tree, i); | |
86 | ||
87 | if (!strcmp(git_tree_entry_name(entry), target)) { | |
88 | /* found matching note object - return */ | |
89 | ||
90 | git_oid_cpy(blob, git_tree_entry_id(entry)); | |
4aa7de15 | 91 | return 0; |
bf477ed4 MS |
92 | } |
93 | } | |
734c6fc1 RB |
94 | |
95 | return note_error_notfound(); | |
bf477ed4 MS |
96 | } |
97 | ||
b93688d0 VM |
98 | static int tree_write( |
99 | git_tree **out, | |
100 | git_repository *repo, | |
101 | git_tree *source_tree, | |
102 | const git_oid *object_oid, | |
103 | const char *treeentry_name, | |
104 | unsigned int attributes) | |
bf477ed4 | 105 | { |
a02e7249 | 106 | int error; |
107 | git_treebuilder *tb = NULL; | |
0e2fcca8 | 108 | const git_tree_entry *entry; |
a02e7249 | 109 | git_oid tree_oid; |
bf477ed4 | 110 | |
208a2c8a | 111 | if ((error = git_treebuilder_new(&tb, repo, source_tree)) < 0) |
a02e7249 | 112 | goto cleanup; |
bf477ed4 | 113 | |
a02e7249 | 114 | if (object_oid) { |
b93688d0 VM |
115 | if ((error = git_treebuilder_insert( |
116 | &entry, tb, treeentry_name, object_oid, attributes)) < 0) | |
a02e7249 | 117 | goto cleanup; |
118 | } else { | |
119 | if ((error = git_treebuilder_remove(tb, treeentry_name)) < 0) | |
120 | goto cleanup; | |
121 | } | |
bf477ed4 | 122 | |
dce7b1a4 | 123 | if ((error = git_treebuilder_write(&tree_oid, tb)) < 0) |
a02e7249 | 124 | goto cleanup; |
bf477ed4 | 125 | |
a02e7249 | 126 | error = git_tree_lookup(out, repo, &tree_oid); |
bf477ed4 | 127 | |
a02e7249 | 128 | cleanup: |
129 | git_treebuilder_free(tb); | |
130 | return error; | |
131 | } | |
bf477ed4 | 132 | |
b93688d0 VM |
133 | static int manipulate_note_in_tree_r( |
134 | git_tree **out, | |
135 | git_repository *repo, | |
136 | git_tree *parent, | |
137 | git_oid *note_oid, | |
138 | const char *annotated_object_sha, | |
4a44087a | 139 | int fanout, |
b93688d0 | 140 | int (*note_exists_cb)( |
4a44087a NV |
141 | git_tree **out, |
142 | git_repository *repo, | |
b93688d0 VM |
143 | git_tree *parent, |
144 | git_oid *note_oid, | |
145 | const char *annotated_object_sha, | |
146 | int fanout, | |
147 | int current_error), | |
148 | int (*note_notfound_cb)( | |
149 | git_tree **out, | |
150 | git_repository *repo, | |
151 | git_tree *parent, | |
152 | git_oid *note_oid, | |
153 | const char *annotated_object_sha, | |
154 | int fanout, | |
155 | int current_error)) | |
a02e7249 | 156 | { |
56543a60 | 157 | int error; |
dca6b228 | 158 | git_tree *subtree = NULL, *new = NULL; |
a02e7249 | 159 | char subtree_name[3]; |
bf477ed4 | 160 | |
b93688d0 VM |
161 | error = find_subtree_in_current_level( |
162 | &subtree, repo, parent, annotated_object_sha, fanout); | |
bf477ed4 | 163 | |
a02e7249 | 164 | if (error == GIT_EEXISTS) { |
b93688d0 VM |
165 | error = note_exists_cb( |
166 | out, repo, parent, note_oid, annotated_object_sha, fanout, error); | |
a02e7249 | 167 | goto cleanup; |
168 | } | |
bf477ed4 | 169 | |
a02e7249 | 170 | if (error == GIT_ENOTFOUND) { |
b93688d0 VM |
171 | error = note_notfound_cb( |
172 | out, repo, parent, note_oid, annotated_object_sha, fanout, error); | |
a02e7249 | 173 | goto cleanup; |
174 | } | |
bf477ed4 | 175 | |
4aa7de15 | 176 | if (error < 0) |
a02e7249 | 177 | goto cleanup; |
bf477ed4 | 178 | |
a02e7249 | 179 | /* An existing fanout has been found, let's dig deeper */ |
b93688d0 | 180 | error = manipulate_note_in_tree_r( |
dca6b228 | 181 | &new, repo, subtree, note_oid, annotated_object_sha, |
b93688d0 | 182 | fanout + 2, note_exists_cb, note_notfound_cb); |
bf477ed4 | 183 | |
a02e7249 | 184 | if (error < 0) |
185 | goto cleanup; | |
bf477ed4 | 186 | |
a02e7249 | 187 | strncpy(subtree_name, annotated_object_sha + fanout, 2); |
188 | subtree_name[2] = '\0'; | |
bf477ed4 | 189 | |
dca6b228 | 190 | error = tree_write(out, repo, parent, git_tree_id(new), |
a7dbac0b | 191 | subtree_name, GIT_FILEMODE_TREE); |
dca6b228 | 192 | |
4aa7de15 | 193 | |
a02e7249 | 194 | cleanup: |
dca6b228 | 195 | git_tree_free(new); |
a02e7249 | 196 | git_tree_free(subtree); |
197 | return error; | |
198 | } | |
bf477ed4 | 199 | |
b93688d0 VM |
200 | static int remove_note_in_tree_eexists_cb( |
201 | git_tree **out, | |
202 | git_repository *repo, | |
203 | git_tree *parent, | |
204 | git_oid *note_oid, | |
205 | const char *annotated_object_sha, | |
206 | int fanout, | |
207 | int current_error) | |
208 | { | |
a02e7249 | 209 | GIT_UNUSED(note_oid); |
210 | GIT_UNUSED(current_error); | |
bf477ed4 | 211 | |
a02e7249 | 212 | return tree_write(out, repo, parent, NULL, annotated_object_sha + fanout, 0); |
213 | } | |
bf477ed4 | 214 | |
b93688d0 VM |
215 | static int remove_note_in_tree_enotfound_cb( |
216 | git_tree **out, | |
217 | git_repository *repo, | |
218 | git_tree *parent, | |
219 | git_oid *note_oid, | |
220 | const char *annotated_object_sha, | |
221 | int fanout, | |
222 | int current_error) | |
223 | { | |
a02e7249 | 224 | GIT_UNUSED(out); |
225 | GIT_UNUSED(repo); | |
226 | GIT_UNUSED(parent); | |
227 | GIT_UNUSED(note_oid); | |
228 | GIT_UNUSED(fanout); | |
bf477ed4 | 229 | |
ac3d33df | 230 | git_error_set(GIT_ERROR_REPOSITORY, "object '%s' has no note", annotated_object_sha); |
a02e7249 | 231 | return current_error; |
232 | } | |
bf477ed4 | 233 | |
b93688d0 VM |
234 | static int insert_note_in_tree_eexists_cb(git_tree **out, |
235 | git_repository *repo, | |
236 | git_tree *parent, | |
237 | git_oid *note_oid, | |
238 | const char *annotated_object_sha, | |
239 | int fanout, | |
240 | int current_error) | |
241 | { | |
a02e7249 | 242 | GIT_UNUSED(out); |
243 | GIT_UNUSED(repo); | |
244 | GIT_UNUSED(parent); | |
245 | GIT_UNUSED(note_oid); | |
246 | GIT_UNUSED(fanout); | |
bf477ed4 | 247 | |
ac3d33df | 248 | git_error_set(GIT_ERROR_REPOSITORY, "note for '%s' exists already", annotated_object_sha); |
a02e7249 | 249 | return current_error; |
250 | } | |
bf477ed4 | 251 | |
b93688d0 VM |
252 | static int insert_note_in_tree_enotfound_cb(git_tree **out, |
253 | git_repository *repo, | |
254 | git_tree *parent, | |
255 | git_oid *note_oid, | |
256 | const char *annotated_object_sha, | |
257 | int fanout, | |
258 | int current_error) | |
259 | { | |
a02e7249 | 260 | GIT_UNUSED(current_error); |
bf477ed4 | 261 | |
a02e7249 | 262 | /* No existing fanout at this level, insert in place */ |
a7dbac0b | 263 | return tree_write( |
264 | out, | |
265 | repo, | |
266 | parent, | |
267 | note_oid, | |
268 | annotated_object_sha + fanout, | |
269 | GIT_FILEMODE_BLOB); | |
bf477ed4 MS |
270 | } |
271 | ||
eae0bfdc PP |
272 | static int note_write( |
273 | git_oid *notes_commit_out, | |
274 | git_oid *notes_blob_out, | |
b93688d0 | 275 | git_repository *repo, |
de5596bf BS |
276 | const git_signature *author, |
277 | const git_signature *committer, | |
b93688d0 VM |
278 | const char *notes_ref, |
279 | const char *note, | |
280 | git_tree *commit_tree, | |
281 | const char *target, | |
8716b499 NV |
282 | git_commit **parents, |
283 | int allow_note_overwrite) | |
bf477ed4 | 284 | { |
a02e7249 | 285 | int error; |
bf477ed4 | 286 | git_oid oid; |
a02e7249 | 287 | git_tree *tree = NULL; |
4a44087a | 288 | |
ac3d33df | 289 | /* TODO: should we apply filters? */ |
a02e7249 | 290 | /* create note object */ |
22a2d3d5 | 291 | if ((error = git_blob_create_from_buffer(&oid, repo, note, strlen(note))) < 0) |
a02e7249 | 292 | goto cleanup; |
bf477ed4 | 293 | |
b93688d0 VM |
294 | if ((error = manipulate_note_in_tree_r( |
295 | &tree, repo, commit_tree, &oid, target, 0, | |
8716b499 NV |
296 | allow_note_overwrite ? insert_note_in_tree_enotfound_cb : insert_note_in_tree_eexists_cb, |
297 | insert_note_in_tree_enotfound_cb)) < 0) | |
a02e7249 | 298 | goto cleanup; |
bf477ed4 | 299 | |
eae0bfdc PP |
300 | if (notes_blob_out) |
301 | git_oid_cpy(notes_blob_out, &oid); | |
302 | ||
4aa7de15 | 303 | |
a02e7249 | 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); | |
4aa7de15 | 307 | |
eae0bfdc PP |
308 | if (notes_commit_out) |
309 | git_oid_cpy(notes_commit_out, &oid); | |
310 | ||
a02e7249 | 311 | cleanup: |
bf477ed4 | 312 | git_tree_free(tree); |
a02e7249 | 313 | return error; |
314 | } | |
bf477ed4 | 315 | |
bad4937e ET |
316 | static int note_new( |
317 | git_note **out, | |
318 | git_oid *note_oid, | |
319 | git_commit *commit, | |
320 | git_blob *blob) | |
a02e7249 | 321 | { |
322 | git_note *note = NULL; | |
22a2d3d5 | 323 | git_object_size_t blobsize; |
bf477ed4 | 324 | |
392702ee | 325 | note = git__malloc(sizeof(git_note)); |
ac3d33df | 326 | GIT_ERROR_CHECK_ALLOC(note); |
bf477ed4 | 327 | |
d0a3de72 | 328 | git_oid_cpy(¬e->id, note_oid); |
bad4937e ET |
329 | |
330 | if (git_signature_dup(¬e->author, git_commit_author(commit)) < 0 || | |
331 | git_signature_dup(¬e->committer, git_commit_committer(commit)) < 0) | |
332 | return -1; | |
333 | ||
ac3d33df JK |
334 | blobsize = git_blob_rawsize(blob); |
335 | GIT_ERROR_CHECK_BLOBSIZE(blobsize); | |
336 | ||
337 | note->message = git__strndup(git_blob_rawcontent(blob), (size_t)blobsize); | |
338 | GIT_ERROR_CHECK_ALLOC(note->message); | |
bf477ed4 MS |
339 | |
340 | *out = note; | |
a02e7249 | 341 | return 0; |
bf477ed4 MS |
342 | } |
343 | ||
734c6fc1 | 344 | static int note_lookup( |
bad4937e ET |
345 | git_note **out, |
346 | git_repository *repo, | |
347 | git_commit *commit, | |
348 | git_tree *tree, | |
349 | const char *target) | |
bf477ed4 MS |
350 | { |
351 | int error, fanout = 0; | |
352 | git_oid oid; | |
a02e7249 | 353 | git_blob *blob = NULL; |
354 | git_note *note = NULL; | |
355 | git_tree *subtree = NULL; | |
bf477ed4 | 356 | |
a02e7249 | 357 | if ((error = find_subtree_r(&subtree, tree, repo, target, &fanout)) < 0) |
358 | goto cleanup; | |
bf477ed4 | 359 | |
a02e7249 | 360 | if ((error = find_blob(&oid, subtree, target + fanout)) < 0) |
361 | goto cleanup; | |
bf477ed4 | 362 | |
a02e7249 | 363 | if ((error = git_blob_lookup(&blob, repo, &oid)) < 0) |
364 | goto cleanup; | |
bf477ed4 | 365 | |
bad4937e | 366 | if ((error = note_new(¬e, &oid, commit, blob)) < 0) |
a02e7249 | 367 | goto cleanup; |
bf477ed4 | 368 | |
a02e7249 | 369 | *out = note; |
bf477ed4 | 370 | |
a02e7249 | 371 | cleanup: |
372 | git_tree_free(subtree); | |
373 | git_blob_free(blob); | |
374 | return error; | |
375 | } | |
bf477ed4 | 376 | |
eae0bfdc PP |
377 | static int note_remove( |
378 | git_oid *notes_commit_out, | |
379 | git_repository *repo, | |
de5596bf BS |
380 | const git_signature *author, const git_signature *committer, |
381 | const char *notes_ref, git_tree *tree, | |
382 | const char *target, git_commit **parents) | |
a02e7249 | 383 | { |
384 | int error; | |
385 | git_tree *tree_after_removal = NULL; | |
386 | git_oid oid; | |
4a44087a | 387 | |
b93688d0 VM |
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) | |
a02e7249 | 391 | goto cleanup; |
bf477ed4 MS |
392 | |
393 | error = git_commit_create(&oid, repo, notes_ref, author, committer, | |
b93688d0 VM |
394 | NULL, GIT_NOTES_DEFAULT_MSG_RM, |
395 | tree_after_removal, | |
396 | *parents == NULL ? 0 : 1, | |
397 | (const git_commit **) parents); | |
bf477ed4 | 398 | |
eae0bfdc PP |
399 | if (error < 0) |
400 | goto cleanup; | |
401 | ||
402 | if (notes_commit_out) | |
403 | git_oid_cpy(notes_commit_out, &oid); | |
404 | ||
a02e7249 | 405 | cleanup: |
406 | git_tree_free(tree_after_removal); | |
bf477ed4 MS |
407 | return error; |
408 | } | |
409 | ||
c25aa7cd | 410 | static int note_get_default_ref(git_buf *out, git_repository *repo) |
caea5e54 | 411 | { |
caea5e54 | 412 | git_config *cfg; |
c25aa7cd PP |
413 | int error; |
414 | ||
415 | if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) | |
416 | return error; | |
caea5e54 | 417 | |
c25aa7cd | 418 | error = git_config_get_string_buf(out, cfg, "core.notesref"); |
caea5e54 | 419 | |
c25aa7cd PP |
420 | if (error == GIT_ENOTFOUND) |
421 | error = git_buf_puts(out, GIT_NOTES_DEFAULT_REF); | |
422 | ||
423 | return error; | |
caea5e54 MS |
424 | } |
425 | ||
c25aa7cd | 426 | static int normalize_namespace(git_buf *out, git_repository *repo, const char *notes_ref) |
86ecd844 | 427 | { |
c25aa7cd PP |
428 | if (notes_ref) |
429 | return git_buf_puts(out, notes_ref); | |
86ecd844 | 430 | |
385449b1 | 431 | return note_get_default_ref(out, repo); |
86ecd844 | 432 | } |
433 | ||
eae0bfdc | 434 | static int retrieve_note_commit( |
b93688d0 | 435 | git_commit **commit_out, |
c25aa7cd | 436 | git_buf *notes_ref_out, |
b93688d0 | 437 | git_repository *repo, |
385449b1 | 438 | const char *notes_ref) |
86ecd844 | 439 | { |
a02e7249 | 440 | int error; |
86ecd844 | 441 | git_oid oid; |
442 | ||
385449b1 | 443 | if ((error = normalize_namespace(notes_ref_out, repo, notes_ref)) < 0) |
a02e7249 | 444 | return error; |
86ecd844 | 445 | |
c25aa7cd | 446 | if ((error = git_reference_name_to_id(&oid, repo, notes_ref_out->ptr)) < 0) |
a02e7249 | 447 | return error; |
86ecd844 | 448 | |
a02e7249 | 449 | if (git_commit_lookup(commit_out, repo, &oid) < 0) |
450 | return error; | |
86ecd844 | 451 | |
a02e7249 | 452 | return 0; |
86ecd844 | 453 | } |
454 | ||
eae0bfdc PP |
455 | int git_note_commit_read( |
456 | git_note **out, | |
457 | git_repository *repo, | |
458 | git_commit *notes_commit, | |
459 | const git_oid *oid) | |
460 | { | |
461 | int error; | |
462 | git_tree *tree = NULL; | |
463 | char target[GIT_OID_HEXSZ + 1]; | |
464 | ||
465 | git_oid_tostr(target, sizeof(target), oid); | |
466 | ||
467 | if ((error = git_commit_tree(&tree, notes_commit)) < 0) | |
468 | goto cleanup; | |
469 | ||
470 | error = note_lookup(out, repo, notes_commit, tree, target); | |
471 | ||
472 | cleanup: | |
473 | git_tree_free(tree); | |
474 | return error; | |
475 | } | |
476 | ||
bf477ed4 | 477 | int git_note_read(git_note **out, git_repository *repo, |
385449b1 | 478 | const char *notes_ref_in, const git_oid *oid) |
bf477ed4 MS |
479 | { |
480 | int error; | |
c25aa7cd | 481 | git_buf notes_ref = GIT_BUF_INIT; |
a02e7249 | 482 | git_commit *commit = NULL; |
bf477ed4 | 483 | |
eae0bfdc | 484 | error = retrieve_note_commit(&commit, ¬es_ref, repo, notes_ref_in); |
bf477ed4 | 485 | |
eae0bfdc PP |
486 | if (error < 0) |
487 | goto cleanup; | |
bf477ed4 | 488 | |
eae0bfdc PP |
489 | error = git_note_commit_read(out, repo, commit, oid); |
490 | ||
491 | cleanup: | |
c25aa7cd | 492 | git_buf_dispose(¬es_ref); |
a02e7249 | 493 | git_commit_free(commit); |
4aa7de15 | 494 | return error; |
bf477ed4 MS |
495 | } |
496 | ||
eae0bfdc PP |
497 | int git_note_commit_create( |
498 | git_oid *notes_commit_out, | |
499 | git_oid *notes_blob_out, | |
500 | git_repository *repo, | |
501 | git_commit *parent, | |
502 | const git_signature *author, | |
503 | const git_signature *committer, | |
504 | const git_oid *oid, | |
505 | const char *note, | |
506 | int allow_note_overwrite) | |
507 | { | |
508 | int error; | |
509 | git_tree *tree = NULL; | |
510 | char target[GIT_OID_HEXSZ + 1]; | |
511 | ||
512 | git_oid_tostr(target, sizeof(target), oid); | |
513 | ||
514 | if (parent != NULL && (error = git_commit_tree(&tree, parent)) < 0) | |
515 | goto cleanup; | |
516 | ||
517 | error = note_write(notes_commit_out, notes_blob_out, repo, author, | |
518 | committer, NULL, note, tree, target, &parent, allow_note_overwrite); | |
519 | ||
520 | if (error < 0) | |
521 | goto cleanup; | |
522 | ||
523 | cleanup: | |
524 | git_tree_free(tree); | |
525 | return error; | |
526 | } | |
527 | ||
4aa7de15 | 528 | int git_note_create( |
de5596bf BS |
529 | git_oid *out, |
530 | git_repository *repo, | |
385449b1 | 531 | const char *notes_ref_in, |
de5596bf BS |
532 | const git_signature *author, |
533 | const git_signature *committer, | |
de5596bf | 534 | const git_oid *oid, |
8716b499 NV |
535 | const char *note, |
536 | int allow_note_overwrite) | |
bf477ed4 | 537 | { |
a02e7249 | 538 | int error; |
c25aa7cd | 539 | git_buf notes_ref = GIT_BUF_INIT; |
eae0bfdc PP |
540 | git_commit *existing_notes_commit = NULL; |
541 | git_reference *ref = NULL; | |
542 | git_oid notes_blob_oid, notes_commit_oid; | |
bf477ed4 | 543 | |
eae0bfdc PP |
544 | error = retrieve_note_commit(&existing_notes_commit, ¬es_ref, |
545 | repo, notes_ref_in); | |
a02e7249 | 546 | |
547 | if (error < 0 && error != GIT_ENOTFOUND) | |
548 | goto cleanup; | |
549 | ||
eae0bfdc PP |
550 | error = git_note_commit_create(¬es_commit_oid, |
551 | ¬es_blob_oid, | |
552 | repo, existing_notes_commit, author, | |
553 | committer, oid, note, | |
554 | allow_note_overwrite); | |
555 | if (error < 0) | |
556 | goto cleanup; | |
557 | ||
c25aa7cd | 558 | error = git_reference_create(&ref, repo, notes_ref.ptr, |
eae0bfdc PP |
559 | ¬es_commit_oid, 1, NULL); |
560 | ||
561 | if (out != NULL) | |
562 | git_oid_cpy(out, ¬es_blob_oid); | |
bf477ed4 | 563 | |
a02e7249 | 564 | cleanup: |
c25aa7cd | 565 | git_buf_dispose(¬es_ref); |
eae0bfdc PP |
566 | git_commit_free(existing_notes_commit); |
567 | git_reference_free(ref); | |
568 | return error; | |
569 | } | |
570 | ||
571 | int git_note_commit_remove( | |
572 | git_oid *notes_commit_out, | |
573 | git_repository *repo, | |
574 | git_commit *notes_commit, | |
575 | const git_signature *author, | |
576 | const git_signature *committer, | |
577 | const git_oid *oid) | |
578 | { | |
579 | int error; | |
580 | git_tree *tree = NULL; | |
581 | char target[GIT_OID_HEXSZ + 1]; | |
582 | ||
583 | git_oid_tostr(target, sizeof(target), oid); | |
584 | ||
585 | if ((error = git_commit_tree(&tree, notes_commit)) < 0) | |
586 | goto cleanup; | |
587 | ||
588 | error = note_remove(notes_commit_out, | |
589 | repo, author, committer, NULL, tree, target, ¬es_commit); | |
590 | ||
591 | cleanup: | |
a02e7249 | 592 | git_tree_free(tree); |
4aa7de15 | 593 | return error; |
bf477ed4 MS |
594 | } |
595 | ||
385449b1 | 596 | int git_note_remove(git_repository *repo, const char *notes_ref_in, |
de5596bf BS |
597 | const git_signature *author, const git_signature *committer, |
598 | const git_oid *oid) | |
bf477ed4 MS |
599 | { |
600 | int error; | |
c25aa7cd | 601 | git_buf notes_ref_target = GIT_BUF_INIT; |
eae0bfdc PP |
602 | git_commit *existing_notes_commit = NULL; |
603 | git_oid new_notes_commit; | |
604 | git_reference *notes_ref = NULL; | |
bf477ed4 | 605 | |
eae0bfdc PP |
606 | error = retrieve_note_commit(&existing_notes_commit, ¬es_ref_target, |
607 | repo, notes_ref_in); | |
bf477ed4 | 608 | |
eae0bfdc PP |
609 | if (error < 0) |
610 | goto cleanup; | |
bf477ed4 | 611 | |
eae0bfdc PP |
612 | error = git_note_commit_remove(&new_notes_commit, repo, |
613 | existing_notes_commit, author, committer, oid); | |
614 | if (error < 0) | |
615 | goto cleanup; | |
616 | ||
c25aa7cd | 617 | error = git_reference_create(¬es_ref, repo, notes_ref_target.ptr, |
eae0bfdc PP |
618 | &new_notes_commit, 1, NULL); |
619 | ||
620 | cleanup: | |
c25aa7cd | 621 | git_buf_dispose(¬es_ref_target); |
eae0bfdc PP |
622 | git_reference_free(notes_ref); |
623 | git_commit_free(existing_notes_commit); | |
4aa7de15 | 624 | return error; |
bf477ed4 MS |
625 | } |
626 | ||
385449b1 | 627 | int git_note_default_ref(git_buf *out, git_repository *repo) |
630c5a4a | 628 | { |
385449b1 CMN |
629 | int error; |
630 | ||
c25aa7cd PP |
631 | GIT_ASSERT_ARG(out); |
632 | GIT_ASSERT_ARG(repo); | |
385449b1 | 633 | |
c25aa7cd PP |
634 | if ((error = git_buf_sanitize(out)) < 0 || |
635 | (error = note_get_default_ref(out, repo)) < 0) | |
636 | git_buf_dispose(out); | |
385449b1 | 637 | |
c25aa7cd | 638 | return error; |
630c5a4a MS |
639 | } |
640 | ||
bad4937e ET |
641 | const git_signature *git_note_committer(const git_note *note) |
642 | { | |
c25aa7cd | 643 | GIT_ASSERT_ARG_WITH_RETVAL(note, NULL); |
bad4937e ET |
644 | return note->committer; |
645 | } | |
646 | ||
647 | const git_signature *git_note_author(const git_note *note) | |
648 | { | |
c25aa7cd | 649 | GIT_ASSERT_ARG_WITH_RETVAL(note, NULL); |
bad4937e ET |
650 | return note->author; |
651 | } | |
652 | ||
c25aa7cd | 653 | const char *git_note_message(const git_note *note) |
bf477ed4 | 654 | { |
c25aa7cd | 655 | GIT_ASSERT_ARG_WITH_RETVAL(note, NULL); |
bf477ed4 MS |
656 | return note->message; |
657 | } | |
658 | ||
c25aa7cd | 659 | const git_oid *git_note_id(const git_note *note) |
bf477ed4 | 660 | { |
c25aa7cd | 661 | GIT_ASSERT_ARG_WITH_RETVAL(note, NULL); |
d0a3de72 | 662 | return ¬e->id; |
bf477ed4 MS |
663 | } |
664 | ||
665 | void git_note_free(git_note *note) | |
666 | { | |
667 | if (note == NULL) | |
668 | return; | |
669 | ||
bad4937e ET |
670 | git_signature_free(note->committer); |
671 | git_signature_free(note->author); | |
bf477ed4 MS |
672 | git__free(note->message); |
673 | git__free(note); | |
674 | } | |
86ecd844 | 675 | |
676 | static int process_entry_path( | |
c25aa7cd | 677 | const char *entry_path, |
f7b18502 | 678 | git_oid *annotated_object_id) |
86ecd844 | 679 | { |
734c6fc1 | 680 | int error = 0; |
b8457baa | 681 | size_t i = 0, j = 0, len; |
86ecd844 | 682 | git_buf buf = GIT_BUF_INIT; |
683 | ||
5dca2010 | 684 | if ((error = git_buf_puts(&buf, entry_path)) < 0) |
86ecd844 | 685 | goto cleanup; |
5dca2010 | 686 | |
86ecd844 | 687 | len = git_buf_len(&buf); |
688 | ||
689 | while (i < len) { | |
690 | if (buf.ptr[i] == '/') { | |
691 | i++; | |
692 | continue; | |
693 | } | |
5dca2010 | 694 | |
86ecd844 | 695 | if (git__fromhex(buf.ptr[i]) < 0) { |
696 | /* This is not a note entry */ | |
86ecd844 | 697 | goto cleanup; |
698 | } | |
699 | ||
700 | if (i != j) | |
701 | buf.ptr[j] = buf.ptr[i]; | |
702 | ||
703 | i++; | |
704 | j++; | |
705 | } | |
706 | ||
707 | buf.ptr[j] = '\0'; | |
708 | buf.size = j; | |
709 | ||
710 | if (j != GIT_OID_HEXSZ) { | |
711 | /* This is not a note entry */ | |
86ecd844 | 712 | goto cleanup; |
713 | } | |
714 | ||
f7b18502 | 715 | error = git_oid_fromstr(annotated_object_id, buf.ptr); |
86ecd844 | 716 | |
86ecd844 | 717 | cleanup: |
ac3d33df | 718 | git_buf_dispose(&buf); |
86ecd844 | 719 | return error; |
720 | } | |
721 | ||
722 | int git_note_foreach( | |
0cb16fe9 L |
723 | git_repository *repo, |
724 | const char *notes_ref, | |
725 | git_note_foreach_cb note_cb, | |
726 | void *payload) | |
86ecd844 | 727 | { |
0cb16fe9 L |
728 | int error; |
729 | git_note_iterator *iter = NULL; | |
730 | git_oid note_id, annotated_id; | |
6edb427b | 731 | |
0cb16fe9 L |
732 | if ((error = git_note_iterator_new(&iter, repo, notes_ref)) < 0) |
733 | return error; | |
86ecd844 | 734 | |
f10d7a36 RB |
735 | while (!(error = git_note_next(¬e_id, &annotated_id, iter))) { |
736 | if ((error = note_cb(¬e_id, &annotated_id, payload)) != 0) { | |
ac3d33df | 737 | git_error_set_after_callback(error); |
f10d7a36 RB |
738 | break; |
739 | } | |
740 | } | |
86ecd844 | 741 | |
0cb16fe9 L |
742 | if (error == GIT_ITEROVER) |
743 | error = 0; | |
5dca2010 | 744 | |
0cb16fe9 L |
745 | git_note_iterator_free(iter); |
746 | return error; | |
86ecd844 | 747 | } |
6edb427b | 748 | |
1a90dcf6 NG |
749 | void git_note_iterator_free(git_note_iterator *it) |
750 | { | |
751 | if (it == NULL) | |
752 | return; | |
753 | ||
754 | git_iterator_free(it); | |
755 | } | |
756 | ||
eae0bfdc PP |
757 | int git_note_commit_iterator_new( |
758 | git_note_iterator **it, | |
759 | git_commit *notes_commit) | |
760 | { | |
761 | int error; | |
762 | git_tree *tree; | |
763 | ||
764 | if ((error = git_commit_tree(&tree, notes_commit)) < 0) | |
765 | goto cleanup; | |
766 | ||
767 | if ((error = git_iterator_for_tree(it, tree, NULL)) < 0) | |
768 | git_iterator_free(*it); | |
769 | ||
770 | cleanup: | |
771 | git_tree_free(tree); | |
772 | ||
773 | return error; | |
774 | } | |
1a90dcf6 | 775 | |
6edb427b | 776 | int git_note_iterator_new( |
1a90dcf6 | 777 | git_note_iterator **it, |
6edb427b | 778 | git_repository *repo, |
385449b1 | 779 | const char *notes_ref_in) |
6edb427b NG |
780 | { |
781 | int error; | |
782 | git_commit *commit = NULL; | |
c25aa7cd | 783 | git_buf notes_ref = GIT_BUF_INIT; |
6edb427b | 784 | |
eae0bfdc | 785 | error = retrieve_note_commit(&commit, ¬es_ref, repo, notes_ref_in); |
f7b18502 NG |
786 | if (error < 0) |
787 | goto cleanup; | |
6edb427b | 788 | |
eae0bfdc | 789 | error = git_note_commit_iterator_new(it, commit); |
6edb427b | 790 | |
f7b18502 | 791 | cleanup: |
c25aa7cd | 792 | git_buf_dispose(¬es_ref); |
6edb427b NG |
793 | git_commit_free(commit); |
794 | ||
795 | return error; | |
796 | } | |
797 | ||
798 | int git_note_next( | |
c25aa7cd PP |
799 | git_oid *note_id, |
800 | git_oid *annotated_id, | |
f7b18502 | 801 | git_note_iterator *it) |
6edb427b NG |
802 | { |
803 | int error; | |
804 | const git_index_entry *item; | |
805 | ||
169dc616 | 806 | if ((error = git_iterator_current(&item, it)) < 0) |
cee695ae | 807 | return error; |
6edb427b | 808 | |
d541170c | 809 | git_oid_cpy(note_id, &item->id); |
6edb427b | 810 | |
22a2d3d5 UG |
811 | if ((error = process_entry_path(item->path, annotated_id)) < 0) |
812 | return error; | |
6edb427b | 813 | |
22a2d3d5 UG |
814 | if ((error = git_iterator_advance(NULL, it)) < 0 && error != GIT_ITEROVER) |
815 | return error; | |
816 | ||
817 | return 0; | |
6edb427b | 818 | } |