refs: generalize `refs_for_each_namespaced_ref()`

The function `refs_for_each_namespaced_ref()` iterates through all references that are part of the current ref namespace. This namespace can be configured by setting the `GIT_NAMESPACE` environment variable and is then retrieved by calling `get_git_namespace()`. If a namespace is configured, then we: - Obviously only yield refs that exist in this namespace. - Rewrite exclude patterns so that they work for the given namespace, if any namespace is currently configured. Port this logic to `refs_for_each_ref_ext()` by adding a new `namespace` field to the options structure. This gives callers more flexibility as they can decide by themselves whether they want to use the globally configured or an arbitrary other namespace. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Feb 23, 2026 at 12:59 UTC 5387919327574b5067f7efd986fca8793c95c71a
2 files changed +36 -18
refs.c
+29 -18
@@ -1848,10 +1848,14 @@ int refs_for_each_ref_ext(struct ref_store *refs,
1848 refs_for_each_cb cb, void *cb_data,
1849 const struct refs_for_each_ref_options *opts)
1850 {
1851 + struct strvec namespaced_exclude_patterns = STRVEC_INIT;
1852 + struct strbuf namespaced_prefix = STRBUF_INIT;
1853 struct strbuf real_pattern = STRBUF_INIT;
1854 struct for_each_ref_filter filter;
1855 struct ref_iterator *iter;
1856 size_t trim_prefix = opts->trim_prefix;
1857 + const char **exclude_patterns;
1858 + const char *prefix;
1859 int ret;
1860
1861 if (!refs)
@@ -1886,11 +1890,29 @@ int refs_for_each_ref_ext(struct ref_store *refs,
1890 cb_data = &filter;
1891 }
1892
1889 - iter = refs_ref_iterator_begin(refs, opts->prefix ? opts->prefix : "",
1890 - opts->exclude_patterns,
1893 + if (opts->namespace) {
1894 + strbuf_addstr(&namespaced_prefix, opts->namespace);
1895 + if (opts->prefix)
1896 + strbuf_addstr(&namespaced_prefix, opts->prefix);
1897 + else
1898 + strbuf_addstr(&namespaced_prefix, "refs/");
1899 +
1900 + prefix = namespaced_prefix.buf;
1901 + exclude_patterns = get_namespaced_exclude_patterns(opts->exclude_patterns,
1902 + opts->namespace,
1903 + &namespaced_exclude_patterns);
1904 + } else {
1905 + prefix = opts->prefix ? opts->prefix : "";
1906 + exclude_patterns = opts->exclude_patterns;
1907 + }
1908 +
1909 + iter = refs_ref_iterator_begin(refs, prefix, exclude_patterns,
1910 trim_prefix, opts->flags);
1911
1912 ret = do_for_each_ref_iterator(iter, cb, cb_data);
1913 +
1914 + strvec_clear(&namespaced_exclude_patterns);
1915 + strbuf_release(&namespaced_prefix);
1916 strbuf_release(&real_pattern);
1917 return ret;
1918 }
@@ -1937,22 +1959,11 @@ int refs_for_each_namespaced_ref(struct ref_store *refs,
1959 const char **exclude_patterns,
1960 refs_for_each_cb cb, void *cb_data)
1961 {
1940 - struct refs_for_each_ref_options opts = { 0 };
1941 - struct strvec namespaced_exclude_patterns = STRVEC_INIT;
1942 - struct strbuf prefix = STRBUF_INIT;
1943 - int ret;
1944 -
1945 - opts.exclude_patterns = get_namespaced_exclude_patterns(exclude_patterns,
1946 - get_git_namespace(),
1947 - &namespaced_exclude_patterns);
1948 - strbuf_addf(&prefix, "%srefs/", get_git_namespace());
1949 - opts.prefix = prefix.buf;
1950 -
1951 - ret = refs_for_each_ref_ext(refs, cb, cb_data, &opts);
1952 -
1953 - strvec_clear(&namespaced_exclude_patterns);
1954 - strbuf_release(&prefix);
1955 - return ret;
1962 + struct refs_for_each_ref_options opts = {
1963 + .exclude_patterns = exclude_patterns,
1964 + .namespace = get_git_namespace(),
1965 + };
1966 + return refs_for_each_ref_ext(refs, cb, cb_data, &opts);
1967 }
1968
1969 int refs_for_each_rawref(struct ref_store *refs, refs_for_each_cb fn, void *cb_data)
refs.h
+7
@@ -468,6 +468,13 @@ struct refs_for_each_ref_options {
468 */
469 const char *pattern;
470
471 + /*
472 + * If set, only yield refs part of the configured namespace. Exclude
473 + * patterns will be rewritten to apply to the namespace, and the prefix
474 + * will be considered relative to the namespace.
475 + */
476 + const char *namespace;
477 +
478 /*
479 * Exclude any references that match any of these patterns on a
480 * best-effort basis. The caller needs to be prepared for the exclude