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).
The caller:
Allocates a struct lock_file either as a static variable or on the heap, initialized to zeros. Once you use the structure to call the hold_lock_file_* family of functions, it belongs to the lockfile subsystem and its storage must remain valid throughout the life of the program (i.e. you cannot use an on-stack variable to hold this structure).
Attempts to create a lockfile by passing that variable and the path of the final destination (e.g. $GIT_DIR/index) to hold_lock_file_for_update or hold_lock_file_for_append.
Writes new content for the destination file by either:
writing to the file descriptor returned by the hold_lock_file_* functions (also available via lock->fd).
calling fdopen_lock_file to get a FILE pointer for the open file and writing to the file using stdio.
When finished writing, the caller can:
Close the file descriptor and rename the lockfile to its final destination by calling commit_lock_file or commit_lock_file_to.
Close the file descriptor and remove the lockfile by calling rollback_lock_file.
Close the file descriptor without removing or renaming the lockfile by calling close_lock_file, and later call commit_lock_file, commit_lock_file_to, rollback_lock_file, or reopen_lock_file.
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.
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:
Append an appropriate error message to a strbuf.
Emit an appropriate error message using error().
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.
The following flags can be passed to hold_lock_file_for_update or hold_lock_file_for_append:
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.
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.
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.
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.
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.
Return the path of the file that is locked by the specified lock_file object. The caller must free the memory.
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.
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.
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.
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.
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.