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

[PATCH v2 7/7] http: silence stderr when progress is enabled

From
Toon Claes <toon@iotcl.com>
Date
Feb 19, 2025, 14:30 UTC
Message-ID
<20250219-toon-bundleuri-progress-v2-7-a84e7ffa921a@iotcl.com>
In-Reply-To
<20250219-toon-bundleuri-progress-v2-0-a84e7ffa921a@iotcl.com>

To download bundle URI bundles over HTTP(s), git-clone(1) spawns a git-remote-http(1) subprocess. Because clone can continue without bundles, all errors sent by the child process over stderr are suppressed.

In previous commits, we've added progress output in the child process, and this happens over stderr, so we can no longer silence stderr. But in case a bundle could not be downloaded, the user sees the following messages:

  fatal: failed to download file at URL 'http://127.0.0.1:5558/bundle-5.bundle'
  warning: failed to download bundle from URI 'http://127.0.0.1:5558/bundle-5.bundle'

Here the child git-remote-http(1) prints a "fatal" error and then the parent git-clone(1) prints a "warning". This is confusing to the user.

Instead of suppressing stderr from the parent process, like we did before, modify stderr to write to /dev/null in the child process itself, while keep using the original stderr for progress logging only.

Signed-off-by: Toon Claes <toon@iotcl.com>
---
 http.c     |  5 ++++-
 progress.c | 17 +++++++++++++----
 progress.h |  1 +
 3 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/http.c b/http.c
index 5517863808..5c0c6ef204 100644
--- a/http.c
+++ b/http.c
@@ -2133,7 +2133,10 @@ static int http_request(const char *url,
 	    http_follow_config == HTTP_FOLLOW_INITIAL)
 		curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 1);
 	if (options && options->progress) {
-		progress = start_progress(the_repository, _("Downloading via HTTP"), 0);
+		progress = start_progress(the_repository,
+					  _("Downloading via HTTP"), 0);
+		progress_set_fd(progress, fileno(stderr));
+		freopen("/dev/null", "w", stderr);
 
 		curl_easy_setopt(slot->curl, CURLOPT_NOPROGRESS, 0L);
 		curl_easy_setopt(slot->curl, CURLOPT_XFERINFODATA, progress);
diff --git a/progress.c b/progress.c
index 89abb231ae..1955262000 100644
--- a/progress.c
+++ b/progress.c
@@ -40,6 +40,7 @@ struct progress {
 	const char *title;
 	uint64_t last_value;
 	uint64_t total;
+	int fd;
 	unsigned last_percent;
 	unsigned delay;
 	unsigned sparse;
@@ -144,7 +145,9 @@ static void display(struct progress *progress, uint64_t n, const char *done)
 	}
 
 	if (show_update) {
-		if (is_foreground_fd(fileno(stderr)) || done) {
+		int fd = progress->fd ? progress->fd : fileno(stderr);
+
+		if (is_foreground_fd(fd) || done) {
 			const char *eol = done ? done : "\r";
 			size_t clear_len = counters_sb->len < last_count_len ?
 					last_count_len - counters_sb->len + 1 :
@@ -155,17 +158,17 @@ static void display(struct progress *progress, uint64_t n, const char *done)
 			int cols = term_columns();
 
 			if (progress->split) {
-				fprintf(stderr, "  %s%*s", counters_sb->buf,
+				dprintf(fd, "  %s%*s", counters_sb->buf,
 					(int) clear_len, eol);
 			} else if (!done && cols < progress_line_len) {
 				clear_len = progress->title_len + 1 < cols ?
 					    cols - progress->title_len - 1 : 0;
-				fprintf(stderr, "%s:%*s\n  %s%s",
+				dprintf(fd, "%s:%*s\n  %s%s",
 					progress->title, (int) clear_len, "",
 					counters_sb->buf, eol);
 				progress->split = 1;
 			} else {
-				fprintf(stderr, "%s: %s%*s", progress->title,
+				dprintf(fd, "%s: %s%*s", progress->title,
 					counters_sb->buf, (int) clear_len, eol);
 			}
 			fflush(stderr);
@@ -287,6 +290,12 @@ void progress_set_total(struct progress *progress, uint64_t total)
 		progress->total = total;
 }
 
+void progress_set_fd(struct progress *progress, int fd)
+{
+	if (progress)
+		progress->fd = fd;
+}
+
 static int get_default_delay(void)
 {
 	static int delay_in_secs = -1;
diff --git a/progress.h b/progress.h
index 2e1bd738c2..f12c82adc4 100644
--- a/progress.h
+++ b/progress.h
@@ -16,6 +16,7 @@ void progress_test_force_update(void);
 void display_throughput(struct progress *progress, uint64_t total);
 void display_progress(struct progress *progress, uint64_t n);
 void progress_set_total(struct progress *progress, uint64_t total);
+void progress_set_fd(struct progress *progress, int fd);
 struct progress *start_progress(struct repository *r,
 				const char *title, uint64_t total);
 struct progress *start_sparse_progress(struct repository *r,
-- 
2.48.1.658.g4767266eb4
Previous: Toon ClaesNext: Jeff King
Message 24 of 25 in “bundle-uri: show progress when downloading from bundle URIs”
  1. 0/4 bundle-uri: show progress when downloading from bundle URIsToon Claes, May 8, 2024
  2. 1/4 progress: add function to set totalToon Claes, May 8, 2024
  3. 2/4 http: add the ability to log progressToon Claes, May 8, 2024
  4. Eric SunshineMay 8, 2024
  5. Jeff KingMay 9, 2024
  6. Junio C HamanoMay 9, 2024
  7. Jeff KingMay 9, 2024
  8. Jeff KingMay 9, 2024
  9. 3/4 remote-curl: optionally show progress for HTTP getToon Claes, May 8, 2024
  10. Junio C HamanoMay 8, 2024
  11. 4/4 bundle-uri: enable git-remote-https progressToon Claes, May 8, 2024
  12. Jeff KingMay 9, 2024
  13. Toon ClaesFeb 14, 2025
  14. Jeff KingFeb 21, 2025
  15. Junio C HamanoMay 8, 2024
  16. 0/7 Show progress when downloading from bundle URIsToon Claes, Feb 19, 2025
  17. 1/7 progress: add function to set totalToon Claes, Feb 19, 2025
  18. Jeff KingFeb 21, 2025
  19. 2/7 progress: allow pure-throughput progress metersToon Claes, Feb 19, 2025
  20. 3/7 http: turn off curl signalsToon Claes, Feb 19, 2025
  21. 4/7 http: add the ability to log progressToon Claes, Feb 19, 2025
  22. 5/7 remote-curl: optionally show progress for HTTP getToon Claes, Feb 19, 2025
  23. 6/7 bundle-uri: enable git-remote-https progressToon Claes, Feb 19, 2025
  24. 7/7 http: silence stderr when progress is enabledToon Claes, Feb 19, 2025
  25. Jeff KingFeb 21, 2025

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.