]> git.proxmox.com Git - libgit2.git/blob - src/commit.c
Merge pull request #1203 from phkelley/reverse_dak
[libgit2.git] / src / commit.c
1 /*
2 * Copyright (C) 2009-2012 the libgit2 contributors
3 *
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.
6 */
7
8 #include "git2/common.h"
9 #include "git2/object.h"
10 #include "git2/repository.h"
11 #include "git2/signature.h"
12
13 #include "common.h"
14 #include "odb.h"
15 #include "commit.h"
16 #include "signature.h"
17 #include "message.h"
18
19 #include <stdarg.h>
20
21 static void clear_parents(git_commit *commit)
22 {
23 unsigned int i;
24
25 for (i = 0; i < commit->parent_ids.length; ++i) {
26 git_oid *parent = git_vector_get(&commit->parent_ids, i);
27 git__free(parent);
28 }
29
30 git_vector_clear(&commit->parent_ids);
31 }
32
33 void git_commit__free(git_commit *commit)
34 {
35 clear_parents(commit);
36 git_vector_free(&commit->parent_ids);
37
38 git_signature_free(commit->author);
39 git_signature_free(commit->committer);
40
41 git__free(commit->message);
42 git__free(commit->message_encoding);
43 git__free(commit);
44 }
45
46 int git_commit_create_v(
47 git_oid *oid,
48 git_repository *repo,
49 const char *update_ref,
50 const git_signature *author,
51 const git_signature *committer,
52 const char *message_encoding,
53 const char *message,
54 const git_tree *tree,
55 int parent_count,
56 ...)
57 {
58 va_list ap;
59 int i, res;
60 const git_commit **parents;
61
62 parents = git__malloc(parent_count * sizeof(git_commit *));
63 GITERR_CHECK_ALLOC(parents);
64
65 va_start(ap, parent_count);
66 for (i = 0; i < parent_count; ++i)
67 parents[i] = va_arg(ap, const git_commit *);
68 va_end(ap);
69
70 res = git_commit_create(
71 oid, repo, update_ref, author, committer,
72 message_encoding, message,
73 tree, parent_count, parents);
74
75 git__free((void *)parents);
76 return res;
77 }
78
79 int git_commit_create(
80 git_oid *oid,
81 git_repository *repo,
82 const char *update_ref,
83 const git_signature *author,
84 const git_signature *committer,
85 const char *message_encoding,
86 const char *message,
87 const git_tree *tree,
88 int parent_count,
89 const git_commit *parents[])
90 {
91 git_buf commit = GIT_BUF_INIT;
92 int i;
93 git_odb *odb;
94
95 assert(git_object_owner((const git_object *)tree) == repo);
96
97 git_oid__writebuf(&commit, "tree ", git_object_id((const git_object *)tree));
98
99 for (i = 0; i < parent_count; ++i) {
100 assert(git_object_owner((const git_object *)parents[i]) == repo);
101 git_oid__writebuf(&commit, "parent ", git_object_id((const git_object *)parents[i]));
102 }
103
104 git_signature__writebuf(&commit, "author ", author);
105 git_signature__writebuf(&commit, "committer ", committer);
106
107 if (message_encoding != NULL)
108 git_buf_printf(&commit, "encoding %s\n", message_encoding);
109
110 git_buf_putc(&commit, '\n');
111
112 if (git_buf_puts(&commit, message) < 0)
113 goto on_error;
114
115 if (git_repository_odb__weakptr(&odb, repo) < 0)
116 goto on_error;
117
118 if (git_odb_write(oid, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0)
119 goto on_error;
120
121 git_buf_free(&commit);
122
123 if (update_ref != NULL)
124 return git_reference__update(repo, oid, update_ref);
125
126 return 0;
127
128 on_error:
129 git_buf_free(&commit);
130 giterr_set(GITERR_OBJECT, "Failed to create commit.");
131 return -1;
132 }
133
134 int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
135 {
136 const char *buffer = data;
137 const char *buffer_end = (const char *)data + len;
138
139 git_oid parent_id;
140
141 git_vector_init(&commit->parent_ids, 4, NULL);
142
143 if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0)
144 goto bad_buffer;
145
146 /*
147 * TODO: commit grafts!
148 */
149
150 while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) {
151 git_oid *new_id;
152
153 new_id = git__malloc(sizeof(git_oid));
154 GITERR_CHECK_ALLOC(new_id);
155
156 git_oid_cpy(new_id, &parent_id);
157
158 if (git_vector_insert(&commit->parent_ids, new_id) < 0)
159 return -1;
160 }
161
162 commit->author = git__malloc(sizeof(git_signature));
163 GITERR_CHECK_ALLOC(commit->author);
164
165 if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)
166 return -1;
167
168 /* Always parse the committer; we need the commit time */
169 commit->committer = git__malloc(sizeof(git_signature));
170 GITERR_CHECK_ALLOC(commit->committer);
171
172 if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
173 return -1;
174
175 if (git__prefixcmp(buffer, "encoding ") == 0) {
176 const char *encoding_end;
177 buffer += strlen("encoding ");
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);
184 GITERR_CHECK_ALLOC(commit->message_encoding);
185
186 buffer = encoding_end;
187 }
188
189 /* parse commit message */
190 while (buffer < buffer_end - 1 && *buffer == '\n')
191 buffer++;
192
193 if (buffer <= buffer_end) {
194 commit->message = git__strndup(buffer, buffer_end - buffer);
195 GITERR_CHECK_ALLOC(commit->message);
196 }
197
198 return 0;
199
200 bad_buffer:
201 giterr_set(GITERR_OBJECT, "Failed to parse bad commit object");
202 return -1;
203 }
204
205 int git_commit__parse(git_commit *commit, git_odb_object *obj)
206 {
207 assert(commit);
208 return git_commit__parse_buffer(commit, obj->raw.data, obj->raw.len);
209 }
210
211 #define GIT_COMMIT_GETTER(_rvalue, _name, _return) \
212 _rvalue git_commit_##_name(const git_commit *commit) \
213 {\
214 assert(commit); \
215 return _return; \
216 }
217
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)
221 GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)
222 GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
223 GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
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);
226
227 int git_commit_tree(git_tree **tree_out, const git_commit *commit)
228 {
229 assert(commit);
230 return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id);
231 }
232
233 const git_oid *git_commit_parent_id(git_commit *commit, unsigned int n)
234 {
235 assert(commit);
236
237 return git_vector_get(&commit->parent_ids, n);
238 }
239
240 int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n)
241 {
242 const git_oid *parent_id;
243 assert(commit);
244
245 parent_id = git_commit_parent_id(commit, n);
246 if (parent_id == NULL) {
247 giterr_set(GITERR_INVALID, "Parent %u does not exist", n);
248 return GIT_ENOTFOUND;
249 }
250
251 return git_commit_lookup(parent, commit->object.repo, parent_id);
252 }
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 }