Commit f99ca523 by Carlos Martín Nieto

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.
parent ab8d9242
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "git2/status.h" #include "git2/status.h"
#include "git2/checkout.h" #include "git2/checkout.h"
#include "git2/index.h" #include "git2/index.h"
#include "git2/transaction.h"
#include "signature.h" #include "signature.h"
static int create_error(int error, const char *msg) static int create_error(int error, const char *msg)
...@@ -601,14 +602,21 @@ int git_stash_drop( ...@@ -601,14 +602,21 @@ int git_stash_drop(
git_repository *repo, git_repository *repo,
size_t index) size_t index)
{ {
git_reference *stash; git_transaction *tx;
git_reference *stash = NULL;
git_reflog *reflog = NULL; git_reflog *reflog = NULL;
size_t max; size_t max;
int error; int error;
if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0) if ((error = git_transaction_new(&tx, repo)) < 0)
return error; 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) if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
goto cleanup; goto cleanup;
...@@ -623,29 +631,25 @@ int git_stash_drop( ...@@ -623,29 +631,25 @@ int git_stash_drop(
if ((error = git_reflog_drop(reflog, index, true)) < 0) if ((error = git_reflog_drop(reflog, index, true)) < 0)
goto cleanup; goto cleanup;
if ((error = git_reflog_write(reflog)) < 0) if ((error = git_transaction_set_reflog(tx, GIT_REFS_STASH_FILE, reflog)) < 0)
goto cleanup; goto cleanup;
if (max == 1) { if (max == 1) {
error = git_reference_delete(stash); if ((error = git_transaction_remove(tx, GIT_REFS_STASH_FILE)) < 0)
git_reference_free(stash); goto cleanup;
stash = NULL;
} else if (index == 0) { } else if (index == 0) {
const git_reflog_entry *entry; const git_reflog_entry *entry;
entry = git_reflog_entry_byindex(reflog, 0); entry = git_reflog_entry_byindex(reflog, 0);
if ((error = git_transaction_set_target(tx, GIT_REFS_STASH_FILE, &entry->oid_cur, NULL, NULL)) < 0)
git_reference_free(stash);
error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1, NULL, NULL);
if (error < 0)
goto cleanup; goto cleanup;
/* We need to undo the writing that we just did */
error = git_reflog_write(reflog);
} }
error = git_transaction_commit(tx);
cleanup: cleanup:
git_reference_free(stash); git_reference_free(stash);
git_transaction_free(tx);
git_reflog_free(reflog); git_reflog_free(reflog);
return error; return error;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment