-
Notifications
You must be signed in to change notification settings - Fork 160
lockfile: add holder info file for debugging stale locks #2011
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
This comment was marked as spam.
This comment was marked as spam.
hcmaATshopify
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would argue for using pid instead of holder everywhere.
Pros of pid
- Precise: The only payload in the file is the PID. Using “pid” makes it 100% clear and avoids ambiguity.
- Conventional: Many tools use
.pidfiles or similar conventions, so the meaning is familiar. - Unambiguous Code: Variables like
pid_fileorread_lock_pid()are explicitly about PIDs, not other info.
lockfile.c
Outdated
| return NULL; | ||
|
|
||
| strbuf_addf(&holder_path, "%s%s", lock_path, LOCK_HOLDER_SUFFIX); | ||
| fd = open(holder_path.buf, O_WRONLY | O_CREAT | O_TRUNC, mode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make sure mode is restrictive enough (match whatever the .lock has and is readable by the creator).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does use the same mode parameter as the lock file:
// In lock_file():
lk->tempfile = create_tempfile_mode(filename.buf, mode); // lock file
lk->pid_tempfile = create_lock_pid_file(filename.buf, mode); // PID file - same mode
lockfile.c
Outdated
|
|
||
| if (lock_holder_info_enabled() && | ||
| !read_lock_holder_pid(lock_path.buf, &holder_pid)) | ||
| strbuf_addf(buf, _("Lock is held by process %"PRIuMAX".\n\n"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lock (acquired via file '%s') is being held by process .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given this will print after the "Unable to create '/path/to/.git/index.lock': File exists." message, I think it would be redundant.
lockfile.c
Outdated
| strbuf_addf(buf, _("Lock is held by process %"PRIuMAX".\n\n"), | ||
| holder_pid); | ||
|
|
||
| strbuf_addstr(buf, _("Another git process seems to be running in this repository, e.g.\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the feature is enabled, we can actually improve this now and be assertive (for instance, if the pid is no longer around, we can say this "lock file is stale and can be removed" or "lock file is valid and held by process "
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
turns out kill was already implement and cross platform. Added a liveness check!
| return 0; | ||
| } | ||
|
|
||
| int rollback_lock_file(struct lock_file *lk) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recommend renaming rollback_lock_file to abort_lock_file for clarity and consistency with Git’s conventions. abort_lock_file is likely the best fit for the Git codebase, as this matches the standard “commit/abort” terminology for transactional semantics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is not new, I just needed to change it from being an inline function to add new functionality.
| strbuf_addf(&holder_path, "%s%s", lock_path, LOCK_HOLDER_SUFFIX); | ||
| fd = open(holder_path.buf, O_WRONLY | O_CREAT | O_TRUNC, mode); | ||
| if (fd >= 0) { | ||
| strbuf_addf(&content, "%"PRIuMAX"\n", (uintmax_t)getpid()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe strbuf_addf(&content, "%ld\n", (long)getpid()); since pid_t is generally int or long; cast to long for portability
Are we sure this will work for non-Unix platforms that might be supported by git? In other words, do we need to ifdef this whole thing so we don't break non-Unix builds?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking around the codebase it seems (uintmax_t)getpid() + PRIuMAX pattern is the established Git convention for portable PID handling:
Examples:
-
daemon.c:1458- writes PID to file:write_file(pid_file, "%"PRIuMAX, (uintmax_t) getpid());
-
builtin/gc.c:789-790- writes PID to lockfile:strbuf_addf(&sb, "%"PRIuMAX" %s", (uintmax_t) getpid(), my_host);
In terms of cross platform compatibility, I think I got lucky because it was already a first class citizen.
Did a little research and it works across the board.
- getpid() is available on all Git-supported platforms (provided by libc on Unix, emulated on Windows)
- pid_t is defined everywhere (native on Unix, typedef int on Windows via mingw-posix.h)
5452ae9 to
51dbf28
Compare
|
Thanks for doing this @pcasaretto - LGTM! |
When a lock file is held, it can be helpful to know which process owns it, especially when debugging stale locks left behind by crashed processes. Add an optional feature that creates a companion .lock.pid file alongside each lock file, containing the PID of the lock holder. The .lock.pid file is created when a lock is acquired (if enabled), and automatically cleaned up when the lock is released (via commit or rollback). The file is registered as a tempfile so it gets cleaned up by signal and atexit handlers if the process terminates abnormally. When a lock conflict occurs, the code checks if the PID from the .pid file is still running using kill(pid, 0). This allows providing context-aware error messages. With PID info enabled: Lock is held by process 12345. Wait for it to finish, or remove the lock file to continue. Or for a stale lock: Lock was held by process 12345, which is no longer running. Remove the stale lock file to continue. Without PID info (default): Another git process seems to be running in this repository. Wait for it to finish, or remove the lock file to continue. The feature is opt-in via GIT_LOCK_PID_INFO=1 environment variable. Signed-off-by: Paulo Casaretto <[email protected]>
51dbf28 to
7068ec0
Compare
|
/preview |
|
Preview email sent as [email protected] |
|
/submit |
|
Submitted as [email protected] To fetch this version into To fetch this version to local tag |
|
On the Git mailing list, "D. Ben Knoble" wrote (reply to this): On Tue, Dec 2, 2025 at 10:07 AM Paulo Casaretto via GitGitGadget
<[email protected]> wrote:
>
> From: Paulo Casaretto <[email protected]>
>
> When a lock file is held, it can be helpful to know which process owns
> it, especially when debugging stale locks left behind by crashed
> processes. Add an optional feature that creates a companion .lock.pid
> file alongside each lock file, containing the PID of the lock holder.
>
> The .lock.pid file is created when a lock is acquired (if enabled), and
> automatically cleaned up when the lock is released (via commit or
> rollback). The file is registered as a tempfile so it gets cleaned up
> by signal and atexit handlers if the process terminates abnormally.
>
> When a lock conflict occurs, the code checks if the PID from the .pid
> file is still running using kill(pid, 0). This allows providing
> context-aware error messages. With PID info enabled:
>
> Lock is held by process 12345. Wait for it to finish, or remove
> the lock file to continue.
>
> Or for a stale lock:
>
> Lock was held by process 12345, which is no longer running.
> Remove the stale lock file to continue.
>
> Without PID info (default):
>
> Another git process seems to be running in this repository.
> Wait for it to finish, or remove the lock file to continue.
>
> The feature is opt-in via GIT_LOCK_PID_INFO=1 environment variable.
>
> Signed-off-by: Paulo Casaretto <[email protected]>
Sounds interesting. I think by the time I wish I knew what else was
using the lockfile, it's too late for me to alter my environment.
Perhaps (in addition to allowing the environment opt-in) we could
opt-in via configuration? Or is this really only useful, say, on the
server side where the environment is carefully controlled? I don't
relish putting this variable into my environment to take advantage of
something that looks very useful.
Are there downsides that make it necessary to be opt-in? I also
imagine this could be a useful default; occasionally folks at work hit
something similar and ask "what's up with that?"
Only other thing is: just because a process X is running doesn't mean
it was the one holding the lock, right? Since PIDs can be reused.
--
D. Ben Knoble |
|
User |
CC: Taylor Blau [email protected]
cc: "D. Ben Knoble" [email protected]