Git development
 help / color / mirror / Atom feed
* [PATCH] bundle-uri: do not die on unresolvable relative URIs
@ 2026-10-28 11:58 Aniket Gupta
  0 siblings, 0 replies; only message in thread
From: Aniket Gupta @ 2026-10-28 11:58 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Derrick Stolee

relative_url() calls die() when the relative URL has more "../"
components than the remote URL can absorb. bundle_list_update() calls
it on URIs taken from a bundle list and a single bad entry takes down
the whole command.

Add relative_url_gently() which returns NULL and records the reason in
a strbuf instead of dying. This will keep relative_url() as a wrapper
that dies so the submodule callers are unchanged. Use the new function
in bundle_list_update() and report the problem as an error like the
other malformed lines in a bundle list.

Update the test in t5750 accordingly and drop its TODO.

Signed-off-by: Aniket Gupta <aniketgupta3001@gmail.com>
---
 bundle-uri.c                | 10 +++++++++-
 remote.c                    | 39 +++++++++++++++++++++++++++++--------
 remote.h                    |  8 ++++++++
 t/t5750-bundle-uri-parse.sh | 15 +++++++-------
 4 files changed, 55 insertions(+), 17 deletions(-)

diff --git a/bundle-uri.c b/bundle-uri.c
index 2bb2eb99e4..1f198ef9a9 100644
--- a/bundle-uri.c
+++ b/bundle-uri.c
@@ -210,9 +210,17 @@ static int bundle_list_update(const char *key, const char *value,
 	strbuf_release(&id);
 
 	if (!strcmp(subkey, "uri")) {
+		struct strbuf err = STRBUF_INIT;
+
 		if (bundle->uri)
 			return -1;
-		bundle->uri = relative_url(list->baseURI, value, NULL);
+		bundle->uri = relative_url_gently(list->baseURI, value, NULL,
+						  &err);
+		if (!bundle->uri) {
+			error("%s", err.buf);
+			strbuf_release(&err);
+			return -1;
+		}
 		return 0;
 	}
 
diff --git a/remote.c b/remote.c
index 49ee2ea318..7472c426aa 100644
--- a/remote.c
+++ b/remote.c
@@ -3005,9 +3005,11 @@ void remote_state_clear(struct remote_state *remote_state)
 }
 
 /*
- * Returns 1 if it was the last chop before ':'.
+ * Returns 1 if it was the last chop before ':', or -1 (and records the
+ * reason in `err`) if there is nothing left to chop.
  */
-static int chop_last_dir(char **remoteurl, int is_relative)
+static int chop_last_dir(char **remoteurl, int is_relative,
+			 struct strbuf *err)
 {
 	char *rfind = find_last_dir_sep(*remoteurl);
 	if (rfind) {
@@ -3021,17 +3023,19 @@ static int chop_last_dir(char **remoteurl, int is_relative)
 		return 1;
 	}
 
-	if (is_relative || !strcmp(".", *remoteurl))
-		die(_("cannot strip one component off url '%s'"),
-			*remoteurl);
+	if (is_relative || !strcmp(".", *remoteurl)) {
+		strbuf_addf(err, _("cannot strip one component off url '%s'"),
+			    *remoteurl);
+		return -1;
+	}
 
 	free(*remoteurl);
 	*remoteurl = xstrdup(".");
 	return 0;
 }
 
-char *relative_url(const char *remote_url, const char *url,
-		   const char *up_path)
+char *relative_url_gently(const char *remote_url, const char *url,
+			  const char *up_path, struct strbuf *err)
 {
 	int is_relative = 0;
 	int colonsep = 0;
@@ -3073,8 +3077,16 @@ char *relative_url(const char *remote_url, const char *url,
 	 */
 	while (*url) {
 		if (starts_with_dot_dot_slash_native(url)) {
+			int chopped;
+
 			url += 3;
-			colonsep |= chop_last_dir(&remoteurl, is_relative);
+			chopped = chop_last_dir(&remoteurl, is_relative, err);
+			if (chopped < 0) {
+				free(remoteurl);
+				strbuf_release(&sb);
+				return NULL;
+			}
+			colonsep |= chopped;
 		} else if (starts_with_dot_slash_native(url))
 			url += 2;
 		else
@@ -3102,6 +3114,17 @@ char *relative_url(const char *remote_url, const char *url,
 	return strbuf_detach(&sb, NULL);
 }
 
+char *relative_url(const char *remote_url, const char *url,
+		   const char *up_path)
+{
+	struct strbuf err = STRBUF_INIT;
+	char *out = relative_url_gently(remote_url, url, up_path, &err);
+
+	if (!out)
+		die("%s", err.buf);
+	return out;
+}
+
 int valid_remote_name(const char *name)
 {
 	int result;
diff --git a/remote.h b/remote.h
index cca02033b9..940474fa71 100644
--- a/remote.h
+++ b/remote.h
@@ -478,6 +478,14 @@ void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);
 char *relative_url(const char *remote_url, const char *url,
 		   const char *up_path);
 
+/*
+ * Like relative_url(), but if `url` cannot be resolved against
+ * `remote_url`, return NULL and add the reason to `err` instead of
+ * dying. `err` must not be NULL.
+ */
+char *relative_url_gently(const char *remote_url, const char *url,
+			  const char *up_path, struct strbuf *err);
+
 int valid_remote_name(const char *name);
 
 #endif
diff --git a/t/t5750-bundle-uri-parse.sh b/t/t5750-bundle-uri-parse.sh
index 70ae94c4d0..0f0ac29565 100755
--- a/t/t5750-bundle-uri-parse.sh
+++ b/t/t5750-bundle-uri-parse.sh
@@ -68,17 +68,16 @@ test_expect_success 'bundle_uri_parse_line(): relative URIs and parent paths' '
 		uri = <uri>/bundle.bdl
 	[bundle "two"]
 		uri = bundle.bdl
-	[bundle "three"]
-		uri = <uri>/../bundle.bdl
 	EOF
 
-	# TODO: We would prefer if parsing a bundle list would not cause
-	# a die() and instead would give a warning and allow the rest of
-	# a Git command to continue. This test_must_fail is necessary for
-	# now until the interface for relative_url() allows for reporting
-	# an error instead of die()ing.
+	cat >err.expect <<-\EOF &&
+	error: cannot strip one component off url '\''.'\''
+	error: bad line: '\''bundle.three.uri=../../bundle.bdl'\''
+	EOF
+
 	test_must_fail test-tool bundle-uri parse-key-values in >actual 2>err &&
-	test_grep "fatal: cannot strip one component off url" err
+	test_cmp err.expect err &&
+	test_cmp_config_output expect actual
 '
 
 test_expect_success 'bundle_uri_parse_line() parsing edge cases: empty key or value' '

base-commit: 37b3475fb530c14109211faca33716a173db61ed
-- 
2.43.0


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-10-28 11:58 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-28 11:58 [PATCH] bundle-uri: do not die on unresolvable relative URIs Aniket Gupta

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox