]> git.proxmox.com Git - libgit2.git/blame - src/commit.c
Merge pull request #3205 from ethomson/crlf_query
[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"
a612a25f 19#include "refs.h"
06160502 20
78606263 21void git_commit__free(void *_commit)
40721f6b 22{
78606263
RB
23 git_commit *commit = _commit;
24
9abc78ae 25 git_array_clear(commit->parent_ids);
52f2390b 26
638c2ca4
VM
27 git_signature_free(commit->author);
28 git_signature_free(commit->committer);
58519018 29
f094f905 30 git__free(commit->raw_header);
598f069b 31 git__free(commit->raw_message);
3286c408 32 git__free(commit->message_encoding);
300d192f 33 git__free(commit->summary);
9abc78ae 34
3286c408 35 git__free(commit);
40721f6b
VM
36}
37
80c29fe9
RB
38int git_commit_create_from_callback(
39 git_oid *id,
9233b3de
RB
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_oid *tree,
80c29fe9
RB
47 git_commit_parent_callback parent_cb,
48 void *parent_payload)
72a3fe42 49{
217c029b
CMN
50 git_reference *ref = NULL;
51 int error = 0, matched_parent = 0;
52 const git_oid *current_id = NULL;
e00b56eb 53 git_buf commit = GIT_BUF_INIT;
80c29fe9 54 size_t i = 0;
9462c471 55 git_odb *odb;
80c29fe9 56 const git_oid *parent;
72a3fe42 57
80c29fe9 58 assert(id && repo && tree && parent_cb);
9233b3de 59
217c029b
CMN
60 if (update_ref) {
61 error = git_reference_lookup_resolved(&ref, repo, update_ref, 10);
62 if (error < 0 && error != GIT_ENOTFOUND)
63 return error;
64 }
65 giterr_clear();
66
67 if (ref)
68 current_id = git_reference_target(ref);
69
92550398 70 git_oid__writebuf(&commit, "tree ", tree);
d5afc039 71
217c029b 72 while ((parent = parent_cb(i, parent_payload)) != NULL) {
80c29fe9 73 git_oid__writebuf(&commit, "parent ", parent);
217c029b
CMN
74 if (i == 0 && current_id && git_oid_equal(current_id, parent))
75 matched_parent = 1;
76 i++;
77 }
78
79 if (ref && !matched_parent) {
80 git_reference_free(ref);
81 git_buf_free(&commit);
82 giterr_set(GITERR_OBJECT, "failed to create commit: current tip is not the first parent");
83 return GIT_EMODIFIED;
84 }
0c3596f1 85
afeecf4f
VM
86 git_signature__writebuf(&commit, "author ", author);
87 git_signature__writebuf(&commit, "committer ", committer);
0c3596f1 88
5ae2f0c0
VM
89 if (message_encoding != NULL)
90 git_buf_printf(&commit, "encoding %s\n", message_encoding);
91
afeecf4f 92 git_buf_putc(&commit, '\n');
0c3596f1 93
e00b56eb 94 if (git_buf_puts(&commit, message) < 0)
458b9450 95 goto on_error;
96
73fe6a8e
VM
97 if (git_repository_odb__weakptr(&odb, repo) < 0)
98 goto on_error;
75abd2b9 99
80c29fe9 100 if (git_odb_write(id, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0)
73fe6a8e 101 goto on_error;
72a3fe42 102
73fe6a8e 103 git_buf_free(&commit);
57450775 104
0adb0606 105 if (update_ref != NULL) {
a612a25f 106 error = git_reference__update_for_commit(
659cf202 107 repo, ref, update_ref, id, "commit");
217c029b 108 git_reference_free(ref);
0adb0606
BS
109 return error;
110 }
4c7a5e9e 111
73fe6a8e 112 return 0;
afeecf4f 113
73fe6a8e 114on_error:
afeecf4f 115 git_buf_free(&commit);
458b9450 116 giterr_set(GITERR_OBJECT, "Failed to create commit.");
73fe6a8e 117 return -1;
0c3596f1
VM
118}
119
80c29fe9
RB
120typedef struct {
121 size_t total;
122 va_list args;
123} commit_parent_varargs;
124
125static const git_oid *commit_parent_from_varargs(size_t curr, void *payload)
126{
127 commit_parent_varargs *data = payload;
128 const git_commit *commit;
129 if (curr >= data->total)
130 return NULL;
131 commit = va_arg(data->args, const git_commit *);
132 return commit ? git_commit_id(commit) : NULL;
133}
134
135int git_commit_create_v(
136 git_oid *id,
137 git_repository *repo,
138 const char *update_ref,
139 const git_signature *author,
140 const git_signature *committer,
141 const char *message_encoding,
142 const char *message,
143 const git_tree *tree,
144 size_t parent_count,
145 ...)
146{
147 int error = 0;
148 commit_parent_varargs data;
149
150 assert(tree && git_tree_owner(tree) == repo);
151
152 data.total = parent_count;
153 va_start(data.args, parent_count);
154
155 error = git_commit_create_from_callback(
156 id, repo, update_ref, author, committer,
157 message_encoding, message, git_tree_id(tree),
158 commit_parent_from_varargs, &data);
159
160 va_end(data.args);
161 return error;
162}
163
164typedef struct {
165 size_t total;
166 const git_oid **parents;
167} commit_parent_oids;
168
169static const git_oid *commit_parent_from_ids(size_t curr, void *payload)
170{
171 commit_parent_oids *data = payload;
172 return (curr < data->total) ? data->parents[curr] : NULL;
173}
174
175int git_commit_create_from_ids(
176 git_oid *id,
177 git_repository *repo,
178 const char *update_ref,
179 const git_signature *author,
180 const git_signature *committer,
181 const char *message_encoding,
182 const char *message,
183 const git_oid *tree,
184 size_t parent_count,
185 const git_oid *parents[])
186{
187 commit_parent_oids data = { parent_count, parents };
188
189 return git_commit_create_from_callback(
190 id, repo, update_ref, author, committer,
191 message_encoding, message, tree,
192 commit_parent_from_ids, &data);
193}
194
195typedef struct {
196 size_t total;
197 const git_commit **parents;
198 git_repository *repo;
199} commit_parent_data;
200
201static const git_oid *commit_parent_from_array(size_t curr, void *payload)
202{
203 commit_parent_data *data = payload;
204 const git_commit *commit;
205 if (curr >= data->total)
206 return NULL;
207 commit = data->parents[curr];
208 if (git_commit_owner(commit) != data->repo)
209 return NULL;
210 return git_commit_id(commit);
211}
212
92550398 213int git_commit_create(
80c29fe9 214 git_oid *id,
9233b3de
RB
215 git_repository *repo,
216 const char *update_ref,
217 const git_signature *author,
218 const git_signature *committer,
219 const char *message_encoding,
220 const char *message,
221 const git_tree *tree,
80c29fe9 222 size_t parent_count,
9233b3de 223 const git_commit *parents[])
92550398 224{
80c29fe9 225 commit_parent_data data = { parent_count, parents, repo };
92550398 226
80c29fe9 227 assert(tree && git_tree_owner(tree) == repo);
92550398 228
80c29fe9
RB
229 return git_commit_create_from_callback(
230 id, repo, update_ref, author, committer,
231 message_encoding, message, git_tree_id(tree),
232 commit_parent_from_array, &data);
233}
92550398 234
80c29fe9
RB
235static const git_oid *commit_parent_for_amend(size_t curr, void *payload)
236{
237 const git_commit *commit_to_amend = payload;
238 if (curr >= git_array_size(commit_to_amend->parent_ids))
239 return NULL;
240 return git_array_get(commit_to_amend->parent_ids, curr);
241}
92550398 242
80c29fe9
RB
243int git_commit_amend(
244 git_oid *id,
245 const git_commit *commit_to_amend,
246 const char *update_ref,
247 const git_signature *author,
248 const git_signature *committer,
249 const char *message_encoding,
250 const char *message,
251 const git_tree *tree)
252{
253 git_repository *repo;
254 git_oid tree_id;
217c029b
CMN
255 git_reference *ref;
256 int error;
80c29fe9
RB
257
258 assert(id && commit_to_amend);
259
260 repo = git_commit_owner(commit_to_amend);
261
262 if (!author)
263 author = git_commit_author(commit_to_amend);
264 if (!committer)
265 committer = git_commit_committer(commit_to_amend);
266 if (!message_encoding)
267 message_encoding = git_commit_message_encoding(commit_to_amend);
268 if (!message)
269 message = git_commit_message(commit_to_amend);
270
271 if (!tree) {
272 git_tree *old_tree;
273 GITERR_CHECK_ERROR( git_commit_tree(&old_tree, commit_to_amend) );
274 git_oid_cpy(&tree_id, git_tree_id(old_tree));
275 git_tree_free(old_tree);
276 } else {
277 assert(git_tree_owner(tree) == repo);
278 git_oid_cpy(&tree_id, git_tree_id(tree));
279 }
9233b3de 280
217c029b
CMN
281 if (update_ref) {
282 if ((error = git_reference_lookup_resolved(&ref, repo, update_ref, 5)) < 0)
283 return error;
284
285 if (git_oid_cmp(git_commit_id(commit_to_amend), git_reference_target(ref))) {
286 git_reference_free(ref);
287 giterr_set(GITERR_REFERENCE, "commit to amend is not the tip of the given branch");
288 return -1;
289 }
290 }
291
292 error = git_commit_create_from_callback(
293 id, repo, NULL, author, committer, message_encoding, message,
80c29fe9 294 &tree_id, commit_parent_for_amend, (void *)commit_to_amend);
217c029b
CMN
295
296 if (!error && update_ref) {
a612a25f 297 error = git_reference__update_for_commit(
659cf202 298 repo, ref, NULL, id, "commit");
217c029b
CMN
299 git_reference_free(ref);
300 }
301
302 return error;
92550398
JW
303}
304
3f27127d 305int git_commit__parse(void *_commit, git_odb_object *odb_obj)
417f0abc 306{
78606263 307 git_commit *commit = _commit;
f094f905
RB
308 const char *buffer_start = git_odb_object_data(odb_obj), *buffer;
309 const char *buffer_end = buffer_start + git_odb_object_size(odb_obj);
cfbe4be3 310 git_oid parent_id;
584f2d30 311 size_t header_len;
417f0abc 312
a6563619 313 buffer = buffer_start;
f094f905 314
a6563619
CMN
315 /* Allocate for one, which will allow not to realloc 90% of the time */
316 git_array_init_to_size(commit->parent_ids, 1);
9abc78ae 317 GITERR_CHECK_ARRAY(commit->parent_ids);
eec95235 318
a6563619 319 /* The tree is always the first field */
cfbe4be3 320 if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0)
73fe6a8e 321 goto bad_buffer;
225fe215 322
9b3577ed 323 /*
9b3577ed
VM
324 * TODO: commit grafts!
325 */
417f0abc 326
cfbe4be3 327 while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) {
9abc78ae 328 git_oid *new_id = git_array_alloc(commit->parent_ids);
cfbe4be3 329 GITERR_CHECK_ALLOC(new_id);
73fe6a8e 330
cfbe4be3 331 git_oid_cpy(new_id, &parent_id);
9b3577ed 332 }
417f0abc 333
6b2a1941 334 commit->author = git__malloc(sizeof(git_signature));
73fe6a8e
VM
335 GITERR_CHECK_ALLOC(commit->author);
336
337 if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)
338 return -1;
52f2390b 339
58519018 340 /* Always parse the committer; we need the commit time */
638c2ca4 341 commit->committer = git__malloc(sizeof(git_signature));
73fe6a8e
VM
342 GITERR_CHECK_ALLOC(commit->committer);
343
344 if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
345 return -1;
5ae2f0c0 346
f094f905
RB
347 /* Parse add'l header entries */
348 while (buffer < buffer_end) {
291090a0 349 const char *eoln = buffer;
a6563619
CMN
350 if (buffer[-1] == '\n' && buffer[0] == '\n')
351 break;
352
291090a0
RB
353 while (eoln < buffer_end && *eoln != '\n')
354 ++eoln;
5ae2f0c0 355
291090a0
RB
356 if (git__prefixcmp(buffer, "encoding ") == 0) {
357 buffer += strlen("encoding ");
5ae2f0c0 358
291090a0
RB
359 commit->message_encoding = git__strndup(buffer, eoln - buffer);
360 GITERR_CHECK_ALLOC(commit->message_encoding);
361 }
5ae2f0c0 362
d47c6aab
CMN
363 if (eoln < buffer_end && *eoln == '\n')
364 ++eoln;
291090a0 365 buffer = eoln;
5ae2f0c0 366 }
58519018 367
a6563619
CMN
368 header_len = buffer - buffer_start;
369 commit->raw_header = git__strndup(buffer_start, header_len);
370 GITERR_CHECK_ALLOC(commit->raw_header);
f094f905 371
a6563619
CMN
372 /* point "buffer" to data after header, +1 for the final LF */
373 buffer = buffer_start + header_len + 1;
52f2390b 374
f094f905 375 /* extract commit message */
04f78802 376 if (buffer <= buffer_end) {
598f069b 377 commit->raw_message = git__strndup(buffer, buffer_end - buffer);
378 GITERR_CHECK_ALLOC(commit->raw_message);
52f2390b 379 }
417f0abc 380
73fe6a8e
VM
381 return 0;
382
383bad_buffer:
384 giterr_set(GITERR_OBJECT, "Failed to parse bad commit object");
385 return -1;
417f0abc 386}
4caa8962 387
6b2a1941 388#define GIT_COMMIT_GETTER(_rvalue, _name, _return) \
cfbe4be3 389 _rvalue git_commit_##_name(const git_commit *commit) \
0c3596f1 390 {\
58519018 391 assert(commit); \
6b2a1941 392 return _return; \
0c3596f1
VM
393 }
394
6b2a1941
VM
395GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
396GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
598f069b 397GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message)
5ae2f0c0 398GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)
f094f905 399GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header)
56d8ca26 400GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
6b2a1941 401GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
9abc78ae 402GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids))
c8e02b87 403GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id)
6b2a1941 404
598f069b 405const char *git_commit_message(const git_commit *commit)
406{
be0a1a79 407 const char *message;
598f069b 408
409 assert(commit);
410
be0a1a79
PH
411 message = commit->raw_message;
412
598f069b 413 /* trim leading newlines from raw message */
414 while (*message && *message == '\n')
415 ++message;
416
417 return message;
418}
419
300d192f
ET
420const char *git_commit_summary(git_commit *commit)
421{
422 git_buf summary = GIT_BUF_INIT;
423 const char *msg, *space;
424
425 assert(commit);
426
427 if (!commit->summary) {
428 for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) {
429 if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n'))
430 break;
431 else if (msg[0] == '\n')
432 git_buf_putc(&summary, ' ');
433 else if (git__isspace(msg[0]))
434 space = space ? space : msg;
435 else if (space) {
436 git_buf_put(&summary, space, (msg - space) + 1);
437 space = NULL;
438 } else
439 git_buf_putc(&summary, *msg);
440 }
441
80c29fe9
RB
442 commit->summary = git_buf_detach(&summary);
443 if (!commit->summary)
238e8149 444 commit->summary = git__strdup("");
300d192f
ET
445 }
446
447 return commit->summary;
448}
449
cfbe4be3 450int git_commit_tree(git_tree **tree_out, const git_commit *commit)
6b2a1941
VM
451{
452 assert(commit);
cfbe4be3 453 return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id);
6b2a1941 454}
57450775 455
58206c9a
RB
456const git_oid *git_commit_parent_id(
457 const git_commit *commit, unsigned int n)
2b92a154 458{
459 assert(commit);
460
9abc78ae 461 return git_array_get(commit->parent_ids, n);
2b92a154 462}
463
58206c9a
RB
464int git_commit_parent(
465 git_commit **parent, const git_commit *commit, unsigned int n)
48c27f86 466{
cfbe4be3 467 const git_oid *parent_id;
48c27f86
VM
468 assert(commit);
469
cfbe4be3
VM
470 parent_id = git_commit_parent_id(commit, n);
471 if (parent_id == NULL) {
3aa351ea
CMN
472 giterr_set(GITERR_INVALID, "Parent %u does not exist", n);
473 return GIT_ENOTFOUND;
474 }
48c27f86 475
cfbe4be3 476 return git_commit_lookup(parent, commit->object.repo, parent_id);
48c27f86 477}
b1aca6ea 478
479int git_commit_nth_gen_ancestor(
480 git_commit **ancestor,
481 const git_commit *commit,
482 unsigned int n)
483{
e583334c 484 git_commit *current, *parent = NULL;
b1aca6ea 485 int error;
486
487 assert(ancestor && commit);
488
7c1ee212
CMN
489 if (git_object_dup((git_object **) &current, (git_object *) commit) < 0)
490 return -1;
b1aca6ea 491
7c1ee212
CMN
492 if (n == 0) {
493 *ancestor = current;
494 return 0;
495 }
b1aca6ea 496
497 while (n--) {
7c1ee212 498 error = git_commit_parent(&parent, current, 0);
b1aca6ea 499
7c1ee212 500 git_commit_free(current);
b1aca6ea 501
502 if (error < 0)
503 return error;
504
505 current = parent;
506 }
507
508 *ancestor = parent;
509 return 0;
510}