Web lists-archives.com

[PATCH 3/7] git.c: provide setup_auto_pager()

The previous patch introduced a way for builtins to declare that they
will take responsibility for handling the `pager.foo`-config item. (See
the commit message of that patch for why that could be useful.)

Provide setup_auto_pager(), which builtins can call in order to handle
`pager.<cmd>`, including possibly starting the pager. Make this function
don't do anything if a pager has already been started.

When the `cmd` given to setup_auto_pager() contains one or more '.', use
a fallback strategy which checks `pager.foo.bar.baz`, then
`pager.foo.bar`, then `pager.foo`, then resorts to the provided default
value. This ensures a consistent fallback strategy for builtins which
take this type of more fine-grained pager configuration.

An alternative fallback strategy would have been to check for
`pager.foo.bar.baz`, then immediately fall back to `def`. However, that
would have meant that git foo would sometimes completely ignore
`pager.foo`, which seems conceptually wrong. It would also have meant
that builtins that are moved to more fine-grained pager configurations
would have regressed for certain usecases.

Whenever this function is called from a builtin, git.c will already have
called commit_pager_choice(). Since commit_pager_choice() treats the
special value -1 as "punt" or "not yet decided", it is not a problem
that we might end up calling commit_pager_choice() many times. Make the
new function use -1 in the same way and document it as "punt".

Don't add any users of setup_auto_pager just yet, one will follow in
later patches.

setup_auto_pager() is more capable than it needs to be for this patch
series. It would have been sufficient to handle zero or one '.'. We
would probably have wanted some verification+BUG()-patterns or to
define whether we split at the first or last '.', so it seems just as
easy, and certainly more flexible, to handle the more general situation.

Signed-off-by: Martin Ågren <martin.agren@xxxxxxxxx>
 builtin.h | 14 ++++++++++++++
 git.c     | 28 ++++++++++++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/builtin.h b/builtin.h
index 498ac80d0..a6ed6c4ac 100644
--- a/builtin.h
+++ b/builtin.h
@@ -25,6 +25,20 @@ struct fmt_merge_msg_opts {
 extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 			 struct fmt_merge_msg_opts *);
+ * If a builtin has IGNORE_PAGER_CONFIG set, the builtin should call this early
+ * when it wishes to respect the `pager.foo`-config. In the simplest case, the
+ * `cmd` is the name of the builtin, e.g., "foo". If a paging-choice has already
+ * been setup, this does nothing. The default in `def` should be 0 for "pager
+ * off", 1 for "pager on" or -1 for "punt".
+ *
+ * With one or more '.', substrings are tried out from longer to shorter. If no
+ * config is found, uses `def`. For example, with `cmd` as "foo.bar.baz", this
+ * function tries `pager.foo.bar.baz`, `pager.foo.bar` and `pager.foo` in that
+ * order before resorting to `def`.
+ */
+extern void setup_auto_pager(const char *cmd, int def);
 extern int is_builtin(const char *s);
 extern int cmd_add(int argc, const char **argv, const char *prefix);
diff --git a/git.c b/git.c
index ae92f8ed5..696eaf87a 100644
--- a/git.c
+++ b/git.c
@@ -33,6 +33,34 @@ static void commit_pager_choice(void) {
+void setup_auto_pager(const char *cmd, int def)
+	if (use_pager != -1)
+		return;
+	use_pager = check_pager_config(cmd);
+	if (use_pager == -1) {
+		struct strbuf buf = STRBUF_INIT;
+		size_t len;
+		strbuf_addstr(&buf, cmd);
+		len = buf.len;
+		while (use_pager == -1 && len--) {
+			if (buf.buf[len] == '.') {
+				strbuf_setlen(&buf, len);
+				use_pager = check_pager_config(buf.buf);
+			}
+		}
+		strbuf_release(&buf);
+	}
+	if (use_pager == -1)
+		use_pager = def;
+	commit_pager_choice();
 static int handle_options(const char ***argv, int *argc, int *envchanged)
 	const char **orig_argv = *argv;