]> git.proxmox.com Git - libgit2.git/blame - src/commit.c
Merge pull request #2070 from ethomson/checkout_filemode
[libgit2.git] / src / commit.c
CommitLineData
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 22void 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 39int 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
72int 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 119on_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 125int 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 161int 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
255bad_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
267GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
268GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
598f069b 269GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message)
5ae2f0c0 270GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)
f094f905 271GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header)
56d8ca26 272GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
6b2a1941 273GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
9abc78ae 274GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids))
cfbe4be3 275GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id);
6b2a1941 276
598f069b 277const 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
292const 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 320int 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
326const 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
334int 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
349int 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}