1 /*
2  * Copyright (C) the libgit2 contributors. All rights reserved.
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 "annotated_commit.h"
9 
10 #include "refs.h"
11 #include "cache.h"
12 
13 #include "git2/commit.h"
14 #include "git2/refs.h"
15 #include "git2/repository.h"
16 #include "git2/annotated_commit.h"
17 #include "git2/revparse.h"
18 #include "git2/tree.h"
19 #include "git2/index.h"
20 
annotated_commit_init(git_annotated_commit ** out,git_commit * commit,const char * description)21 static int annotated_commit_init(
22 	git_annotated_commit **out,
23 	git_commit *commit,
24 	const char *description)
25 {
26 	git_annotated_commit *annotated_commit;
27 	int error = 0;
28 
29 	GIT_ASSERT_ARG(out);
30 	GIT_ASSERT_ARG(commit);
31 
32 	*out = NULL;
33 
34 	annotated_commit = git__calloc(1, sizeof(git_annotated_commit));
35 	GIT_ERROR_CHECK_ALLOC(annotated_commit);
36 
37 	annotated_commit->type = GIT_ANNOTATED_COMMIT_REAL;
38 
39 	if ((error = git_commit_dup(&annotated_commit->commit, commit)) < 0)
40 		goto done;
41 
42 	git_oid_fmt(annotated_commit->id_str, git_commit_id(commit));
43 	annotated_commit->id_str[GIT_OID_HEXSZ] = '\0';
44 
45 	if (!description)
46 		description = annotated_commit->id_str;
47 
48 	annotated_commit->description = git__strdup(description);
49 	GIT_ERROR_CHECK_ALLOC(annotated_commit->description);
50 
51 done:
52 	if (!error)
53 		*out = annotated_commit;
54 
55 	return error;
56 }
57 
annotated_commit_init_from_id(git_annotated_commit ** out,git_repository * repo,const git_oid * id,const char * description)58 static int annotated_commit_init_from_id(
59 	git_annotated_commit **out,
60 	git_repository *repo,
61 	const git_oid *id,
62 	const char *description)
63 {
64 	git_commit *commit = NULL;
65 	int error = 0;
66 
67 	GIT_ASSERT_ARG(out);
68 	GIT_ASSERT_ARG(repo);
69 	GIT_ASSERT_ARG(id);
70 
71 	*out = NULL;
72 
73 	if ((error = git_commit_lookup(&commit, repo, id)) < 0)
74 		goto done;
75 
76 	error = annotated_commit_init(out, commit, description);
77 
78 done:
79 	git_commit_free(commit);
80 	return error;
81 }
82 
git_annotated_commit_lookup(git_annotated_commit ** out,git_repository * repo,const git_oid * id)83 int git_annotated_commit_lookup(
84 	git_annotated_commit **out,
85 	git_repository *repo,
86 	const git_oid *id)
87 {
88 	return annotated_commit_init_from_id(out, repo, id, NULL);
89 }
90 
git_annotated_commit_from_commit(git_annotated_commit ** out,git_commit * commit)91 int git_annotated_commit_from_commit(
92 	git_annotated_commit **out,
93 	git_commit *commit)
94 {
95 	return annotated_commit_init(out, commit, NULL);
96 }
97 
git_annotated_commit_from_revspec(git_annotated_commit ** out,git_repository * repo,const char * revspec)98 int git_annotated_commit_from_revspec(
99 	git_annotated_commit **out,
100 	git_repository *repo,
101 	const char *revspec)
102 {
103 	git_object *obj, *commit;
104 	int error;
105 
106 	GIT_ASSERT_ARG(out);
107 	GIT_ASSERT_ARG(repo);
108 	GIT_ASSERT_ARG(revspec);
109 
110 	if ((error = git_revparse_single(&obj, repo, revspec)) < 0)
111 		return error;
112 
113 	if ((error = git_object_peel(&commit, obj, GIT_OBJECT_COMMIT))) {
114 		git_object_free(obj);
115 		return error;
116 	}
117 
118 	error = annotated_commit_init(out, (git_commit *)commit, revspec);
119 
120 	git_object_free(obj);
121 	git_object_free(commit);
122 
123 	return error;
124 }
125 
git_annotated_commit_from_ref(git_annotated_commit ** out,git_repository * repo,const git_reference * ref)126 int git_annotated_commit_from_ref(
127 	git_annotated_commit **out,
128 	git_repository *repo,
129 	const git_reference *ref)
130 {
131 	git_object *peeled;
132 	int error = 0;
133 
134 	GIT_ASSERT_ARG(out);
135 	GIT_ASSERT_ARG(repo);
136 	GIT_ASSERT_ARG(ref);
137 
138 	*out = NULL;
139 
140 	if ((error = git_reference_peel(&peeled, ref, GIT_OBJECT_COMMIT)) < 0)
141 		return error;
142 
143 	error = annotated_commit_init_from_id(out,
144 		repo,
145 		git_object_id(peeled),
146 		git_reference_name(ref));
147 
148 	if (!error) {
149 		(*out)->ref_name = git__strdup(git_reference_name(ref));
150 		GIT_ERROR_CHECK_ALLOC((*out)->ref_name);
151 	}
152 
153 	git_object_free(peeled);
154 	return error;
155 }
156 
git_annotated_commit_from_head(git_annotated_commit ** out,git_repository * repo)157 int git_annotated_commit_from_head(
158 	git_annotated_commit **out,
159 	git_repository *repo)
160 {
161 	git_reference *head;
162 	int error;
163 
164 	GIT_ASSERT_ARG(out);
165 	GIT_ASSERT_ARG(repo);
166 
167 	*out = NULL;
168 
169 	if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0)
170 		return -1;
171 
172 	error = git_annotated_commit_from_ref(out, repo, head);
173 
174 	git_reference_free(head);
175 	return error;
176 }
177 
git_annotated_commit_from_fetchhead(git_annotated_commit ** out,git_repository * repo,const char * branch_name,const char * remote_url,const git_oid * id)178 int git_annotated_commit_from_fetchhead(
179 	git_annotated_commit **out,
180 	git_repository *repo,
181 	const char *branch_name,
182 	const char *remote_url,
183 	const git_oid *id)
184 {
185 	GIT_ASSERT_ARG(out);
186 	GIT_ASSERT_ARG(repo);
187 	GIT_ASSERT_ARG(branch_name);
188 	GIT_ASSERT_ARG(remote_url);
189 	GIT_ASSERT_ARG(id);
190 
191 	if (annotated_commit_init_from_id(out, repo, id, branch_name) < 0)
192 		return -1;
193 
194 	(*out)->ref_name = git__strdup(branch_name);
195 	GIT_ERROR_CHECK_ALLOC((*out)->ref_name);
196 
197 	(*out)->remote_url = git__strdup(remote_url);
198 	GIT_ERROR_CHECK_ALLOC((*out)->remote_url);
199 
200 	return 0;
201 }
202 
203 
git_annotated_commit_id(const git_annotated_commit * annotated_commit)204 const git_oid *git_annotated_commit_id(
205 	const git_annotated_commit *annotated_commit)
206 {
207 	GIT_ASSERT_ARG_WITH_RETVAL(annotated_commit, NULL);
208 	return git_commit_id(annotated_commit->commit);
209 }
210 
git_annotated_commit_ref(const git_annotated_commit * annotated_commit)211 const char *git_annotated_commit_ref(
212 	const git_annotated_commit *annotated_commit)
213 {
214 	GIT_ASSERT_ARG_WITH_RETVAL(annotated_commit, NULL);
215 	return annotated_commit->ref_name;
216 }
217 
git_annotated_commit_free(git_annotated_commit * annotated_commit)218 void git_annotated_commit_free(git_annotated_commit *annotated_commit)
219 {
220 	if (annotated_commit == NULL)
221 		return;
222 
223 	switch (annotated_commit->type) {
224 		case GIT_ANNOTATED_COMMIT_REAL:
225 			git_commit_free(annotated_commit->commit);
226 			git_tree_free(annotated_commit->tree);
227 			git__free((char *)annotated_commit->description);
228 			git__free((char *)annotated_commit->ref_name);
229 			git__free((char *)annotated_commit->remote_url);
230 			break;
231 		case GIT_ANNOTATED_COMMIT_VIRTUAL:
232 			git_index_free(annotated_commit->index);
233 			git_array_clear(annotated_commit->parents);
234 			break;
235 		default:
236 			abort();
237 	}
238 
239 	git__free(annotated_commit);
240 }
241