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