Line Flow Count Block(s) Source
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 "transaction.h"
9 -
10 - #include "repository.h"
11 - #include "strmap.h"
12 - #include "refdb.h"
13 - #include "pool.h"
14 - #include "reflog.h"
15 - #include "signature.h"
16 - #include "config.h"
17 -
18 - #include "git2/transaction.h"
19 - #include "git2/signature.h"
20 - #include "git2/sys/refs.h"
21 - #include "git2/sys/refdb_backend.h"
22 -
23 - typedef enum {
24 - TRANSACTION_NONE,
25 - TRANSACTION_REFS,
26 - TRANSACTION_CONFIG,
27 - } transaction_t;
28 -
29 - typedef struct {
30 - const char *name;
31 - void *payload;
32 -
33 - git_reference_t ref_type;
34 - union {
35 - git_oid id;
36 - char *symbolic;
37 - } target;
38 - git_reflog *reflog;
39 -
40 - const char *message;
41 - git_signature *sig;
42 -
43 - unsigned int committed :1,
44 - remove :1;
45 - } transaction_node;
46 -
47 - struct git_transaction {
48 - transaction_t type;
49 - git_repository *repo;
50 - git_refdb *db;
51 - git_config *cfg;
52 -
53 - git_strmap *locks;
54 - git_pool pool;
55 - };
56 -
57 2 2 int git_transaction_config_new(git_transaction **out, git_config *cfg)
58 - {
59 - git_transaction *tx;
60 2 2-4 assert(out && cfg);
61 -
62 2 5 tx = git__calloc(1, sizeof(git_transaction));
63 2 6,7 GIT_ERROR_CHECK_ALLOC(tx);
64 -
65 2 8 tx->type = TRANSACTION_CONFIG;
66 2 8 tx->cfg = cfg;
67 2 8 *out = tx;
68 2 8 return 0;
69 - }
70 -
71 30 2 int git_transaction_new(git_transaction **out, git_repository *repo)
72 - {
73 - int error;
74 - git_pool pool;
75 30 2 git_transaction *tx = NULL;
76 -
77 30 2-4 assert(out && repo);
78 -
79 30 5,6 if ((error = git_pool_init(&pool, 1)) < 0)
80 ##### 7 goto on_error;
81 -
82 30 8 tx = git_pool_mallocz(&pool, sizeof(git_transaction));
83 30 9 if (!tx) {
84 ##### 10 error = -1;
85 ##### 10 goto on_error;
86 - }
87 -
88 30 11,12 if ((error = git_strmap_new(&tx->locks)) < 0) {
89 ##### 13 error = -1;
90 ##### 13 goto on_error;
91 - }
92 -
93 30 14,15 if ((error = git_repository_refdb(&tx->db, repo)) < 0)
94 ##### 16 goto on_error;
95 -
96 30 17 tx->type = TRANSACTION_REFS;
97 30 17 memcpy(&tx->pool, &pool, sizeof(git_pool));
98 30 17 tx->repo = repo;
99 30 17 *out = tx;
100 30 17 return 0;
101 -
102 - on_error:
103 ##### 18 git_pool_clear(&pool);
104 ##### 19 return error;
105 - }
106 -
107 31 2 int git_transaction_lock_ref(git_transaction *tx, const char *refname)
108 - {
109 - int error;
110 - transaction_node *node;
111 -
112 31 2-4 assert(tx && refname);
113 -
114 31 5 node = git_pool_mallocz(&tx->pool, sizeof(transaction_node));
115 31 6,7 GIT_ERROR_CHECK_ALLOC(node);
116 -
117 31 8 node->name = git_pool_strdup(&tx->pool, refname);
118 31 9,10 GIT_ERROR_CHECK_ALLOC(node->name);
119 -
120 31 11,12 if ((error = git_refdb_lock(&node->payload, tx->db, refname)) < 0)
121 1 13 return error;
122 -
123 30 14,15 if ((error = git_strmap_set(tx->locks, node->name, node)) < 0)
124 ##### 16 goto cleanup;
125 -
126 30 17 return 0;
127 -
128 - cleanup:
129 ##### 18 git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL);
130 -
131 ##### 19 return error;
132 - }
133 -
134 29 2 static int find_locked(transaction_node **out, git_transaction *tx, const char *refname)
135 - {
136 - transaction_node *node;
137 -
138 29 2,3 if ((node = git_strmap_get(tx->locks, refname)) == NULL) {
139 2 4 git_error_set(GIT_ERROR_REFERENCE, "the specified reference is not locked");
140 2 5 return GIT_ENOTFOUND;
141 - }
142 -
143 27 6 *out = node;
144 27 6 return 0;
145 - }
146 -
147 10 2 static int copy_common(transaction_node *node, git_transaction *tx, const git_signature *sig, const char *msg)
148 - {
149 10 2-4 if (sig && git_signature__pdup(&node->sig, sig, &tx->pool) < 0)
150 ##### 5 return -1;
151 -
152 10 6 if (!node->sig) {
153 - git_signature *tmp;
154 - int error;
155 -
156 10 7,8 if (git_reference__log_signature(&tmp, tx->repo) < 0)
157 ##### 9,15 return -1;
158 -
159 - /* make sure the sig we use is in our pool */
160 10 10 error = git_signature__pdup(&node->sig, tmp, &tx->pool);
161 10 11 git_signature_free(tmp);
162 10 12 if (error < 0)
163 10 13,14 return error;
164 - }
165 -
166 10 16 if (msg) {
167 ##### 17 node->message = git_pool_strdup(&tx->pool, msg);
168 ##### 18,19 GIT_ERROR_CHECK_ALLOC(node->message);
169 - }
170 -
171 10 20 return 0;
172 - }
173 -
174 10 2 int git_transaction_set_target(git_transaction *tx, const char *refname, const git_oid *target, const git_signature *sig, const char *msg)
175 - {
176 - int error;
177 - transaction_node *node;
178 -
179 10 2-5 assert(tx && refname && target);
180 -
181 10 6,7 if ((error = find_locked(&node, tx, refname)) < 0)
182 2 8 return error;
183 -
184 8 9,10 if ((error = copy_common(node, tx, sig, msg)) < 0)
185 ##### 11 return error;
186 -
187 8 12 git_oid_cpy(&node->target.id, target);
188 8 13 node->ref_type = GIT_REFERENCE_DIRECT;
189 -
190 8 13 return 0;
191 - }
192 -
193 2 2 int git_transaction_set_symbolic_target(git_transaction *tx, const char *refname, const char *target, const git_signature *sig, const char *msg)
194 - {
195 - int error;
196 - transaction_node *node;
197 -
198 2 2-5 assert(tx && refname && target);
199 -
200 2 6,7 if ((error = find_locked(&node, tx, refname)) < 0)
201 ##### 8 return error;
202 -
203 2 9,10 if ((error = copy_common(node, tx, sig, msg)) < 0)
204 ##### 11 return error;
205 -
206 2 12 node->target.symbolic = git_pool_strdup(&tx->pool, target);
207 2 13,14 GIT_ERROR_CHECK_ALLOC(node->target.symbolic);
208 2 15 node->ref_type = GIT_REFERENCE_SYMBOLIC;
209 -
210 2 15 return 0;
211 - }
212 -
213 5 2 int git_transaction_remove(git_transaction *tx, const char *refname)
214 - {
215 - int error;
216 - transaction_node *node;
217 -
218 5 2,3 if ((error = find_locked(&node, tx, refname)) < 0)
219 ##### 4 return error;
220 -
221 5 5 node->remove = true;
222 5 5 node->ref_type = GIT_REFERENCE_DIRECT; /* the id will be ignored */
223 -
224 5 5 return 0;
225 - }
226 -
227 12 2 static int dup_reflog(git_reflog **out, const git_reflog *in, git_pool *pool)
228 - {
229 - git_reflog *reflog;
230 - git_reflog_entry *entries;
231 - size_t len, i;
232 -
233 12 2 reflog = git_pool_mallocz(pool, sizeof(git_reflog));
234 12 3,4 GIT_ERROR_CHECK_ALLOC(reflog);
235 -
236 12 5 reflog->ref_name = git_pool_strdup(pool, in->ref_name);
237 12 6,7 GIT_ERROR_CHECK_ALLOC(reflog->ref_name);
238 -
239 12 8 len = in->entries.length;
240 12 8 reflog->entries.length = len;
241 12 8 reflog->entries.contents = git_pool_mallocz(pool, len * sizeof(void *));
242 12 9,10 GIT_ERROR_CHECK_ALLOC(reflog->entries.contents);
243 -
244 12 11 entries = git_pool_mallocz(pool, len * sizeof(git_reflog_entry));
245 12 12,13 GIT_ERROR_CHECK_ALLOC(entries);
246 -
247 25 14,24,25 for (i = 0; i < len; i++) {
248 - const git_reflog_entry *src;
249 - git_reflog_entry *tgt;
250 -
251 13 15 tgt = &entries[i];
252 13 15 reflog->entries.contents[i] = tgt;
253 -
254 13 15 src = git_vector_get(&in->entries, i);
255 13 16 git_oid_cpy(&tgt->oid_old, &src->oid_old);
256 13 17 git_oid_cpy(&tgt->oid_cur, &src->oid_cur);
257 -
258 13 18 tgt->msg = git_pool_strdup(pool, src->msg);
259 13 19,20 GIT_ERROR_CHECK_ALLOC(tgt->msg);
260 -
261 13 21,22 if (git_signature__pdup(&tgt->committer, src->committer, pool) < 0)
262 ##### 23 return -1;
263 - }
264 -
265 -
266 12 26 *out = reflog;
267 12 26 return 0;
268 - }
269 -
270 12 2 int git_transaction_set_reflog(git_transaction *tx, const char *refname, const git_reflog *reflog)
271 - {
272 - int error;
273 - transaction_node *node;
274 -
275 12 2-5 assert(tx && refname && reflog);
276 -
277 12 6,7 if ((error = find_locked(&node, tx, refname)) < 0)
278 ##### 8 return error;
279 -
280 12 9,10 if ((error = dup_reflog(&node->reflog, reflog, &tx->pool)) < 0)
281 ##### 11 return error;
282 -
283 12 12 return 0;
284 - }
285 -
286 15 2 static int update_target(git_refdb *db, transaction_node *node)
287 - {
288 - git_reference *ref;
289 - int error, update_reflog;
290 -
291 15 2 if (node->ref_type == GIT_REFERENCE_DIRECT) {
292 13 3 ref = git_reference__alloc(node->name, &node->target.id, NULL);
293 2 4 } else if (node->ref_type == GIT_REFERENCE_SYMBOLIC) {
294 2 5 ref = git_reference__alloc_symbolic(node->name, node->target.symbolic);
295 - } else {
296 - 6 abort();
297 - }
298 -
299 15 7,8 GIT_ERROR_CHECK_ALLOC(ref);
300 15 9 update_reflog = node->reflog == NULL;
301 -
302 15 9 if (node->remove) {
303 5 10 error = git_refdb_unlock(db, node->payload, 2, false, ref, NULL, NULL);
304 10 11 } else if (node->ref_type == GIT_REFERENCE_DIRECT) {
305 8 12 error = git_refdb_unlock(db, node->payload, true, update_reflog, ref, node->sig, node->message);
306 2 13 } else if (node->ref_type == GIT_REFERENCE_SYMBOLIC) {
307 2 14 error = git_refdb_unlock(db, node->payload, true, update_reflog, ref, node->sig, node->message);
308 - } else {
309 - 15 abort();
310 - }
311 -
312 15 16 git_reference_free(ref);
313 15 17 node->committed = true;
314 -
315 15 17 return error;
316 - }
317 -
318 21 2 int git_transaction_commit(git_transaction *tx)
319 - {
320 - transaction_node *node;
321 21 2 int error = 0;
322 -
323 21 2,3 assert(tx);
324 -
325 21 4 if (tx->type == TRANSACTION_CONFIG) {
326 2 5 error = git_config_unlock(tx->cfg, true);
327 2 6 tx->cfg = NULL;
328 -
329 2 6 return error;
330 - }
331 -
332 39 7-21,23 git_strmap_foreach_value(tx->locks, node, {
333 - if (node->reflog) {
334 - if ((error = tx->db->backend->reflog_write(tx->db->backend, node->reflog)) < 0)
335 - return error;
336 - }
337 -
338 - if (node->ref_type == GIT_REFERENCE_INVALID) {
339 - /* ref was locked but not modified */
340 - if ((error = git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL)) < 0) {
341 - return error;
342 - }
343 - node->committed = true;
344 - } else {
345 - if ((error = update_target(tx->db, node)) < 0)
346 - return error;
347 - }
348 - });
349 -
350 19 22 return 0;
351 - }
352 -
353 32 2 void git_transaction_free(git_transaction *tx)
354 - {
355 - transaction_node *node;
356 - git_pool pool;
357 -
358 32 2,3 assert(tx);
359 -
360 32 4 if (tx->type == TRANSACTION_CONFIG) {
361 2 5 if (tx->cfg) {
362 ##### 6 git_config_unlock(tx->cfg, false);
363 ##### 7 git_config_free(tx->cfg);
364 - }
365 -
366 2 8 git__free(tx);
367 32 9,20 return;
368 - }
369 -
370 - /* start by unlocking the ones we've left hanging, if any */
371 60 10-15 git_strmap_foreach_value(tx->locks, node, {
372 - if (node->committed)
373 - continue;
374 -
375 - git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL);
376 - });
377 -
378 30 16 git_refdb_free(tx->db);
379 30 17 git_strmap_free(tx->locks);
380 -
381 - /* tx is inside the pool, so we need to extract the data */
382 30 18 memcpy(&pool, &tx->pool, sizeof(git_pool));
383 30 18 git_pool_clear(&pool);
384 - }