Skip to content

Commit 47531f4

Browse files
committed
refs: unlock unmodified refs on transaction commit
Refs which are locked in a transaction without an altered target, still should to be unlocked on `git_transaction_commit`. `git_transaction_free` also unlocks refs but the moment of calling of `git_transaction_free` cannot be controlled in all situations. Some binding libs call `git_transaction_free` on garbage collection or not at all if the application exits before and don't provide public access to `git_transaction_free`. It is better to release locks as soon as possible.
1 parent ef5a385 commit 47531f4

File tree

2 files changed

+33
-1
lines changed

2 files changed

+33
-1
lines changed

src/transaction.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,13 @@ int git_transaction_commit(git_transaction *tx)
334334
return error;
335335
}
336336

337-
if (node->ref_type != GIT_REFERENCE_INVALID) {
337+
if (node->ref_type == GIT_REFERENCE_INVALID) {
338+
/* ref was locked but not modified */
339+
if ((error = git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL)) < 0) {
340+
return error;
341+
}
342+
node->committed = true;
343+
} else {
338344
if ((error = update_target(tx->db, node)) < 0)
339345
return error;
340346
}

tests/refs/transactions.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,29 @@ void test_refs_transactions__error_on_locking_locked_ref(void)
129129
git_transaction_free(g_tx_with_lock);
130130
git_repository_free(g_repo_with_locking_tx);
131131
}
132+
133+
void test_refs_transactions__commit_unlocks_unmodified_ref(void)
134+
{
135+
git_transaction *second_tx;
136+
137+
cl_git_pass(git_transaction_new(&second_tx, g_repo));
138+
cl_git_pass(git_transaction_lock_ref(second_tx, "refs/heads/master"));
139+
cl_git_pass(git_transaction_commit(second_tx));
140+
141+
/* a transaction must now be able to get the lock */
142+
cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master"));
143+
144+
git_transaction_free(second_tx);
145+
}
146+
147+
void test_refs_transactions__free_unlocks_unmodified_ref(void)
148+
{
149+
git_transaction *second_tx;
150+
151+
cl_git_pass(git_transaction_new(&second_tx, g_repo));
152+
cl_git_pass(git_transaction_lock_ref(second_tx, "refs/heads/master"));
153+
git_transaction_free(second_tx);
154+
155+
/* a transaction must now be able to get the lock */
156+
cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master"));
157+
}

0 commit comments

Comments
 (0)