]>
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 */ |
0c9c969a | 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; | |
0c9c969a | 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 | ||
385449b1 | 410 | static int note_get_default_ref(char **out, git_repository *repo) |
caea5e54 | 411 | { |
caea5e54 | 412 | git_config *cfg; |
9f77b3f6 | 413 | int ret = git_repository_config__weakptr(&cfg, repo); |
caea5e54 | 414 | |
9f77b3f6 RB |
415 | *out = (ret != 0) ? NULL : git_config__get_string_force( |
416 | cfg, "core.notesref", GIT_NOTES_DEFAULT_REF); | |
caea5e54 | 417 | |
f95e8cc0 | 418 | return ret; |
caea5e54 MS |
419 | } |
420 | ||
385449b1 | 421 | static int normalize_namespace(char **out, git_repository *repo, const char *notes_ref) |
86ecd844 | 422 | { |
385449b1 CMN |
423 | if (notes_ref) { |
424 | *out = git__strdup(notes_ref); | |
ac3d33df | 425 | GIT_ERROR_CHECK_ALLOC(*out); |
86ecd844 | 426 | return 0; |
385449b1 | 427 | } |
86ecd844 | 428 | |
385449b1 | 429 | return note_get_default_ref(out, repo); |
86ecd844 | 430 | } |
431 | ||
eae0bfdc | 432 | static int retrieve_note_commit( |
b93688d0 | 433 | git_commit **commit_out, |
385449b1 | 434 | char **notes_ref_out, |
b93688d0 | 435 | git_repository *repo, |
385449b1 | 436 | const char *notes_ref) |
86ecd844 | 437 | { |
a02e7249 | 438 | int error; |
86ecd844 | 439 | git_oid oid; |
440 | ||
385449b1 | 441 | if ((error = normalize_namespace(notes_ref_out, repo, notes_ref)) < 0) |
a02e7249 | 442 | return error; |
86ecd844 | 443 | |
385449b1 | 444 | if ((error = git_reference_name_to_id(&oid, repo, *notes_ref_out)) < 0) |
a02e7249 | 445 | return error; |
86ecd844 | 446 | |
a02e7249 | 447 | if (git_commit_lookup(commit_out, repo, &oid) < 0) |
448 | return error; | |
86ecd844 | 449 | |
a02e7249 | 450 | return 0; |
86ecd844 | 451 | } |
452 | ||
eae0bfdc PP |
453 | int git_note_commit_read( |
454 | git_note **out, | |
455 | git_repository *repo, | |
456 | git_commit *notes_commit, | |
457 | const git_oid *oid) | |
458 | { | |
459 | int error; | |
460 | git_tree *tree = NULL; | |
461 | char target[GIT_OID_HEXSZ + 1]; | |
462 | ||
463 | git_oid_tostr(target, sizeof(target), oid); | |
464 | ||
465 | if ((error = git_commit_tree(&tree, notes_commit)) < 0) | |
466 | goto cleanup; | |
467 | ||
468 | error = note_lookup(out, repo, notes_commit, tree, target); | |
469 | ||
470 | cleanup: | |
471 | git_tree_free(tree); | |
472 | return error; | |
473 | } | |
474 | ||
bf477ed4 | 475 | int git_note_read(git_note **out, git_repository *repo, |
385449b1 | 476 | const char *notes_ref_in, const git_oid *oid) |
bf477ed4 MS |
477 | { |
478 | int error; | |
eae0bfdc | 479 | char *notes_ref = NULL; |
a02e7249 | 480 | git_commit *commit = NULL; |
bf477ed4 | 481 | |
eae0bfdc | 482 | error = retrieve_note_commit(&commit, ¬es_ref, repo, notes_ref_in); |
bf477ed4 | 483 | |
eae0bfdc PP |
484 | if (error < 0) |
485 | goto cleanup; | |
bf477ed4 | 486 | |
eae0bfdc PP |
487 | error = git_note_commit_read(out, repo, commit, oid); |
488 | ||
489 | cleanup: | |
385449b1 | 490 | git__free(notes_ref); |
a02e7249 | 491 | git_commit_free(commit); |
4aa7de15 | 492 | return error; |
bf477ed4 MS |
493 | } |
494 | ||
eae0bfdc PP |
495 | int git_note_commit_create( |
496 | git_oid *notes_commit_out, | |
497 | git_oid *notes_blob_out, | |
498 | git_repository *repo, | |
499 | git_commit *parent, | |
500 | const git_signature *author, | |
501 | const git_signature *committer, | |
502 | const git_oid *oid, | |
503 | const char *note, | |
504 | int allow_note_overwrite) | |
505 | { | |
506 | int error; | |
507 | git_tree *tree = NULL; | |
508 | char target[GIT_OID_HEXSZ + 1]; | |
509 | ||
510 | git_oid_tostr(target, sizeof(target), oid); | |
511 | ||
512 | if (parent != NULL && (error = git_commit_tree(&tree, parent)) < 0) | |
513 | goto cleanup; | |
514 | ||
515 | error = note_write(notes_commit_out, notes_blob_out, repo, author, | |
516 | committer, NULL, note, tree, target, &parent, allow_note_overwrite); | |
517 | ||
518 | if (error < 0) | |
519 | goto cleanup; | |
520 | ||
521 | cleanup: | |
522 | git_tree_free(tree); | |
523 | return error; | |
524 | } | |
525 | ||
4aa7de15 | 526 | int git_note_create( |
de5596bf BS |
527 | git_oid *out, |
528 | git_repository *repo, | |
385449b1 | 529 | const char *notes_ref_in, |
de5596bf BS |
530 | const git_signature *author, |
531 | const git_signature *committer, | |
de5596bf | 532 | const git_oid *oid, |
8716b499 NV |
533 | const char *note, |
534 | int allow_note_overwrite) | |
bf477ed4 | 535 | { |
a02e7249 | 536 | int error; |
eae0bfdc PP |
537 | char *notes_ref = NULL; |
538 | git_commit *existing_notes_commit = NULL; | |
539 | git_reference *ref = NULL; | |
540 | git_oid notes_blob_oid, notes_commit_oid; | |
bf477ed4 | 541 | |
eae0bfdc PP |
542 | error = retrieve_note_commit(&existing_notes_commit, ¬es_ref, |
543 | repo, notes_ref_in); | |
a02e7249 | 544 | |
545 | if (error < 0 && error != GIT_ENOTFOUND) | |
546 | goto cleanup; | |
547 | ||
eae0bfdc PP |
548 | error = git_note_commit_create(¬es_commit_oid, |
549 | ¬es_blob_oid, | |
550 | repo, existing_notes_commit, author, | |
551 | committer, oid, note, | |
552 | allow_note_overwrite); | |
553 | if (error < 0) | |
554 | goto cleanup; | |
555 | ||
556 | error = git_reference_create(&ref, repo, notes_ref, | |
557 | ¬es_commit_oid, 1, NULL); | |
558 | ||
559 | if (out != NULL) | |
560 | git_oid_cpy(out, ¬es_blob_oid); | |
bf477ed4 | 561 | |
a02e7249 | 562 | cleanup: |
385449b1 | 563 | git__free(notes_ref); |
eae0bfdc PP |
564 | git_commit_free(existing_notes_commit); |
565 | git_reference_free(ref); | |
566 | return error; | |
567 | } | |
568 | ||
569 | int git_note_commit_remove( | |
570 | git_oid *notes_commit_out, | |
571 | git_repository *repo, | |
572 | git_commit *notes_commit, | |
573 | const git_signature *author, | |
574 | const git_signature *committer, | |
575 | const git_oid *oid) | |
576 | { | |
577 | int error; | |
578 | git_tree *tree = NULL; | |
579 | char target[GIT_OID_HEXSZ + 1]; | |
580 | ||
581 | git_oid_tostr(target, sizeof(target), oid); | |
582 | ||
583 | if ((error = git_commit_tree(&tree, notes_commit)) < 0) | |
584 | goto cleanup; | |
585 | ||
586 | error = note_remove(notes_commit_out, | |
587 | repo, author, committer, NULL, tree, target, ¬es_commit); | |
588 | ||
589 | cleanup: | |
a02e7249 | 590 | git_tree_free(tree); |
4aa7de15 | 591 | return error; |
bf477ed4 MS |
592 | } |
593 | ||
385449b1 | 594 | int git_note_remove(git_repository *repo, const char *notes_ref_in, |
de5596bf BS |
595 | const git_signature *author, const git_signature *committer, |
596 | const git_oid *oid) | |
bf477ed4 MS |
597 | { |
598 | int error; | |
eae0bfdc PP |
599 | char *notes_ref_target = NULL; |
600 | git_commit *existing_notes_commit = NULL; | |
601 | git_oid new_notes_commit; | |
602 | git_reference *notes_ref = NULL; | |
bf477ed4 | 603 | |
eae0bfdc PP |
604 | error = retrieve_note_commit(&existing_notes_commit, ¬es_ref_target, |
605 | repo, notes_ref_in); | |
bf477ed4 | 606 | |
eae0bfdc PP |
607 | if (error < 0) |
608 | goto cleanup; | |
bf477ed4 | 609 | |
eae0bfdc PP |
610 | error = git_note_commit_remove(&new_notes_commit, repo, |
611 | existing_notes_commit, author, committer, oid); | |
612 | if (error < 0) | |
613 | goto cleanup; | |
614 | ||
615 | error = git_reference_create(¬es_ref, repo, notes_ref_target, | |
616 | &new_notes_commit, 1, NULL); | |
617 | ||
618 | cleanup: | |
619 | git__free(notes_ref_target); | |
620 | git_reference_free(notes_ref); | |
621 | git_commit_free(existing_notes_commit); | |
4aa7de15 | 622 | return error; |
bf477ed4 MS |
623 | } |
624 | ||
385449b1 | 625 | int git_note_default_ref(git_buf *out, git_repository *repo) |
630c5a4a | 626 | { |
385449b1 CMN |
627 | char *default_ref; |
628 | int error; | |
629 | ||
630 | assert(out && repo); | |
631 | ||
632 | git_buf_sanitize(out); | |
633 | ||
634 | if ((error = note_get_default_ref(&default_ref, repo)) < 0) | |
635 | return error; | |
636 | ||
637 | git_buf_attach(out, default_ref, strlen(default_ref)); | |
638 | return 0; | |
630c5a4a MS |
639 | } |
640 | ||
bad4937e ET |
641 | const git_signature *git_note_committer(const git_note *note) |
642 | { | |
643 | assert(note); | |
644 | return note->committer; | |
645 | } | |
646 | ||
647 | const git_signature *git_note_author(const git_note *note) | |
648 | { | |
649 | assert(note); | |
650 | return note->author; | |
651 | } | |
652 | ||
de5596bf | 653 | const char * git_note_message(const git_note *note) |
bf477ed4 MS |
654 | { |
655 | assert(note); | |
656 | return note->message; | |
657 | } | |
658 | ||
d0a3de72 | 659 | const git_oid * git_note_id(const git_note *note) |
bf477ed4 MS |
660 | { |
661 | assert(note); | |
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( | |
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; | |
385449b1 | 783 | char *notes_ref; |
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: |
385449b1 | 792 | git__free(notes_ref); |
6edb427b NG |
793 | git_commit_free(commit); |
794 | ||
795 | return error; | |
796 | } | |
797 | ||
798 | int git_note_next( | |
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 | |
66a70851 UG |
811 | if ((error = process_entry_path(item->path, annotated_id)) < 0) |
812 | return error; | |
6edb427b | 813 | |
66a70851 UG |
814 | if ((error = git_iterator_advance(NULL, it)) < 0 && error != GIT_ITEROVER) |
815 | return error; | |
816 | ||
817 | return 0; | |
6edb427b | 818 | } |