From aa5d9886d1264033d241423b67cd8e685e71b19a Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Wed, 14 Jan 2026 01:11:30 -0500 Subject: [PATCH 1/2] test(remote): show buggy detached remote insteadOf behavior `git_remote_create_detached` does not apply `url.*.insteadOf` or url.*.pushInsteadOf from global config. These tests currently pass, asserting the buggy behavior and serving as a minimal reproduction. The assertions will be updated when the fix is applied. See --- tests/libgit2/remote/insteadof.c | 77 ++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/tests/libgit2/remote/insteadof.c b/tests/libgit2/remote/insteadof.c index c39df4be7..c54d50d54 100644 --- a/tests/libgit2/remote/insteadof.c +++ b/tests/libgit2/remote/insteadof.c @@ -152,3 +152,80 @@ void test_remote_insteadof__anonymous_remote_both(void) git_remote_pushurl(g_remote), "git@github.com:url/both/libgit2"); } + +void test_remote_insteadof__detached_remote_fetch_insteadof(void) +{ + git_config *cfg; + + cl_fake_globalconfig(NULL); + + cl_git_pass(git_config_open_default(&cfg)); + cl_git_pass(git_config_set_string(cfg, "url.http://github.com/url/fetch.insteadOf", + "http://example.com/url/fetch")); + git_config_free(cfg); + + cl_git_pass(git_remote_create_detached(&g_remote, + "http://example.com/url/fetch/libgit2")); + + /* + * TODO: this should be "http://github.com/url/fetch/libgit2" once + * git_remote_create_detached applies insteadOf from global config. + * See: https://github.com/libgit2/libgit2/issues/5469 + */ + cl_assert_equal_s( + git_remote_url(g_remote), + "http://example.com/url/fetch/libgit2"); + cl_assert_equal_p(git_remote_pushurl(g_remote), NULL); +} + +void test_remote_insteadof__detached_remote_push_insteadof(void) +{ + git_config *cfg; + + cl_fake_globalconfig(NULL); + + cl_git_pass(git_config_open_default(&cfg)); + cl_git_pass(git_config_set_string(cfg, "url.git@github.com:url/push.pushInsteadOf", + "http://example.com/url/push")); + git_config_free(cfg); + + cl_git_pass(git_remote_create_detached(&g_remote, + "http://example.com/url/push/libgit2")); + + cl_assert_equal_s( + git_remote_url(g_remote), + "http://example.com/url/push/libgit2"); + /* + * TODO: this should be "git@github.com:url/push/libgit2" once + * git_remote_create_detached applies pushInsteadOf from global config. + * See: https://github.com/libgit2/libgit2/issues/5469 + */ + cl_assert_equal_p(git_remote_pushurl(g_remote), NULL); +} + +void test_remote_insteadof__detached_remote_both_insteadof(void) +{ + git_config *cfg; + + cl_fake_globalconfig(NULL); + + cl_git_pass(git_config_open_default(&cfg)); + cl_git_pass(git_config_set_string(cfg, "url.http://github.com/url/both.insteadOf", + "http://example.com/url/both")); + cl_git_pass(git_config_set_string(cfg, "url.git@github.com:url/both.pushInsteadOf", + "http://example.com/url/both")); + git_config_free(cfg); + + cl_git_pass(git_remote_create_detached(&g_remote, + "http://example.com/url/both/libgit2")); + + /* + * TODO: these should be the rewritten URLs once + * git_remote_create_detached applies insteadOf from global config. + * See: https://github.com/libgit2/libgit2/issues/5469 + */ + cl_assert_equal_s( + git_remote_url(g_remote), + "http://example.com/url/both/libgit2"); + cl_assert_equal_p(git_remote_pushurl(g_remote), NULL); +} From d375ab70c1d036ed03ddf748e6d2b6ce03a78538 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Wed, 14 Jan 2026 01:21:59 -0500 Subject: [PATCH 2/2] fix(remote): apply insteadOf from global config for detached remotes Detached remotes already read global/system config for http proxy settings, but did not apply url.*.insteadOf or url.*.pushInsteadOf rules. This inconsistency meant that `git_remote_create_detached` behaved differently from git's `ls-remote`, which was the primary use case for detached remotes. This fixes it by loading the default config when no repository is provided and apply insteadOf rules consistently. While this is a behavior change, it still respects `GIT_REMOTE_CREATE_SKIP_INSTEADOF`, meaning that user can restore the previous behavior with minimal effort Fixes https://github.com/libgit2/libgit2/issues/5469 --- src/libgit2/remote.c | 5 ++++- tests/libgit2/remote/insteadof.c | 27 ++++++++------------------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/libgit2/remote.c b/src/libgit2/remote.c index 6fe6be196..bd90dd50b 100644 --- a/src/libgit2/remote.c +++ b/src/libgit2/remote.c @@ -236,6 +236,9 @@ int git_remote_create_with_opts(git_remote **out, const char *url, const git_rem if (opts->repository) { if ((error = git_repository_config_snapshot(&config_ro, opts->repository)) < 0) goto on_error; + } else if (!(opts->flags & GIT_REMOTE_CREATE_SKIP_INSTEADOF)) { + if ((error = git_config_open_default(&config_ro)) < 0) + goto on_error; } remote = git__calloc(1, sizeof(git_remote)); @@ -247,7 +250,7 @@ int git_remote_create_with_opts(git_remote **out, const char *url, const git_rem (error = canonicalize_url(&canonical_url, url)) < 0) goto on_error; - if (opts->repository && !(opts->flags & GIT_REMOTE_CREATE_SKIP_INSTEADOF)) { + if (config_ro && !(opts->flags & GIT_REMOTE_CREATE_SKIP_INSTEADOF)) { if ((error = apply_insteadof(&remote->url, config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH, true)) < 0 || (error = apply_insteadof(&remote->pushurl, config_ro, canonical_url.ptr, GIT_DIRECTION_PUSH, false)) < 0) goto on_error; diff --git a/tests/libgit2/remote/insteadof.c b/tests/libgit2/remote/insteadof.c index c54d50d54..1951f332b 100644 --- a/tests/libgit2/remote/insteadof.c +++ b/tests/libgit2/remote/insteadof.c @@ -167,14 +167,9 @@ void test_remote_insteadof__detached_remote_fetch_insteadof(void) cl_git_pass(git_remote_create_detached(&g_remote, "http://example.com/url/fetch/libgit2")); - /* - * TODO: this should be "http://github.com/url/fetch/libgit2" once - * git_remote_create_detached applies insteadOf from global config. - * See: https://github.com/libgit2/libgit2/issues/5469 - */ cl_assert_equal_s( git_remote_url(g_remote), - "http://example.com/url/fetch/libgit2"); + "http://github.com/url/fetch/libgit2"); cl_assert_equal_p(git_remote_pushurl(g_remote), NULL); } @@ -195,12 +190,9 @@ void test_remote_insteadof__detached_remote_push_insteadof(void) cl_assert_equal_s( git_remote_url(g_remote), "http://example.com/url/push/libgit2"); - /* - * TODO: this should be "git@github.com:url/push/libgit2" once - * git_remote_create_detached applies pushInsteadOf from global config. - * See: https://github.com/libgit2/libgit2/issues/5469 - */ - cl_assert_equal_p(git_remote_pushurl(g_remote), NULL); + cl_assert_equal_s( + git_remote_pushurl(g_remote), + "git@github.com:url/push/libgit2"); } void test_remote_insteadof__detached_remote_both_insteadof(void) @@ -219,13 +211,10 @@ void test_remote_insteadof__detached_remote_both_insteadof(void) cl_git_pass(git_remote_create_detached(&g_remote, "http://example.com/url/both/libgit2")); - /* - * TODO: these should be the rewritten URLs once - * git_remote_create_detached applies insteadOf from global config. - * See: https://github.com/libgit2/libgit2/issues/5469 - */ cl_assert_equal_s( git_remote_url(g_remote), - "http://example.com/url/both/libgit2"); - cl_assert_equal_p(git_remote_pushurl(g_remote), NULL); + "http://github.com/url/both/libgit2"); + cl_assert_equal_s( + git_remote_pushurl(g_remote), + "git@github.com:url/both/libgit2"); }