diff options
-rw-r--r-- | builtin/reflog.c | 60 |
1 files changed, 41 insertions, 19 deletions
diff --git a/builtin/reflog.c b/builtin/reflog.c index 37b33c9d70..ba5b3d378f 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) |