]>
Commit | Line | Data |
---|---|---|
06160502 | 1 | /* |
5e0de328 | 2 | * Copyright (C) 2009-2012 the libgit2 contributors |
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" |
58519018 | 12 | |
64a47c01 | 13 | #include "common.h" |
72a3fe42 | 14 | #include "odb.h" |
4f0adcd0 | 15 | #include "commit.h" |
638c2ca4 | 16 | #include "signature.h" |
458b9450 | 17 | #include "message.h" |
06160502 | 18 | |
72a3fe42 VM |
19 | #include <stdarg.h> |
20 | ||
3315782c VM |
21 | static void clear_parents(git_commit *commit) |
22 | { | |
584f49a5 VM |
23 | unsigned int i; |
24 | ||
cfbe4be3 VM |
25 | for (i = 0; i < commit->parent_ids.length; ++i) { |
26 | git_oid *parent = git_vector_get(&commit->parent_ids, i); | |
3286c408 | 27 | git__free(parent); |
584f49a5 VM |
28 | } |
29 | ||
cfbe4be3 | 30 | git_vector_clear(&commit->parent_ids); |
3315782c VM |
31 | } |
32 | ||
40721f6b VM |
33 | void git_commit__free(git_commit *commit) |
34 | { | |
3315782c | 35 | clear_parents(commit); |
cfbe4be3 | 36 | git_vector_free(&commit->parent_ids); |
52f2390b | 37 | |
638c2ca4 VM |
38 | git_signature_free(commit->author); |
39 | git_signature_free(commit->committer); | |
58519018 | 40 | |
3286c408 VM |
41 | git__free(commit->message); |
42 | git__free(commit->message_encoding); | |
43 | git__free(commit); | |
40721f6b VM |
44 | } |
45 | ||
72a3fe42 | 46 | int git_commit_create_v( |
72a3fe42 VM |
47 | git_oid *oid, |
48 | git_repository *repo, | |
49 | const char *update_ref, | |
50 | const git_signature *author, | |
51 | const git_signature *committer, | |
5ae2f0c0 | 52 | const char *message_encoding, |
72a3fe42 VM |
53 | const char *message, |
54 | const git_tree *tree, | |
55 | int parent_count, | |
56 | ...) | |
57 | { | |
58 | va_list ap; | |
73fe6a8e | 59 | int i, res; |
d5afc039 | 60 | const git_commit **parents; |
72a3fe42 | 61 | |
d5afc039 | 62 | parents = git__malloc(parent_count * sizeof(git_commit *)); |
73fe6a8e | 63 | GITERR_CHECK_ALLOC(parents); |
72a3fe42 VM |
64 | |
65 | va_start(ap, parent_count); | |
66 | for (i = 0; i < parent_count; ++i) | |
d5afc039 | 67 | parents[i] = va_arg(ap, const git_commit *); |
72a3fe42 VM |
68 | va_end(ap); |
69 | ||
73fe6a8e | 70 | res = git_commit_create( |
5ae2f0c0 VM |
71 | oid, repo, update_ref, author, committer, |
72 | message_encoding, message, | |
d5afc039 | 73 | tree, parent_count, parents); |
72a3fe42 | 74 | |
3286c408 | 75 | git__free((void *)parents); |
73fe6a8e VM |
76 | return res; |
77 | } | |
78 | ||
d5afc039 | 79 | int git_commit_create( |
72a3fe42 VM |
80 | git_oid *oid, |
81 | git_repository *repo, | |
82 | const char *update_ref, | |
83 | const git_signature *author, | |
84 | const git_signature *committer, | |
5ae2f0c0 | 85 | const char *message_encoding, |
72a3fe42 VM |
86 | const char *message, |
87 | const git_tree *tree, | |
88 | int parent_count, | |
89 | const git_commit *parents[]) | |
72a3fe42 | 90 | { |
e00b56eb | 91 | git_buf commit = GIT_BUF_INIT; |
73fe6a8e | 92 | int i; |
9462c471 | 93 | git_odb *odb; |
72a3fe42 | 94 | |
d4d648b0 | 95 | assert(git_object_owner((const git_object *)tree) == repo); |
0c3596f1 | 96 | |
afeecf4f | 97 | git_oid__writebuf(&commit, "tree ", git_object_id((const git_object *)tree)); |
d5afc039 VM |
98 | |
99 | for (i = 0; i < parent_count; ++i) { | |
73fe6a8e | 100 | assert(git_object_owner((const git_object *)parents[i]) == repo); |
afeecf4f | 101 | git_oid__writebuf(&commit, "parent ", git_object_id((const git_object *)parents[i])); |
d5afc039 | 102 | } |
0c3596f1 | 103 | |
afeecf4f VM |
104 | git_signature__writebuf(&commit, "author ", author); |
105 | git_signature__writebuf(&commit, "committer ", committer); | |
0c3596f1 | 106 | |
5ae2f0c0 VM |
107 | if (message_encoding != NULL) |
108 | git_buf_printf(&commit, "encoding %s\n", message_encoding); | |
109 | ||
afeecf4f | 110 | git_buf_putc(&commit, '\n'); |
0c3596f1 | 111 | |
e00b56eb | 112 | if (git_buf_puts(&commit, message) < 0) |
458b9450 | 113 | goto on_error; |
114 | ||
73fe6a8e VM |
115 | if (git_repository_odb__weakptr(&odb, repo) < 0) |
116 | goto on_error; | |
75abd2b9 | 117 | |
73fe6a8e VM |
118 | if (git_odb_write(oid, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0) |
119 | goto on_error; | |
72a3fe42 | 120 | |
73fe6a8e | 121 | git_buf_free(&commit); |
57450775 | 122 | |
73fe6a8e | 123 | if (update_ref != NULL) |
edebceff | 124 | return git_reference__update(repo, oid, update_ref); |
4c7a5e9e | 125 | |
73fe6a8e | 126 | return 0; |
afeecf4f | 127 | |
73fe6a8e | 128 | on_error: |
afeecf4f | 129 | git_buf_free(&commit); |
458b9450 | 130 | giterr_set(GITERR_OBJECT, "Failed to create commit."); |
73fe6a8e | 131 | return -1; |
0c3596f1 VM |
132 | } |
133 | ||
d568d585 | 134 | int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len) |
417f0abc | 135 | { |
14468c6b KS |
136 | const char *buffer = data; |
137 | const char *buffer_end = (const char *)data + len; | |
417f0abc | 138 | |
cfbe4be3 | 139 | git_oid parent_id; |
417f0abc | 140 | |
cfbe4be3 | 141 | git_vector_init(&commit->parent_ids, 4, NULL); |
eec95235 | 142 | |
cfbe4be3 | 143 | if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0) |
73fe6a8e | 144 | goto bad_buffer; |
225fe215 | 145 | |
9b3577ed | 146 | /* |
9b3577ed VM |
147 | * TODO: commit grafts! |
148 | */ | |
417f0abc | 149 | |
cfbe4be3 VM |
150 | while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) { |
151 | git_oid *new_id; | |
417f0abc | 152 | |
cfbe4be3 VM |
153 | new_id = git__malloc(sizeof(git_oid)); |
154 | GITERR_CHECK_ALLOC(new_id); | |
73fe6a8e | 155 | |
cfbe4be3 | 156 | git_oid_cpy(new_id, &parent_id); |
417f0abc | 157 | |
cfbe4be3 | 158 | if (git_vector_insert(&commit->parent_ids, new_id) < 0) |
73fe6a8e | 159 | return -1; |
9b3577ed | 160 | } |
417f0abc | 161 | |
6b2a1941 | 162 | commit->author = git__malloc(sizeof(git_signature)); |
73fe6a8e VM |
163 | GITERR_CHECK_ALLOC(commit->author); |
164 | ||
165 | if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0) | |
166 | return -1; | |
52f2390b | 167 | |
58519018 | 168 | /* Always parse the committer; we need the commit time */ |
638c2ca4 | 169 | commit->committer = git__malloc(sizeof(git_signature)); |
73fe6a8e VM |
170 | GITERR_CHECK_ALLOC(commit->committer); |
171 | ||
172 | if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0) | |
173 | return -1; | |
5ae2f0c0 VM |
174 | |
175 | if (git__prefixcmp(buffer, "encoding ") == 0) { | |
176 | const char *encoding_end; | |
932669b8 | 177 | buffer += strlen("encoding "); |
5ae2f0c0 VM |
178 | |
179 | encoding_end = buffer; | |
180 | while (encoding_end < buffer_end && *encoding_end != '\n') | |
181 | encoding_end++; | |
182 | ||
183 | commit->message_encoding = git__strndup(buffer, encoding_end - buffer); | |
73fe6a8e | 184 | GITERR_CHECK_ALLOC(commit->message_encoding); |
5ae2f0c0 VM |
185 | |
186 | buffer = encoding_end; | |
187 | } | |
58519018 | 188 | |
52f2390b | 189 | /* parse commit message */ |
04f78802 | 190 | while (buffer < buffer_end - 1 && *buffer == '\n') |
52f2390b VM |
191 | buffer++; |
192 | ||
04f78802 | 193 | if (buffer <= buffer_end) { |
5ae2f0c0 | 194 | commit->message = git__strndup(buffer, buffer_end - buffer); |
73fe6a8e | 195 | GITERR_CHECK_ALLOC(commit->message); |
52f2390b | 196 | } |
417f0abc | 197 | |
73fe6a8e VM |
198 | return 0; |
199 | ||
200 | bad_buffer: | |
201 | giterr_set(GITERR_OBJECT, "Failed to parse bad commit object"); | |
202 | return -1; | |
417f0abc | 203 | } |
4caa8962 | 204 | |
72a3fe42 | 205 | int git_commit__parse(git_commit *commit, git_odb_object *obj) |
58519018 | 206 | { |
72a3fe42 | 207 | assert(commit); |
d568d585 | 208 | return git_commit__parse_buffer(commit, obj->raw.data, obj->raw.len); |
58519018 VM |
209 | } |
210 | ||
6b2a1941 | 211 | #define GIT_COMMIT_GETTER(_rvalue, _name, _return) \ |
cfbe4be3 | 212 | _rvalue git_commit_##_name(const git_commit *commit) \ |
0c3596f1 | 213 | {\ |
58519018 | 214 | assert(commit); \ |
6b2a1941 | 215 | return _return; \ |
0c3596f1 VM |
216 | } |
217 | ||
6b2a1941 VM |
218 | GIT_COMMIT_GETTER(const git_signature *, author, commit->author) |
219 | GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer) | |
220 | GIT_COMMIT_GETTER(const char *, message, commit->message) | |
5ae2f0c0 | 221 | GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding) |
56d8ca26 | 222 | GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time) |
6b2a1941 | 223 | GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset) |
cfbe4be3 VM |
224 | GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)commit->parent_ids.length) |
225 | GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id); | |
6b2a1941 | 226 | |
cfbe4be3 | 227 | int git_commit_tree(git_tree **tree_out, const git_commit *commit) |
6b2a1941 VM |
228 | { |
229 | assert(commit); | |
cfbe4be3 | 230 | return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id); |
6b2a1941 | 231 | } |
57450775 | 232 | |
cfbe4be3 | 233 | const git_oid *git_commit_parent_id(git_commit *commit, unsigned int n) |
2b92a154 | 234 | { |
235 | assert(commit); | |
236 | ||
cfbe4be3 | 237 | return git_vector_get(&commit->parent_ids, n); |
2b92a154 | 238 | } |
239 | ||
6b2a1941 | 240 | int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n) |
48c27f86 | 241 | { |
cfbe4be3 | 242 | const git_oid *parent_id; |
48c27f86 VM |
243 | assert(commit); |
244 | ||
cfbe4be3 VM |
245 | parent_id = git_commit_parent_id(commit, n); |
246 | if (parent_id == NULL) { | |
3aa351ea CMN |
247 | giterr_set(GITERR_INVALID, "Parent %u does not exist", n); |
248 | return GIT_ENOTFOUND; | |
249 | } | |
48c27f86 | 250 | |
cfbe4be3 | 251 | return git_commit_lookup(parent, commit->object.repo, parent_id); |
48c27f86 | 252 | } |
b1aca6ea | 253 | |
254 | int git_commit_nth_gen_ancestor( | |
255 | git_commit **ancestor, | |
256 | const git_commit *commit, | |
257 | unsigned int n) | |
258 | { | |
259 | git_commit *current, *parent; | |
260 | int error; | |
261 | ||
262 | assert(ancestor && commit); | |
263 | ||
264 | current = (git_commit *)commit; | |
265 | ||
266 | if (n == 0) | |
267 | return git_commit_lookup( | |
268 | ancestor, | |
269 | commit->object.repo, | |
270 | git_object_id((const git_object *)commit)); | |
271 | ||
272 | while (n--) { | |
273 | error = git_commit_parent(&parent, (git_commit *)current, 0); | |
274 | ||
275 | if (current != commit) | |
276 | git_commit_free(current); | |
277 | ||
278 | if (error < 0) | |
279 | return error; | |
280 | ||
281 | current = parent; | |
282 | } | |
283 | ||
284 | *ancestor = parent; | |
285 | return 0; | |
286 | } |