git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v7 02/31] path.c: make get_pathname() call sites return const char *

From
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Date
Jul 13, 2014, 04:50 UTC
Message-ID
<1405227068-25506-3-git-send-email-pclouds@gmail.com>
In-Reply-To
<1405227068-25506-1-git-send-email-pclouds@gmail.com>

Before the previous commit, get_pathname returns an array of PATH_MAX length. Even if git_path() and similar functions does not use the whole array, git_path() caller can, in theory.

After the commit, get_pathname() may return a buffer that has just enough room for the returned string and git_path() caller should never write beyond that.

Make git_path(), mkpath() and git_path_submodule() return a const buffer to make sure callers do not write in it at all.

This could have been part of the previous commit, but the "const" conversion is too much distraction from the core changes in path.c.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/checkout.c     | 2 +-
 builtin/clone.c        | 9 +++++----
 builtin/fetch.c        | 5 +++--
 builtin/fsck.c         | 4 ++--
 builtin/receive-pack.c | 2 +-
 builtin/remote.c       | 2 +-
 builtin/repack.c       | 8 +++++---
 cache.h                | 6 +++---
 fast-import.c          | 2 +-
 notes-merge.c          | 6 +++---
 path.c                 | 6 +++---
 refs.c                 | 8 ++++----
 run-command.c          | 4 ++--
 run-command.h          | 2 +-
 sha1_file.c            | 2 +-
 15 files changed, 36 insertions(+), 32 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 463cfee..3abcef1 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -585,7 +585,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
 			if (opts->new_branch_log && !log_all_ref_updates) {
 				int temp;
 				char log_file[PATH_MAX];
-				char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
+				const char *ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
 
 				temp = log_all_ref_updates;
 				log_all_ref_updates = 1;
diff --git a/builtin/clone.c b/builtin/clone.c
index e15ca33..91fac9d 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -289,16 +289,17 @@ static void copy_alternates(struct strbuf *src, struct strbuf *dst,
 	struct strbuf line = STRBUF_INIT;
 
 	while (strbuf_getline(&line, in, '\n') != EOF) {
-		char *abs_path, abs_buf[PATH_MAX];
+		char *abs_path;
 		if (!line.len || line.buf[0] == '#')
 			continue;
 		if (is_absolute_path(line.buf)) {
 			add_to_alternates_file(line.buf);
 			continue;
 		}
-		abs_path = mkpath("%s/objects/%s", src_repo, line.buf);
-		normalize_path_copy(abs_buf, abs_path);
-		add_to_alternates_file(abs_buf);
+		abs_path = mkpathdup("%s/objects/%s", src_repo, line.buf);
+		normalize_path_copy(abs_path, abs_path);
+		add_to_alternates_file(abs_path);
+		free(abs_path);
 	}
 	strbuf_release(&line);
 	fclose(in);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index e8d0cca..9522b1b 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -573,7 +573,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 	struct strbuf note = STRBUF_INIT;
 	const char *what, *kind;
 	struct ref *rm;
-	char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
+	char *url;
+	const char *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
 	int want_status;
 
 	fp = fopen(filename, "a");
@@ -807,7 +808,7 @@ static void check_not_current_branch(struct ref *ref_map)
 
 static int truncate_fetch_head(void)
 {
-	char *filename = git_path("FETCH_HEAD");
+	const char *filename = git_path("FETCH_HEAD");
 	FILE *fp = fopen(filename, "w");
 
 	if (!fp)
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 8aadca1..d414962 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -225,12 +225,12 @@ static void check_unreachable_object(struct object *obj)
 			printf("dangling %s %s\n", typename(obj->type),
 			       sha1_to_hex(obj->sha1));
 		if (write_lost_and_found) {
-			char *filename = git_path("lost-found/%s/%s",
+			const char *filename = git_path("lost-found/%s/%s",
 				obj->type == OBJ_COMMIT ? "commit" : "other",
 				sha1_to_hex(obj->sha1));
 			FILE *f;
 
-			if (safe_create_leading_directories(filename)) {
+			if (safe_create_leading_directories_const(filename)) {
 				error("Could not create lost-found");
 				return;
 			}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 18458e8..ed11e7e 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -599,7 +599,7 @@ static void run_update_post_hook(struct command *commands)
 	int argc;
 	const char **argv;
 	struct child_process proc;
-	char *hook;
+	const char *hook;
 
 	hook = find_hook("post-update");
 	for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
diff --git a/builtin/remote.c b/builtin/remote.c
index a8efe3d..3d99c9c 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -580,7 +580,7 @@ static int migrate_file(struct remote *remote)
 {
 	struct strbuf buf = STRBUF_INIT;
 	int i;
-	char *path = NULL;
+	const char *path = NULL;
 
 	strbuf_addf(&buf, "remote.%s.url", remote->name);
 	for (i = 0; i < remote->url_nr; i++)
diff --git a/builtin/repack.c b/builtin/repack.c
index ff2216a..a64a4a9 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -285,7 +285,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
 	failed = 0;
 	for_each_string_list_item(item, &names) {
 		for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
-			char *fname, *fname_old;
+			const char *fname_old;
+			char *fname;
 			fname = mkpathdup("%s/pack-%s%s", packdir,
 						item->string, exts[ext].name);
 			if (!file_exists(fname)) {
@@ -313,7 +314,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
 	if (failed) {
 		struct string_list rollback_failure = STRING_LIST_INIT_DUP;
 		for_each_string_list_item(item, &rollback) {
-			char *fname, *fname_old;
+			const char *fname_old;
+			char *fname;
 			fname = mkpathdup("%s/%s", packdir, item->string);
 			fname_old = mkpath("%s/old-%s", packdir, item->string);
 			if (rename(fname_old, fname))
@@ -366,7 +368,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
 	/* Remove the "old-" files */
 	for_each_string_list_item(item, &names) {
 		for (ext = 0; ext < ARRAY_SIZE(exts); ext++) {
-			char *fname;
+			const char *fname;
 			fname = mkpath("%s/old-%s%s",
 					packdir,
 					item->string,
diff --git a/cache.h b/cache.h
index 44aa439..707408b 100644
--- a/cache.h
+++ b/cache.h
@@ -682,9 +682,9 @@ extern char *mkpathdup(const char *fmt, ...)
 	__attribute__((format (printf, 1, 2)));
 
 /* Return a statically allocated filename matching the sha1 signature */
-extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern char *git_path_submodule(const char *path, const char *fmt, ...)
+extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern const char *git_path_submodule(const char *path, const char *fmt, ...)
 	__attribute__((format (printf, 2, 3)));
 
 /*
diff --git a/fast-import.c b/fast-import.c
index fa635f7..d9c068b 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -404,7 +404,7 @@ static void dump_marks_helper(FILE *, uintmax_t, struct mark_set *);
 
 static void write_crash_report(const char *err)
 {
-	char *loc = git_path("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
+	const char *loc = git_path("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
 	FILE *rpt = fopen(loc, "w");
 	struct branch *b;
 	unsigned long lu;
diff --git a/notes-merge.c b/notes-merge.c
index fd5fae2..a9e6b15 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -280,7 +280,7 @@ static void check_notes_merge_worktree(struct notes_merge_options *o)
 				    "(%s exists).", git_path("NOTES_MERGE_*"));
 		}
 
-		if (safe_create_leading_directories(git_path(
+		if (safe_create_leading_directories_const(git_path(
 				NOTES_MERGE_WORKTREE "/.test")))
 			die_errno("unable to create directory %s",
 				  git_path(NOTES_MERGE_WORKTREE));
@@ -295,8 +295,8 @@ static void write_buf_to_worktree(const unsigned char *obj,
 				  const char *buf, unsigned long size)
 {
 	int fd;
-	char *path = git_path(NOTES_MERGE_WORKTREE "/%s", sha1_to_hex(obj));
-	if (safe_create_leading_directories(path))
+	const char *path = git_path(NOTES_MERGE_WORKTREE "/%s", sha1_to_hex(obj));
+	if (safe_create_leading_directories_const(path))
 		die_errno("unable to create directory for '%s'", path);
 	if (file_exists(path))
 		die("found existing file at '%s'", path);
diff --git a/path.c b/path.c
index 42ef3af..a3f8826 100644
--- a/path.c
+++ b/path.c
@@ -106,7 +106,7 @@ char *mkpathdup(const char *fmt, ...)
 	return strbuf_detach(&sb, NULL);
 }
 
-char *mkpath(const char *fmt, ...)
+const char *mkpath(const char *fmt, ...)
 {
 	va_list args;
 	struct strbuf *pathname = get_pathname();
@@ -116,7 +116,7 @@ char *mkpath(const char *fmt, ...)
 	return cleanup_path(pathname->buf);
 }
 
-char *git_path(const char *fmt, ...)
+const char *git_path(const char *fmt, ...)
 {
 	struct strbuf *pathname = get_pathname();
 	va_list args;
@@ -152,7 +152,7 @@ void home_config_paths(char **global, char **xdg, char *file)
 	free(to_free);
 }
 
-char *git_path_submodule(const char *path, const char *fmt, ...)
+const char *git_path_submodule(const char *path, const char *fmt, ...)
 {
 	struct strbuf *buf = get_pathname();
 	const char *git_dir;
diff --git a/refs.c b/refs.c
index 82e4842..dbd131e 100644
--- a/refs.c
+++ b/refs.c
@@ -1442,7 +1442,7 @@ static int resolve_gitlink_ref_recursive(struct ref_cache *refs,
 {
 	int fd, len;
 	char buffer[128], *p;
-	char *path;
+	const char *path;
 
 	if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
 		return -1;
@@ -2255,7 +2255,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
 					    const unsigned char *old_sha1,
 					    int flags, int *type_p)
 {
-	char *ref_file;
+	const char *ref_file;
 	const char *orig_refname = refname;
 	struct ref_lock *lock;
 	int last_errno = 0;
@@ -2318,7 +2318,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
 		lock->force_write = 1;
 
  retry:
-	switch (safe_create_leading_directories(ref_file)) {
+	switch (safe_create_leading_directories_const(ref_file)) {
 	case SCLD_OK:
 		break; /* success */
 	case SCLD_VANISHED:
@@ -2756,7 +2756,7 @@ static int rename_tmp_log(const char *newrefname)
 	int attempts_remaining = 4;
 
  retry:
-	switch (safe_create_leading_directories(git_path("logs/%s", newrefname))) {
+	switch (safe_create_leading_directories_const(git_path("logs/%s", newrefname))) {
 	case SCLD_OK:
 		break; /* success */
 	case SCLD_VANISHED:
diff --git a/run-command.c b/run-command.c
index be07d4a..614b8ac 100644
--- a/run-command.c
+++ b/run-command.c
@@ -758,9 +758,9 @@ int finish_async(struct async *async)
 #endif
 }
 
-char *find_hook(const char *name)
+const char *find_hook(const char *name)
 {
-	char *path = git_path("hooks/%s", name);
+	const char *path = git_path("hooks/%s", name);
 	if (access(path, X_OK) < 0)
 		path = NULL;
 
diff --git a/run-command.h b/run-command.h
index ea73de3..890cc95 100644
--- a/run-command.h
+++ b/run-command.h
@@ -48,7 +48,7 @@ int start_command(struct child_process *);
 int finish_command(struct child_process *);
 int run_command(struct child_process *);
 
-extern char *find_hook(const char *name);
+extern const char *find_hook(const char *name);
 LAST_ARG_MUST_BE_NULL
 extern int run_hook_le(const char *const *env, const char *name, ...);
 extern int run_hook_ve(const char *const *env, const char *name, va_list args);
diff --git a/sha1_file.c b/sha1_file.c
index a38854c..9700108 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -402,7 +402,7 @@ void add_to_alternates_file(const char *reference)
 {
 	struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 	int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR);
-	char *alt = mkpath("%s\n", reference);
+	const char *alt = mkpath("%s\n", reference);
 	write_or_die(fd, alt, strlen(alt));
 	if (commit_lock_file(lock))
 		die("could not close alternates file");
-- 
1.9.1.346.ga2b5940
Previous: Nguyễn Thái Ngọc DuyNext: Nguyễn Thái Ngọc Duy
Message 42 of 83 in “Support multiple checkouts”
  1. 00/32 Support multiple checkoutsNguyễn Thái Ngọc Duy, Jul 9, 2014
  2. 01/32 path.c: make get_pathname() return strbuf instead of static bufferNguyễn Thái Ngọc Duy, Jul 9, 2014
  3. 02/32 path.c: make get_pathname() call sites return const char *Nguyễn Thái Ngọc Duy, Jul 9, 2014
  4. 03/32 git_snpath(): retire and replace with strbuf_git_path()Nguyễn Thái Ngọc Duy, Jul 9, 2014
  5. 04/32 path.c: rename vsnpath() to do_git_path()Nguyễn Thái Ngọc Duy, Jul 9, 2014
  6. 05/32 path.c: group git_path(), git_pathdup() and strbuf_git_path() togetherNguyễn Thái Ngọc Duy, Jul 9, 2014
  7. 06/32 setup_git_env: use git_pathdup instead of xmalloc + sprintfNguyễn Thái Ngọc Duy, Jul 9, 2014
  8. 07/32 setup_git_env(): introduce git_path_from_env() helperNguyễn Thái Ngọc Duy, Jul 9, 2014
  9. 08/32 git_path(): be aware of file relocation in $GIT_DIRNguyễn Thái Ngọc Duy, Jul 9, 2014
  10. 09/32 *.sh: respect $GIT_INDEX_FILENguyễn Thái Ngọc Duy, Jul 9, 2014
  11. 10/32 reflog: avoid constructing .lock path with git_pathNguyễn Thái Ngọc Duy, Jul 9, 2014
  12. 11/32 fast-import: use git_path() for accessing .git dir instead of get_git_dir()Nguyễn Thái Ngọc Duy, Jul 9, 2014
  13. 12/32 commit: use SEQ_DIR instead of hardcoding "sequencer"Nguyễn Thái Ngọc Duy, Jul 9, 2014
  14. 13/32 $GIT_COMMON_DIR: a new environment variableNguyễn Thái Ngọc Duy, Jul 9, 2014
  15. 14/32 git-sh-setup.sh: use rev-parse --git-path to get $GIT_DIR/objectsNguyễn Thái Ngọc Duy, Jul 9, 2014
  16. 15/32 *.sh: avoid hardcoding $GIT_DIR/hooks/...Nguyễn Thái Ngọc Duy, Jul 9, 2014
  17. 16/32 git-stash: avoid hardcoding $GIT_DIR/logs/....Nguyễn Thái Ngọc Duy, Jul 9, 2014
  18. 17/32 setup.c: convert is_git_directory() to use strbufNguyễn Thái Ngọc Duy, Jul 9, 2014
  19. 18/32 setup.c: detect $GIT_COMMON_DIR in is_git_directory()Nguyễn Thái Ngọc Duy, Jul 9, 2014
  20. 19/32 setup.c: convert check_repository_format_gently to use strbufNguyễn Thái Ngọc Duy, Jul 9, 2014
  21. 20/32 setup.c: detect $GIT_COMMON_DIR check_repository_format_gently()Nguyễn Thái Ngọc Duy, Jul 9, 2014
  22. 21/32 setup.c: support multi-checkout repo setupNguyễn Thái Ngọc Duy, Jul 9, 2014
  23. 22/32 wrapper.c: wrapper to open a file, fprintf then closeNguyễn Thái Ngọc Duy, Jul 9, 2014
  24. 23/32 use new wrapper write_file() for simple file writingNguyễn Thái Ngọc Duy, Jul 9, 2014
  25. 24/32 checkout: support checking out into a new working directoryNguyễn Thái Ngọc Duy, Jul 9, 2014
  26. 25/32 checkout: clean up half-prepared directories in --to modeNguyễn Thái Ngọc Duy, Jul 9, 2014
  27. 26/32 checkout: detach if the branch is already checked out elsewhereNguyễn Thái Ngọc Duy, Jul 9, 2014
  28. Max KirillovJul 12, 2014
  29. 27/32 prune: strategies for linked checkoutsNguyễn Thái Ngọc Duy, Jul 9, 2014
  30. Eric SunshineJul 9, 2014
  31. 28/32 gc: style change -- no SP before closing bracketNguyễn Thái Ngọc Duy, Jul 9, 2014
  32. Eric SunshineJul 9, 2014
  33. Junio C HamanoJul 14, 2014
  34. 29/32 gc: support prune --reposNguyễn Thái Ngọc Duy, Jul 9, 2014
  35. Eric SunshineJul 9, 2014
  36. 30/32 count-objects: report unused files in $GIT_DIR/repos/...Nguyễn Thái Ngọc Duy, Jul 9, 2014
  37. 31/32 git_path(): keep "info/sparse-checkout" per work-treeNguyễn Thái Ngọc Duy, Jul 9, 2014
  38. 32/32 checkout: don't require a work tree when checking out into a new oneNguyễn Thái Ngọc Duy, Jul 9, 2014
  39. Dennis KaarsemakerJul 11, 2014
  40. 00/31 Support multiple checkoutsNguyễn Thái Ngọc Duy, Jul 13, 2014
  41. 01/31 path.c: make get_pathname() return strbuf instead of static bufferNguyễn Thái Ngọc Duy, Jul 13, 2014
  42. 02/31 path.c: make get_pathname() call sites return const char *Nguyễn Thái Ngọc Duy, Jul 13, 2014
  43. 03/31 git_snpath(): retire and replace with strbuf_git_path()Nguyễn Thái Ngọc Duy, Jul 13, 2014
  44. 04/31 path.c: rename vsnpath() to do_git_path()Nguyễn Thái Ngọc Duy, Jul 13, 2014
  45. 05/31 path.c: group git_path(), git_pathdup() and strbuf_git_path() togetherNguyễn Thái Ngọc Duy, Jul 13, 2014
  46. 06/31 git_path(): be aware of file relocation in $GIT_DIRNguyễn Thái Ngọc Duy, Jul 13, 2014
  47. 07/31 *.sh: respect $GIT_INDEX_FILENguyễn Thái Ngọc Duy, Jul 13, 2014
  48. 08/31 reflog: avoid constructing .lock path with git_pathNguyễn Thái Ngọc Duy, Jul 13, 2014
  49. 09/31 fast-import: use git_path() for accessing .git dir instead of get_git_dir()Nguyễn Thái Ngọc Duy, Jul 13, 2014
  50. 10/31 commit: use SEQ_DIR instead of hardcoding "sequencer"Nguyễn Thái Ngọc Duy, Jul 13, 2014
  51. 11/31 $GIT_COMMON_DIR: a new environment variableNguyễn Thái Ngọc Duy, Jul 13, 2014
  52. Eric SunshineJul 23, 2014
  53. 12/31 git-sh-setup.sh: use rev-parse --git-path to get $GIT_DIR/objectsNguyễn Thái Ngọc Duy, Jul 13, 2014
  54. 13/31 *.sh: avoid hardcoding $GIT_DIR/hooks/...Nguyễn Thái Ngọc Duy, Jul 13, 2014
  55. 14/31 git-stash: avoid hardcoding $GIT_DIR/logs/....Nguyễn Thái Ngọc Duy, Jul 13, 2014
  56. 15/31 setup.c: convert is_git_directory() to use strbufNguyễn Thái Ngọc Duy, Jul 13, 2014
  57. 16/31 setup.c: detect $GIT_COMMON_DIR in is_git_directory()Nguyễn Thái Ngọc Duy, Jul 13, 2014
  58. 17/31 setup.c: convert check_repository_format_gently to use strbufNguyễn Thái Ngọc Duy, Jul 13, 2014
  59. 18/31 setup.c: detect $GIT_COMMON_DIR check_repository_format_gently()Nguyễn Thái Ngọc Duy, Jul 13, 2014
  60. 19/31 setup.c: support multi-checkout repo setupNguyễn Thái Ngọc Duy, Jul 13, 2014
  61. 20/31 wrapper.c: wrapper to open a file, fprintf then closeNguyễn Thái Ngọc Duy, Jul 13, 2014
  62. 21/31 use new wrapper write_file() for simple file writingNguyễn Thái Ngọc Duy, Jul 13, 2014
  63. 22/31 checkout: support checking out into a new working directoryNguyễn Thái Ngọc Duy, Jul 13, 2014
  64. Max KirillovJul 17, 2014
  65. Junio C HamanoJul 17, 2014
  66. Eric SunshineJul 18, 2014
  67. 23/31 checkout: clean up half-prepared directories in --to modeNguyễn Thái Ngọc Duy, Jul 13, 2014
  68. Eric SunshineJul 20, 2014
  69. Eric SunshineJul 21, 2014
  70. Duy NguyenJul 23, 2014
  71. 24/31 checkout: detach if the branch is already checked out elsewhereNguyễn Thái Ngọc Duy, Jul 13, 2014
  72. 25/31 prune: strategies for linked checkoutsNguyễn Thái Ngọc Duy, Jul 13, 2014
  73. Thomas RastJul 18, 2014
  74. Duy NguyenJul 19, 2014
  75. 26/31 gc: style change -- no SP before closing bracketNguyễn Thái Ngọc Duy, Jul 13, 2014
  76. 27/31 gc: factor out gc.pruneexpire parsing codeNguyễn Thái Ngọc Duy, Jul 13, 2014
  77. 28/31 gc: support prune --reposNguyễn Thái Ngọc Duy, Jul 13, 2014
  78. 29/31 count-objects: report unused files in $GIT_DIR/repos/...Nguyễn Thái Ngọc Duy, Jul 13, 2014
  79. 30/31 git_path(): keep "info/sparse-checkout" per work-treeNguyễn Thái Ngọc Duy, Jul 13, 2014
  80. 31/31 checkout: don't require a work tree when checking out into a new oneNguyễn Thái Ngọc Duy, Jul 13, 2014
  81. Junio C HamanoJul 14, 2014
  82. Duy NguyenJul 14, 2014
  83. Junio C HamanoJul 14, 2014

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.