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

[PATCH 1/7] commit: provide a function to find a header in a buffer

From
Jeff King <peff@peff.net>
Date
Jun 18, 2014, 20:27 UTC
Message-ID
<20140618202737.GA23896@sigill.intra.peff.net>
In-Reply-To
<20140618201944.GA23238@sigill.intra.peff.net>

Usually when we parse a commit, we read it line by line and handle each header in a single pass (e.g., in parse_commit and parse_commit_header). Sometimes, however, we only care about extracting a single header. Code in this situation is stuck doing an ad-hoc parse of the commit buffer.

Let's provide a reusable function to locate a header within the commit. The code is modeled after pretty.c's get_header, which is used to extract the encoding.

Since some callers may not have the "struct commit" to go along with the buffer, we drop that parameter. The only thing lost is a warning for truncated commits, but that's OK. This shouldn't happen in practice, and even if it does, there's no particular reason that this function needs to complain about it. It either finds the header it was asked for, or it doesn't (and in the latter case, the caller can complain).

Signed-off-by: Jeff King <peff@peff.net>
---
As noted in the comments below, I punted on extracting
multi-line headers like mergetag, because this function only
returns a pointer. It might make sense to wrap it with a
function to pull out a copy of the header, with continuation
lines connected to each other (that's almost what the static
get_header does, but I didn't make it public exactly because
it doesn't handle continuation lines).

That might be a more natural interface than read_commit_extra_header_lines, which pulls out all headers except ones that are specifically excluded. I haven't looked closely enough. But in either case, that could easily come on top of this.

 commit.c | 23 +++++++++++++++++++++++
 commit.h | 11 +++++++++++
 pretty.c | 33 ++++++---------------------------
 3 files changed, 40 insertions(+), 27 deletions(-)
diff --git a/commit.c b/commit.c
index 11106fb..d04b525 100644
--- a/commit.c
+++ b/commit.c
@@ -1652,3 +1652,26 @@ void print_commit_list(struct commit_list *list,
 		printf(format, sha1_to_hex(list->item->object.sha1));
 	}
 }
+
+const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
+{
+	int key_len = strlen(key);
+	const char *line = msg;
+
+	while (line) {
+		const char *eol = strchrnul(line, '\n'), *next;
+
+		if (line == eol)
+			return NULL;
+		next = *eol ? eol + 1 : NULL;
+
+		if (eol - line > key_len &&
+		    !strncmp(line, key, key_len) &&
+		    line[key_len] == ' ') {
+			*out_len = eol - line - key_len - 1;
+			return line + key_len + 1;
+		}
+		line = next;
+	}
+	return NULL;
+}
diff --git a/commit.h b/commit.h
index 61559a9..7c766e9 100644
--- a/commit.h
+++ b/commit.h
@@ -312,6 +312,17 @@ extern struct commit_extra_header *read_commit_extra_headers(struct commit *, co
 
 extern void free_commit_extra_headers(struct commit_extra_header *extra);
 
+/*
+ * Search the commit object contents given by "msg" for the header "key".
+ * Returns a pointer to the start of the header contents, or NULL. The length
+ * of the header, up to the first newline, is returned via out_len.
+ *
+ * Note that some headers (like mergetag) may be multi-line. It is the caller's
+ * responsibility to parse further in this case!
+ */
+extern const char *find_commit_header(const char *msg, const char *key,
+				      size_t *out_len);
+
 struct merge_remote_desc {
 	struct object *obj; /* the named object, could be a tag */
 	const char *name;
diff --git a/pretty.c b/pretty.c
index cc5b45d..6081750 100644
--- a/pretty.c
+++ b/pretty.c
@@ -548,31 +548,11 @@ static void add_merge_info(const struct pretty_print_context *pp,
 	strbuf_addch(sb, '\n');
 }
 
-static char *get_header(const struct commit *commit, const char *msg,
-			const char *key)
+static char *get_header(const char *msg, const char *key)
 {
-	int key_len = strlen(key);
-	const char *line = msg;
-
-	while (line) {
-		const char *eol = strchrnul(line, '\n'), *next;
-
-		if (line == eol)
-			return NULL;
-		if (!*eol) {
-			warning("malformed commit (header is missing newline): %s",
-				sha1_to_hex(commit->object.sha1));
-			next = NULL;
-		} else
-			next = eol + 1;
-		if (eol - line > key_len &&
-		    !strncmp(line, key, key_len) &&
-		    line[key_len] == ' ') {
-			return xmemdupz(line + key_len + 1, eol - line - key_len - 1);
-		}
-		line = next;
-	}
-	return NULL;
+	size_t len;
+	const char *v = find_commit_header(msg, key, &len);
+	return v ? xmemdupz(v, len) : NULL;
 }
 
 static char *replace_encoding_header(char *buf, const char *encoding)
@@ -618,11 +598,10 @@ const char *logmsg_reencode(const struct commit *commit,
 
 	if (!output_encoding || !*output_encoding) {
 		if (commit_encoding)
-			*commit_encoding =
-				get_header(commit, msg, "encoding");
+			*commit_encoding = get_header(msg, "encoding");
 		return msg;
 	}
-	encoding = get_header(commit, msg, "encoding");
+	encoding = get_header(msg, "encoding");
 	if (commit_encoding)
 		*commit_encoding = encoding;
 	use_encoding = encoding ? encoding : utf8;
-- 
2.0.0.566.gfe3e6b2
Previous: Jeff KingNext: Eric Sunshine
Message 2 of 16 in “cleaning up determine_author_info”
  1. 0/7 cleaning up determine_author_infoJeff King, Jun 18, 2014
  2. 1/7 commit: provide a function to find a header in a bufferJeff King, Jun 18, 2014
  3. Eric SunshineJun 23, 2014
  4. Jeff KingJun 23, 2014
  5. 2/7 record_author_info: fix memory leak on malformed commitJeff King, Jun 18, 2014
  6. 3/7 record_author_info: use find_commit_headerJeff King, Jun 18, 2014
  7. 4/7 ident_split: store begin/end pairs on their own structJeff King, Jun 18, 2014
  8. Eric SunshineJun 23, 2014
  9. 5/7 use strbufs in date functionsJeff King, Jun 18, 2014
  10. 6/7 determine_author_info: reuse parsing functionsJeff King, Jun 18, 2014
  11. 7/7 determine_author_info: stop leaking name/emailJeff King, Jun 18, 2014
  12. Eric SunshineJun 23, 2014
  13. Erik Faye-LundJun 23, 2014
  14. Eric SunshineJun 23, 2014
  15. Jeff KingJun 23, 2014
  16. Jeff KingJun 23, 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.