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

Re: To page or not to page

From
Jeff King <peff@peff.net>
Date
May 6, 2008, 05:51 UTC
Message-ID
<20080506055128.GA26311@sigill.intra.peff.net>
In-Reply-To
<7v1w4ky3hh.fsf@gitster.siamese.dyndns.org>
On Fri, May 02, 2008 at 11:18:02AM -0700, Junio C Hamano wrote:
Show 10 quoted lines
> Hmm. How about doing things this way?
> 
>  - at the beginning of handle_options() remember argv[0]
> 
>  - restructure handle_options() so that it does not run setup_pager() and
>    setenv("GIT_PAGER", "cat", 1) inside the loop, but instead remember
>    what we had on the command line;
> 
>  - after the handle_options() loop, if we saw an explicit --pager,
>    --no-pager, that's the decision;

This is actually a bit tricky, since there are two code paths; builtins called as git-foo never even go to handle_options, but they can still look up the config value.

So how about this:
 - keep a global use_pager = { 0 (explicit no), 1 (explicit yes), -1
   (unknown) }
 - if git-foo, lookup config for pager.foo
 - otherwise we have "git [options] foo"; look for -p / --no-pager; if
   none found, then lookup config for "foo"
 - before proceeding further, "commit" the pager choice by running it
   (if 1), munging GIT_PAGER=cat (if 0), or doing nothing (if -1)
 - before handling an internal command, if use_pager is -1 and the
   command defaults to a pager, we run it then
The patch below implements this.

It would be nice to actually defer running the pager until we are about to run a git command. I.e., never "commit" to the pager until we are actually running an internal command or exec'ing an external command. That way it would be safe to make an alias that called "--no-pager" (which is currently disallowed).

That works for internal commands, since we know we are going to run one, and if it fails, we die. However, for external commands, we just exec and hope it works. So if we run the pager beforehand, we are now committed to it, and a further alias cannot set --no-pager. We could alternatively run it after deciding that running the command is what we'll do, but then we have to do the PATH lookup ourselves.

So anyway, here is the less invasive version.
---
 git.c |   48 ++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/git.c b/git.c
index 89b431f..dadca41 100644
--- a/git.c
+++ b/git.c
@@ -6,12 +6,46 @@
 const char git_usage_string[] =
 	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
 
+static int use_pager = -1;
+static const char *pager_command_key;
+static int pager_command_value;
+
+int pager_command_config(const char *var, const char *value)
+{
+	if (!prefixcmp(var, "pager.") && !strcmp(var + 6, pager_command_key))
+		pager_command_value = git_config_bool(var, value);
+	return 0;
+}
+
+/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
+int check_pager_config(const char *cmd)
+{
+	pager_command_key = cmd;
+	pager_command_value = -1;
+	git_config(pager_command_config);
+	return pager_command_value;
+}
+
+static void commit_pager_choice(void) {
+	switch (use_pager) {
+	case 0:
+		setenv("GIT_PAGER", "cat", 1);
+		break;
+	case 1:
+		setup_pager();
+		break;
+	default:
+		break;
+	}
+}
+
 static int handle_options(const char*** argv, int* argc, int* envchanged)
 {
 	int handled = 0;
+	const char *cmd = NULL;
 
 	while (*argc > 0) {
-		const char *cmd = (*argv)[0];
+		cmd = (*argv)[0];
 		if (cmd[0] != '-')
 			break;
 
@@ -35,9 +69,9 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
 				exit(0);
 			}
 		} else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
-			setup_pager();
+			use_pager = 1;
 		} else if (!strcmp(cmd, "--no-pager")) {
-			setenv("GIT_PAGER", "cat", 1);
+			use_pager = 0;
 			if (envchanged)
 				*envchanged = 1;
 		} else if (!strcmp(cmd, "--git-dir")) {
@@ -84,6 +118,9 @@ static int handle_options(const char*** argv, int* argc, int* envchanged)
 		(*argc)--;
 		handled++;
 	}
+
+	if (use_pager == -1 && cmd)
+		use_pager = check_pager_config(cmd);
 	return handled;
 }
 
@@ -239,7 +276,7 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv)
 	prefix = NULL;
 	if (p->option & RUN_SETUP)
 		prefix = setup_git_directory();
-	if (p->option & USE_PAGER)
+	if (use_pager == -1 && p->option & USE_PAGER)
 		setup_pager();
 	if (p->option & NEED_WORK_TREE)
 		setup_work_tree();
@@ -411,6 +448,8 @@ int main(int argc, const char **argv)
 	if (!prefixcmp(cmd, "git-")) {
 		cmd += 4;
 		argv[0] = cmd;
+		use_pager = check_pager_config(cmd);
+		commit_pager_choice();
 		handle_internal_command(argc, argv);
 		die("cannot handle %s internally", cmd);
 	}
@@ -419,6 +458,7 @@ int main(int argc, const char **argv)
 	argv++;
 	argc--;
 	handle_options(&argv, &argc, NULL);
+	commit_pager_choice();
 	if (argc > 0) {
 		if (!prefixcmp(argv[0], "--"))
 			argv[0] += 2;
-- 
1.5.5.1.244.g148a.dirty
Previous: Jeff KingNext: Jeff King
Message 23 of 31 in “To page or not to page”
  1. Kevin BallardMay 2, 2008
  2. Jeff KingMay 2, 2008
  3. Junio C HamanoMay 2, 2008
  4. Kevin BallardMay 2, 2008
  5. Junio C HamanoMay 2, 2008
  6. Bart TrojanowskiMay 2, 2008
  7. Pedro MeloMay 2, 2008
  8. Kevin BallardMay 2, 2008
  9. Wincent ColaiutaMay 2, 2008
  10. Jeff KingMay 2, 2008
  11. Pedro MeloMay 2, 2008
  12. Aidan Van DykMay 2, 2008
  13. Wincent ColaiutaMay 2, 2008
  14. Kevin BallardMay 2, 2008
  15. Wincent ColaiutaMay 2, 2008
  16. Jeff KingMay 2, 2008
  17. Johannes SchindelinMay 2, 2008
  18. Jeff KingMay 2, 2008
  19. Junio C HamanoMay 2, 2008
  20. Jeff KingMay 2, 2008
  21. Junio C HamanoMay 2, 2008
  22. Jeff KingMay 5, 2008
  23. Jeff KingMay 6, 2008
  24. Jeff KingMay 6, 2008
  25. Junio C HamanoMay 11, 2008
  26. Jeff KingMay 16, 2008
  27. Jeff KingMay 16, 2008
  28. Johannes SchindelinMay 16, 2008
  29. Jakub NarebskiMay 2, 2008
  30. Jeff KingMay 2, 2008
  31. Jeff KingMay 2, 2008

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.