summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2014-06-30 06:38:45 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2014-09-30 15:52:13 +0200
commitf99ca52378cf3097fb56c7dbec379b8fc4c5459b (patch)
tree2577e291fc15a08a34d33f8fa944c8bdaf67a63b
parentab8d9242f54e2c570f86a45a509b9420911a4d44 (diff)
downloadlibgit2-f99ca52378cf3097fb56c7dbec379b8fc4c5459b.tar.gz
stash: use a transaction to modify the reflog
The stash is implemented as the refs/stash reference and its reflog. In order to modify the reflog, we need avoid races by making sure we're the only ones allowed to modify the reflog. We achieve this via the transactions API. Locking the reference gives us exclusive write access, letting us modify and write it without races.
-rw-r--r--src/stash.c30
1 files changed, 17 insertions, 13 deletions
diff --git a/src/stash.c b/src/stash.c
index caffd0cea..ea4ba1af4 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -15,6 +15,7 @@
#include "git2/status.h"
#include "git2/checkout.h"
#include "git2/index.h"
+#include "git2/transaction.h"
#include "signature.h"
static int create_error(int error, const char *msg)
@@ -601,14 +602,21 @@ int git_stash_drop(
git_repository *repo,
size_t index)
{
- git_reference *stash;
+ git_transaction *tx;
+ git_reference *stash = NULL;
git_reflog *reflog = NULL;
size_t max;
int error;
- if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
+ if ((error = git_transaction_new(&tx, repo)) < 0)
return error;
+ if ((error = git_transaction_lock(tx, GIT_REFS_STASH_FILE)) < 0)
+ goto cleanup;
+
+ if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
+ goto cleanup;
+
if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
goto cleanup;
@@ -623,29 +631,25 @@ int git_stash_drop(
if ((error = git_reflog_drop(reflog, index, true)) < 0)
goto cleanup;
- if ((error = git_reflog_write(reflog)) < 0)
+ if ((error = git_transaction_set_reflog(tx, GIT_REFS_STASH_FILE, reflog)) < 0)
goto cleanup;
if (max == 1) {
- error = git_reference_delete(stash);
- git_reference_free(stash);
- stash = NULL;
+ if ((error = git_transaction_remove(tx, GIT_REFS_STASH_FILE)) < 0)
+ goto cleanup;
} else if (index == 0) {
const git_reflog_entry *entry;
entry = git_reflog_entry_byindex(reflog, 0);
-
- git_reference_free(stash);
- error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1, NULL, NULL);
- if (error < 0)
+ if ((error = git_transaction_set_target(tx, GIT_REFS_STASH_FILE, &entry->oid_cur, NULL, NULL)) < 0)
goto cleanup;
-
- /* We need to undo the writing that we just did */
- error = git_reflog_write(reflog);
}
+ error = git_transaction_commit(tx);
+
cleanup:
git_reference_free(stash);
+ git_transaction_free(tx);
git_reflog_free(reflog);
return error;
}