Skip to content

Commit f3b661f

Browse files
mhaggergitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 2e376b3 commit f3b661f

File tree

1 file changed

+41
-19
lines changed

1 file changed

+41
-19
lines changed

builtin/reflog.c

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -352,18 +352,20 @@ static int push_tip_to_list(const char *refname, const unsigned char *sha1, int
352352
static int expire_reflog(const char *refname, const unsigned char *sha1,
353353
struct cmd_reflog_expire_cb *cmd)
354354
{
355+
static struct lock_file reflog_lock;
355356
struct expire_reflog_cb cb;
356357
struct ref_lock *lock;
357-
char *log_file, *newlog_path = NULL;
358+
char *log_file;
358359
struct commit *tip_commit;
359360
struct commit_list *tips;
360361
int status = 0;
361362

362363
memset(&cb, 0, sizeof(cb));
363364

364365
/*
365-
* we take the lock for the ref itself to prevent it from
366-
* getting updated.
366+
* The reflog file is locked by holding the lock on the
367+
* reference itself, plus we might need to update the
368+
* reference if --updateref was specified:
367369
*/
368370
lock = lock_any_ref_for_update(refname, sha1, 0, NULL);
369371
if (!lock)
@@ -372,10 +374,29 @@ static int expire_reflog(const char *refname, const unsigned char *sha1,
372374
unlock_ref(lock);
373375
return 0;
374376
}
377+
375378
log_file = git_pathdup("logs/%s", refname);
376379
if (!cmd->dry_run) {
377-
newlog_path = git_pathdup("logs/%s.lock", refname);
378-
cb.newlog = fopen(newlog_path, "w");
380+
/*
381+
* Even though holding $GIT_DIR/logs/$reflog.lock has
382+
* no locking implications, we use the lock_file
383+
* machinery here anyway because it does a lot of the
384+
* work we need, including cleaning up if the program
385+
* exits unexpectedly.
386+
*/
387+
if (hold_lock_file_for_update(&reflog_lock, log_file, 0) < 0) {
388+
struct strbuf err = STRBUF_INIT;
389+
unable_to_lock_message(log_file, errno, &err);
390+
error("%s", err.buf);
391+
strbuf_release(&err);
392+
goto failure;
393+
}
394+
cb.newlog = fdopen_lock_file(&reflog_lock, "w");
395+
if (!cb.newlog) {
396+
error("cannot fdopen %s (%s)",
397+
reflog_lock.filename.buf, strerror(errno));
398+
goto failure;
399+
}
379400
}
380401

381402
cb.cmd = cmd;
@@ -423,32 +444,33 @@ static int expire_reflog(const char *refname, const unsigned char *sha1,
423444
}
424445

425446
if (cb.newlog) {
426-
if (fclose(cb.newlog)) {
427-
status |= error("%s: %s", strerror(errno),
428-
newlog_path);
429-
unlink(newlog_path);
447+
if (close_lock_file(&reflog_lock)) {
448+
status |= error("couldn't write %s: %s", log_file,
449+
strerror(errno));
430450
} else if (cmd->updateref &&
431451
(write_in_full(lock->lock_fd,
432452
sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
433453
write_str_in_full(lock->lock_fd, "\n") != 1 ||
434454
close_ref(lock) < 0)) {
435-
status |= error("Couldn't write %s",
455+
status |= error("couldn't write %s",
436456
lock->lk->filename.buf);
437-
unlink(newlog_path);
438-
} else if (rename(newlog_path, log_file)) {
439-
status |= error("cannot rename %s to %s",
440-
newlog_path, log_file);
441-
unlink(newlog_path);
457+
rollback_lock_file(&reflog_lock);
458+
} else if (commit_lock_file(&reflog_lock)) {
459+
status |= error("unable to commit reflog '%s' (%s)",
460+
log_file, strerror(errno));
442461
} else if (cmd->updateref && commit_ref(lock)) {
443-
status |= error("Couldn't set %s", lock->ref_name);
444-
} else {
445-
adjust_shared_perm(log_file);
462+
status |= error("couldn't set %s", lock->ref_name);
446463
}
447464
}
448-
free(newlog_path);
449465
free(log_file);
450466
unlock_ref(lock);
451467
return status;
468+
469+
failure:
470+
rollback_lock_file(&reflog_lock);
471+
free(log_file);
472+
unlock_ref(lock);
473+
return -1;
452474
}
453475

454476
static int collect_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)

0 commit comments

Comments
 (0)