]> git.proxmox.com Git - libgit2.git/blob - src/commit.c
pkt-line: parse other-ref lines
[libgit2.git] / src / commit.c
1 /*
2 * This file is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2,
4 * as published by the Free Software Foundation.
5 *
6 * In addition to the permissions in the GNU General Public License,
7 * the authors give you unlimited permission to link the compiled
8 * version of this file into combinations with other programs,
9 * and to distribute those combinations without any restriction
10 * coming from the use of this file. (The General Public License
11 * restrictions do apply in other respects; for example, they cover
12 * modification of the file, and distribution when not linked into
13 * a combined executable.)
14 *
15 * This file is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; see the file COPYING. If not, write to
22 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26 #include "git2/common.h"
27 #include "git2/object.h"
28 #include "git2/repository.h"
29 #include "git2/signature.h"
30
31 #include "common.h"
32 #include "odb.h"
33 #include "commit.h"
34 #include "signature.h"
35
36 #include <stdarg.h>
37
38 #define COMMIT_BASIC_PARSE 0x0
39 #define COMMIT_FULL_PARSE 0x1
40
41 #define COMMIT_PRINT(commit) {\
42 char oid[41]; oid[40] = 0;\
43 git_oid_fmt(oid, &commit->object.id);\
44 printf("Oid: %s | In degree: %d | Time: %u\n", oid, commit->in_degree, commit->commit_time);\
45 }
46
47 static void clear_parents(git_commit *commit)
48 {
49 unsigned int i;
50
51 for (i = 0; i < commit->parent_oids.length; ++i) {
52 git_oid *parent = git_vector_get(&commit->parent_oids, i);
53 free(parent);
54 }
55
56 git_vector_clear(&commit->parent_oids);
57 }
58
59 void git_commit__free(git_commit *commit)
60 {
61 clear_parents(commit);
62 git_vector_free(&commit->parent_oids);
63
64 git_signature_free(commit->author);
65 git_signature_free(commit->committer);
66
67 free(commit->message);
68 free(commit->message_short);
69 free(commit);
70 }
71
72 const git_oid *git_commit_id(git_commit *c)
73 {
74 return git_object_id((git_object *)c);
75 }
76
77
78 int git_commit_create_v(
79 git_oid *oid,
80 git_repository *repo,
81 const char *update_ref,
82 const git_signature *author,
83 const git_signature *committer,
84 const char *message,
85 const git_oid *tree_oid,
86 int parent_count,
87 ...)
88 {
89 va_list ap;
90 int i, error;
91 const git_oid **oids;
92
93 oids = git__malloc(parent_count * sizeof(git_oid *));
94
95 va_start(ap, parent_count);
96 for (i = 0; i < parent_count; ++i)
97 oids[i] = va_arg(ap, const git_oid *);
98 va_end(ap);
99
100 error = git_commit_create(
101 oid, repo, update_ref, author, committer, message,
102 tree_oid, parent_count, oids);
103
104 free((void *)oids);
105
106 return error;
107 }
108
109 int git_commit_create_ov(
110 git_oid *oid,
111 git_repository *repo,
112 const char *update_ref,
113 const git_signature *author,
114 const git_signature *committer,
115 const char *message,
116 const git_tree *tree,
117 int parent_count,
118 ...)
119 {
120 va_list ap;
121 int i, error;
122 const git_oid **oids;
123
124 oids = git__malloc(parent_count * sizeof(git_oid *));
125
126 va_start(ap, parent_count);
127 for (i = 0; i < parent_count; ++i)
128 oids[i] = git_object_id(va_arg(ap, const git_object *));
129 va_end(ap);
130
131 error = git_commit_create(
132 oid, repo, update_ref, author, committer, message,
133 git_object_id((git_object *)tree),
134 parent_count, oids);
135
136 free((void *)oids);
137
138 return error;
139 }
140
141 int git_commit_create_o(
142 git_oid *oid,
143 git_repository *repo,
144 const char *update_ref,
145 const git_signature *author,
146 const git_signature *committer,
147 const char *message,
148 const git_tree *tree,
149 int parent_count,
150 const git_commit *parents[])
151 {
152 int i, error;
153 const git_oid **oids;
154
155 oids = git__malloc(parent_count * sizeof(git_oid *));
156
157 for (i = 0; i < parent_count; ++i)
158 oids[i] = git_object_id((git_object *)parents[i]);
159
160 error = git_commit_create(
161 oid, repo, update_ref, author, committer, message,
162 git_object_id((git_object *)tree),
163 parent_count, oids);
164
165 free((void *)oids);
166
167 return error;
168 }
169
170 int git_commit_create(
171 git_oid *oid,
172 git_repository *repo,
173 const char *update_ref,
174 const git_signature *author,
175 const git_signature *committer,
176 const char *message,
177 const git_oid *tree_oid,
178 int parent_count,
179 const git_oid *parents[])
180 {
181 size_t final_size = 0;
182 int message_length, author_length, committer_length;
183
184 char *author_str, *committer_str;
185
186 int error, i;
187 git_odb_stream *stream;
188
189 message_length = strlen(message);
190 author_length = git_signature__write(&author_str, "author", author);
191 committer_length = git_signature__write(&committer_str, "committer", committer);
192
193 if (author_length < 0 || committer_length < 0)
194 return git__throw(GIT_EINVALIDARGS, "Cannot create commit. Failed to parse signature");
195
196 final_size += GIT_OID_LINE_LENGTH("tree");
197 final_size += GIT_OID_LINE_LENGTH("parent") * parent_count;
198 final_size += author_length;
199 final_size += committer_length;
200 final_size += 1 + message_length;
201
202 if ((error = git_odb_open_wstream(&stream, repo->db, final_size, GIT_OBJ_COMMIT)) < GIT_SUCCESS)
203 return git__rethrow(error, "Failed to create commit");
204
205 git__write_oid(stream, "tree", tree_oid);
206
207 for (i = 0; i < parent_count; ++i)
208 git__write_oid(stream, "parent", parents[i]);
209
210 stream->write(stream, author_str, author_length);
211 free(author_str);
212
213 stream->write(stream, committer_str, committer_length);
214 free(committer_str);
215
216
217 stream->write(stream, "\n", 1);
218 stream->write(stream, message, message_length);
219
220 error = stream->finalize_write(oid, stream);
221 stream->free(stream);
222
223 if (error == GIT_SUCCESS && update_ref != NULL) {
224 git_reference *head;
225
226 error = git_reference_lookup(&head, repo, update_ref);
227 if (error < GIT_SUCCESS)
228 return git__rethrow(error, "Failed to create commit");
229
230 error = git_reference_resolve(&head, head);
231 if (error < GIT_SUCCESS) {
232 if (error != GIT_ENOTFOUND)
233 return git__rethrow(error, "Failed to create commit");
234 /*
235 * The target of the reference was not found. This can happen
236 * just after a repository has been initialized (the master
237 * branch doesn't exist yet, as it doesn't have anything to
238 * point to) or after an orphan checkout, so if the target
239 * branch doesn't exist yet, create it and return.
240 */
241 return git_reference_create_oid_f(&head, repo, git_reference_target(head), oid);
242 }
243
244 error = git_reference_set_oid(head, oid);
245 }
246
247 if (error < GIT_SUCCESS)
248 return git__rethrow(error, "Failed to create commit");
249
250 return GIT_SUCCESS;
251 }
252
253 int commit_parse_buffer(git_commit *commit, const void *data, size_t len)
254 {
255 const char *buffer = (char *)data;
256 const char *buffer_end = (char *)data + len;
257
258 git_oid parent_oid;
259 int error;
260
261 git_vector_init(&commit->parent_oids, 4, NULL);
262
263 if ((error = git__parse_oid(&commit->tree_oid, &buffer, buffer_end, "tree ")) < GIT_SUCCESS)
264 return git__rethrow(error, "Failed to parse buffer");
265
266 /*
267 * TODO: commit grafts!
268 */
269
270 while (git__parse_oid(&parent_oid, &buffer, buffer_end, "parent ") == GIT_SUCCESS) {
271 git_oid *new_oid;
272
273 new_oid = git__malloc(sizeof(git_oid));
274 git_oid_cpy(new_oid, &parent_oid);
275
276 if (git_vector_insert(&commit->parent_oids, new_oid) < GIT_SUCCESS)
277 return GIT_ENOMEM;
278 }
279
280 commit->author = git__malloc(sizeof(git_signature));
281 if ((error = git_signature__parse(commit->author, &buffer, buffer_end, "author ")) < GIT_SUCCESS)
282 return git__rethrow(error, "Failed to parse buffer");
283
284 /* Always parse the committer; we need the commit time */
285 commit->committer = git__malloc(sizeof(git_signature));
286 if ((error = git_signature__parse(commit->committer, &buffer, buffer_end, "committer ")) < GIT_SUCCESS)
287 return git__rethrow(error, "Failed to parse buffer");
288
289 /* parse commit message */
290 while (buffer <= buffer_end && *buffer == '\n')
291 buffer++;
292
293 if (buffer < buffer_end) {
294 const char *line_end;
295 unsigned int i;
296 size_t message_len;
297
298 /* Long message */
299 message_len = buffer_end - buffer;
300 commit->message = git__malloc(message_len + 1);
301 memcpy(commit->message, buffer, message_len);
302 commit->message[message_len] = 0;
303
304 /* Short message */
305 if((line_end = strstr(buffer, "\n\n")) == NULL) {
306 /* Cut the last '\n' if there is one */
307 if (message_len && buffer[message_len - 1] == '\n')
308 line_end = buffer_end - 1;
309 else
310 line_end = buffer_end;
311 }
312 message_len = line_end - buffer;
313 commit->message_short = git__malloc(message_len + 1);
314 for (i = 0; i < message_len; ++i) {
315 commit->message_short[i] = (buffer[i] == '\n') ? ' ' : buffer[i];
316 }
317 commit->message_short[message_len] = 0;
318 }
319
320 return GIT_SUCCESS;
321 }
322
323 int git_commit__parse(git_commit *commit, git_odb_object *obj)
324 {
325 assert(commit);
326 return commit_parse_buffer(commit, obj->raw.data, obj->raw.len);
327 }
328
329 #define GIT_COMMIT_GETTER(_rvalue, _name, _return) \
330 _rvalue git_commit_##_name(git_commit *commit) \
331 {\
332 assert(commit); \
333 return _return; \
334 }
335
336 GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
337 GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
338 GIT_COMMIT_GETTER(const char *, message, commit->message)
339 GIT_COMMIT_GETTER(const char *, message_short, commit->message_short)
340 GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
341 GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
342 GIT_COMMIT_GETTER(unsigned int, parentcount, commit->parent_oids.length)
343 GIT_COMMIT_GETTER(const git_oid *, tree_oid, &commit->tree_oid);
344
345
346 int git_commit_tree(git_tree **tree_out, git_commit *commit)
347 {
348 assert(commit);
349 return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_oid);
350 }
351
352 int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n)
353 {
354 git_oid *parent_oid;
355 assert(commit);
356
357 parent_oid = git_vector_get(&commit->parent_oids, n);
358 if (parent_oid == NULL)
359 return git__throw(GIT_ENOTFOUND, "Parent %u does not exist", n);
360
361 return git_commit_lookup(parent, commit->object.repo, parent_oid);
362 }
363
364 const git_oid *git_commit_parent_oid(git_commit *commit, unsigned int n)
365 {
366 assert(commit);
367
368 return git_vector_get(&commit->parent_oids, n);
369 }