summaryrefslogtreecommitdiff
path: root/builtin
diff options
context:
space:
mode:
Diffstat (limited to 'builtin')
-rw-r--r--builtin/reflog.c60
1 files changed, 41 insertions, 19 deletions
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 37b33c9..ba5b3d3 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -352,9 +352,10 @@ static int push_tip_to_list(const char *refname, const unsigned char *sha1, int
static int expire_reflog(const char *refname, const unsigned char *sha1,
struct cmd_reflog_expire_cb *cmd)
{
+ static struct lock_file reflog_lock;
struct expire_reflog_cb cb;
struct ref_lock *lock;
- char *log_file, *newlog_path = NULL;
+ char *log_file;
struct commit *tip_commit;
struct commit_list *tips;
int status = 0;
@@ -362,8 +363,9 @@ static int expire_reflog(const char *refname, const unsigned char *sha1,
memset(&cb, 0, sizeof(cb));
/*
- * we take the lock for the ref itself to prevent it from
- * getting updated.
+ * The reflog file is locked by holding the lock on the
+ * reference itself, plus we might need to update the
+ * reference if --updateref was specified:
*/
lock = lock_any_ref_for_update(refname, sha1, 0, NULL);
if (!lock)
@@ -372,10 +374,29 @@ static int expire_reflog(const char *refname, const unsigned char *sha1,
unlock_ref(lock);
return 0;
}
+
log_file = git_pathdup("logs/%s", refname);
if (!cmd->dry_run) {
- newlog_path = git_pathdup("logs/%s.lock", refname);
- cb.newlog = fopen(newlog_path, "w");
+ /*
+ * Even though holding $GIT_DIR/logs/$reflog.lock has
+ * no locking implications, we use the lock_file
+ * machinery here anyway because it does a lot of the
+ * work we need, including cleaning up if the program
+ * exits unexpectedly.
+ */
+ if (hold_lock_file_for_update(&reflog_lock, log_file, 0) < 0) {
+ struct strbuf err = STRBUF_INIT;
+ unable_to_lock_message(log_file, errno, &err);
+ error("%s", err.buf);
+ strbuf_release(&err);
+ goto failure;
+ }
+ cb.newlog = fdopen_lock_file(&reflog_lock, "w");
+ if (!cb.newlog) {
+ error("cannot fdopen %s (%s)",
+ reflog_lock.filename.buf, strerror(errno));
+ goto failure;
+ }
}
cb.cmd = cmd;
@@ -423,32 +444,33 @@ static int expire_reflog(const char *refname, const unsigned char *sha1,
}
if (cb.newlog) {
- if (fclose(cb.newlog)) {
- status |= error("%s: %s", strerror(errno),
- newlog_path);
- unlink(newlog_path);
+ if (close_lock_file(&reflog_lock)) {
+ status |= error("couldn't write %s: %s", log_file,
+ strerror(errno));
} else if (cmd->updateref &&
(write_in_full(lock->lock_fd,
sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
write_str_in_full(lock->lock_fd, "\n") != 1 ||
close_ref(lock) < 0)) {
- status |= error("Couldn't write %s",
+ status |= error("couldn't write %s",
lock->lk->filename.buf);
- unlink(newlog_path);
- } else if (rename(newlog_path, log_file)) {
- status |= error("cannot rename %s to %s",
- newlog_path, log_file);
- unlink(newlog_path);
+ rollback_lock_file(&reflog_lock);
+ } else if (commit_lock_file(&reflog_lock)) {
+ status |= error("unable to commit reflog '%s' (%s)",
+ log_file, strerror(errno));
} else if (cmd->updateref && commit_ref(lock)) {
- status |= error("Couldn't set %s", lock->ref_name);
- } else {
- adjust_shared_perm(log_file);
+ status |= error("couldn't set %s", lock->ref_name);
}
}
- free(newlog_path);
free(log_file);
unlock_ref(lock);
return status;
+
+ failure:
+ rollback_lock_file(&reflog_lock);
+ free(log_file);
+ unlock_ref(lock);
+ return -1;
}
static int collect_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)