]>
Commit | Line | Data |
---|---|---|
06160502 | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
06160502 | 3 | * |
bb742ede VM |
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. | |
06160502 SP |
6 | */ |
7 | ||
44908fe7 VM |
8 | #include "git2/common.h" |
9 | #include "git2/object.h" | |
10 | #include "git2/repository.h" | |
638c2ca4 | 11 | #include "git2/signature.h" |
9233b3de | 12 | #include "git2/sys/commit.h" |
58519018 | 13 | |
64a47c01 | 14 | #include "common.h" |
72a3fe42 | 15 | #include "odb.h" |
4f0adcd0 | 16 | #include "commit.h" |
638c2ca4 | 17 | #include "signature.h" |
458b9450 | 18 | #include "message.h" |
06160502 | 19 | |
72a3fe42 VM |
20 | #include <stdarg.h> |
21 | ||
78606263 | 22 | void git_commit__free(void *_commit) |
40721f6b | 23 | { |
78606263 RB |
24 | git_commit *commit = _commit; |
25 | ||
9abc78ae | 26 | git_array_clear(commit->parent_ids); |
52f2390b | 27 | |
638c2ca4 VM |
28 | git_signature_free(commit->author); |
29 | git_signature_free(commit->committer); | |
58519018 | 30 | |
f094f905 | 31 | git__free(commit->raw_header); |
598f069b | 32 | git__free(commit->raw_message); |
3286c408 | 33 | git__free(commit->message_encoding); |
300d192f | 34 | git__free(commit->summary); |
9abc78ae | 35 | |
3286c408 | 36 | git__free(commit); |
40721f6b VM |
37 | } |
38 | ||
72a3fe42 | 39 | int git_commit_create_v( |
9233b3de RB |
40 | git_oid *oid, |
41 | git_repository *repo, | |
42 | const char *update_ref, | |
43 | const git_signature *author, | |
44 | const git_signature *committer, | |
45 | const char *message_encoding, | |
46 | const char *message, | |
47 | const git_tree *tree, | |
48 | int parent_count, | |
49 | ...) | |
72a3fe42 VM |
50 | { |
51 | va_list ap; | |
73fe6a8e | 52 | int i, res; |
d5afc039 | 53 | const git_commit **parents; |
72a3fe42 | 54 | |
d5afc039 | 55 | parents = git__malloc(parent_count * sizeof(git_commit *)); |
73fe6a8e | 56 | GITERR_CHECK_ALLOC(parents); |
72a3fe42 VM |
57 | |
58 | va_start(ap, parent_count); | |
59 | for (i = 0; i < parent_count; ++i) | |
d5afc039 | 60 | parents[i] = va_arg(ap, const git_commit *); |
72a3fe42 VM |
61 | va_end(ap); |
62 | ||
73fe6a8e | 63 | res = git_commit_create( |
5ae2f0c0 VM |
64 | oid, repo, update_ref, author, committer, |
65 | message_encoding, message, | |
d5afc039 | 66 | tree, parent_count, parents); |
72a3fe42 | 67 | |
3286c408 | 68 | git__free((void *)parents); |
73fe6a8e VM |
69 | return res; |
70 | } | |
71 | ||
9233b3de RB |
72 | int git_commit_create_from_oids( |
73 | git_oid *oid, | |
74 | git_repository *repo, | |
75 | const char *update_ref, | |
76 | const git_signature *author, | |
77 | const git_signature *committer, | |
78 | const char *message_encoding, | |
79 | const char *message, | |
80 | const git_oid *tree, | |
81 | int parent_count, | |
82 | const git_oid *parents[]) | |
72a3fe42 | 83 | { |
e00b56eb | 84 | git_buf commit = GIT_BUF_INIT; |
73fe6a8e | 85 | int i; |
9462c471 | 86 | git_odb *odb; |
72a3fe42 | 87 | |
9233b3de | 88 | assert(oid && repo && tree && parent_count >= 0); |
9233b3de | 89 | |
92550398 | 90 | git_oid__writebuf(&commit, "tree ", tree); |
d5afc039 | 91 | |
92550398 JW |
92 | for (i = 0; i < parent_count; ++i) |
93 | git_oid__writebuf(&commit, "parent ", parents[i]); | |
0c3596f1 | 94 | |
afeecf4f VM |
95 | git_signature__writebuf(&commit, "author ", author); |
96 | git_signature__writebuf(&commit, "committer ", committer); | |
0c3596f1 | 97 | |
5ae2f0c0 VM |
98 | if (message_encoding != NULL) |
99 | git_buf_printf(&commit, "encoding %s\n", message_encoding); | |
100 | ||
afeecf4f | 101 | git_buf_putc(&commit, '\n'); |
0c3596f1 | 102 | |
e00b56eb | 103 | if (git_buf_puts(&commit, message) < 0) |
458b9450 | 104 | goto on_error; |
105 | ||
73fe6a8e VM |
106 | if (git_repository_odb__weakptr(&odb, repo) < 0) |
107 | goto on_error; | |
75abd2b9 | 108 | |
73fe6a8e VM |
109 | if (git_odb_write(oid, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0) |
110 | goto on_error; | |
72a3fe42 | 111 | |
73fe6a8e | 112 | git_buf_free(&commit); |
57450775 | 113 | |
73fe6a8e | 114 | if (update_ref != NULL) |
0b28217b | 115 | return git_reference__update_terminal(repo, update_ref, oid, NULL, NULL); |
4c7a5e9e | 116 | |
73fe6a8e | 117 | return 0; |
afeecf4f | 118 | |
73fe6a8e | 119 | on_error: |
afeecf4f | 120 | git_buf_free(&commit); |
458b9450 | 121 | giterr_set(GITERR_OBJECT, "Failed to create commit."); |
73fe6a8e | 122 | return -1; |
0c3596f1 VM |
123 | } |
124 | ||
92550398 | 125 | int git_commit_create( |
9233b3de RB |
126 | git_oid *oid, |
127 | git_repository *repo, | |
128 | const char *update_ref, | |
129 | const git_signature *author, | |
130 | const git_signature *committer, | |
131 | const char *message_encoding, | |
132 | const char *message, | |
133 | const git_tree *tree, | |
134 | int parent_count, | |
135 | const git_commit *parents[]) | |
92550398 | 136 | { |
9233b3de | 137 | int retval, i; |
92550398 JW |
138 | const git_oid **parent_oids; |
139 | ||
9233b3de | 140 | assert(parent_count >= 0); |
ce72e399 | 141 | assert(git_object_owner((const git_object *)tree) == repo); |
92550398 JW |
142 | |
143 | parent_oids = git__malloc(parent_count * sizeof(git_oid *)); | |
144 | GITERR_CHECK_ALLOC(parent_oids); | |
145 | ||
146 | for (i = 0; i < parent_count; ++i) { | |
147 | assert(git_object_owner((const git_object *)parents[i]) == repo); | |
148 | parent_oids[i] = git_object_id((const git_object *)parents[i]); | |
149 | } | |
150 | ||
9233b3de RB |
151 | retval = git_commit_create_from_oids( |
152 | oid, repo, update_ref, author, committer, | |
153 | message_encoding, message, | |
154 | git_object_id((const git_object *)tree), parent_count, parent_oids); | |
92550398 JW |
155 | |
156 | git__free((void *)parent_oids); | |
9233b3de RB |
157 | |
158 | return retval; | |
92550398 JW |
159 | } |
160 | ||
3f27127d | 161 | int git_commit__parse(void *_commit, git_odb_object *odb_obj) |
417f0abc | 162 | { |
78606263 | 163 | git_commit *commit = _commit; |
f094f905 RB |
164 | const char *buffer_start = git_odb_object_data(odb_obj), *buffer; |
165 | const char *buffer_end = buffer_start + git_odb_object_size(odb_obj); | |
cfbe4be3 | 166 | git_oid parent_id; |
584f2d30 RB |
167 | uint32_t parent_count = 0; |
168 | size_t header_len; | |
417f0abc | 169 | |
f094f905 RB |
170 | /* find end-of-header (counting parents as we go) */ |
171 | for (buffer = buffer_start; buffer < buffer_end; ++buffer) { | |
172 | if (!strncmp("\n\n", buffer, 2)) { | |
173 | ++buffer; | |
174 | break; | |
175 | } | |
176 | if (!strncmp("\nparent ", buffer, strlen("\nparent "))) | |
177 | ++parent_count; | |
178 | } | |
179 | ||
180 | header_len = buffer - buffer_start; | |
181 | commit->raw_header = git__strndup(buffer_start, header_len); | |
182 | GITERR_CHECK_ALLOC(commit->raw_header); | |
183 | ||
184 | /* point "buffer" to header data */ | |
185 | buffer = commit->raw_header; | |
186 | buffer_end = commit->raw_header + header_len; | |
187 | ||
188 | if (parent_count < 1) | |
189 | parent_count = 1; | |
190 | ||
9abc78ae RB |
191 | git_array_init_to_size(commit->parent_ids, parent_count); |
192 | GITERR_CHECK_ARRAY(commit->parent_ids); | |
eec95235 | 193 | |
cfbe4be3 | 194 | if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0) |
73fe6a8e | 195 | goto bad_buffer; |
225fe215 | 196 | |
9b3577ed | 197 | /* |
9b3577ed VM |
198 | * TODO: commit grafts! |
199 | */ | |
417f0abc | 200 | |
cfbe4be3 | 201 | while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) { |
9abc78ae | 202 | git_oid *new_id = git_array_alloc(commit->parent_ids); |
cfbe4be3 | 203 | GITERR_CHECK_ALLOC(new_id); |
73fe6a8e | 204 | |
cfbe4be3 | 205 | git_oid_cpy(new_id, &parent_id); |
9b3577ed | 206 | } |
417f0abc | 207 | |
6b2a1941 | 208 | commit->author = git__malloc(sizeof(git_signature)); |
73fe6a8e VM |
209 | GITERR_CHECK_ALLOC(commit->author); |
210 | ||
211 | if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0) | |
212 | return -1; | |
52f2390b | 213 | |
58519018 | 214 | /* Always parse the committer; we need the commit time */ |
638c2ca4 | 215 | commit->committer = git__malloc(sizeof(git_signature)); |
73fe6a8e VM |
216 | GITERR_CHECK_ALLOC(commit->committer); |
217 | ||
218 | if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0) | |
219 | return -1; | |
5ae2f0c0 | 220 | |
f094f905 RB |
221 | /* Parse add'l header entries */ |
222 | while (buffer < buffer_end) { | |
291090a0 RB |
223 | const char *eoln = buffer; |
224 | while (eoln < buffer_end && *eoln != '\n') | |
225 | ++eoln; | |
5ae2f0c0 | 226 | |
291090a0 RB |
227 | if (git__prefixcmp(buffer, "encoding ") == 0) { |
228 | buffer += strlen("encoding "); | |
5ae2f0c0 | 229 | |
291090a0 RB |
230 | commit->message_encoding = git__strndup(buffer, eoln - buffer); |
231 | GITERR_CHECK_ALLOC(commit->message_encoding); | |
232 | } | |
5ae2f0c0 | 233 | |
d47c6aab CMN |
234 | if (eoln < buffer_end && *eoln == '\n') |
235 | ++eoln; | |
291090a0 | 236 | buffer = eoln; |
5ae2f0c0 | 237 | } |
58519018 | 238 | |
f094f905 RB |
239 | /* point "buffer" to data after header */ |
240 | buffer = git_odb_object_data(odb_obj); | |
241 | buffer_end = buffer + git_odb_object_size(odb_obj); | |
242 | ||
243 | buffer += header_len; | |
598f069b | 244 | if (*buffer == '\n') |
f094f905 | 245 | ++buffer; |
52f2390b | 246 | |
f094f905 | 247 | /* extract commit message */ |
04f78802 | 248 | if (buffer <= buffer_end) { |
598f069b | 249 | commit->raw_message = git__strndup(buffer, buffer_end - buffer); |
250 | GITERR_CHECK_ALLOC(commit->raw_message); | |
52f2390b | 251 | } |
417f0abc | 252 | |
73fe6a8e VM |
253 | return 0; |
254 | ||
255 | bad_buffer: | |
256 | giterr_set(GITERR_OBJECT, "Failed to parse bad commit object"); | |
257 | return -1; | |
417f0abc | 258 | } |
4caa8962 | 259 | |
6b2a1941 | 260 | #define GIT_COMMIT_GETTER(_rvalue, _name, _return) \ |
cfbe4be3 | 261 | _rvalue git_commit_##_name(const git_commit *commit) \ |
0c3596f1 | 262 | {\ |
58519018 | 263 | assert(commit); \ |
6b2a1941 | 264 | return _return; \ |
0c3596f1 VM |
265 | } |
266 | ||
6b2a1941 VM |
267 | GIT_COMMIT_GETTER(const git_signature *, author, commit->author) |
268 | GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer) | |
598f069b | 269 | GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message) |
5ae2f0c0 | 270 | GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding) |
f094f905 | 271 | GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header) |
56d8ca26 | 272 | GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time) |
6b2a1941 | 273 | GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset) |
9abc78ae | 274 | GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids)) |
cfbe4be3 | 275 | GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id); |
6b2a1941 | 276 | |
598f069b | 277 | const char *git_commit_message(const git_commit *commit) |
278 | { | |
be0a1a79 | 279 | const char *message; |
598f069b | 280 | |
281 | assert(commit); | |
282 | ||
be0a1a79 PH |
283 | message = commit->raw_message; |
284 | ||
598f069b | 285 | /* trim leading newlines from raw message */ |
286 | while (*message && *message == '\n') | |
287 | ++message; | |
288 | ||
289 | return message; | |
290 | } | |
291 | ||
300d192f ET |
292 | const char *git_commit_summary(git_commit *commit) |
293 | { | |
294 | git_buf summary = GIT_BUF_INIT; | |
295 | const char *msg, *space; | |
296 | ||
297 | assert(commit); | |
298 | ||
299 | if (!commit->summary) { | |
300 | for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) { | |
301 | if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n')) | |
302 | break; | |
303 | else if (msg[0] == '\n') | |
304 | git_buf_putc(&summary, ' '); | |
305 | else if (git__isspace(msg[0])) | |
306 | space = space ? space : msg; | |
307 | else if (space) { | |
308 | git_buf_put(&summary, space, (msg - space) + 1); | |
309 | space = NULL; | |
310 | } else | |
311 | git_buf_putc(&summary, *msg); | |
312 | } | |
313 | ||
314 | commit->summary = git_buf_detach(&summary); | |
315 | } | |
316 | ||
317 | return commit->summary; | |
318 | } | |
319 | ||
cfbe4be3 | 320 | int git_commit_tree(git_tree **tree_out, const git_commit *commit) |
6b2a1941 VM |
321 | { |
322 | assert(commit); | |
cfbe4be3 | 323 | return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id); |
6b2a1941 | 324 | } |
57450775 | 325 | |
58206c9a RB |
326 | const git_oid *git_commit_parent_id( |
327 | const git_commit *commit, unsigned int n) | |
2b92a154 | 328 | { |
329 | assert(commit); | |
330 | ||
9abc78ae | 331 | return git_array_get(commit->parent_ids, n); |
2b92a154 | 332 | } |
333 | ||
58206c9a RB |
334 | int git_commit_parent( |
335 | git_commit **parent, const git_commit *commit, unsigned int n) | |
48c27f86 | 336 | { |
cfbe4be3 | 337 | const git_oid *parent_id; |
48c27f86 VM |
338 | assert(commit); |
339 | ||
cfbe4be3 VM |
340 | parent_id = git_commit_parent_id(commit, n); |
341 | if (parent_id == NULL) { | |
3aa351ea CMN |
342 | giterr_set(GITERR_INVALID, "Parent %u does not exist", n); |
343 | return GIT_ENOTFOUND; | |
344 | } | |
48c27f86 | 345 | |
cfbe4be3 | 346 | return git_commit_lookup(parent, commit->object.repo, parent_id); |
48c27f86 | 347 | } |
b1aca6ea | 348 | |
349 | int git_commit_nth_gen_ancestor( | |
350 | git_commit **ancestor, | |
351 | const git_commit *commit, | |
352 | unsigned int n) | |
353 | { | |
e583334c | 354 | git_commit *current, *parent = NULL; |
b1aca6ea | 355 | int error; |
356 | ||
357 | assert(ancestor && commit); | |
358 | ||
359 | current = (git_commit *)commit; | |
360 | ||
361 | if (n == 0) | |
362 | return git_commit_lookup( | |
363 | ancestor, | |
364 | commit->object.repo, | |
365 | git_object_id((const git_object *)commit)); | |
366 | ||
367 | while (n--) { | |
368 | error = git_commit_parent(&parent, (git_commit *)current, 0); | |
369 | ||
370 | if (current != commit) | |
371 | git_commit_free(current); | |
372 | ||
373 | if (error < 0) | |
374 | return error; | |
375 | ||
376 | current = parent; | |
377 | } | |
378 | ||
379 | *ancestor = parent; | |
380 | return 0; | |
381 | } |