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