diff options
-rw-r--r-- | include/git2/sys/refdb_backend.h | 13 | ||||
-rw-r--r-- | include/git2/transaction.h | 25 | ||||
-rw-r--r-- | include/git2/types.h | 3 | ||||
-rw-r--r-- | src/refdb.c | 19 | ||||
-rw-r--r-- | src/refdb.h | 3 | ||||
-rw-r--r-- | src/refdb_fs.c | 118 | ||||
-rw-r--r-- | src/refs.c | 6 | ||||
-rw-r--r-- | src/refs.h | 2 | ||||
-rw-r--r-- | src/transaction.c | 352 | ||||
-rw-r--r-- | tests/refs/transactions.c | 110 |
10 files changed, 629 insertions, 22 deletions
diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h index 3b216a287..d943e550f 100644 --- a/include/git2/sys/refdb_backend.h +++ b/include/git2/sys/refdb_backend.h @@ -153,6 +153,19 @@ struct git_refdb_backend { * Remove a reflog. */ int (*reflog_delete)(git_refdb_backend *backend, const char *name); + + /** + * Lock a reference. The opaque parameter will be passed to the unlock function + */ + int (*lock)(void **payload_out, git_refdb_backend *backend, const char *refname); + + /** + * Unlock a reference. Only one of target or symbolic_target + * will be set. success indicates whether to update the + * reference or discard the lock (if it's false) + */ + int (*unlock)(git_refdb_backend *backend, void *payload, int success, int update_reflog, + const git_reference *ref, const git_signature *sig, const char *message); }; #define GIT_REFDB_BACKEND_VERSION 1 diff --git a/include/git2/transaction.h b/include/git2/transaction.h new file mode 100644 index 000000000..46f4cfba0 --- /dev/null +++ b/include/git2/transaction.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_git_transaction_h__ +#define INCLUDE_git_transaction_h__ + +#include "common.h" +GIT_BEGIN_DECL + +GIT_EXTERN(int) git_transaction_new(git_transaction **out, git_repository *repo); +GIT_EXTERN(int) git_transaction_lock(git_transaction *tx, const char *refname); +GIT_EXTERN(int) git_transaction_set_target(git_transaction *tx, const char *refname, const git_oid *target, const git_signature *sig, const char *msg); + +GIT_EXTERN(int) git_transaction_set_symbolic_target(git_transaction *tx, const char *refname, const char *target, const git_signature *sig, const char *msg); +GIT_EXTERN(int) git_transaction_set_reflog(git_transaction *tx, const char *refname, const git_reflog *reflog); +GIT_EXTERN(int) git_transaction_commit(git_transaction *tx); +GIT_EXTERN(void) git_transaction_free(git_transaction *tx); +GIT_EXTERN(int) git_transaction_remove(git_transaction *tx, const char *refname); + + +GIT_END_DECL +#endif diff --git a/include/git2/types.h b/include/git2/types.h index 7ee7cc344..14b7071d2 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -171,6 +171,9 @@ typedef struct git_reference git_reference; /** Iterator for references */ typedef struct git_reference_iterator git_reference_iterator; +/** Transactional interface to references */ +typedef struct git_transaction git_transaction; + /** Merge heads, the input to merge */ typedef struct git_merge_head git_merge_head; diff --git a/src/refdb.c b/src/refdb.c index 69bf74734..16fb519a6 100644 --- a/src/refdb.c +++ b/src/refdb.c @@ -242,3 +242,22 @@ int git_refdb_init_backend(git_refdb_backend *backend, unsigned int version) backend, version, git_refdb_backend, GIT_REFDB_BACKEND_INIT); return 0; } + +int git_refdb_lock(void **payload, git_refdb *db, const char *refname) +{ + assert(payload && db && refname); + + if (!db->backend->lock) { + giterr_set(GITERR_REFERENCE, "backend does not support locking"); + return -1; + } + + return db->backend->lock(payload, db->backend, refname); +} + +int git_refdb_unlock(git_refdb *db, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message) +{ + assert(db); + + return db->backend->unlock(db->backend, payload, success, update_reflog, ref, sig, message); +} diff --git a/src/refdb.h b/src/refdb.h index cbad86faf..4ee3b8065 100644 --- a/src/refdb.h +++ b/src/refdb.h @@ -51,6 +51,7 @@ int git_refdb_reflog_write(git_reflog *reflog); int git_refdb_has_log(git_refdb *db, const char *refname); int git_refdb_ensure_log(git_refdb *refdb, const char *refname); - +int git_refdb_lock(void **payload, git_refdb *db, const char *refname); +int git_refdb_unlock(git_refdb *db, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message); #endif diff --git a/src/refdb_fs.c b/src/refdb_fs.c index 0e36ca8ac..f39ba4f9c 100644 --- a/src/refdb_fs.c +++ b/src/refdb_fs.c @@ -745,6 +745,57 @@ static int loose_commit(git_filebuf *file, const git_reference *ref) return git_filebuf_commit(file); } +static int refdb_fs_backend__lock(void **out, git_refdb_backend *_backend, const char *refname) +{ + int error; + git_filebuf *lock; + refdb_fs_backend *backend = (refdb_fs_backend *) _backend; + + lock = git__calloc(1, sizeof(git_filebuf)); + GITERR_CHECK_ALLOC(lock); + + if ((error = loose_lock(lock, backend, refname)) < 0) { + git__free(lock); + return error; + } + + *out = lock; + return 0; +} + +static int refdb_fs_backend__write_tail( + git_refdb_backend *_backend, + const git_reference *ref, + git_filebuf *file, + int update_reflog, + const git_signature *who, + const char *message, + const git_oid *old_id, + const char *old_target); + +static int refdb_fs_backend__delete_tail( + git_refdb_backend *_backend, + git_filebuf *file, + const char *ref_name, + const git_oid *old_id, const char *old_target); + +static int refdb_fs_backend__unlock(git_refdb_backend *backend, void *payload, int success, int update_reflog, + const git_reference *ref, const git_signature *sig, const char *message) +{ + git_filebuf *lock = (git_filebuf *) payload; + int error = 0; + + if (success == 2) + error = refdb_fs_backend__delete_tail(backend, lock, ref->name, NULL, NULL); + else if (success) + error = refdb_fs_backend__write_tail(backend, ref, lock, update_reflog, sig, message, NULL, NULL); + else + git_filebuf_cleanup(lock); + + git__free(lock); + return error; +} + /* * Find out what object this reference resolves to. * @@ -1063,7 +1114,6 @@ cleanup: return error; } - static int refdb_fs_backend__write( git_refdb_backend *_backend, const git_reference *ref, @@ -1075,9 +1125,7 @@ static int refdb_fs_backend__write( { refdb_fs_backend *backend = (refdb_fs_backend *)_backend; git_filebuf file = GIT_FILEBUF_INIT; - int error = 0, cmp = 0, should_write; - const char *new_target = NULL; - const git_oid *new_id = NULL; + int error = 0; assert(backend); @@ -1089,6 +1137,24 @@ static int refdb_fs_backend__write( if ((error = loose_lock(&file, backend, ref->name)) < 0) return error; + return refdb_fs_backend__write_tail(_backend, ref, &file, true, who, message, old_id, old_target); +} + +static int refdb_fs_backend__write_tail( + git_refdb_backend *_backend, + const git_reference *ref, + git_filebuf *file, + int update_reflog, + const git_signature *who, + const char *message, + const git_oid *old_id, + const char *old_target) +{ + refdb_fs_backend *backend = (refdb_fs_backend *)_backend; + int error = 0, cmp = 0, should_write; + const char *new_target = NULL; + const git_oid *new_id = NULL; + if ((error = cmp_old_ref(&cmp, _backend, ref->name, old_id, old_target)) < 0) goto on_error; @@ -1113,20 +1179,22 @@ static int refdb_fs_backend__write( goto on_error; /* not really error */ } - if ((error = should_write_reflog(&should_write, backend->repo, ref->name)) < 0) - goto on_error; - - if (should_write) { - if ((error = reflog_append(backend, ref, NULL, NULL, who, message)) < 0) - goto on_error; - if ((error = maybe_append_head(backend, ref, who, message)) < 0) + if (update_reflog) { + if ((error = should_write_reflog(&should_write, backend->repo, ref->name)) < 0) goto on_error; + + if (should_write) { + if ((error = reflog_append(backend, ref, NULL, NULL, who, message)) < 0) + goto on_error; + if ((error = maybe_append_head(backend, ref, who, message)) < 0) + goto on_error; + } } - return loose_commit(&file, ref); + return loose_commit(file, ref); on_error: - git_filebuf_cleanup(&file); + git_filebuf_cleanup(file); return error; } @@ -1136,17 +1204,29 @@ static int refdb_fs_backend__delete( const git_oid *old_id, const char *old_target) { refdb_fs_backend *backend = (refdb_fs_backend *)_backend; - git_buf loose_path = GIT_BUF_INIT; - size_t pack_pos; git_filebuf file = GIT_FILEBUF_INIT; - int error = 0, cmp = 0; - bool loose_deleted = 0; + int error = 0; assert(backend && ref_name); if ((error = loose_lock(&file, backend, ref_name)) < 0) return error; + return refdb_fs_backend__delete_tail(_backend, &file, ref_name, old_id, old_target); +} + +static int refdb_fs_backend__delete_tail( + git_refdb_backend *_backend, + git_filebuf *file, + const char *ref_name, + const git_oid *old_id, const char *old_target) +{ + refdb_fs_backend *backend = (refdb_fs_backend *)_backend; + git_buf loose_path = GIT_BUF_INIT; + size_t pack_pos; + int error = 0, cmp = 0; + bool loose_deleted = 0; + error = cmp_old_ref(&cmp, _backend, ref_name, old_id, old_target); if (error < 0) goto cleanup; @@ -1192,7 +1272,7 @@ static int refdb_fs_backend__delete( error = packed_write(backend); cleanup: - git_filebuf_cleanup(&file); + git_filebuf_cleanup(file); return error; } @@ -1837,6 +1917,8 @@ int git_refdb_backend_fs( backend->parent.del = &refdb_fs_backend__delete; backend->parent.rename = &refdb_fs_backend__rename; backend->parent.compress = &refdb_fs_backend__compress; + backend->parent.lock = &refdb_fs_backend__lock; + backend->parent.unlock = &refdb_fs_backend__unlock; backend->parent.has_log = &refdb_reflog_fs__has_log; backend->parent.ensure_log = &refdb_reflog_fs__ensure_log; backend->parent.free = &refdb_fs_backend__free; diff --git a/src/refs.c b/src/refs.c index 1603876da..08e407e48 100644 --- a/src/refs.c +++ b/src/refs.c @@ -411,7 +411,7 @@ static int reference__create( return 0; } -static int log_signature(git_signature **out, git_repository *repo) +int git_reference__log_signature(git_signature **out, git_repository *repo) { int error; git_signature *who; @@ -441,7 +441,7 @@ int git_reference_create_matching( assert(id); if (!signature) { - if ((error = log_signature(&who, repo)) < 0) + if ((error = git_reference__log_signature(&who, repo)) < 0) return error; else signature = who; @@ -482,7 +482,7 @@ int git_reference_symbolic_create_matching( assert(target); if (!signature) { - if ((error = log_signature(&who, repo)) < 0) + if ((error = git_reference__log_signature(&who, repo)) < 0) return error; else signature = who; diff --git a/src/refs.h b/src/refs.h index 2e79bdfca..c6ec429a5 100644 --- a/src/refs.h +++ b/src/refs.h @@ -98,4 +98,6 @@ int git_reference_lookup_resolved( const char *name, int max_deref); +int git_reference__log_signature(git_signature **out, git_repository *repo); + #endif diff --git a/src/transaction.c b/src/transaction.c new file mode 100644 index 000000000..b515deacf --- /dev/null +++ b/src/transaction.c @@ -0,0 +1,352 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "common.h" +#include "repository.h" +#include "strmap.h" +#include "refdb.h" +#include "pool.h" +#include "reflog.h" +#include "signature.h" + +#include "git2/signature.h" +#include "git2/sys/refs.h" +#include "git2/sys/refdb_backend.h" + +GIT__USE_STRMAP; + +typedef struct { + const char *name; + void *payload; + + git_ref_t ref_type; + union { + git_oid id; + char *symbolic; + } target; + git_reflog *reflog; + + const char *message; + git_signature *sig; + + unsigned int committed :1, + remove :1; +} transaction_node; + +struct git_transaction { + git_repository *repo; + git_refdb *db; + + git_strmap *locks; + git_pool pool; +}; + +int git_transaction_new(git_transaction **out, git_repository *repo) +{ + int error; + git_pool pool; + git_transaction *tx = NULL; + + assert(out && repo); + + if ((error = git_pool_init(&pool, 1, 0)) < 0) + return error; + + tx = git_pool_mallocz(&pool, sizeof(git_transaction)); + if (!tx) { + error = -1; + goto on_error; + } + + if ((error = git_strmap_alloc(&tx->locks)) < 0) { + error = -1; + goto on_error; + } + + if ((error = git_repository_refdb(&tx->db, repo)) < 0) + goto on_error; + + memcpy(&tx->pool, &pool, sizeof(git_pool)); + tx->repo = repo; + *out = tx; + return 0; + +on_error: + git_pool_clear(&pool); + return error; +} + +int git_transaction_lock(git_transaction *tx, const char *refname) +{ + int error; + transaction_node *node; + + assert(tx && refname); + + node = git_pool_mallocz(&tx->pool, sizeof(transaction_node)); + GITERR_CHECK_ALLOC(node); + + node->name = git_pool_strdup(&tx->pool, refname); + GITERR_CHECK_ALLOC(node->name); + + if ((error = git_refdb_lock(&node->payload, tx->db, refname)) < 0) + return error; + + git_strmap_insert(tx->locks, node->name, node, error); + if (error < 0) + goto cleanup; + + return 0; + +cleanup: + git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL); + + return error; +} + +static int find_locked(transaction_node **out, git_transaction *tx, const char *refname) +{ + git_strmap_iter pos; + transaction_node *node; + + pos = git_strmap_lookup_index(tx->locks, refname); + if (!git_strmap_valid_index(tx->locks, pos)) { + giterr_set(GITERR_REFERENCE, "the specified reference is not locked"); + return GIT_ENOTFOUND; + } + + node = git_strmap_value_at(tx->locks, pos); + + *out = node; + return 0; +} + +static int copy_common(transaction_node *node, git_transaction *tx, const git_signature *sig, const char *msg) +{ + if (sig && git_signature__pdup(&node->sig, sig, &tx->pool) < 0) + return -1; + + if (!node->sig) { + git_signature *tmp; + int error; + + if (git_reference__log_signature(&tmp, tx->repo) < 0) + return -1; + + /* make sure the sig we use is in our pool */ + error = git_signature__pdup(&node->sig, tmp, &tx->pool); + git_signature_free(tmp); + if (error < 0) + return error; + } + + if (msg) { + node->message = git_pool_strdup(&tx->pool, msg); + GITERR_CHECK_ALLOC(node->message); + } + + return 0; +} + +int git_transaction_set_target(git_transaction *tx, const char *refname, const git_oid *target, const git_signature *sig, const char *msg) +{ + int error; + transaction_node *node; + + assert(tx && refname && target); + + if ((error = find_locked(&node, tx, refname)) < 0) + return error; + + if ((error = copy_common(node, tx, sig, msg)) < 0) + return error; + + git_oid_cpy(&node->target.id, target); + node->ref_type = GIT_REF_OID; + + return 0; +} + +int git_transaction_set_symbolic_target(git_transaction *tx, const char *refname, const char *target, const git_signature *sig, const char *msg) +{ + int error; + transaction_node *node; + + assert(tx && refname && target); + + if ((error = find_locked(&node, tx, refname)) < 0) + return error; + + if ((error = copy_common(node, tx, sig, msg)) < 0) + return error; + + node->target.symbolic = git_pool_strdup(&tx->pool, target); + GITERR_CHECK_ALLOC(node->target.symbolic); + node->ref_type = GIT_REF_SYMBOLIC; + + return 0; +} + +int git_transaction_remove(git_transaction *tx, const char *refname) +{ + int error; + transaction_node *node; + + if ((error = find_locked(&node, tx, refname)) < 0) + return error; + + node->remove = true; + node->ref_type = GIT_REF_OID; /* the id will be ignored */ + + return 0; +} + +static int dup_reflog(git_reflog **out, const git_reflog *in, git_pool *pool) +{ + git_reflog *reflog; + git_reflog_entry *entries; + size_t len, i; + + reflog = git_pool_mallocz(pool, sizeof(git_reflog)); + GITERR_CHECK_ALLOC(reflog); + + reflog->ref_name = git_pool_strdup(pool, in->ref_name); + GITERR_CHECK_ALLOC(reflog->ref_name); + + len = in->entries.length; + reflog->entries.length = len; + reflog->entries.contents = git_pool_mallocz(pool, len * sizeof(void *)); + GITERR_CHECK_ALLOC(reflog->entries.contents); + + entries = git_pool_mallocz(pool, len * sizeof(git_reflog_entry)); + GITERR_CHECK_ALLOC(entries); + + for (i = 0; i < len; i++) { + const git_reflog_entry *src; + git_reflog_entry *tgt; + + tgt = &entries[i]; + reflog->entries.contents[i] = tgt; + + src = git_vector_get(&in->entries, i); + git_oid_cpy(&tgt->oid_old, &src->oid_old); + git_oid_cpy(&tgt->oid_cur, &src->oid_cur); + + tgt->msg = git_pool_strdup(pool, src->msg); + GITERR_CHECK_ALLOC(tgt->msg); + + if (git_signature__pdup(&tgt->committer, src->committer, pool) < 0) + return -1; + } + + + *out = reflog; + return 0; +} + +int git_transaction_set_reflog(git_transaction *tx, const char *refname, const git_reflog *reflog) +{ + int error; + transaction_node *node; + + assert(tx && refname && reflog); + + if ((error = find_locked(&node, tx, refname)) < 0) + return error; + + if ((error = dup_reflog(&node->reflog, reflog, &tx->pool)) < 0) + return error; + + return 0; +} + +static int update_target(git_refdb *db, transaction_node *node) +{ + git_reference *ref; + int error, update_reflog; + + if (node->ref_type == GIT_REF_OID) { + ref = git_reference__alloc(node->name, &node->target.id, NULL); + } else if (node->ref_type == GIT_REF_SYMBOLIC) { + ref = git_reference__alloc_symbolic(node->name, node->target.symbolic); + } else { + assert(0); + } + + GITERR_CHECK_ALLOC(ref); + update_reflog = node->reflog == NULL; + + if (node->remove) { + error = git_refdb_unlock(db, node->payload, 2, false, ref, NULL, NULL); + } else if (node->ref_type == GIT_REF_OID) { + error = git_refdb_unlock(db, node->payload, true, update_reflog, ref, node->sig, node->message); + } else if (node->ref_type == GIT_REF_SYMBOLIC) { + error = git_refdb_unlock(db, node->payload, true, update_reflog, ref, node->sig, node->message); + } else { + assert(0); + } + + git_reference_free(ref); + node->committed = true; + + return error; +} + +int git_transaction_commit(git_transaction *tx) +{ + transaction_node *node; + git_strmap_iter pos; + int error; + + assert(tx); + + for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) { + if (!git_strmap_has_data(tx->locks, pos)) + continue; + + node = git_strmap_value_at(tx->locks, pos); + if (node->reflog) { + if ((error = tx->db->backend->reflog_write(tx->db->backend, node->reflog)) < 0) + return error; + } + + if (node->ref_type != GIT_REF_INVALID) { + if ((error = update_target(tx->db, node)) < 0) + return error; + } + } + + return 0; +} + +void git_transaction_free(git_transaction *tx) +{ + transaction_node *node; + git_pool pool; + git_strmap_iter pos; + + assert(tx); + + /* start by unlocking the ones we've left hanging, if any */ + for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) { + if (!git_strmap_has_data(tx->locks, pos)) + continue; + + node = git_strmap_value_at(tx->locks, pos); + if (node->committed) + continue; + + git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL); + } + + git_refdb_free(tx->db); + git_strmap_free(tx->locks); + + /* tx is inside the pool, so we need to extract the data */ + memcpy(&pool, &tx->pool, sizeof(git_pool)); + git_pool_clear(&pool); +} diff --git a/tests/refs/transactions.c b/tests/refs/transactions.c new file mode 100644 index 000000000..5284ea82b --- /dev/null +++ b/tests/refs/transactions.c @@ -0,0 +1,110 @@ +#include "clar_libgit2.h" +#include "git2/transaction.h" + +static git_repository *g_repo; +static git_transaction *g_tx; + +void test_refs_transactions__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); + cl_git_pass(git_transaction_new(&g_tx, g_repo)); +} + +void test_refs_transactions__cleanup(void) +{ + git_transaction_free(g_tx); + cl_git_sandbox_cleanup(); +} + +void test_refs_transactions__single_ref_oid(void) +{ + git_reference *ref; + git_oid id; + + git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + + cl_git_pass(git_transaction_lock(g_tx, "refs/heads/master")); + cl_git_pass(git_transaction_set_target(g_tx, "refs/heads/master", &id, NULL, NULL)); + cl_git_pass(git_transaction_commit(g_tx)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master")); + + cl_assert(!git_oid_cmp(&id, git_reference_target(ref))); + git_reference_free(ref); +} + +void test_refs_transactions__single_ref_symbolic(void) +{ + git_reference *ref; + + cl_git_pass(git_transaction_lock(g_tx, "HEAD")); + cl_git_pass(git_transaction_set_symbolic_target(g_tx, "HEAD", "refs/heads/foo", NULL, NULL)); + cl_git_pass(git_transaction_commit(g_tx)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD")); + + cl_assert_equal_s("refs/heads/foo", git_reference_symbolic_target(ref)); + git_reference_free(ref); +} + +void test_refs_transactions__single_ref_mix_types(void) +{ + git_reference *ref; + git_oid id; + + git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + + cl_git_pass(git_transaction_lock(g_tx, "refs/heads/master")); + cl_git_pass(git_transaction_lock(g_tx, "HEAD")); + cl_git_pass(git_transaction_set_symbolic_target(g_tx, "refs/heads/master", "refs/heads/foo", NULL, NULL)); + cl_git_pass(git_transaction_set_target(g_tx, "HEAD", &id, NULL, NULL)); + cl_git_pass(git_transaction_commit(g_tx)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master")); + cl_assert_equal_s("refs/heads/foo", git_reference_symbolic_target(ref)); + git_reference_free(ref); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD")); + cl_assert(!git_oid_cmp(&id, git_reference_target(ref))); + git_reference_free(ref); +} + +void test_refs_transactions__single_ref_delete(void) +{ + git_reference *ref; + + cl_git_pass(git_transaction_lock(g_tx, "refs/heads/master")); + cl_git_pass(git_transaction_remove(g_tx, "refs/heads/master")); + cl_git_pass(git_transaction_commit(g_tx)); + + cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo, "refs/heads/master")); +} + +void test_refs_transactions__single_create(void) +{ + git_reference *ref; + const char *name = "refs/heads/new-branch"; + git_oid id; + + cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo, name)); + + cl_git_pass(git_transaction_lock(g_tx, name)); + + git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + cl_git_pass(git_transaction_set_target(g_tx, name, &id, NULL, NULL)); + cl_git_pass(git_transaction_commit(g_tx)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, name)); + cl_assert(!git_oid_cmp(&id, git_reference_target(ref))); + git_reference_free(ref); +} + +void test_refs_transactions__unlocked_set(void) +{ + git_oid id; + + cl_git_pass(git_transaction_lock(g_tx, "refs/heads/master")); + git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + cl_git_fail_with(GIT_ENOTFOUND, git_transaction_set_target(g_tx, "refs/heads/foo", &id, NULL, NULL)); + cl_git_pass(git_transaction_commit(g_tx)); +} |