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