The lockfile API serves two purposes:

  • Mutual exclusion and atomic file updates. When we want to change a file, we create a lockfile <filename>.lock, write the new file contents into it, and then rename the lockfile to its final destination <filename>. We create the <filename>.lock file with O_CREAT|O_EXCL so that we can notice and fail if somebody else has already locked the file, then atomically rename the lockfile to its final destination to commit the changes and unlock the file.

  • Automatic cruft removal. If the program exits after we lock a file but before the changes have been committed, we want to make sure that we remove the lockfile. This is done by remembering the lockfiles we have created in a linked list and setting up an atexit(3) handler and a signal handler that clean up the lockfiles. This mechanism ensures that outstanding lockfiles are cleaned up if the program exits (including when die() is called) or if the program dies on a signal.

Please note that lockfiles only block other writers. Readers do not block, but they are guaranteed to see either the old contents of the file or the new contents of the file (assuming that the filesystem implements rename(2) atomically).

Calling sequence

The caller:

When finished writing, the caller can:

Even after the lockfile is committed or rolled back, the lock_file object must not be freed or altered by the caller. However, it may be reused; just pass it to another call of hold_lock_file_for_update or hold_lock_file_for_append.

If the program exits before you have called one of commit_lock_file, commit_lock_file_to, rollback_lock_file, or close_lock_file, an atexit(3) handler will close and remove the lockfile, rolling back any uncommitted changes.

If you need to close the file descriptor you obtained from a hold_lock_file_* function yourself, do so by calling close_lock_file. You should never call close(2) or fclose(3) yourself! Otherwise the struct lock_file structure would still think that the file descriptor needs to be closed, and a commit or rollback would result in duplicate calls to close(2). Worse yet, if you close and then later open another file descriptor for a completely different purpose, then a commit or rollback might close that unrelated file descriptor.

Error handling

The hold_lock_file_* functions return a file descriptor on success or -1 on failure (unless LOCK_DIE_ON_ERROR is used; see below). On errors, errno describes the reason for failure. Errors can be reported by passing errno to one of the following helper functions:

unable_to_lock_message

Append an appropriate error message to a strbuf.

unable_to_lock_error

Emit an appropriate error message using error().

unable_to_lock_die

Emit an appropriate error message and die().

Similarly, commit_lock_file, commit_lock_file_to, and close_lock_file return 0 on success. On failure they set errno appropriately, do their best to roll back the lockfile, and return -1.

Flags

The following flags can be passed to hold_lock_file_for_update or hold_lock_file_for_append:

LOCK_NO_DEREF

Usually symbolic links in the destination path are resolved and the lockfile is created by adding ".lock" to the resolved path. If LOCK_NO_DEREF is set, then the lockfile is created by adding ".lock" to the path argument itself. This option is used, for example, when locking a symbolic reference, which for backwards-compatibility reasons can be a symbolic link containing the name of the referred-to-reference.

LOCK_DIE_ON_ERROR

If a lock is already taken for the file, die() with an error message. If this option is not specified, trying to lock a file that is already locked returns -1 to the caller.

The functions

hold_lock_file_for_update

Take a pointer to struct lock_file, the path of the file to be locked (e.g. $GIT_DIR/index) and a flags argument (see above). Attempt to create a lockfile for the destination and return the file descriptor for writing to the file.

hold_lock_file_for_append

Like hold_lock_file_for_update, but before returning copy the existing contents of the file (if any) to the lockfile and position its write pointer at the end of the file.

fdopen_lock_file

Associate a stdio stream with the lockfile. Return NULL (without rolling back the lockfile) on error. The stream is closed automatically when close_lock_file is called or when the file is committed or rolled back.

get_locked_file_path

Return the path of the file that is locked by the specified lock_file object. The caller must free the memory.

commit_lock_file

Take a pointer to the struct lock_file initialized with an earlier call to hold_lock_file_for_update or hold_lock_file_for_append, close the file descriptor, and rename the lockfile to its final destination. Return 0 upon success. On failure, roll back the lock file and return -1, with errno set to the value from the failing call to close(2) or rename(2). It is a bug to call commit_lock_file for a lock_file object that is not currently locked.

commit_lock_file_to

Like commit_lock_file(), except that it takes an explicit path argument to which the lockfile should be renamed. The path must be on the same filesystem as the lock file.

rollback_lock_file

Take a pointer to the struct lock_file initialized with an earlier call to hold_lock_file_for_update or hold_lock_file_for_append, close the file descriptor and remove the lockfile. It is a NOOP to call rollback_lock_file() for a lock_file object that has already been committed or rolled back.

close_lock_file

Take a pointer to the struct lock_file initialized with an earlier call to hold_lock_file_for_update or hold_lock_file_for_append. Close the file descriptor (and the file pointer if it has been opened using fdopen_lock_file). Return 0 upon success. On failure to close(2), return a negative value and roll back the lock file. Usually commit_lock_file, commit_lock_file_to, or rollback_lock_file should eventually be called if close_lock_file succeeds.

reopen_lock_file

Re-open a lockfile that has been closed (using close_lock_file) but not yet committed or rolled back. This can be used to implement a sequence of operations like the following:

  • Lock file.

  • Write new contents to lockfile, then close_lock_file to cause the contents to be written to disk.

  • Pass the name of the lockfile to another program to allow it (and nobody else) to inspect the contents you wrote, while still holding the lock yourself.

  • reopen_lock_file to reopen the lockfile. Make further updates to the contents.

  • commit_lock_file to make the final version permanent.