mirror of
https://github.com/git/git.git
synced 2024-11-01 06:47:52 +01:00
expire_reflog(): use a lock_file for rewriting the reflog file
We don't actually need the locking functionality, because we already hold the lock on the reference itself, which is how the reflog file is locked. But the lock_file code can do some of the bookkeeping for us, and it is more careful than the old code here was. For example: * It correctly handles the case that the reflog lock file already exists for some reason or cannot be opened. * It correctly cleans up the lockfile if the program dies. Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
2e376b3156
commit
f3b661f766
1 changed files with 41 additions and 19 deletions
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue