Subject: [PATCH] status: hint the user about -uno if read_directory takes too long

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Wed, 13 Mar 2013 19:59:16 +0700

This patch attempts to advertise -uno to the users who tolerate slow
"git status" on large repositories (or slow machines/disks). The 2
seconds limit is quite arbitrary but is probably long enough to start
using -uno.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 Documentation/config.txt |  4 ++++
 advice.c                 |  2 ++
 advice.h                 |  1 +
 t/t7060-wtstatus.sh      |  2 ++
 t/t7508-status.sh        |  4 ++++
 t/t7512-status-help.sh   |  1 +
 wt-status.c              | 20 +++++++++++++++++++-
 wt-status.h              |  1 +
 8 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index bbba728..e91d06f 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -178,6 +178,10 @@ advice.*::
 		the template shown when writing commit messages in
 		linkgit:git-commit[1], and in the help message shown
 		by linkgit:git-checkout[1] when switching branch.
+	statusUno::
+		If collecting untracked files in linkgit:git-status[1]
+		takes more than 2 seconds, hint the user that the option
+		`-uno` could be used to stop collecting untracked files.
 	commitBeforeMerge::
 		Advice shown when linkgit:git-merge[1] refuses to
 		merge to avoid overwriting local changes.
diff --git a/advice.c b/advice.c
index 780f58d..72b5c66 100644
--- a/advice.c
+++ b/advice.c
@@ -8,6 +8,7 @@ int advice_push_already_exists = 1;
 int advice_push_fetch_first = 1;
 int advice_push_needs_force = 1;
 int advice_status_hints = 1;
+int advice_status_uno = 1;
 int advice_commit_before_merge = 1;
 int advice_resolve_conflict = 1;
 int advice_implicit_identity = 1;
@@ -25,6 +26,7 @@ static struct {
 	{ "pushfetchfirst", &advice_push_fetch_first },
 	{ "pushneedsforce", &advice_push_needs_force },
 	{ "statushints", &advice_status_hints },
+	{ "statusuno", &advice_status_uno },
 	{ "commitbeforemerge", &advice_commit_before_merge },
 	{ "resolveconflict", &advice_resolve_conflict },
 	{ "implicitidentity", &advice_implicit_identity },
diff --git a/advice.h b/advice.h
index fad36df..d7e03be 100644
--- a/advice.h
+++ b/advice.h
@@ -11,6 +11,7 @@ extern int advice_push_already_exists;
 extern int advice_push_fetch_first;
 extern int advice_push_needs_force;
 extern int advice_status_hints;
+extern int advice_status_uno;
 extern int advice_commit_before_merge;
 extern int advice_resolve_conflict;
 extern int advice_implicit_identity;
diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
index f4f38a5..dd340d5 100755
--- a/t/t7060-wtstatus.sh
+++ b/t/t7060-wtstatus.sh
@@ -5,6 +5,7 @@ test_description='basic work tree status reporting'
 . ./test-lib.sh
 
 test_expect_success setup '
+	git config advice.statusuno false &&
 	test_commit A &&
 	test_commit B oneside added &&
 	git checkout A^0 &&
@@ -46,6 +47,7 @@ test_expect_success 'M/D conflict does not segfault' '
 	(
 		cd mdconflict &&
 		git init &&
+		git config advice.statusuno false
 		test_commit initial foo "" &&
 		test_commit modify foo foo &&
 		git checkout -b side HEAD^ &&
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index a79c032..9d6e4db 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -8,11 +8,13 @@ test_description='git status'
 . ./test-lib.sh
 
 test_expect_success 'status -h in broken repository' '
+	git config advice.statusuno false &&
 	mkdir broken &&
 	test_when_finished "rm -fr broken" &&
 	(
 		cd broken &&
 		git init &&
+		git config advice.statusuno false &&
 		echo "[status] showuntrackedfiles = CORRUPT" >>.git/config &&
 		test_expect_code 129 git status -h >usage 2>&1
 	) &&
@@ -25,6 +27,7 @@ test_expect_success 'commit -h in broken repository' '
 	(
 		cd broken &&
 		git init &&
+		git config advice.statusuno false &&
 		echo "[status] showuntrackedfiles = CORRUPT" >>.git/config &&
 		test_expect_code 129 git commit -h >usage 2>&1
 	) &&
@@ -780,6 +783,7 @@ test_expect_success 'status refreshes the index' '
 test_expect_success 'setup status submodule summary' '
 	test_create_repo sm && (
 		cd sm &&
+		git config advice.statusuno false &&
 		>foo &&
 		git add foo &&
 		git commit -m "Add foo"
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index d2da89a..033a1b3 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -14,6 +14,7 @@ test_description='git status advice'
 set_fake_editor
 
 test_expect_success 'prepare for conflicts' '
+	git config advice.statusuno false &&
 	test_commit init main.txt init &&
 	git branch conflicts &&
 	test_commit on_master main.txt on_master &&
diff --git a/wt-status.c b/wt-status.c
index ef405d0..6fde08b 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -540,7 +540,16 @@ void wt_status_collect(struct wt_status *s)
 		wt_status_collect_changes_initial(s);
 	else
 		wt_status_collect_changes_index(s);
-	wt_status_collect_untracked(s);
+	if (s->show_untracked_files && advice_status_uno) {
+		struct timeval tv1, tv2;
+		gettimeofday(&tv1, NULL);
+		wt_status_collect_untracked(s);
+		gettimeofday(&tv2, NULL);
+		s->untracked_in_ms =
+			(uint64_t)tv2.tv_sec * 1000 + tv2.tv_usec / 1000 -
+			((uint64_t)tv1.tv_sec * 1000 + tv1.tv_usec / 1000);
+	} else
+		wt_status_collect_untracked(s);
 }
 
 static void wt_status_print_unmerged(struct wt_status *s)
@@ -1097,6 +1106,15 @@ void wt_status_print(struct wt_status *s)
 		wt_status_print_other(s, &s->untracked, _("Untracked files"), "add");
 		if (s->show_ignored_files)
 			wt_status_print_other(s, &s->ignored, _("Ignored files"), "add -f");
+		if (advice_status_uno && s->untracked_in_ms > 2000) {
+			status_printf_ln(s, GIT_COLOR_NORMAL,
+					 _("It took %.2f seconds to collect untracked files."),
+					 (float)s->untracked_in_ms / 1000);
+			status_printf_ln(s, GIT_COLOR_NORMAL,
+					 _("If it happens often, you may want to use option -uno"));
+			status_printf_ln(s, GIT_COLOR_NORMAL,
+					 _("to speed up by stopping displaying untracked files"));
+		}
 	} else if (s->commitable)
 		status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),
 			advice_status_hints
diff --git a/wt-status.h b/wt-status.h
index 81e1dcf..74208c0 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -69,6 +69,7 @@ struct wt_status {
 	struct string_list change;
 	struct string_list untracked;
 	struct string_list ignored;
+	uint32_t untracked_in_ms;
 };
 
 struct wt_status_state {
-- 
1.8.1.2.536.gf441e6d