]>
git.proxmox.com Git - libgit2.git/blob - src/transaction.c
2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
8 #include "transaction.h"
10 #include "repository.h"
15 #include "signature.h"
18 #include "git2/transaction.h"
19 #include "git2/signature.h"
20 #include "git2/sys/refs.h"
21 #include "git2/sys/refdb_backend.h"
33 git_reference_t ref_type
;
43 unsigned int committed
:1,
47 struct git_transaction
{
57 int git_transaction_config_new(git_transaction
**out
, git_config
*cfg
)
64 tx
= git__calloc(1, sizeof(git_transaction
));
65 GIT_ERROR_CHECK_ALLOC(tx
);
67 tx
->type
= TRANSACTION_CONFIG
;
73 int git_transaction_new(git_transaction
**out
, git_repository
*repo
)
77 git_transaction
*tx
= NULL
;
82 if ((error
= git_pool_init(&pool
, 1)) < 0)
85 tx
= git_pool_mallocz(&pool
, sizeof(git_transaction
));
91 if ((error
= git_strmap_new(&tx
->locks
)) < 0) {
96 if ((error
= git_repository_refdb(&tx
->db
, repo
)) < 0)
99 tx
->type
= TRANSACTION_REFS
;
100 memcpy(&tx
->pool
, &pool
, sizeof(git_pool
));
106 git_pool_clear(&pool
);
110 int git_transaction_lock_ref(git_transaction
*tx
, const char *refname
)
113 transaction_node
*node
;
116 GIT_ASSERT_ARG(refname
);
118 node
= git_pool_mallocz(&tx
->pool
, sizeof(transaction_node
));
119 GIT_ERROR_CHECK_ALLOC(node
);
121 node
->name
= git_pool_strdup(&tx
->pool
, refname
);
122 GIT_ERROR_CHECK_ALLOC(node
->name
);
124 if ((error
= git_refdb_lock(&node
->payload
, tx
->db
, refname
)) < 0)
127 if ((error
= git_strmap_set(tx
->locks
, node
->name
, node
)) < 0)
133 git_refdb_unlock(tx
->db
, node
->payload
, false, false, NULL
, NULL
, NULL
);
138 static int find_locked(transaction_node
**out
, git_transaction
*tx
, const char *refname
)
140 transaction_node
*node
;
142 if ((node
= git_strmap_get(tx
->locks
, refname
)) == NULL
) {
143 git_error_set(GIT_ERROR_REFERENCE
, "the specified reference is not locked");
144 return GIT_ENOTFOUND
;
151 static int copy_common(transaction_node
*node
, git_transaction
*tx
, const git_signature
*sig
, const char *msg
)
153 if (sig
&& git_signature__pdup(&node
->sig
, sig
, &tx
->pool
) < 0)
160 if (git_reference__log_signature(&tmp
, tx
->repo
) < 0)
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
);
171 node
->message
= git_pool_strdup(&tx
->pool
, msg
);
172 GIT_ERROR_CHECK_ALLOC(node
->message
);
178 int git_transaction_set_target(git_transaction
*tx
, const char *refname
, const git_oid
*target
, const git_signature
*sig
, const char *msg
)
181 transaction_node
*node
;
184 GIT_ASSERT_ARG(refname
);
185 GIT_ASSERT_ARG(target
);
187 if ((error
= find_locked(&node
, tx
, refname
)) < 0)
190 if ((error
= copy_common(node
, tx
, sig
, msg
)) < 0)
193 git_oid_cpy(&node
->target
.id
, target
);
194 node
->ref_type
= GIT_REFERENCE_DIRECT
;
199 int git_transaction_set_symbolic_target(git_transaction
*tx
, const char *refname
, const char *target
, const git_signature
*sig
, const char *msg
)
202 transaction_node
*node
;
205 GIT_ASSERT_ARG(refname
);
206 GIT_ASSERT_ARG(target
);
208 if ((error
= find_locked(&node
, tx
, refname
)) < 0)
211 if ((error
= copy_common(node
, tx
, sig
, msg
)) < 0)
214 node
->target
.symbolic
= git_pool_strdup(&tx
->pool
, target
);
215 GIT_ERROR_CHECK_ALLOC(node
->target
.symbolic
);
216 node
->ref_type
= GIT_REFERENCE_SYMBOLIC
;
221 int git_transaction_remove(git_transaction
*tx
, const char *refname
)
224 transaction_node
*node
;
226 if ((error
= find_locked(&node
, tx
, refname
)) < 0)
230 node
->ref_type
= GIT_REFERENCE_DIRECT
; /* the id will be ignored */
235 static int dup_reflog(git_reflog
**out
, const git_reflog
*in
, git_pool
*pool
)
238 git_reflog_entry
*entries
;
241 reflog
= git_pool_mallocz(pool
, sizeof(git_reflog
));
242 GIT_ERROR_CHECK_ALLOC(reflog
);
244 reflog
->ref_name
= git_pool_strdup(pool
, in
->ref_name
);
245 GIT_ERROR_CHECK_ALLOC(reflog
->ref_name
);
247 len
= in
->entries
.length
;
248 reflog
->entries
.length
= len
;
249 reflog
->entries
.contents
= git_pool_mallocz(pool
, len
* sizeof(void *));
250 GIT_ERROR_CHECK_ALLOC(reflog
->entries
.contents
);
252 entries
= git_pool_mallocz(pool
, len
* sizeof(git_reflog_entry
));
253 GIT_ERROR_CHECK_ALLOC(entries
);
255 for (i
= 0; i
< len
; i
++) {
256 const git_reflog_entry
*src
;
257 git_reflog_entry
*tgt
;
260 reflog
->entries
.contents
[i
] = tgt
;
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
);
266 tgt
->msg
= git_pool_strdup(pool
, src
->msg
);
267 GIT_ERROR_CHECK_ALLOC(tgt
->msg
);
269 if (git_signature__pdup(&tgt
->committer
, src
->committer
, pool
) < 0)
278 int git_transaction_set_reflog(git_transaction
*tx
, const char *refname
, const git_reflog
*reflog
)
281 transaction_node
*node
;
284 GIT_ASSERT_ARG(refname
);
285 GIT_ASSERT_ARG(reflog
);
287 if ((error
= find_locked(&node
, tx
, refname
)) < 0)
290 if ((error
= dup_reflog(&node
->reflog
, reflog
, &tx
->pool
)) < 0)
296 static int update_target(git_refdb
*db
, transaction_node
*node
)
299 int error
, update_reflog
;
301 if (node
->ref_type
== GIT_REFERENCE_DIRECT
) {
302 ref
= git_reference__alloc(node
->name
, &node
->target
.id
, NULL
);
303 } else if (node
->ref_type
== GIT_REFERENCE_SYMBOLIC
) {
304 ref
= git_reference__alloc_symbolic(node
->name
, node
->target
.symbolic
);
309 GIT_ERROR_CHECK_ALLOC(ref
);
310 update_reflog
= node
->reflog
== NULL
;
313 error
= git_refdb_unlock(db
, node
->payload
, 2, false, ref
, NULL
, NULL
);
314 } else if (node
->ref_type
== GIT_REFERENCE_DIRECT
) {
315 error
= git_refdb_unlock(db
, node
->payload
, true, update_reflog
, ref
, node
->sig
, node
->message
);
316 } else if (node
->ref_type
== GIT_REFERENCE_SYMBOLIC
) {
317 error
= git_refdb_unlock(db
, node
->payload
, true, update_reflog
, ref
, node
->sig
, node
->message
);
322 git_reference_free(ref
);
323 node
->committed
= true;
328 int git_transaction_commit(git_transaction
*tx
)
330 transaction_node
*node
;
335 if (tx
->type
== TRANSACTION_CONFIG
) {
336 error
= git_config_unlock(tx
->cfg
, true);
342 git_strmap_foreach_value(tx
->locks
, node
, {
344 if ((error
= tx
->db
->backend
->reflog_write(tx
->db
->backend
, node
->reflog
)) < 0)
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) {
353 node
->committed
= true;
355 if ((error
= update_target(tx
->db
, node
)) < 0)
363 void git_transaction_free(git_transaction
*tx
)
365 transaction_node
*node
;
371 if (tx
->type
== TRANSACTION_CONFIG
) {
373 git_config_unlock(tx
->cfg
, false);
374 git_config_free(tx
->cfg
);
381 /* start by unlocking the ones we've left hanging, if any */
382 git_strmap_foreach_value(tx
->locks
, node
, {
386 git_refdb_unlock(tx
->db
, node
->payload
, false, false, NULL
, NULL
, NULL
);
389 git_refdb_free(tx
->db
);
390 git_strmap_free(tx
->locks
);
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
);