{"thread":{"id":"65060","subject":"[PATCH 0/1] Fix zombie children when git is PID 1 in containers","startedAt":"2026-02-23T16:51:55Z","lastAt":"2026-03-16T21:25:00Z","messageCount":22,"participants":["Andrew Au","Kristoffer Haugsbakk","Junio C Hamano","Jeff King","brian m. carlson"],"isPatch":true,"patchVersion":1,"patchTotal":1},"messages":[{"id":"536850","messageId":"20260223165147.3294516-1-cshung@gmail.com","threadId":"65060","inReplyTo":null,"subject":"[PATCH 0/1] Fix zombie children when git is PID 1 in containers","fromName":"Andrew Au","fromEmail":"cshung@gmail.com","sentAt":"2026-02-23T16:51:46Z","receivedAt":"2026-02-23T16:51:55Z","isPatch":true,"sender":{"key":"cshung@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3410332?v=4"},"body":"When git runs as PID 1 inside a container and exits via exit(128) on\ntransport errors, child processes (git-remote-https, ssh, proxy) are\nnever waited on because the normal cleanup paths (disconnect_helper,\nfinish_connect) are bypassed. Since PID 1 has no parent to reap its\nchildren, these become zombies that persist for the container's lifetime.\n\nThis patch registers atexit handlers in transport-helper.c and connect.c\nto ensure children are reaped on any exit path, and clears them on the\nnormal cleanup paths to avoid double-waiting.\n\nBlog post with detailed investigation: https://cshung.github.io/posts/zombie-git/\n\nAndrew Au (1):\n  transport-helper, connect: add atexit handler to reap children on\n    abnormal exit\n\n connect.c          | 17 +++++++++++++++++\n transport-helper.c | 11 +++++++++++\n 2 files changed, 28 insertions(+)\n\n-- \n2.43.0\n\n"},{"id":"536851","messageId":"20260223165147.3294516-2-cshung@gmail.com","threadId":"65060","inReplyTo":"20260223165147.3294516-1-cshung@gmail.com","subject":"[PATCH 1/1] transport-helper, connect: add atexit handler to reap children on abnormal exit","fromName":"Andrew Au","fromEmail":"cshung@gmail.com","sentAt":"2026-02-23T16:51:47Z","receivedAt":"2026-02-23T16:51:56Z","isPatch":true,"sender":{"key":"cshung@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3410332?v=4"},"body":"From: Andrew Au <3410332+cshung@users.noreply.github.com>\n\nWhen git exits via exit(128) on transport errors, child processes\n(git-remote-https, ssh, proxy) are never waited on because the normal\ncleanup paths (disconnect_helper, finish_connect) are bypassed. When\ngit is PID 1 in a container, these un-reaped children become zombies.\n\nRegister atexit handlers in both transport-helper.c and connect.c to\nensure children are reaped on any exit path. Clear the handlers on the\nnormal cleanup paths to avoid double-waiting.\n\nSigned-off-by: Andrew Au <cshung@gmail.com>\n---\n connect.c          | 17 +++++++++++++++++\n transport-helper.c | 11 +++++++++++\n 2 files changed, 28 insertions(+)\n\ndiff --git a/connect.c b/connect.c\nindex eef752f14..322b1f816 100644\n--- a/connect.c\n+++ b/connect.c\n@@ -20,6 +20,18 @@ static char *server_capabilities_v1;\n static struct strvec server_capabilities_v2 = STRVEC_INIT;\n static const char *next_server_feature_value(const char *feature, int *len, int *offset);\n \n+/*\n+ * Ensure the connection child (ssh, proxy, or local git) is reaped on\n+ * any exit path, mirroring the transport-helper.c atexit pattern.\n+ */\n+static struct child_process *conn_to_reap;\n+\n+static void cleanup_conn_on_exit(void)\n+{\n+\tif (conn_to_reap)\n+\t\tfinish_command(conn_to_reap);\n+}\n+\n static int check_ref(const char *name, unsigned int flags)\n {\n \tif (!flags)\n@@ -991,6 +1003,8 @@ static struct child_process *git_proxy_connect(int fd[2], char *host)\n \tproxy->out = -1;\n \tif (start_command(proxy))\n \t\tdie(_(\"cannot start proxy %s\"), git_proxy_command);\n+\tconn_to_reap = proxy;\n+\tatexit(cleanup_conn_on_exit);\n \tfd[0] = proxy->out; /* read from proxy stdout */\n \tfd[1] = proxy->in;  /* write to proxy stdin */\n \treturn proxy;\n@@ -1449,6 +1463,8 @@ struct child_process *git_connect(int fd[2], const char *url,\n \n \t\tif (start_command(conn))\n \t\t\tdie(_(\"unable to fork\"));\n+\t\tconn_to_reap = conn;\n+\t\tatexit(cleanup_conn_on_exit);\n \n \t\tfd[0] = conn->out; /* read from child's stdout */\n \t\tfd[1] = conn->in;  /* write to child's stdin */\n@@ -1466,6 +1482,7 @@ int finish_connect(struct child_process *conn)\n \t\treturn 0;\n \n \tcode = finish_command(conn);\n+\tconn_to_reap = NULL;\n \tfree(conn);\n \treturn code;\n }\ndiff --git a/transport-helper.c b/transport-helper.c\nindex e95267a4a..cdfd40dfc 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -17,6 +17,14 @@\n \n static int debug;\n \n+static struct child_process *helper_to_reap;\n+\n+static void cleanup_helper_on_exit(void)\n+{\n+\tif (helper_to_reap)\n+\t\tfinish_command(helper_to_reap);\n+}\n+\n struct helper_data {\n \tconst char *name;\n \tstruct child_process *helper;\n@@ -147,6 +155,8 @@ static struct child_process *get_helper(struct transport *transport)\n \t\texit(code);\n \n \tdata->helper = helper;\n+\thelper_to_reap = helper;\n+\tatexit(cleanup_helper_on_exit);\n \tdata->no_disconnect_req = 0;\n \trefspec_init(&data->rs, REFSPEC_FETCH);\n \n@@ -249,6 +259,7 @@ static int disconnect_helper(struct transport *transport)\n \t\tclose(data->helper->out);\n \t\tfclose(data->out);\n \t\tres = finish_command(data->helper);\n+\t\thelper_to_reap = NULL;\n \t\tFREE_AND_NULL(data->helper);\n \t}\n \treturn res;\n-- \n2.43.0\n\n"},{"id":"536855","messageId":"92e33f7c-f45f-4f5c-9d51-83ef6232364b@app.fastmail.com","threadId":"65060","inReplyTo":"20260223165147.3294516-2-cshung@gmail.com","subject":"Re: [PATCH 1/1] transport-helper, connect: add atexit handler to reap children on abnormal exit","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2026-02-23T17:14:58Z","receivedAt":"2026-02-23T17:15:44Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Mon, Feb 23, 2026, at 17:51, Andrew Au wrote:\n> From: Andrew Au <3410332+cshung@users.noreply.github.com>\n\nThis email here,\n\n>[snip]\n>\n> Signed-off-by: Andrew Au <cshung@gmail.com>\n\nhas to match with the email here. Or probably vice versa since a real\nemail address is better than a GitHub noreply email in this context.\n\nMaybe this was just an automatic don’t-use-my-real-email-address thing\nthat GitHub did?\n\n> ---\n>[snip]\n"},{"id":"536865","messageId":"CAGVkMb5cO9_7fvVQOLNSiznZHcTkGA8fw2q3JFnTEnvNZzMwXw@mail.gmail.com","threadId":"65060","inReplyTo":"92e33f7c-f45f-4f5c-9d51-83ef6232364b@app.fastmail.com","subject":"Re: [PATCH 1/1] transport-helper, connect: add atexit handler to reap children on abnormal exit","fromName":"Andrew Au","fromEmail":"cshung@gmail.com","sentAt":"2026-02-23T18:12:02Z","receivedAt":"2026-02-23T18:12:14Z","isPatch":true,"sender":{"key":"cshung@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3410332?v=4"},"body":"Yes, this is forced by the GitHub don't use my real email address\noption. I won't be allowed to push if I used my real address there.\n\nI can change the commit author to use my real email address if that's\npreferred for upstream.\n\nOn Mon, Feb 23, 2026 at 9:15 AM Kristoffer Haugsbakk\n<kristofferhaugsbakk@fastmail.com> wrote:\n>\n> On Mon, Feb 23, 2026, at 17:51, Andrew Au wrote:\n> > From: Andrew Au <3410332+cshung@users.noreply.github.com>\n>\n> This email here,\n>\n> >[snip]\n> >\n> > Signed-off-by: Andrew Au <cshung@gmail.com>\n>\n> has to match with the email here. Or probably vice versa since a real\n> email address is better than a GitHub noreply email in this context.\n>\n> Maybe this was just an automatic don’t-use-my-real-email-address thing\n> that GitHub did?\n>\n> > ---\n> >[snip]\n"},{"id":"538609","messageId":"20260311142021.3464789-1-cshung@gmail.com","threadId":"65060","inReplyTo":"20260223165147.3294516-1-cshung@gmail.com","subject":"[PATCH v2] transport-helper, connect: add atexit handler to reap children on abnormal exit","fromName":"Andrew Au","fromEmail":"cshung@gmail.com","sentAt":"2026-03-11T14:20:21Z","receivedAt":"2026-03-11T14:24:15Z","isPatch":true,"sender":{"key":"cshung@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3410332?v=4"},"body":"When git exits via exit(128) on transport errors, child processes\n(git-remote-https, ssh, proxy) are never waited on because the normal\ncleanup paths (disconnect_helper, finish_connect) are bypassed. When\ngit is PID 1 in a container, these un-reaped children become zombies.\n\nRegister atexit handlers in both transport-helper.c and connect.c to\nensure children are reaped on any exit path. Clear the handlers on the\nnormal cleanup paths to avoid double-waiting.\n\nSigned-off-by: Andrew Au <cshung@gmail.com>\n---\n connect.c          | 17 +++++++++++++++++\n transport-helper.c | 11 +++++++++++\n 2 files changed, 28 insertions(+)\n\ndiff --git a/connect.c b/connect.c\nindex eef752f14..322b1f816 100644\n--- a/connect.c\n+++ b/connect.c\n@@ -20,6 +20,18 @@ static char *server_capabilities_v1;\n static struct strvec server_capabilities_v2 = STRVEC_INIT;\n static const char *next_server_feature_value(const char *feature, int *len, int *offset);\n \n+/*\n+ * Ensure the connection child (ssh, proxy, or local git) is reaped on\n+ * any exit path, mirroring the transport-helper.c atexit pattern.\n+ */\n+static struct child_process *conn_to_reap;\n+\n+static void cleanup_conn_on_exit(void)\n+{\n+\tif (conn_to_reap)\n+\t\tfinish_command(conn_to_reap);\n+}\n+\n static int check_ref(const char *name, unsigned int flags)\n {\n \tif (!flags)\n@@ -991,6 +1003,8 @@ static struct child_process *git_proxy_connect(int fd[2], char *host)\n \tproxy->out = -1;\n \tif (start_command(proxy))\n \t\tdie(_(\"cannot start proxy %s\"), git_proxy_command);\n+\tconn_to_reap = proxy;\n+\tatexit(cleanup_conn_on_exit);\n \tfd[0] = proxy->out; /* read from proxy stdout */\n \tfd[1] = proxy->in;  /* write to proxy stdin */\n \treturn proxy;\n@@ -1449,6 +1463,8 @@ struct child_process *git_connect(int fd[2], const char *url,\n \n \t\tif (start_command(conn))\n \t\t\tdie(_(\"unable to fork\"));\n+\t\tconn_to_reap = conn;\n+\t\tatexit(cleanup_conn_on_exit);\n \n \t\tfd[0] = conn->out; /* read from child's stdout */\n \t\tfd[1] = conn->in;  /* write to child's stdin */\n@@ -1466,6 +1482,7 @@ int finish_connect(struct child_process *conn)\n \t\treturn 0;\n \n \tcode = finish_command(conn);\n+\tconn_to_reap = NULL;\n \tfree(conn);\n \treturn code;\n }\ndiff --git a/transport-helper.c b/transport-helper.c\nindex e95267a4a..cdfd40dfc 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -17,6 +17,14 @@\n \n static int debug;\n \n+static struct child_process *helper_to_reap;\n+\n+static void cleanup_helper_on_exit(void)\n+{\n+\tif (helper_to_reap)\n+\t\tfinish_command(helper_to_reap);\n+}\n+\n struct helper_data {\n \tconst char *name;\n \tstruct child_process *helper;\n@@ -147,6 +155,8 @@ static struct child_process *get_helper(struct transport *transport)\n \t\texit(code);\n \n \tdata->helper = helper;\n+\thelper_to_reap = helper;\n+\tatexit(cleanup_helper_on_exit);\n \tdata->no_disconnect_req = 0;\n \trefspec_init(&data->rs, REFSPEC_FETCH);\n \n@@ -249,6 +259,7 @@ static int disconnect_helper(struct transport *transport)\n \t\tclose(data->helper->out);\n \t\tfclose(data->out);\n \t\tres = finish_command(data->helper);\n+\t\thelper_to_reap = NULL;\n \t\tFREE_AND_NULL(data->helper);\n \t}\n \treturn res;\n-- \n2.43.0\n\n"},{"id":"538645","messageId":"xmqqsea6p7st.fsf@gitster.g","threadId":"65060","inReplyTo":"20260311142021.3464789-1-cshung@gmail.com","subject":"Re: [PATCH v2] transport-helper, connect: add atexit handler to reap children on abnormal exit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-11T17:58:26Z","receivedAt":"2026-03-11T17:58:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andrew Au <cshung@gmail.com> writes:\n\n> When git exits via exit(128) on transport errors, child processes\n> (git-remote-https, ssh, proxy) are never waited on because the normal\n> cleanup paths (disconnect_helper, finish_connect) are bypassed. When\n> git is PID 1 in a container, these un-reaped children become zombies.\n\nCould you tell me more about the real use case behind such a set-up.\n\nThese children become zombies, and then what will be done to the\ncontainer that lost the \"git\" process, running of which presumably\nwas the primary reason why the container was brought up in the first\nplace?  Wouldn't these zombies go away when the container that\nfinished its sole purpose of running \"git\" gets dismantled?\n\nThanks.\n"},{"id":"538652","messageId":"CAGVkMb6M2buc5zS+SFfYa6LLs7fN369MrVagETVg0U_PN7njOg@mail.gmail.com","threadId":"65060","inReplyTo":"xmqqsea6p7st.fsf@gitster.g","subject":"Re: [PATCH v2] transport-helper, connect: add atexit handler to reap children on abnormal exit","fromName":"Andrew Au","fromEmail":"cshung@gmail.com","sentAt":"2026-03-11T18:19:54Z","receivedAt":"2026-03-11T18:20:07Z","isPatch":true,"sender":{"key":"cshung@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3410332?v=4"},"body":"Thank you for the feedback.\n\nThe use case is a long-running service running as PID 1 inside a\ncontainer. The service continuously spawns git to detect repository\nchanges — it is not a one-shot container where git itself is the\nprimary process. Because the service is meant to stay alive\nindefinitely, any zombies git leaves behind accumulate over time\nrather than being cleaned up when the container exits.\n\nIn my specific case, I observed over 6,500 zombie processes before\nidentifying this as the root cause. The blog post linked in the cover\nletter documents the investigation in detail.\n\nThe fix ensures git cleans up its own children on abnormal exit paths,\nwhich is the right behavior regardless of whether the parent is PID 1\nor not.\n\nOn Wed, Mar 11, 2026 at 10:58 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Andrew Au <cshung@gmail.com> writes:\n>\n> > When git exits via exit(128) on transport errors, child processes\n> > (git-remote-https, ssh, proxy) are never waited on because the normal\n> > cleanup paths (disconnect_helper, finish_connect) are bypassed. When\n> > git is PID 1 in a container, these un-reaped children become zombies.\n>\n> Could you tell me more about the real use case behind such a set-up.\n>\n> These children become zombies, and then what will be done to the\n> container that lost the \"git\" process, running of which presumably\n> was the primary reason why the container was brought up in the first\n> place?  Wouldn't these zombies go away when the container that\n> finished its sole purpose of running \"git\" gets dismantled?\n>\n> Thanks.\n"},{"id":"538656","messageId":"20260311184206.GA1911377@coredump.intra.peff.net","threadId":"65060","inReplyTo":"20260311142021.3464789-1-cshung@gmail.com","subject":"Re: [PATCH v2] transport-helper, connect: add atexit handler to reap children on abnormal exit","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2026-03-11T18:42:06Z","receivedAt":"2026-03-11T18:42:07Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Mar 11, 2026 at 02:20:21PM +0000, Andrew Au wrote:\n\n> +/*\n> + * Ensure the connection child (ssh, proxy, or local git) is reaped on\n> + * any exit path, mirroring the transport-helper.c atexit pattern.\n> + */\n> +static struct child_process *conn_to_reap;\n> +\n> +static void cleanup_conn_on_exit(void)\n> +{\n> +\tif (conn_to_reap)\n> +\t\tfinish_command(conn_to_reap);\n> +}\n\nThis waits for the command to exit. Are we sure it will always do so,\nand it won't sometimes be waiting on us to do something (like close a\npipe that is feeding it)? If not, then we can get deadlocks.\n\nI think you actually want to kill(), then wait. There is already support\nfor this in run-command.[ch]. You just need to set the clean_on_exit\nflag of the child_process struct.\n\nI actually wonder if clean_on_exit should become the default behavior.\nIt should be rare for our subprocesses to outlive us. Commit afe19ff7b5\n(run-command: optionally kill children on exit, 2012-01-07) mentions the\npager, but I don't think that was true even back then (we wait around\nfor the pager to finish). There are a few cases where we spawn daemon\nprograms, which would need to be marked as survivable. I think mostly we\nhave not looked into it because somebody would have to look at each\nrun_command() callsite.\n\nAnyway, that is a bit of a tangent. I think it would be safe to mark the\nspots in this patch as clean_on_exit.\n\n-Peff\n"},{"id":"538668","messageId":"xmqqfr66nol8.fsf@gitster.g","threadId":"65060","inReplyTo":"CAGVkMb6M2buc5zS+SFfYa6LLs7fN369MrVagETVg0U_PN7njOg@mail.gmail.com","subject":"Re: [PATCH v2] transport-helper, connect: add atexit handler to reap children on abnormal exit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-11T19:38:43Z","receivedAt":"2026-03-11T19:38:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andrew Au <cshung@gmail.com> writes:\n\n> Thank you for the feedback.\n>\n> The use case is a long-running service running as PID 1 inside a\n> container. The service continuously spawns git to detect repository\n> changes — it is not a one-shot container where git itself is the\n> primary process. Because the service is meant to stay alive\n> indefinitely, any zombies git leaves behind accumulate over time\n> rather than being cleaned up when the container exits.\n\nWait, the proposed log message said \"Git is the pid 1\", and now the\nabove is \"there is some long-running service that runs Git\".  Which\none is it?  Wouldn't that long-running service be the right process\nthat should reap these unwaited children?  Or is \"git\" used as that\nlong-running service somehow?\n\n\n"},{"id":"538680","messageId":"abHbddcYKpW4hlMz@fruit.crustytoothpaste.net","threadId":"65060","inReplyTo":"20260223165147.3294516-1-cshung@gmail.com","subject":"Re: [PATCH 0/1] Fix zombie children when git is PID 1 in containers","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2026-03-11T21:15:33Z","receivedAt":"2026-03-11T21:15:35Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2026-02-23 at 16:51:46, Andrew Au wrote:\n> When git runs as PID 1 inside a container and exits via exit(128) on\n> transport errors, child processes (git-remote-https, ssh, proxy) are\n> never waited on because the normal cleanup paths (disconnect_helper,\n> finish_connect) are bypassed. Since PID 1 has no parent to reap its\n> children, these become zombies that persist for the container's lifetime.\n> \n> This patch registers atexit handlers in transport-helper.c and connect.c\n> to ensure children are reaped on any exit path, and clears them on the\n> normal cleanup paths to avoid double-waiting.\n> \n> Blog post with detailed investigation: https://cshung.github.io/posts/zombie-git/\n\nUsually people use a tool like tini as PID 1 in containers, which allows\nthat process to handle process reaping while still exec'ing the normal\nGit or other command.  It's the case that _most_ processes are not\ndesigned to run properly as process 1 in a container or otherwise, so\na specialized init-capable helper program is usually a good idea.\n\nEven with this series to fix some of the process reaping problems, I\nexpect you'll find other cases in Git where we don't always reap\nprocesses correctly as well, so an init helper would still be useful.\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"},{"id":"538802","messageId":"CAGVkMb6eBWWo1bd_xLx6K9qAvhORVJtkoiFX+Z9X3sdYTcU+Uw@mail.gmail.com","threadId":"65060","inReplyTo":"abHbddcYKpW4hlMz@fruit.crustytoothpaste.net","subject":"Re: [PATCH 0/1] Fix zombie children when git is PID 1 in containers","fromName":"Andrew Au","fromEmail":"cshung@gmail.com","sentAt":"2026-03-12T19:40:34Z","receivedAt":"2026-03-12T19:40:46Z","isPatch":true,"sender":{"key":"cshung@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3410332?v=4"},"body":"Thank you for the suggestion. You are right that tini is a common and\nreasonable approach, and I will look into using it regardless.\n\nThat said, I think fixing git to clean up its own children is still\nworthwhile — processes should not leave zombies on abnormal exit paths\nwhether or not an init helper is present. The two approaches are\ncomplementary rather than mutually exclusive.\n\nOn Wed, Mar 11, 2026 at 2:15 PM brian m. carlson\n<sandals@crustytoothpaste.net> wrote:\n>\n> On 2026-02-23 at 16:51:46, Andrew Au wrote:\n> > When git runs as PID 1 inside a container and exits via exit(128) on\n> > transport errors, child processes (git-remote-https, ssh, proxy) are\n> > never waited on because the normal cleanup paths (disconnect_helper,\n> > finish_connect) are bypassed. Since PID 1 has no parent to reap its\n> > children, these become zombies that persist for the container's lifetime.\n> >\n> > This patch registers atexit handlers in transport-helper.c and connect.c\n> > to ensure children are reaped on any exit path, and clears them on the\n> > normal cleanup paths to avoid double-waiting.\n> >\n> > Blog post with detailed investigation: https://cshung.github.io/posts/zombie-git/\n>\n> Usually people use a tool like tini as PID 1 in containers, which allows\n> that process to handle process reaping while still exec'ing the normal\n> Git or other command.  It's the case that _most_ processes are not\n> designed to run properly as process 1 in a container or otherwise, so\n> a specialized init-capable helper program is usually a good idea.\n>\n> Even with this series to fix some of the process reaping problems, I\n> expect you'll find other cases in Git where we don't always reap\n> processes correctly as well, so an init helper would still be useful.\n> --\n> brian m. carlson (they/them)\n> Toronto, Ontario, CA\n"},{"id":"538805","messageId":"20260312195813.4006430-1-cshung@gmail.com","threadId":"65060","inReplyTo":"20260311184206.GA1911377@coredump.intra.peff.net","subject":"[PATCH v3] transport-helper, connect: use clean_on_exit to reap children on abnormal exit","fromName":"Andrew Au","fromEmail":"cshung@gmail.com","sentAt":"2026-03-12T19:55:59Z","receivedAt":"2026-03-12T19:59:59Z","isPatch":true,"sender":{"key":"cshung@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3410332?v=4"},"body":"When a long-running service (e.g., a source indexer) runs as PID 1\ninside a container and repeatedly spawns git, git may in turn spawn\nchild processes such as git-remote-https or ssh. If git exits abnormally\n(e.g., via exit(128) on a transport error), the normal cleanup paths\n(disconnect_helper, finish_connect) are bypassed, and these children are\nnever waited on. The children are reparented to PID 1, which does not\nreap them, so they accumulate as zombies over time.\n\nSet clean_on_exit and wait_after_clean on child_process structs in both\ntransport-helper.c and connect.c so that the existing run-command\ncleanup infrastructure handles reaping on any exit path. This avoids\nrolling custom atexit handlers that call finish_command(), which could\ndeadlock if the child is blocked waiting for the parent to close a pipe.\n\nThe clean_on_exit mechanism sends SIGTERM first, then waits, ensuring\nthe child terminates promptly. It also handles signal-based exits, not\njust atexit.\n\nSigned-off-by: Andrew Au <cshung@gmail.com>\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>\n---\nThanks to Jeff King for suggesting the clean_on_exit approach,\nwhich is simpler and avoids potential deadlocks from the atexit\nhandler in v2. Also thanks to Junio for catching the inaccurate\ndescription of the PID 1 scenario.\n\n connect.c          | 4 ++++\n transport-helper.c | 2 ++\n 2 files changed, 6 insertions(+)\n\ndiff --git a/connect.c b/connect.c\nindex eef752f14..5039adca7 100644\n--- a/connect.c\n+++ b/connect.c\n@@ -989,6 +989,8 @@ static struct child_process *git_proxy_connect(int fd[2], char *host)\n \tstrvec_push(&proxy->args, port);\n \tproxy->in = -1;\n \tproxy->out = -1;\n+\tproxy->clean_on_exit = 1;\n+\tproxy->wait_after_clean = 1;\n \tif (start_command(proxy))\n \t\tdie(_(\"cannot start proxy %s\"), git_proxy_command);\n \tfd[0] = proxy->out; /* read from proxy stdout */\n@@ -1447,6 +1449,8 @@ struct child_process *git_connect(int fd[2], const char *url,\n \t\t}\n \t\tstrvec_push(&conn->args, cmd.buf);\n \n+\t\tconn->clean_on_exit = 1;\n+\t\tconn->wait_after_clean = 1;\n \t\tif (start_command(conn))\n \t\t\tdie(_(\"unable to fork\"));\n \ndiff --git a/transport-helper.c b/transport-helper.c\nindex e95267a4a..6633a999b 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -140,6 +140,8 @@ static struct child_process *get_helper(struct transport *transport)\n \n \thelper->trace2_child_class = helper->args.v[0]; /* \"remote-<name>\" */\n \n+\thelper->clean_on_exit = 1;\n+\thelper->wait_after_clean = 1;\n \tcode = start_command(helper);\n \tif (code < 0 && errno == ENOENT)\n \t\tdie(_(\"unable to find remote helper for '%s'\"), data->name);\n-- \n2.43.0\n\n"},{"id":"538815","messageId":"20260312204023.GB2552877@coredump.intra.peff.net","threadId":"65060","inReplyTo":"20260312195813.4006430-1-cshung@gmail.com","subject":"Re: [PATCH v3] transport-helper, connect: use clean_on_exit to reap children on abnormal exit","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2026-03-12T20:40:23Z","receivedAt":"2026-03-12T20:40:25Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Mar 12, 2026 at 07:55:59PM +0000, Andrew Au wrote:\n\n> Set clean_on_exit and wait_after_clean on child_process structs in both\n> transport-helper.c and connect.c so that the existing run-command\n> cleanup infrastructure handles reaping on any exit path. This avoids\n> rolling custom atexit handlers that call finish_command(), which could\n> deadlock if the child is blocked waiting for the parent to close a pipe.\n\nAh, right, I forgot about wait_after_clean when I suggested\nclean_on_exit. Yes, you definitely want both here.\n\n> The clean_on_exit mechanism sends SIGTERM first, then waits, ensuring\n> the child terminates promptly. It also handles signal-based exits, not\n> just atexit.\n> \n> Signed-off-by: Andrew Au <cshung@gmail.com>\n> \n> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>\n\nI don't know if we have established a pattern in the project for\nAI-assisted work (and whether it is worth marking at all, and if so, if\nco-author is the right way to do so). But if we are going to, usually\nthe trailers would all be together, with no blank line in between.\n\n>  connect.c          | 4 ++++\n>  transport-helper.c | 2 ++\n>  2 files changed, 6 insertions(+)\n\nThe changes here are all a trivial implementation of the idea. So if the\nidea is good (and I think it is), then the patch looks correct to me.\n\n-Peff\n"},{"id":"538816","messageId":"20260312204142.GA2553488@coredump.intra.peff.net","threadId":"65060","inReplyTo":"20260312204023.GB2552877@coredump.intra.peff.net","subject":"Re: [PATCH v3] transport-helper, connect: use clean_on_exit to reap children on abnormal exit","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2026-03-12T20:41:42Z","receivedAt":"2026-03-12T20:41:44Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Mar 12, 2026 at 04:40:24PM -0400, Jeff King wrote:\n\n> > Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>\n> \n> I don't know if we have established a pattern in the project for\n> AI-assisted work (and whether it is worth marking at all, and if so, if\n> co-author is the right way to do so). But if we are going to, usually\n> the trailers would all be together, with no blank line in between.\n\nTrailers aside, we definitely don't want it in the mail's cc headers, as\nthat address bounces (unsurprisingly).\n\n-Peff\n"},{"id":"538817","messageId":"xmqqzf4cbwop.fsf@gitster.g","threadId":"65060","inReplyTo":"20260312195813.4006430-1-cshung@gmail.com","subject":"Re: [PATCH v3] transport-helper, connect: use clean_on_exit to reap children on abnormal exit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-12T20:49:10Z","receivedAt":"2026-03-12T20:49:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andrew Au <cshung@gmail.com> writes:\n\n> When a long-running service (e.g., a source indexer) runs as PID 1\n> inside a container and repeatedly spawns git, git may in turn spawn\n> child processes such as git-remote-https or ssh. If git exits abnormally\n> (e.g., via exit(128) on a transport error), the normal cleanup paths\n> (disconnect_helper, finish_connect) are bypassed, and these children are\n> never waited on. The children are reparented to PID 1, which does not\n> reap them, so they accumulate as zombies over time.\n>\n> Set clean_on_exit and wait_after_clean on child_process structs in both\n> transport-helper.c and connect.c so that the existing run-command\n> cleanup infrastructure handles reaping on any exit path. This avoids\n> rolling custom atexit handlers that call finish_command(), which could\n> deadlock if the child is blocked waiting for the parent to close a pipe.\n>\n> The clean_on_exit mechanism sends SIGTERM first, then waits, ensuring\n> the child terminates promptly. It also handles signal-based exits, not\n> just atexit.\n>\n> Signed-off-by: Andrew Au <cshung@gmail.com>\n>\n> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>\n\nDo not throw a blank line inside your trailer block.  In addition,\nwe do not want somebody who cannot stand beind the patch and certify\nDCO listed there.  See also SubmittingPatches::[ai].\n\n> Thanks to Jeff King for suggesting the clean_on_exit approach,\n> which is simpler and avoids potential deadlocks from the atexit\n> handler in v2. Also thanks to Junio for catching the inaccurate\n> description of the PID 1 scenario.\n\nThis version looks quite simple and clean, taking advantage of\nexisting machinery to clean these processes up.\n\n>  connect.c          | 4 ++++\n>  transport-helper.c | 2 ++\n>  2 files changed, 6 insertions(+)\n>\n> diff --git a/connect.c b/connect.c\n> index eef752f14..5039adca7 100644\n> --- a/connect.c\n> +++ b/connect.c\n> @@ -989,6 +989,8 @@ static struct child_process *git_proxy_connect(int fd[2], char *host)\n>  \tstrvec_push(&proxy->args, port);\n>  \tproxy->in = -1;\n>  \tproxy->out = -1;\n> +\tproxy->clean_on_exit = 1;\n> +\tproxy->wait_after_clean = 1;\n>  \tif (start_command(proxy))\n>  \t\tdie(_(\"cannot start proxy %s\"), git_proxy_command);\n>  \tfd[0] = proxy->out; /* read from proxy stdout */\n> @@ -1447,6 +1449,8 @@ struct child_process *git_connect(int fd[2], const char *url,\n>  \t\t}\n>  \t\tstrvec_push(&conn->args, cmd.buf);\n>  \n> +\t\tconn->clean_on_exit = 1;\n> +\t\tconn->wait_after_clean = 1;\n>  \t\tif (start_command(conn))\n>  \t\t\tdie(_(\"unable to fork\"));\n>  \n> diff --git a/transport-helper.c b/transport-helper.c\n> index e95267a4a..6633a999b 100644\n> --- a/transport-helper.c\n> +++ b/transport-helper.c\n> @@ -140,6 +140,8 @@ static struct child_process *get_helper(struct transport *transport)\n>  \n>  \thelper->trace2_child_class = helper->args.v[0]; /* \"remote-<name>\" */\n>  \n> +\thelper->clean_on_exit = 1;\n> +\thelper->wait_after_clean = 1;\n>  \tcode = start_command(helper);\n>  \tif (code < 0 && errno == ENOENT)\n>  \t\tdie(_(\"unable to find remote helper for '%s'\"), data->name);\n"},{"id":"538819","messageId":"20260312214945.4050010-1-cshung@gmail.com","threadId":"65060","inReplyTo":"20260311184206.GA1911377@coredump.intra.peff.net","subject":"[PATCH v4] transport-helper, connect: use clean_on_exit to reap children on abnormal exit","fromName":"Andrew Au","fromEmail":"cshung@gmail.com","sentAt":"2026-03-12T21:49:37Z","receivedAt":"2026-03-12T21:50:05Z","isPatch":true,"sender":{"key":"cshung@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3410332?v=4"},"body":"When a long-running service (e.g., a source indexer) runs as PID 1\ninside a container and repeatedly spawns git, git may in turn spawn\nchild processes such as git-remote-https or ssh. If git exits abnormally\n(e.g., via exit(128) on a transport error), the normal cleanup paths\n(disconnect_helper, finish_connect) are bypassed, and these children are\nnever waited on. The children are reparented to PID 1, which does not\nreap them, so they accumulate as zombies over time.\n\nSet clean_on_exit and wait_after_clean on child_process structs in both\ntransport-helper.c and connect.c so that the existing run-command\ncleanup infrastructure handles reaping on any exit path. This avoids\nrolling custom atexit handlers that call finish_command(), which could\ndeadlock if the child is blocked waiting for the parent to close a pipe.\n\nThe clean_on_exit mechanism sends SIGTERM first, then waits, ensuring\nthe child terminates promptly. It also handles signal-based exits, not\njust atexit.\n\nSigned-off-by: Andrew Au <cshung@gmail.com>\n---\n connect.c          | 4 ++++\n transport-helper.c | 2 ++\n 2 files changed, 6 insertions(+)\n\ndiff --git a/connect.c b/connect.c\nindex eef752f14..5039adca7 100644\n--- a/connect.c\n+++ b/connect.c\n@@ -989,6 +989,8 @@ static struct child_process *git_proxy_connect(int fd[2], char *host)\n \tstrvec_push(&proxy->args, port);\n \tproxy->in = -1;\n \tproxy->out = -1;\n+\tproxy->clean_on_exit = 1;\n+\tproxy->wait_after_clean = 1;\n \tif (start_command(proxy))\n \t\tdie(_(\"cannot start proxy %s\"), git_proxy_command);\n \tfd[0] = proxy->out; /* read from proxy stdout */\n@@ -1447,6 +1449,8 @@ struct child_process *git_connect(int fd[2], const char *url,\n \t\t}\n \t\tstrvec_push(&conn->args, cmd.buf);\n \n+\t\tconn->clean_on_exit = 1;\n+\t\tconn->wait_after_clean = 1;\n \t\tif (start_command(conn))\n \t\t\tdie(_(\"unable to fork\"));\n \ndiff --git a/transport-helper.c b/transport-helper.c\nindex e95267a4a..6633a999b 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -140,6 +140,8 @@ static struct child_process *get_helper(struct transport *transport)\n \n \thelper->trace2_child_class = helper->args.v[0]; /* \"remote-<name>\" */\n \n+\thelper->clean_on_exit = 1;\n+\thelper->wait_after_clean = 1;\n \tcode = start_command(helper);\n \tif (code < 0 && errno == ENOENT)\n \t\tdie(_(\"unable to find remote helper for '%s'\"), data->name);\n-- \n2.43.0\n\n"},{"id":"538821","messageId":"xmqqsea4aen2.fsf@gitster.g","threadId":"65060","inReplyTo":"20260312214945.4050010-1-cshung@gmail.com","subject":"Re: [PATCH v4] transport-helper, connect: use clean_on_exit to reap children on abnormal exit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-12T22:04:17Z","receivedAt":"2026-03-12T22:04:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andrew Au <cshung@gmail.com> writes:\n\n> When a long-running service (e.g., a source indexer) runs as PID 1\n> inside a container and repeatedly spawns git, git may in turn spawn\n> child processes such as git-remote-https or ssh. If git exits abnormally\n> (e.g., via exit(128) on a transport error), the normal cleanup paths\n> (disconnect_helper, finish_connect) are bypassed, and these children are\n> never waited on. The children are reparented to PID 1, which does not\n> reap them, so they accumulate as zombies over time.\n>\n> Set clean_on_exit and wait_after_clean on child_process structs in both\n> transport-helper.c and connect.c so that the existing run-command\n> cleanup infrastructure handles reaping on any exit path. This avoids\n> rolling custom atexit handlers that call finish_command(), which could\n> deadlock if the child is blocked waiting for the parent to close a pipe.\n>\n> The clean_on_exit mechanism sends SIGTERM first, then waits, ensuring\n> the child terminates promptly. It also handles signal-based exits, not\n> just atexit.\n>\n> Signed-off-by: Andrew Au <cshung@gmail.com>\n> ---\n>  connect.c          | 4 ++++\n>  transport-helper.c | 2 ++\n>  2 files changed, 6 insertions(+)\n\nThanks, queued.\n\n\n>\n> diff --git a/connect.c b/connect.c\n> index eef752f14..5039adca7 100644\n> --- a/connect.c\n> +++ b/connect.c\n> @@ -989,6 +989,8 @@ static struct child_process *git_proxy_connect(int fd[2], char *host)\n>  \tstrvec_push(&proxy->args, port);\n>  \tproxy->in = -1;\n>  \tproxy->out = -1;\n> +\tproxy->clean_on_exit = 1;\n> +\tproxy->wait_after_clean = 1;\n>  \tif (start_command(proxy))\n>  \t\tdie(_(\"cannot start proxy %s\"), git_proxy_command);\n>  \tfd[0] = proxy->out; /* read from proxy stdout */\n> @@ -1447,6 +1449,8 @@ struct child_process *git_connect(int fd[2], const char *url,\n>  \t\t}\n>  \t\tstrvec_push(&conn->args, cmd.buf);\n>  \n> +\t\tconn->clean_on_exit = 1;\n> +\t\tconn->wait_after_clean = 1;\n>  \t\tif (start_command(conn))\n>  \t\t\tdie(_(\"unable to fork\"));\n>  \n> diff --git a/transport-helper.c b/transport-helper.c\n> index e95267a4a..6633a999b 100644\n> --- a/transport-helper.c\n> +++ b/transport-helper.c\n> @@ -140,6 +140,8 @@ static struct child_process *get_helper(struct transport *transport)\n>  \n>  \thelper->trace2_child_class = helper->args.v[0]; /* \"remote-<name>\" */\n>  \n> +\thelper->clean_on_exit = 1;\n> +\thelper->wait_after_clean = 1;\n>  \tcode = start_command(helper);\n>  \tif (code < 0 && errno == ENOENT)\n>  \t\tdie(_(\"unable to find remote helper for '%s'\"), data->name);\n"},{"id":"538976","messageId":"20260314160814.GA918806@coredump.intra.peff.net","threadId":"65060","inReplyTo":"xmqqsea4aen2.fsf@gitster.g","subject":"Re: [PATCH v4] transport-helper, connect: use clean_on_exit to reap children on abnormal exit","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2026-03-14T16:08:14Z","receivedAt":"2026-03-14T16:08:22Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Mar 12, 2026 at 03:04:17PM -0700, Junio C Hamano wrote:\n\n> Thanks, queued.\n\nCuriously this patch seems to cause a failure in one of the CI leak\njobs, but I don't think it's the culprit. See below for a fix and\nexplanation.\n\nI don't know if you want to apply it separately (since it's really a\ntotally different topic) or on top (since it is only the application of\nAndrew's patch which lets us find the problem).\n\n-- >8 --\nSubject: [PATCH] transport: plug leaks in transport_color_config()\n\nWe retrieve config values with repo_config_get_string(), which will\nallocate a new copy of the string for us. But we don't hold on to those\nstrings, since they are just fed to git_config_colorbool() and\ncolor_parse(). But nor do we free them, which means they leak.\n\nWe can fix this by using the \"_tmp\" form of repo_config_get_string(),\nwhich just hands us a pointer directly to the internal storage. This is\nOK for our purposes, since we don't need it to last for longer than our\nparsing calls.\n\nTwo interesting side notes here:\n\n  1. Many types already have a repo_config_get_X() variant that handles\n     this for us (e.g., repo_config_get_bool()). But neither colorbools\n     nor colors themselves have such helpers. We might think about\n     adding them, but converting all callers is a larger task, and out\n     of scope for this fix.\n\n  2. As far as I can tell, this leak has been there since 960786e761\n     (push: colorize errors, 2018-04-21), but wasn't detected by LSan in\n     our test suite. It started triggering when we applied dd3693eb08\n     (transport-helper, connect: use clean_on_exit to reap children on\n     abnormal exit, 2026-03-12) which is mostly unrelated.\n\n     Even weirder, it seems to trigger only with clang (and not gcc),\n     and only with GIT_TEST_DEFAULT_REF_FORMAT=reftable. So I think this\n     is another odd case where the pointers happened to be hanging\n     around in stack memory, but changing the pattern of function calls\n     in nearby code was enough for them to be incidentally overwritten.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n transport.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/transport.c b/transport.c\nindex 107f4fa5dc..2fb4767821 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -54,14 +54,14 @@ static int transport_color_config(void)\n \t\treturn 0;\n \tinitialized = 1;\n \n-\tif (!repo_config_get_string(the_repository, key, &value))\n+\tif (!repo_config_get_string_tmp(the_repository, key, &value))\n \t\ttransport_use_color = git_config_colorbool(key, value);\n \n \tif (!want_color_stderr(transport_use_color))\n \t\treturn 0;\n \n \tfor (size_t i = 0; i < ARRAY_SIZE(keys); i++)\n-\t\tif (!repo_config_get_string(the_repository, keys[i], &value)) {\n+\t\tif (!repo_config_get_string_tmp(the_repository, keys[i], &value)) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(keys[i]);\n \t\t\tif (color_parse(value, transport_colors[i]) < 0)\n-- \n2.53.0.887.g3d5d06adec\n\n"},{"id":"538982","messageId":"xmqqikaywchl.fsf@gitster.g","threadId":"65060","inReplyTo":"20260314160814.GA918806@coredump.intra.peff.net","subject":"Re: [PATCH v4] transport-helper, connect: use clean_on_exit to reap children on abnormal exit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-14T17:24:22Z","receivedAt":"2026-03-14T17:24:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n>   1. Many types already have a repo_config_get_X() variant that handles\n>      this for us (e.g., repo_config_get_bool()). But neither colorbools\n>      nor colors themselves have such helpers. We might think about\n>      adding them, but converting all callers is a larger task, and out\n>      of scope for this fix.\n\nThat certainly is an interesting #leftoverbits project.\n\nThe patch looks good.  Will queue.  Thanks.\n"},{"id":"539158","messageId":"xmqq4imfo6sz.fsf@gitster.g","threadId":"65060","inReplyTo":"20260314160814.GA918806@coredump.intra.peff.net","subject":"Re: [PATCH v4] transport-helper, connect: use clean_on_exit to reap children on abnormal exit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-16T20:31:08Z","receivedAt":"2026-03-16T20:31:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> I don't know if you want to apply it separately (since it's really a\n> totally different topic) or on top (since it is only the application of\n> Andrew's patch which lets us find the problem).\n> ...\n>  transport.c | 4 ++--\n>  1 file changed, 2 insertions(+), 2 deletions(-)\n>\n> diff --git a/transport.c b/transport.c\n> index 107f4fa5dc..2fb4767821 100644\n> --- a/transport.c\n> +++ b/transport.c\n> @@ -54,14 +54,14 @@ static int transport_color_config(void)\n>  \t\treturn 0;\n>  \tinitialized = 1;\n>  \n> -\tif (!repo_config_get_string(the_repository, key, &value))\n> +\tif (!repo_config_get_string_tmp(the_repository, key, &value))\n>  \t\ttransport_use_color = git_config_colorbool(key, value);\n>  \n>  \tif (!want_color_stderr(transport_use_color))\n>  \t\treturn 0;\n>  \n>  \tfor (size_t i = 0; i < ARRAY_SIZE(keys); i++)\n> -\t\tif (!repo_config_get_string(the_repository, keys[i], &value)) {\n> +\t\tif (!repo_config_get_string_tmp(the_repository, keys[i], &value)) {\n>  \t\t\tif (!value)\n>  \t\t\t\treturn config_error_nonbool(keys[i]);\n>  \t\t\tif (color_parse(value, transport_colors[i]) < 0)\n\nRegardless of where it goes, we need to change a bit more, it seems?\n\n    CC transport.o\ntransport.c: In function 'transport_color_config':\ntransport.c:57:62: error: passing argument 3 of 'repo_config_get_string_tmp' from incompatible pointer type [-Wincompatible-pointer-types]\n   57 |         if (!repo_config_get_string_tmp(the_repository, key, &value))\n      |                                                              ^~~~~~\n      |                                                              |\n      |                                                              char **\nIn file included from transport.c:5:\nconfig.h:644:62: note: expected 'const char **' but argument is of type 'char **'\n  644 |                                const char *key, const char **dest);\n      |                                                 ~~~~~~~~~~~~~^~~~\ntransport.c:64:74: error: passing argument 3 of 'repo_config_get_string_tmp' from incompatible pointer type [-Wincompatible-pointer-types]\n   64 |                 if (!repo_config_get_string_tmp(the_repository, keys[i], &value)) {\n      |                                                                          ^~~~~~\n      |                                                                          |\n      |                                                                          char **\nconfig.h:644:62: note: expected 'const char **' but argument is of type 'char **'\n  644 |                                const char *key, const char **dest);\n      |                                                 ~~~~~~~~~~~~~^~~~\ngmake: *** [Makefile:2815: transport.o] Error 1\n\n\nI'll squash an obvious patch in.\n\n transport.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git c/transport.c w/transport.c\nindex 358bc38585..7985b42a74 100644\n--- c/transport.c\n+++ w/transport.c\n@@ -47,7 +47,7 @@ static int transport_color_config(void)\n \t\t\"color.transport.reset\",\n \t\t\"color.transport.rejected\"\n \t}, *key = \"color.transport\";\n-\tchar *value;\n+\tconst char *value;\n \tstatic int initialized;\n \n \tif (initialized)\n"},{"id":"539161","messageId":"20260316211934.GA1042816@coredump.intra.peff.net","threadId":"65060","inReplyTo":"xmqq4imfo6sz.fsf@gitster.g","subject":"Re: [PATCH v4] transport-helper, connect: use clean_on_exit to reap children on abnormal exit","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2026-03-16T21:19:34Z","receivedAt":"2026-03-16T21:19:41Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Mar 16, 2026 at 01:31:08PM -0700, Junio C Hamano wrote:\n\n> Regardless of where it goes, we need to change a bit more, it seems?\n> \n>     CC transport.o\n> transport.c: In function 'transport_color_config':\n> transport.c:57:62: error: passing argument 3 of 'repo_config_get_string_tmp' from incompatible pointer type [-Wincompatible-pointer-types]\n>    57 |         if (!repo_config_get_string_tmp(the_repository, key, &value))\n>       |                                                              ^~~~~~\n>       |                                                              |\n>       |                                                              char **\n\nHuh. Obviously yes, but how did I manage to bungle this so badly?\n\nI _think_ what happened is that I compiled the topic without -Werror,\nbecause of all of the -Wdiscarded-qualifier errors that happen on\n'master' with a recent glibc.\n\nMy integration cycle would have caught it, since I have another topic\nwith fixes for the discarded-qualifier issue. But I hadn't run one yet. ;)\n\nNone of that is important for you, but just wondering if there was\nsomething more subtle going on, or if I just screwed up something as\nsimple as typing 'make'.\n\n> I'll squash an obvious patch in.\n\nYep, that looks good. Thanks for fixing.\n\n-Peff\n"},{"id":"539162","messageId":"xmqqpl53mpqu.fsf@gitster.g","threadId":"65060","inReplyTo":"20260316211934.GA1042816@coredump.intra.peff.net","subject":"Re: [PATCH v4] transport-helper, connect: use clean_on_exit to reap children on abnormal exit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-03-16T21:24:57Z","receivedAt":"2026-03-16T21:25:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> Huh. Obviously yes, but how did I manage to bungle this so badly?\n>\n> I _think_ what happened is that I compiled the topic without -Werror,\n> because of all of the -Wdiscarded-qualifier errors that happen on\n> 'master' with a recent glibc.\n>\n> My integration cycle would have caught it, since I have another topic\n> with fixes for the discarded-qualifier issue. But I hadn't run one yet. ;)\n>\n> None of that is important for you, but just wondering if there was\n> something more subtle going on, or if I just screwed up something as\n> simple as typing 'make'.\n>\n>> I'll squash an obvious patch in.\n>\n> Yep, that looks good. Thanks for fixing.\n\nThanks.  It is probably a good thing that you are using toolchain\nahead of the version I use.  We catch different kind of errors that\nway, even though occasionally we see a gotcha like this one.\n"}]}