From: Aniket Gupta <aniketgupta3001@gmail.com>
To: git@vger.kernel.org
Cc: Junio C Hamano <gitster@pobox.com>,
Derrick Stolee <stolee@gmail.com>, Patrick Steinhardt <ps@pks.im>
Subject: [PATCH v2] bundle-uri: do not die on unresolvable relative URIs
Date: Tue, 3 Nov 2026 15:52:31 +0000 [thread overview]
Message-ID: <20261103155231.381-1-aniketgupta3001@gmail.com> (raw)
In-Reply-To: <20261028115818.410-1-aniketgupta3001@gmail.com>
A bundle list, either advertised by a server or given to "git clone
--bundle-uri", may contain relative URIs. relative_url() resolves them
and calls die() when there are more "../" components than the base can
absorb, so one bad entry aborts the whole clone.
Add relative_url_gently() which returns NULL and records the reason in
a strbuf instead of dying. relative_url() keeps dying, so the submodule
callers are unchanged. bundle_list_update() reports the problem as an
error and leaves the bundle without a URI. The download code already
skips such a bundle, so the clone carries on with the others.
Name the URL in the error message, without any credentials, and update
the tests.
Signed-off-by: Aniket Gupta <aniketgupta3001@gmail.com>
---
Changes since v1:
- the commit message says which lists are meant and why carrying on
is fine
- the error now names the URL that could not be resolved, with any
credentials removed
- add a test for that in t0060
bundle-uri.c | 10 +++++++++-
remote.c | 37 +++++++++++++++++++++++++++++++------
remote.h | 8 ++++++++
t/t0060-path-utils.sh | 7 +++++++
t/t5750-bundle-uri-parse.sh | 15 +++++++--------
5 files changed, 62 insertions(+), 15 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..36834b129c 100644
--- a/remote.c
+++ b/remote.c
@@ -3005,7 +3005,8 @@ 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 if there is
+ * nothing left to chop.
*/
static int chop_last_dir(char **remoteurl, int is_relative)
{
@@ -3022,16 +3023,15 @@ static int chop_last_dir(char **remoteurl, int is_relative)
}
if (is_relative || !strcmp(".", *remoteurl))
- die(_("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 +3073,22 @@ 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);
+ if (chopped < 0) {
+ char *anon = transport_anonymize_url(remote_url);
+
+ strbuf_addf(err,
+ _("cannot strip one component off url '%s'"),
+ anon);
+ free(anon);
+ free(remoteurl);
+ strbuf_release(&sb);
+ return NULL;
+ }
+ colonsep |= chopped;
} else if (starts_with_dot_slash_native(url))
url += 2;
else
@@ -3102,6 +3116,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/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 56faf5fe73..50cc37bc98 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -394,6 +394,13 @@ test_submodule_relative_url "../" "$(pwd)/addtest" "../repo" "$(pwd)/repo"
test_submodule_relative_url "../" "foo/bar" "../submodule" "../foo/submodule"
test_submodule_relative_url "../" "foo" "../submodule" "../submodule"
+test_expect_success 'relative_url() does not show credentials when it fails' '
+ test_must_fail test-tool submodule resolve-relative-url "" \
+ "https://user:secret@host.example/a" ../../../../../../x 2>err &&
+ test_grep "cannot strip one component off url" err &&
+ test_grep ! secret err
+'
+
test_submodule_relative_url "(null)" "../foo/bar" "../sub/a/b/c" "../foo/sub/a/b/c"
test_submodule_relative_url "(null)" "../foo/bar" "../sub/a/b/c/" "../foo/sub/a/b/c"
test_submodule_relative_url "(null)" "../foo/bar/" "../sub/a/b/c" "../foo/sub/a/b/c"
diff --git a/t/t5750-bundle-uri-parse.sh b/t/t5750-bundle-uri-parse.sh
index 70ae94c4d0..8abf8f31ae 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 '\''<uri>'\''
+ 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' '
--
2.43.0
prev parent reply other threads:[~2026-11-03 15:52 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-28 11:58 [PATCH] bundle-uri: do not die on unresolvable relative URIs Aniket Gupta
2026-11-03 13:04 ` Patrick Steinhardt
2026-11-03 15:55 ` Aniket Gupta
2026-11-03 15:52 ` Aniket Gupta [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261103155231.381-1-aniketgupta3001@gmail.com \
--to=aniketgupta3001@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=ps@pks.im \
--cc=stolee@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox