From: Taylor Blau <me@ttaylorr.com>
To: Paulo Casaretto via GitGitGadget <gitgitgadget@gmail.com>
Cc: git@vger.kernel.org, Paulo Casaretto <pcasaretto@gmail.com>
Subject: Re: [PATCH] lockfile: add PID file for debugging stale locks
Date: Wed, 3 Dec 2025 18:39:23 -0500 [thread overview]
Message-ID: <aTDKK6Ibcx35q3Tz@nand.local> (raw)
In-Reply-To: <pull.2011.git.1764688047077.gitgitgadget@gmail.com>
Hi Paulo,
On Tue, Dec 02, 2025 at 03:07:27PM +0000, Paulo Casaretto via GitGitGadget wrote:
> 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.
(For the purposes of this review, I'll ignore the naming conventions
that are discussed elsewhere in the thread, which I think can be
resolved separately of any technical concerns.)
> diff --git a/Documentation/git.adoc b/Documentation/git.adoc
> index ce099e78b8..6fdd509d34 100644
> --- a/Documentation/git.adoc
> +++ b/Documentation/git.adoc
> @@ -1010,6 +1010,16 @@ be in the future).
> the background which do not want to cause lock contention with
> other operations on the repository. Defaults to `1`.
>
> +`GIT_LOCK_PID_INFO`::
> + If this Boolean environment variable is set to `1`, Git will create
> + a `.lock.pid` file alongside each lock file containing the PID of the
> + process that created the lock. This information is displayed in error
> + messages when a lock conflict occurs, making it easier to identify
> + stale locks or debug locking issues. The PID files are automatically
> + cleaned up via signal and atexit handlers; however, if a process is
> + terminated abnormally (e.g., SIGKILL), the file may remain as a stale
> + indicator. Disabled by default.
Regardless of whether or not we expose this functionality behind an
environment variable or configuration, I think it would be nice to be
able to turn PID tracking on and off for different components (e.g., for
scenarios where you care about who is holding open, say, $GIT_DIR/index,
but not who is creating a lock during ref creation).
If we determined this through an environment variable, I think it would
be reasonable to adopt the convention from the "core.fsync"
configuration and use a comma-separated list. Alternatively, we could
adopt that same convention for a configuration variable, say,
"core.lockfile".
But I think that having this exposed as a per-component setting via
configuration is preferable than a global switch, since callers don't
have to remember to set this variable in their environment to get the
desired effect. Callers that want to opt-in or out of this feature on a
one-off basis can still override the configuration via the top-level
"-c" flag.
> `GIT_REDIRECT_STDIN`::
> `GIT_REDIRECT_STDOUT`::
> `GIT_REDIRECT_STDERR`::
> diff --git a/lockfile.c b/lockfile.c
> index 1d5ed01682..4a694b9c3d 100644
> --- a/lockfile.c
> +++ b/lockfile.c
> @@ -6,6 +6,9 @@
> #include "abspath.h"
> #include "gettext.h"
> #include "lockfile.h"
> +#include "parse.h"
> +#include "strbuf.h"
> +#include "wrapper.h"
>
> /*
> * path = absolute or relative path name
> @@ -71,6 +74,62 @@ static void resolve_symlink(struct strbuf *path)
> strbuf_reset(&link);
> }
>
> +/*
> + * Lock PID file functions - write PID to a .lock.pid file alongside
> + * the lock file for debugging stale locks. The PID file is registered
> + * as a tempfile so it gets cleaned up by signal/atexit handlers.
> + */
> +
> +static int lock_pid_info_enabled(void)
> +{
With the above suggestion, I think this function would get a little more
complicated to instead take a component argument. That's not a huge deal
in and of itself, but callers that *create* a lockfile will have to
somehow pass in the component name when acquiring the lock.
> + return git_env_bool(GIT_LOCK_PID_INFO_ENVIRONMENT, 0);
> +}
> +
> +static struct tempfile *create_lock_pid_file(const char *lock_path, int mode)
> +{
> + struct strbuf pid_path = STRBUF_INIT;
> + struct strbuf content = STRBUF_INIT;
> + struct tempfile *pid_tempfile = NULL;
> + int fd;
> +
> + if (!lock_pid_info_enabled())
> + return NULL;
> +
> + strbuf_addf(&pid_path, "%s%s", lock_path, LOCK_PID_SUFFIX);
> + fd = open(pid_path.buf, O_WRONLY | O_CREAT | O_TRUNC, mode);
I'm not sure whether or not we should pass O_EXCL here, but I think it
depends on the naming convention we pick.
> + if (fd >= 0) {
This is a style preference, but I'd suggest handling the happy path
outside of this conditional if possible by inverting it. Perhaps
something like:
if (fd < 0)
goto out;
strbuf_addf(&content, ...);
if (write_in_full(fd, content.buf, content.len) < 0)
warning_errno(...);
close(fd);
pid_tempfile = register_tempfile(pid_path.buf);
out:
strbuf_release(...);
return pid_tempfile;
> +static int read_lock_pid(const char *lock_path, uintmax_t *pid_out)
> +{
> + struct strbuf pid_path = STRBUF_INIT;
> + struct strbuf content = STRBUF_INIT;
> + int ret = -1;
> +
> + strbuf_addf(&pid_path, "%s%s", lock_path, LOCK_PID_SUFFIX);
> + if (strbuf_read_file(&content, pid_path.buf, LOCK_PID_MAXLEN) > 0) {
> + char *endptr;
> + *pid_out = strtoumax(content.buf, &endptr, 10);
> + if (*pid_out > 0 && (*endptr == '\n' || *endptr == '\0'))
> + ret = 0;
Same note here. I'd suggest setting "ret = 0" during initialization, and
inverting this conditional to:
if (*pid_out <= 0 || (*endptr != '\n' && *endptr != '\0')) {
warning(...);
ret = -1;
goto out;
}
> + else
> + warning(_("malformed lock pid file '%s'"), pid_path.buf);
> + }
> + strbuf_release(&pid_path);
> + strbuf_release(&content);
> + return ret;
> +}
> +
> /* Make sure errno contains a meaningful value on error */
> static int lock_file(struct lock_file *lk, const char *path, int flags,
> int mode)
> @@ -80,9 +139,12 @@ static int lock_file(struct lock_file *lk, const char *path, int flags,
> strbuf_addstr(&filename, path);
> if (!(flags & LOCK_NO_DEREF))
> resolve_symlink(&filename);
> -
> strbuf_addstr(&filename, LOCK_SUFFIX);
> +
These look like stray whitespace changes that were left behind from
development.
> + if (pid_status == 1)
> + strbuf_addf(buf, _("Lock is held by process %"PRIuMAX". "
> + "Wait for it to finish, or remove the lock file to continue"),
> + pid);
> + else if (pid_status == -1)
> + strbuf_addf(buf, _("Lock was held by process %"PRIuMAX", "
> + "which is no longer running. Remove the stale lock file to continue"),
> + pid);
> + else
> + strbuf_addstr(buf, _("Another git process seems to be running in this repository. "
> + "Wait for it to finish, or remove the lock file to continue"));
On one hand I prefer the new "Another git process" message for when we
don't have a PID lockfile. But on the other hand, I think the "If it
still fails, a git process may have crashed..." message is useful for
users who may not be immediately aware of the consequences of simply
removing the lockfile to continue.
I do think the original message is somewhat verbose, so maybe the change
here in the non-PID case is worth doing. What are your thoughts?
> +
> + strbuf_release(&lock_path);
> } else
> strbuf_addf(buf, _("Unable to create '%s.lock': %s"),
> absolute_path(path), strerror(err));
> @@ -207,6 +292,8 @@ int commit_lock_file(struct lock_file *lk)
> {
> char *result_path = get_locked_file_path(lk);
>
> + delete_tempfile(&lk->pid_tempfile);
> +
Do we want to wait to delete the PID file until after we know that we
successfully committed the lockfile?
> +/* Maximum length for PID file content */
> +#define LOCK_PID_MAXLEN 32
Makes sense ;-). This should be plenty of space, since on my system
maximum PID value is 2^22:
$ cat /proc/sys/kernel/pid_max
4194304
> +test_expect_success 'PID info file cleaned up on successful operation when enabled' '
> + git init repo4 &&
> + (
> + cd repo4 &&
> + echo content >file &&
> + env GIT_LOCK_PID_INFO=1 git add file &&
> + # After successful add, no lock or PID files should exist
> + ! test -f .git/index.lock &&
> + ! test -f .git/index.lock.pid
These should be:
test_path_is_missing .git/index.lock &&
test_path_is_missing .git/index.lock.pid
instead of bare "! test -f"'s.
Thanks,
Taylor
next prev parent reply other threads:[~2025-12-03 23:39 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-02 15:07 [PATCH] lockfile: add PID file for debugging stale locks Paulo Casaretto via GitGitGadget
2025-12-02 22:29 ` D. Ben Knoble
2025-12-03 19:48 ` Torsten Bögershausen
2025-12-03 21:16 ` Jeff King
2025-12-03 22:21 ` Junio C Hamano
2025-12-03 22:32 ` Jeff King
2025-12-03 23:19 ` Taylor Blau
2025-12-05 11:03 ` Patrick Steinhardt
2025-12-05 18:46 ` Jeff King
2025-12-03 23:39 ` Taylor Blau [this message]
2025-12-17 18:59 ` [PATCH v2] " Paulo Casaretto via GitGitGadget
2025-12-18 0:32 ` Junio C Hamano
2025-12-18 0:47 ` Junio C Hamano
2025-12-18 1:33 ` Junio C Hamano
2025-12-18 3:38 ` Ben Knoble
2025-12-18 8:07 ` Patrick Steinhardt
2025-12-24 12:24 ` [PATCH v3] " Paulo Casaretto via GitGitGadget
2025-12-25 0:01 ` Junio C Hamano
2025-12-27 7:50 ` Jeff King
2026-01-05 12:23 ` Patrick Steinhardt
2026-01-07 16:45 ` [PATCH v4] " Paulo Casaretto via GitGitGadget
2026-01-08 1:59 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aTDKK6Ibcx35q3Tz@nand.local \
--to=me@ttaylorr.com \
--cc=git@vger.kernel.org \
--cc=gitgitgadget@gmail.com \
--cc=pcasaretto@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).