{"thread":{"id":"56659","subject":"[PATCH 0/2] remote: replace static variables with struct remote_state","startedAt":"2021-10-07T19:07:20Z","lastAt":"2021-11-16T17:45:58Z","messageCount":56,"participants":["Glen Choo via GitGitGadget","Junio C Hamano","Glen Choo","Jonathan Tan"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"438194","messageId":"pull.1103.git.git.1633633635.gitgitgadget@gmail.com","threadId":"56659","inReplyTo":null,"subject":"[PATCH 0/2] remote: replace static variables with struct remote_state","fromName":"Glen Choo via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T19:07:13Z","receivedAt":"2021-10-07T19:07:20Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"This series aims to make the remotes subsystem work with non-the_repository,\nwhich will allow submodule remotes to be accessed in-process, rather than\nthrough child processes. This is accomplished by creating a struct\nremote_state and adding it to struct repository.\n\nOne motivation for this is that it allows future submodule commands to run\nin-process. An example is an RFC series of mine [1], where I tried to\nimplement \"git branch --recurse-submodules\" in-process but couldn't figure\nout how to read the remotes of a submodule.\n\nFor the most part, this was a mechanical process of taking static variables\nand putting them in a struct. As such, I think this series might benefit\nfrom thoughtful review, especially from people with more insight into this\narea. Some areas I am extra concerned about are:\n\n * Naming, especially the renaming of variables in patch 1 and the field\n   names in patch 2. I don't really like the name \"remote_state\", but I\n   can't think of a better one.\n * Memory leaks - I am not confident that remote_state_clear() frees all of\n   the memory that it should. I tried to eliminate as many leaks as I could\n   with \"make SANITIZE=address,leak\", but I'm not confident that I've caught\n   them all.\n\n[1] https://lore.kernel.org/git/20210921232529.81811-1-chooglen@google.com/\n\nGlen Choo (2):\n  remote: move static variables into struct\n  remote: add remote_state to struct repository\n\n remote.c     | 245 ++++++++++++++++++++++++++++++---------------------\n remote.h     |  69 +++++++++++++--\n repository.c |   8 ++\n repository.h |   4 +\n 4 files changed, 222 insertions(+), 104 deletions(-)\n\n\nbase-commit: 0785eb769886ae81e346df10e88bc49ffc0ac64e\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1103%2Fchooglen%2Fremote%2Fstruct-no-global-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1103/chooglen/remote/struct-no-global-v1\nPull-Request: https://github.com/git/git/pull/1103\n-- \ngitgitgadget\n"},{"id":"438195","messageId":"f7639a20fcd437d5f50665b5c6f5318c1245b9b5.1633633635.git.gitgitgadget@gmail.com","threadId":"56659","inReplyTo":"pull.1103.git.git.1633633635.gitgitgadget@gmail.com","subject":"[PATCH 1/2] remote: move static variables into struct","fromName":"Glen Choo via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T19:07:14Z","receivedAt":"2021-10-07T19:07:23Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"From: Glen Choo <chooglen@google.com>\n\nremote.c only works with the_repository because it stores its state as\nstatic variables. To support non-the_repository, we can use a\nper-repository struct for the remotes subsystem.\n\nPrepare for this change by moving the static variables of remote.c into\na static struct remote_state.\n\nThis introduces no behavioral or API changes.\n\nSigned-off-by: Glen Choo <chooglen@google.com>\n---\n remote.c | 109 ++++++++++++++++++++++++++++++++-----------------------\n 1 file changed, 63 insertions(+), 46 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 31e141b01fa..55997210060 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -34,20 +34,24 @@ struct rewrites {\n \tint rewrite_nr;\n };\n \n-static struct remote **remotes;\n-static int remotes_alloc;\n-static int remotes_nr;\n-static struct hashmap remotes_hash;\n+static struct remote_state {\n+\tstruct remote **remotes;\n+\tint remotes_alloc;\n+\tint remotes_nr;\n+\tstruct hashmap remotes_hash;\n \n-static struct branch **branches;\n-static int branches_alloc;\n-static int branches_nr;\n+\tstruct branch **branches;\n+\tint branches_alloc;\n+\tint branches_nr;\n \n-static struct branch *current_branch;\n-static const char *pushremote_name;\n+\tstruct branch *current_branch;\n+\tconst char *pushremote_name;\n \n-static struct rewrites rewrites;\n-static struct rewrites rewrites_push;\n+\tstruct rewrites rewrites;\n+\tstruct rewrites rewrites_push;\n+} remotes;\n+\n+struct remote_state *remote_state = &remotes;\n \n static int valid_remote(const struct remote *remote)\n {\n@@ -94,14 +98,14 @@ static void add_pushurl(struct remote *remote, const char *pushurl)\n \n static void add_pushurl_alias(struct remote *remote, const char *url)\n {\n-\tconst char *pushurl = alias_url(url, &rewrites_push);\n+\tconst char *pushurl = alias_url(url, &remote_state->rewrites_push);\n \tif (pushurl != url)\n \t\tadd_pushurl(remote, pushurl);\n }\n \n static void add_url_alias(struct remote *remote, const char *url)\n {\n-\tadd_url(remote, alias_url(url, &rewrites));\n+\tadd_url(remote, alias_url(url, &remote_state->rewrites));\n \tadd_pushurl_alias(remote, url);\n }\n \n@@ -129,8 +133,9 @@ static int remotes_hash_cmp(const void *unused_cmp_data,\n \n static inline void init_remotes_hash(void)\n {\n-\tif (!remotes_hash.cmpfn)\n-\t\thashmap_init(&remotes_hash, remotes_hash_cmp, NULL, 0);\n+\tif (!remote_state->remotes_hash.cmpfn)\n+\t\thashmap_init(&remote_state->remotes_hash, remotes_hash_cmp,\n+\t\t\t     NULL, 0);\n }\n \n static struct remote *make_remote(const char *name, int len)\n@@ -147,7 +152,7 @@ static struct remote *make_remote(const char *name, int len)\n \tlookup.len = len;\n \thashmap_entry_init(&lookup_entry, memhash(name, len));\n \n-\te = hashmap_get(&remotes_hash, &lookup_entry, &lookup);\n+\te = hashmap_get(&remote_state->remotes_hash, &lookup_entry, &lookup);\n \tif (e)\n \t\treturn container_of(e, struct remote, ent);\n \n@@ -158,11 +163,12 @@ static struct remote *make_remote(const char *name, int len)\n \trefspec_init(&ret->push, REFSPEC_PUSH);\n \trefspec_init(&ret->fetch, REFSPEC_FETCH);\n \n-\tALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);\n-\tremotes[remotes_nr++] = ret;\n+\tALLOC_GROW(remote_state->remotes, remote_state->remotes_nr + 1,\n+\t\t   remote_state->remotes_alloc);\n+\tremote_state->remotes[remote_state->remotes_nr++] = ret;\n \n \thashmap_entry_init(&ret->ent, lookup_entry.hash);\n-\tif (hashmap_put_entry(&remotes_hash, ret, ent))\n+\tif (hashmap_put_entry(&remote_state->remotes_hash, ret, ent))\n \t\tBUG(\"hashmap_put overwrote entry after hashmap_get returned NULL\");\n \treturn ret;\n }\n@@ -179,15 +185,16 @@ static struct branch *make_branch(const char *name, size_t len)\n \tstruct branch *ret;\n \tint i;\n \n-\tfor (i = 0; i < branches_nr; i++) {\n-\t\tif (!strncmp(name, branches[i]->name, len) &&\n-\t\t    !branches[i]->name[len])\n-\t\t\treturn branches[i];\n+\tfor (i = 0; i < remote_state->branches_nr; i++) {\n+\t\tif (!strncmp(name, remote_state->branches[i]->name, len) &&\n+\t\t    !remote_state->branches[i]->name[len])\n+\t\t\treturn remote_state->branches[i];\n \t}\n \n-\tALLOC_GROW(branches, branches_nr + 1, branches_alloc);\n+\tALLOC_GROW(remote_state->branches, remote_state->branches_nr + 1,\n+\t\t   remote_state->branches_alloc);\n \tCALLOC_ARRAY(ret, 1);\n-\tbranches[branches_nr++] = ret;\n+\tremote_state->branches[remote_state->branches_nr++] = ret;\n \tret->name = xstrndup(name, len);\n \tret->refname = xstrfmt(\"refs/heads/%s\", ret->name);\n \n@@ -327,12 +334,14 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\tif (!strcmp(subkey, \"insteadof\")) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n-\t\t\trewrite = make_rewrite(&rewrites, name, namelen);\n+\t\t\trewrite = make_rewrite(&remote_state->rewrites, name,\n+\t\t\t\t\t       namelen);\n \t\t\tadd_instead_of(rewrite, xstrdup(value));\n \t\t} else if (!strcmp(subkey, \"pushinsteadof\")) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n-\t\t\trewrite = make_rewrite(&rewrites_push, name, namelen);\n+\t\t\trewrite = make_rewrite(&remote_state->rewrites_push,\n+\t\t\t\t\t       name, namelen);\n \t\t\tadd_instead_of(rewrite, xstrdup(value));\n \t\t}\n \t}\n@@ -342,7 +351,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \n \t/* Handle remote.* variables */\n \tif (!name && !strcmp(subkey, \"pushdefault\"))\n-\t\treturn git_config_string(&pushremote_name, key, value);\n+\t\treturn git_config_string(&remote_state->pushremote_name, key,\n+\t\t\t\t\t value);\n \n \tif (!name)\n \t\treturn 0;\n@@ -425,18 +435,24 @@ static int handle_config(const char *key, const char *value, void *cb)\n static void alias_all_urls(void)\n {\n \tint i, j;\n-\tfor (i = 0; i < remotes_nr; i++) {\n+\tfor (i = 0; i < remote_state->remotes_nr; i++) {\n \t\tint add_pushurl_aliases;\n-\t\tif (!remotes[i])\n+\t\tif (!remote_state->remotes[i])\n \t\t\tcontinue;\n-\t\tfor (j = 0; j < remotes[i]->pushurl_nr; j++) {\n-\t\t\tremotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j], &rewrites);\n+\t\tfor (j = 0; j < remote_state->remotes[i]->pushurl_nr; j++) {\n+\t\t\tremote_state->remotes[i]->pushurl[j] =\n+\t\t\t\talias_url(remote_state->remotes[i]->pushurl[j],\n+\t\t\t\t\t  &remote_state->rewrites);\n \t\t}\n-\t\tadd_pushurl_aliases = remotes[i]->pushurl_nr == 0;\n-\t\tfor (j = 0; j < remotes[i]->url_nr; j++) {\n+\t\tadd_pushurl_aliases = remote_state->remotes[i]->pushurl_nr == 0;\n+\t\tfor (j = 0; j < remote_state->remotes[i]->url_nr; j++) {\n \t\t\tif (add_pushurl_aliases)\n-\t\t\t\tadd_pushurl_alias(remotes[i], remotes[i]->url[j]);\n-\t\t\tremotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);\n+\t\t\t\tadd_pushurl_alias(\n+\t\t\t\t\tremote_state->remotes[i],\n+\t\t\t\t\tremote_state->remotes[i]->url[j]);\n+\t\t\tremote_state->remotes[i]->url[j] =\n+\t\t\t\talias_url(remote_state->remotes[i]->url[j],\n+\t\t\t\t\t  &remote_state->rewrites);\n \t\t}\n \t}\n }\n@@ -450,12 +466,13 @@ static void read_config(void)\n \t\treturn;\n \tloaded = 1;\n \n-\tcurrent_branch = NULL;\n+\tremote_state->current_branch = NULL;\n \tif (startup_info->have_repository) {\n \t\tconst char *head_ref = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flag);\n \t\tif (head_ref && (flag & REF_ISSYMREF) &&\n \t\t    skip_prefix(head_ref, \"refs/heads/\", &head_ref)) {\n-\t\t\tcurrent_branch = make_branch(head_ref, strlen(head_ref));\n+\t\t\tremote_state->current_branch =\n+\t\t\t\tmake_branch(head_ref, strlen(head_ref));\n \t\t}\n \t}\n \tgit_config(handle_config, NULL);\n@@ -493,10 +510,10 @@ const char *pushremote_for_branch(struct branch *branch, int *explicit)\n \t\t\t*explicit = 1;\n \t\treturn branch->pushremote_name;\n \t}\n-\tif (pushremote_name) {\n+\tif (remote_state->pushremote_name) {\n \t\tif (explicit)\n \t\t\t*explicit = 1;\n-\t\treturn pushremote_name;\n+\t\treturn remote_state->pushremote_name;\n \t}\n \treturn remote_for_branch(branch, explicit);\n }\n@@ -534,7 +551,7 @@ static struct remote *remote_get_1(const char *name,\n \tif (name)\n \t\tname_given = 1;\n \telse\n-\t\tname = get_default(current_branch, &name_given);\n+\t\tname = get_default(remote_state->current_branch, &name_given);\n \n \tret = make_remote(name, 0);\n \tif (valid_remote_nick(name) && have_git_dir()) {\n@@ -573,11 +590,11 @@ int for_each_remote(each_remote_fn fn, void *priv)\n {\n \tint i, result = 0;\n \tread_config();\n-\tfor (i = 0; i < remotes_nr && !result; i++) {\n-\t\tstruct remote *r = remotes[i];\n-\t\tif (!r)\n+\tfor (i = 0; i < remote_state->remotes_nr && !result; i++) {\n+\t\tstruct remote *remote = remote_state->remotes[i];\n+\t\tif (!remote)\n \t\t\tcontinue;\n-\t\tresult = fn(r, priv);\n+\t\tresult = fn(remote, priv);\n \t}\n \treturn result;\n }\n@@ -1685,7 +1702,7 @@ struct branch *branch_get(const char *name)\n \n \tread_config();\n \tif (!name || !*name || !strcmp(name, \"HEAD\"))\n-\t\tret = current_branch;\n+\t\tret = remote_state->current_branch;\n \telse\n \t\tret = make_branch(name, strlen(name));\n \tset_merge(ret);\n-- \ngitgitgadget\n\n"},{"id":"438196","messageId":"5316ac086d89065cd9bda38bd475f6699adc928e.1633633635.git.gitgitgadget@gmail.com","threadId":"56659","inReplyTo":"pull.1103.git.git.1633633635.gitgitgadget@gmail.com","subject":"[PATCH 2/2] remote: add remote_state to struct repository","fromName":"Glen Choo via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-10-07T19:07:15Z","receivedAt":"2021-10-07T19:07:23Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"From: Glen Choo <chooglen@google.com>\n\nAdd struct remote_state to struct repository and remove the global\nstruct remote_state from remote.c. In order for remote.c to receive\nstruct repository, introduce a family of repo_* functions that\naccept a struct repository argument.\n\nThe existing functions in remote.h that use the_repository are\nreimplemented as thin wrappers around their repo_* counterparts.\n\nSigned-off-by: Glen Choo <chooglen@google.com>\n---\n remote.c     | 186 ++++++++++++++++++++++++++++++---------------------\n remote.h     |  69 +++++++++++++++++--\n repository.c |   8 +++\n repository.h |   4 ++\n 4 files changed, 184 insertions(+), 83 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 55997210060..3a0a130ad2a 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -21,37 +21,6 @@ struct counted_string {\n \tsize_t len;\n \tconst char *s;\n };\n-struct rewrite {\n-\tconst char *base;\n-\tsize_t baselen;\n-\tstruct counted_string *instead_of;\n-\tint instead_of_nr;\n-\tint instead_of_alloc;\n-};\n-struct rewrites {\n-\tstruct rewrite **rewrite;\n-\tint rewrite_alloc;\n-\tint rewrite_nr;\n-};\n-\n-static struct remote_state {\n-\tstruct remote **remotes;\n-\tint remotes_alloc;\n-\tint remotes_nr;\n-\tstruct hashmap remotes_hash;\n-\n-\tstruct branch **branches;\n-\tint branches_alloc;\n-\tint branches_nr;\n-\n-\tstruct branch *current_branch;\n-\tconst char *pushremote_name;\n-\n-\tstruct rewrites rewrites;\n-\tstruct rewrites rewrites_push;\n-} remotes;\n-\n-struct remote_state *remote_state = &remotes;\n \n static int valid_remote(const struct remote *remote)\n {\n@@ -96,17 +65,19 @@ static void add_pushurl(struct remote *remote, const char *pushurl)\n \tremote->pushurl[remote->pushurl_nr++] = pushurl;\n }\n \n-static void add_pushurl_alias(struct remote *remote, const char *url)\n+static void add_pushurl_alias(struct remote_state *remote_state,\n+\t\t\t      struct remote *remote, const char *url)\n {\n \tconst char *pushurl = alias_url(url, &remote_state->rewrites_push);\n \tif (pushurl != url)\n \t\tadd_pushurl(remote, pushurl);\n }\n \n-static void add_url_alias(struct remote *remote, const char *url)\n+static void add_url_alias(struct remote_state *remote_state,\n+\t\t\t  struct remote *remote, const char *url)\n {\n \tadd_url(remote, alias_url(url, &remote_state->rewrites));\n-\tadd_pushurl_alias(remote, url);\n+\tadd_pushurl_alias(remote_state, remote, url);\n }\n \n struct remotes_hash_key {\n@@ -131,14 +102,15 @@ static int remotes_hash_cmp(const void *unused_cmp_data,\n \t\treturn strcmp(a->name, b->name);\n }\n \n-static inline void init_remotes_hash(void)\n+static inline void init_remotes_hash(struct remote_state *remote_state)\n {\n \tif (!remote_state->remotes_hash.cmpfn)\n \t\thashmap_init(&remote_state->remotes_hash, remotes_hash_cmp,\n \t\t\t     NULL, 0);\n }\n \n-static struct remote *make_remote(const char *name, int len)\n+static struct remote *make_remote(struct remote_state *remote_state,\n+\t\t\t\t  const char *name, int len)\n {\n \tstruct remote *ret;\n \tstruct remotes_hash_key lookup;\n@@ -147,7 +119,7 @@ static struct remote *make_remote(const char *name, int len)\n \tif (!len)\n \t\tlen = strlen(name);\n \n-\tinit_remotes_hash();\n+\tinit_remotes_hash(remote_state);\n \tlookup.str = name;\n \tlookup.len = len;\n \thashmap_entry_init(&lookup_entry, memhash(name, len));\n@@ -173,6 +145,28 @@ static struct remote *make_remote(const char *name, int len)\n \treturn ret;\n }\n \n+static void remote_clear(struct remote *remote)\n+{\n+\tint i;\n+\n+\tfree((char *)remote->name);\n+\tfree((char *)remote->foreign_vcs);\n+\n+\tfor (i = 0; i < remote->url_nr; i++) {\n+\t\tfree((char *)remote->url[i]);\n+\t}\n+\tFREE_AND_NULL(remote->pushurl);\n+\n+\tfor (i = 0; i < remote->pushurl_nr; i++) {\n+\t\tfree((char *)remote->pushurl[i]);\n+\t}\n+\tFREE_AND_NULL(remote->pushurl);\n+\tfree((char *)remote->receivepack);\n+\tfree((char *)remote->uploadpack);\n+\tFREE_AND_NULL(remote->http_proxy);\n+\tFREE_AND_NULL(remote->http_proxy_authmethod);\n+}\n+\n static void add_merge(struct branch *branch, const char *name)\n {\n \tALLOC_GROW(branch->merge_name, branch->merge_nr + 1,\n@@ -180,7 +174,8 @@ static void add_merge(struct branch *branch, const char *name)\n \tbranch->merge_name[branch->merge_nr++] = name;\n }\n \n-static struct branch *make_branch(const char *name, size_t len)\n+static struct branch *make_branch(struct remote_state *remote_state,\n+\t\t\t\t  const char *name, size_t len)\n {\n \tstruct branch *ret;\n \tint i;\n@@ -236,7 +231,8 @@ static const char *skip_spaces(const char *s)\n \treturn s;\n }\n \n-static void read_remotes_file(struct remote *remote)\n+static void read_remotes_file(struct remote_state *remote_state,\n+\t\t\t      struct remote *remote)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tFILE *f = fopen_or_warn(git_path(\"remotes/%s\", remote->name), \"r\");\n@@ -251,7 +247,8 @@ static void read_remotes_file(struct remote *remote)\n \t\tstrbuf_rtrim(&buf);\n \n \t\tif (skip_prefix(buf.buf, \"URL:\", &v))\n-\t\t\tadd_url_alias(remote, xstrdup(skip_spaces(v)));\n+\t\t\tadd_url_alias(remote_state, remote,\n+\t\t\t\t      xstrdup(skip_spaces(v)));\n \t\telse if (skip_prefix(buf.buf, \"Push:\", &v))\n \t\t\trefspec_append(&remote->push, skip_spaces(v));\n \t\telse if (skip_prefix(buf.buf, \"Pull:\", &v))\n@@ -261,7 +258,8 @@ static void read_remotes_file(struct remote *remote)\n \tfclose(f);\n }\n \n-static void read_branches_file(struct remote *remote)\n+static void read_branches_file(struct remote_state *remote_state,\n+\t\t\t       struct remote *remote)\n {\n \tchar *frag;\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -293,7 +291,7 @@ static void read_branches_file(struct remote *remote)\n \telse\n \t\tfrag = (char *)git_default_branch_name(0);\n \n-\tadd_url_alias(remote, strbuf_detach(&buf, NULL));\n+\tadd_url_alias(remote_state, remote, strbuf_detach(&buf, NULL));\n \trefspec_appendf(&remote->fetch, \"refs/heads/%s:refs/heads/%s\",\n \t\t\tfrag, remote->name);\n \n@@ -312,10 +310,11 @@ static int handle_config(const char *key, const char *value, void *cb)\n \tconst char *subkey;\n \tstruct remote *remote;\n \tstruct branch *branch;\n+\tstruct remote_state *remote_state = cb;\n \tif (parse_config_key(key, \"branch\", &name, &namelen, &subkey) >= 0) {\n \t\tif (!name)\n \t\t\treturn 0;\n-\t\tbranch = make_branch(name, namelen);\n+\t\tbranch = make_branch(remote_state, name, namelen);\n \t\tif (!strcmp(subkey, \"remote\")) {\n \t\t\treturn git_config_string(&branch->remote_name, key, value);\n \t\t} else if (!strcmp(subkey, \"pushremote\")) {\n@@ -362,7 +361,7 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\t\tname);\n \t\treturn 0;\n \t}\n-\tremote = make_remote(name, namelen);\n+\tremote = make_remote(remote_state, name, namelen);\n \tremote->origin = REMOTE_CONFIG;\n \tif (current_config_scope() == CONFIG_SCOPE_LOCAL ||\n \t    current_config_scope() == CONFIG_SCOPE_WORKTREE)\n@@ -432,7 +431,7 @@ static int handle_config(const char *key, const char *value, void *cb)\n \treturn 0;\n }\n \n-static void alias_all_urls(void)\n+static void alias_all_urls(struct remote_state *remote_state)\n {\n \tint i, j;\n \tfor (i = 0; i < remote_state->remotes_nr; i++) {\n@@ -448,7 +447,7 @@ static void alias_all_urls(void)\n \t\tfor (j = 0; j < remote_state->remotes[i]->url_nr; j++) {\n \t\t\tif (add_pushurl_aliases)\n \t\t\t\tadd_pushurl_alias(\n-\t\t\t\t\tremote_state->remotes[i],\n+\t\t\t\t\tremote_state, remote_state->remotes[i],\n \t\t\t\t\tremote_state->remotes[i]->url[j]);\n \t\t\tremote_state->remotes[i]->url[j] =\n \t\t\t\talias_url(remote_state->remotes[i]->url[j],\n@@ -457,26 +456,25 @@ static void alias_all_urls(void)\n \t}\n }\n \n-static void read_config(void)\n+static void read_config(struct repository *repo)\n {\n-\tstatic int loaded;\n \tint flag;\n \n-\tif (loaded)\n+\tif (repo->remote_state->config_loaded)\n \t\treturn;\n-\tloaded = 1;\n+\trepo->remote_state->config_loaded = 1;\n \n-\tremote_state->current_branch = NULL;\n+\trepo->remote_state->current_branch = NULL;\n \tif (startup_info->have_repository) {\n \t\tconst char *head_ref = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flag);\n \t\tif (head_ref && (flag & REF_ISSYMREF) &&\n \t\t    skip_prefix(head_ref, \"refs/heads/\", &head_ref)) {\n-\t\t\tremote_state->current_branch =\n-\t\t\t\tmake_branch(head_ref, strlen(head_ref));\n+\t\t\trepo->remote_state->current_branch = make_branch(\n+\t\t\t\trepo->remote_state, head_ref, strlen(head_ref));\n \t\t}\n \t}\n-\tgit_config(handle_config, NULL);\n-\talias_all_urls();\n+\trepo_config(repo, handle_config, repo->remote_state);\n+\talias_all_urls(repo->remote_state);\n }\n \n static int valid_remote_nick(const char *name)\n@@ -503,17 +501,18 @@ const char *remote_for_branch(struct branch *branch, int *explicit)\n \treturn \"origin\";\n }\n \n-const char *pushremote_for_branch(struct branch *branch, int *explicit)\n+const char *repo_pushremote_for_branch(struct repository *repo,\n+\t\t\t\t       struct branch *branch, int *explicit)\n {\n \tif (branch && branch->pushremote_name) {\n \t\tif (explicit)\n \t\t\t*explicit = 1;\n \t\treturn branch->pushremote_name;\n \t}\n-\tif (remote_state->pushremote_name) {\n+\tif (repo->remote_state->pushremote_name) {\n \t\tif (explicit)\n \t\t\t*explicit = 1;\n-\t\treturn remote_state->pushremote_name;\n+\t\treturn repo->remote_state->pushremote_name;\n \t}\n \treturn remote_for_branch(branch, explicit);\n }\n@@ -540,41 +539,43 @@ const char *remote_ref_for_branch(struct branch *branch, int for_push)\n \treturn NULL;\n }\n \n-static struct remote *remote_get_1(const char *name,\n-\t\t\t\t   const char *(*get_default)(struct branch *, int *))\n+static struct remote *remote_get_1(struct repository *repo, const char *name,\n+\t\t\t\t   const char *(*get_default)(struct branch *,\n+\t\t\t\t\t\t\t      int *))\n {\n \tstruct remote *ret;\n \tint name_given = 0;\n \n-\tread_config();\n+\tread_config(repo);\n \n \tif (name)\n \t\tname_given = 1;\n \telse\n-\t\tname = get_default(remote_state->current_branch, &name_given);\n+\t\tname = get_default(repo->remote_state->current_branch,\n+\t\t\t\t   &name_given);\n \n-\tret = make_remote(name, 0);\n+\tret = make_remote(repo->remote_state, name, 0);\n \tif (valid_remote_nick(name) && have_git_dir()) {\n \t\tif (!valid_remote(ret))\n-\t\t\tread_remotes_file(ret);\n+\t\t\tread_remotes_file(repo->remote_state, ret);\n \t\tif (!valid_remote(ret))\n-\t\t\tread_branches_file(ret);\n+\t\t\tread_branches_file(repo->remote_state, ret);\n \t}\n \tif (name_given && !valid_remote(ret))\n-\t\tadd_url_alias(ret, name);\n+\t\tadd_url_alias(repo->remote_state, ret, name);\n \tif (!valid_remote(ret))\n \t\treturn NULL;\n \treturn ret;\n }\n \n-struct remote *remote_get(const char *name)\n+struct remote *repo_remote_get(struct repository *repo, const char *name)\n {\n-\treturn remote_get_1(name, remote_for_branch);\n+\treturn remote_get_1(repo, name, remote_for_branch);\n }\n \n-struct remote *pushremote_get(const char *name)\n+struct remote *repo_pushremote_get(struct repository *repo, const char *name)\n {\n-\treturn remote_get_1(name, pushremote_for_branch);\n+\treturn remote_get_1(repo, name, pushremote_for_branch);\n }\n \n int remote_is_configured(struct remote *remote, int in_repo)\n@@ -586,12 +587,12 @@ int remote_is_configured(struct remote *remote, int in_repo)\n \treturn !!remote->origin;\n }\n \n-int for_each_remote(each_remote_fn fn, void *priv)\n+int repo_for_each_remote(struct repository *repo, each_remote_fn fn, void *priv)\n {\n \tint i, result = 0;\n-\tread_config();\n-\tfor (i = 0; i < remote_state->remotes_nr && !result; i++) {\n-\t\tstruct remote *remote = remote_state->remotes[i];\n+\tread_config(repo);\n+\tfor (i = 0; i < repo->remote_state->remotes_nr && !result; i++) {\n+\t\tstruct remote *remote = repo->remote_state->remotes[i];\n \t\tif (!remote)\n \t\t\tcontinue;\n \t\tresult = fn(remote, priv);\n@@ -1696,15 +1697,15 @@ static void set_merge(struct branch *ret)\n \t}\n }\n \n-struct branch *branch_get(const char *name)\n+struct branch *repo_branch_get(struct repository *repo, const char *name)\n {\n \tstruct branch *ret;\n \n-\tread_config();\n+\tread_config(repo);\n \tif (!name || !*name || !strcmp(name, \"HEAD\"))\n-\t\tret = remote_state->current_branch;\n+\t\tret = repo->remote_state->current_branch;\n \telse\n-\t\tret = make_branch(name, strlen(name));\n+\t\tret = make_branch(repo->remote_state, name, strlen(name));\n \tset_merge(ret);\n \treturn ret;\n }\n@@ -2602,3 +2603,32 @@ void apply_push_cas(struct push_cas_option *cas,\n \t\t\tcheck_if_includes_upstream(ref);\n \t}\n }\n+\n+struct remote_state *remote_state_new(void)\n+{\n+\tstruct remote_state *r = xmalloc(sizeof(*r));\n+\n+\tmemset(r, 0, sizeof(*r));\n+\treturn r;\n+}\n+\n+void remote_state_clear(struct remote_state *remote_state)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < remote_state->remotes_nr; i++) {\n+\t\tremote_clear(remote_state->remotes[i]);\n+\t}\n+\tFREE_AND_NULL(remote_state->remotes);\n+\tremote_state->remotes_alloc = 0;\n+\tremote_state->remotes_nr = 0;\n+\n+\thashmap_clear_and_free(&remote_state->remotes_hash, struct remote, ent);\n+\n+\tfor (i = 0; i < remote_state->branches_nr; i++) {\n+\t\tFREE_AND_NULL(remote_state->branches[i]);\n+\t}\n+\tFREE_AND_NULL(remote_state->branches);\n+\tremote_state->branches_alloc = 0;\n+\tremote_state->branches_nr = 0;\n+}\ndiff --git a/remote.h b/remote.h\nindex 5a591982528..3cba8eb8cb6 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -23,6 +23,40 @@ enum {\n \tREMOTE_BRANCHES\n };\n \n+struct rewrite {\n+\tconst char *base;\n+\tsize_t baselen;\n+\tstruct counted_string *instead_of;\n+\tint instead_of_nr;\n+\tint instead_of_alloc;\n+};\n+struct rewrites {\n+\tstruct rewrite **rewrite;\n+\tint rewrite_alloc;\n+\tint rewrite_nr;\n+};\n+\n+struct remote_state {\n+\tint config_loaded;\n+\n+\tstruct remote **remotes;\n+\tint remotes_alloc;\n+\tint remotes_nr;\n+\tstruct hashmap remotes_hash;\n+\n+\tstruct branch **branches;\n+\tint branches_alloc;\n+\tint branches_nr;\n+\n+\tstruct branch *current_branch;\n+\tconst char *pushremote_name;\n+\n+\tstruct rewrites rewrites;\n+\tstruct rewrites rewrites_push;\n+};\n+void remote_state_clear(struct remote_state *remote_state);\n+struct remote_state *remote_state_new(void);\n+\n struct remote {\n \tstruct hashmap_entry ent;\n \n@@ -83,15 +117,29 @@ struct remote {\n  * remote_get(NULL) will return the default remote, given the current branch\n  * and configuration.\n  */\n-struct remote *remote_get(const char *name);\n+struct remote *repo_remote_get(struct repository *repo, const char *name);\n+static inline struct remote *remote_get(const char *name)\n+{\n+\treturn repo_remote_get(the_repository, name);\n+}\n+\n+struct remote *repo_pushremote_get(struct repository *repo, const char *name);\n+static inline struct remote *pushremote_get(const char *name)\n+{\n+\treturn repo_pushremote_get(the_repository, name);\n+}\n \n-struct remote *pushremote_get(const char *name);\n int remote_is_configured(struct remote *remote, int in_repo);\n \n typedef int each_remote_fn(struct remote *remote, void *priv);\n \n /* iterate through struct remotes */\n-int for_each_remote(each_remote_fn fn, void *priv);\n+int repo_for_each_remote(struct repository *repo, each_remote_fn fn,\n+\t\t\t void *priv);\n+static inline int for_each_remote(each_remote_fn fn, void *priv)\n+{\n+\treturn repo_for_each_remote(the_repository, fn, priv);\n+}\n \n int remote_has_url(struct remote *remote, const char *url);\n \n@@ -286,9 +334,20 @@ struct branch {\n \tconst char *push_tracking_ref;\n };\n \n-struct branch *branch_get(const char *name);\n+struct branch *repo_branch_get(struct repository *repo, const char *name);\n+static inline struct branch *branch_get(const char *name)\n+{\n+\treturn repo_branch_get(the_repository, name);\n+}\n const char *remote_for_branch(struct branch *branch, int *explicit);\n-const char *pushremote_for_branch(struct branch *branch, int *explicit);\n+const char *repo_pushremote_for_branch(struct repository *repo,\n+\t\t\t\t       struct branch *branch, int *explicit);\n+static inline const char *pushremote_for_branch(struct branch *branch,\n+\t\t\t\t\t\tint *explicit)\n+{\n+\treturn repo_pushremote_for_branch(the_repository, branch, explicit);\n+}\n+\n const char *remote_ref_for_branch(struct branch *branch, int for_push);\n \n /* returns true if the given branch has merge configuration given. */\ndiff --git a/repository.c b/repository.c\nindex 710a3b4bf87..47a27c8efb3 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -9,6 +9,7 @@\n #include \"config.h\"\n #include \"object.h\"\n #include \"lockfile.h\"\n+#include \"remote.h\"\n #include \"submodule-config.h\"\n #include \"sparse-index.h\"\n #include \"promisor-remote.h\"\n@@ -24,6 +25,7 @@ void initialize_the_repository(void)\n \n \tthe_repo.index = &the_index;\n \tthe_repo.objects = raw_object_store_new();\n+\tthe_repo.remote_state = remote_state_new();\n \tthe_repo.parsed_objects = parsed_object_pool_new();\n \n \trepo_set_hash_algo(&the_repo, GIT_HASH_SHA1);\n@@ -164,6 +166,7 @@ int repo_init(struct repository *repo,\n \n \trepo->objects = raw_object_store_new();\n \trepo->parsed_objects = parsed_object_pool_new();\n+\trepo->remote_state = remote_state_new();\n \n \tif (repo_init_gitdir(repo, gitdir))\n \t\tgoto error;\n@@ -267,6 +270,11 @@ void repo_clear(struct repository *repo)\n \t\tpromisor_remote_clear(repo->promisor_remote_config);\n \t\tFREE_AND_NULL(repo->promisor_remote_config);\n \t}\n+\n+\tif (repo->remote_state) {\n+\t\tremote_state_clear(repo->remote_state);\n+\t\tFREE_AND_NULL(repo->remote_state);\n+\t}\n }\n \n int repo_read_index(struct repository *repo)\ndiff --git a/repository.h b/repository.h\nindex 3740c93bc0f..4bcdbc22ed4 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -11,6 +11,7 @@ struct pathspec;\n struct raw_object_store;\n struct submodule_cache;\n struct promisor_remote_config;\n+struct remote_state;\n \n enum untracked_cache_setting {\n \tUNTRACKED_CACHE_UNSET = -1,\n@@ -131,6 +132,9 @@ struct repository {\n \t */\n \tstruct index_state *index;\n \n+\t/* Repository's remotes and associated structures. */\n+\tstruct remote_state *remote_state;\n+\n \t/* Repository's current hash algorithm, as serialized on disk. */\n \tconst struct git_hash_algo *hash_algo;\n \n-- \ngitgitgadget\n"},{"id":"438247","messageId":"xmqq8rz4o1ay.fsf@gitster.g","threadId":"56659","inReplyTo":"f7639a20fcd437d5f50665b5c6f5318c1245b9b5.1633633635.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 1/2] remote: move static variables into struct","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-07T23:36:05Z","receivedAt":"2021-10-07T23:36:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Glen Choo via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Glen Choo <chooglen@google.com>\n>\n> remote.c only works with the_repository because it stores its state as\n> static variables. To support non-the_repository, we can use a\n> per-repository struct for the remotes subsystem.\n\nOK, so eventually we can hook the struct this patch introduces as a\nmember in the repository structure, so that we can have one such\nstruct per in-core repository instances?\n\nWill some of the component of this new structure have to be aware of\nthe containing structure, even the repository instance the\ncontaining structure belongs to?\n\nFor example, the apply_push_cas() function (there is nothing special\nabout it---just a random example near the end of file) takes \"struct\nremote *\" which will be passed to underlying apply_cas() and the\ncontrol flow eventually need to reach read_ref() via\nremote_tracking().  In a future world where we keep multiple in-core\nrepositories and interact with multiple repositories in-process,\nthis read_ref() must know in which repository it is asked to read\nthe value of the ref.  Do we expect to plumb \"struct repository *\"\nthroughout the callchain, or will some of the struct types gain a\nbackpointer to \"struct remote_state\", and \"struct remote_state\" will\nin turn have a backpointer to the containing repository, so that\nremote_tracking() can learn the ref_store it needs to use when\nmaking a call to refs_read_ref_full() to replace the current call it\nmakes to the read_ref() function?\n\nAs to the name, I do not think \"remote state\" is not too bad, but\nthe word \"remote\" there certainly does not mean \"state of a remote\nrepository\", and in that sense, it might be confusing.\n\nBut It is still a seemingly random \"collection of data that we need\nto interact with remote repositories\", so in that sense, the word\n\"remote\" fits its purpose.  There are pieces of information needed\nfor a branch to interact with a remote repository, and the branches\nvariable serves as a caching mechanism for that, for example.\n"},{"id":"438248","messageId":"xmqq4k9so15i.fsf@gitster.g","threadId":"56659","inReplyTo":"5316ac086d89065cd9bda38bd475f6699adc928e.1633633635.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 2/2] remote: add remote_state to struct repository","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-07T23:39:21Z","receivedAt":"2021-10-07T23:39:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Glen Choo via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> -static void add_pushurl_alias(struct remote *remote, const char *url)\n> +static void add_pushurl_alias(struct remote_state *remote_state,\n> +\t\t\t      struct remote *remote, const char *url)\n>  {\n\nI am not sure if this is a good interface.  It allows a caller to\nobtain \"struct remote *\" instance from somewhere, and feed it with\nan instance of \"struct remote_state *\" that has nothing to do with\nthe \"struct remote *\", no?\n\n> -static struct remote *make_remote(const char *name, int len)\n> +static struct remote *make_remote(struct remote_state *remote_state,\n> +\t\t\t\t  const char *name, int len)\n>  {\n>  \tstruct remote *ret;\n>  \tstruct remotes_hash_key lookup;\n> @@ -147,7 +119,7 @@ static struct remote *make_remote(const char *name, int len)\n>  \tif (!len)\n>  \t\tlen = strlen(name);\n>  \n> -\tinit_remotes_hash();\n> +\tinit_remotes_hash(remote_state);\n>  \tlookup.str = name;\n>  \tlookup.len = len;\n>  \thashmap_entry_init(&lookup_entry, memhash(name, len));\n> @@ -173,6 +145,28 @@ static struct remote *make_remote(const char *name, int len)\n>  \treturn ret;\n\nInstead, shouldn't \"struct remote *\" _know_ which remote-state it\ncame from?\n\nI didn't look, but I suspect that there may be similar problems with\nother structures like \"branch\" in this change.\n\nThanks.\n"},{"id":"438301","messageId":"kl6lo87zxw3x.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"56659","inReplyTo":"xmqq4k9so15i.fsf@gitster.g","subject":"Re: [PATCH 2/2] remote: add remote_state to struct repository","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-08T17:30:26Z","receivedAt":"2021-10-08T17:30:30Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> -static void add_pushurl_alias(struct remote *remote, const char *url)\n>> +static void add_pushurl_alias(struct remote_state *remote_state,\n>> +\t\t\t      struct remote *remote, const char *url)\n>>  {\n>\n> I am not sure if this is a good interface.  It allows a caller to\n> obtain \"struct remote *\" instance from somewhere, and feed it with\n> an instance of \"struct remote_state *\" that has nothing to do with\n> the \"struct remote *\", no?\n\nValid point, this interface should not be so easy to misuse.\n\n> Instead, shouldn't \"struct remote *\" _know_ which remote-state it\n> came from?\n\nI am less certain about this. The pattern of\ncontainer->contained->container is convenient for callers, but requires\nvery deliberate maintenance and the reduced separation might promote\nthoughtless use of the interfaces e.g. \"Should I use struct remote_state\n+ name or struct remote? Eh doesn't matter, they're equivalent.\"\n\nInstead, we could converge on a pattern of:\n\n* struct remote_state + name when the caller does something in the\n  context of the remote's repository.\n* struct remote when the caller doesn't need the remote's repository\n\nI think we might do this in slightly different ways for branches vs\nremotes.\n\n> I didn't look, but I suspect that there may be similar problems with\n> other structures like \"branch\" in this change.\n\nLooking into it, it appears that only static functions pass struct\nremote_state and struct remote at the same time. That gives us a lot of\nleeway to clean things up inside of remote.c.\n\nThe same cannot be said for struct branch, that will be much harder to\nclean up. In fact, rereading patch 2, I missed many implicit references\nto the_repository in the branch functions, so the pattern of struct\nremote_state + struct branch would actually more pervasive than it seems.\n\nHowever, I suspect that we don't need to pass around struct branch very\noften. We may be able to maintain referential integrity by passing the\nbranch name instead.\n"},{"id":"438678","messageId":"20211013193127.76537-1-chooglen@google.com","threadId":"56659","inReplyTo":"pull.1103.git.git.1633633635.gitgitgadget@gmail.com","subject":"[PATCH v2 0/3] remote: replace static variables with struct remote_state","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-13T19:31:24Z","receivedAt":"2021-10-13T19:31:34Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"This series aims to make the remotes subsystem work with\nnon-the_repository, which will allow submodule remotes to be accessed\nin-process, rather than through child processes. This is accomplished by\ncreating a struct remote_state and adding it to struct repository.\n\nOne motivation for this is that it allows future submodule commands to run\nin-process. An example is an RFC series of mine [1], where I tried to implement\n\"git branch --recurse-submodules\" in-process but couldn't figure out how to\nread the remotes of a submodule.\n\nv2 aims to catch all of the instances of struct remote_state that I had\nmissed in v1, particularly with the struct branch functions. Because\nmore repo_* functions were added, the preparatory patches have been\nreorganized.\n\nIn this version, my biggest uncertainty is in the interfaces of the\nrepo_* functions. Some functions expect a \"struct repository\" and one\nof its contained members (e.g. \"struct branch\"). This interface allows\nfor incorrect behavior if the caller supplies a \"struct repository\"\ninstance that is unrelated from the \"struct branch\". This concern was\nraised by Junio in [2].\n\nWhile this concern is valid, I decided to keep this interface for a few\nreasons:\n\n1. The correct way of calling the function is 'obvious'.\n2. It is relatively easy to get the contained object (struct branch/remote)\n   and its containing struct repository/remote_state (e.g. we don't pass\n   struct branch or remote through long call chains). For \"struct\n   branch\", callers usually get the branch from the repo and use it\n   immediately. For \"struct remote\", we don't use container objects\n   outside of static functions. If you are interested in seeing all of\n   the call sites, you can see a sample commit in [3].\n3. The separation between container/contained objects allows us to\n   reason better about what the correct interface is. e.g. we might be\n   tempted to include a backpointer from struct branch to struct\n   remote_state so that we can pass around struct branch and be\n   confident that struct branch has all of the information it needs.\n\n   However, I believe that some questions *shouldn't* be answered by\n   just struct branch. The prime example in this series is\n   branch_get_push() - it conceptually answers 'What is the pushremote\n   of this branch', but the behavior we want is closer to 'If\n   configured, give me the pushremote for the branch. Otherwise, give me\n   the default pushremote of the repo.'. This is arguably a relevant\n   detail that should be exposed to callers.\n\nIf we want the interface to prevent misuse, we can check that the\ncorrect repository is passed at runtime. Alternatively, if we are\nconvinced that some questions can only be answered in the context of a\nrepository, we can take things one step further by using (struct\nrepository, branch_name) instead of (struct repository, struct branch).\n\nA different concern is that struct repository is added to some callback\nsignatures even though the function body doesn't use it e.g.\nrepo_remote_for_branch(). However, this might be more of an accident of\nhistory than anything else - the difference between remote and\npushremote is that remote always defaults to 'origin', whereas\nthe default value of pushremote is configurable.\n\nChanges since v1:\n\n* In v1, we moved static variables into the_repository->remote_state in\n  two steps: static variables > static remote_state >\n  the_repository->remote_state. In v2, make this change in one step:\n  static variables > the_repository->remote_state.\n* Add more instances of repo_* that were missed.\n\n[1] https://lore.kernel.org/git/20210921232529.81811-1-chooglen@google.com/\n[2] https://lore.kernel.org/git/xmqq4k9so15i.fsf@gitster.g/\n[3] https://github.com/chooglen/git/commit/173e0268fd076044dd6b3cae893eedfa33965942\n\nGlen Choo (3):\n  remote: move static variables into per-repository struct\n  remote: use remote_state parameter internally\n  remote: add struct repository parameter to external functions\n\n remote.c     | 305 +++++++++++++++++++++++++++++++--------------------\n remote.h     | 130 +++++++++++++++++++---\n repository.c |   8 ++\n repository.h |   4 +\n 4 files changed, 312 insertions(+), 135 deletions(-)\n\nRange-diff against v1:\n1:  6972ba4dcb = 1:  6972ba4dcb remote: move static variables into per-repository struct\n2:  71b1da4389 = 2:  71b1da4389 remote: use remote_state parameter internally\n3:  ff12771f06 = 3:  ff12771f06 remote: add struct repository parameter to external functions\n-- \n2.33.0.882.g93a45727a2-goog\n\n"},{"id":"438679","messageId":"20211013193127.76537-2-chooglen@google.com","threadId":"56659","inReplyTo":"20211013193127.76537-1-chooglen@google.com","subject":"[PATCH v2 1/3] remote: move static variables into per-repository struct","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-13T19:31:25Z","receivedAt":"2021-10-13T19:31:36Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"remote.c does not work with non-the_repository because it stores its\nstate as static variables. To support non-the_repository, we can use a\nper-repository struct for the remotes subsystem.\n\nPrepare for this change by defining a struct remote_state that holds\nthe remotes subsystem state and move the static variables of remote.c\ninto the_repository->remote_state.\n\nThis introduces no behavioral or API changes.\n\nSigned-off-by: Glen Choo <chooglen@google.com>\n---\n remote.c     | 187 ++++++++++++++++++++++++++++++++++-----------------\n remote.h     |  34 ++++++++++\n repository.c |   8 +++\n repository.h |   4 ++\n 4 files changed, 171 insertions(+), 62 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex f958543d70..29c29fcc3b 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -21,33 +21,6 @@ struct counted_string {\n \tsize_t len;\n \tconst char *s;\n };\n-struct rewrite {\n-\tconst char *base;\n-\tsize_t baselen;\n-\tstruct counted_string *instead_of;\n-\tint instead_of_nr;\n-\tint instead_of_alloc;\n-};\n-struct rewrites {\n-\tstruct rewrite **rewrite;\n-\tint rewrite_alloc;\n-\tint rewrite_nr;\n-};\n-\n-static struct remote **remotes;\n-static int remotes_alloc;\n-static int remotes_nr;\n-static struct hashmap remotes_hash;\n-\n-static struct branch **branches;\n-static int branches_alloc;\n-static int branches_nr;\n-\n-static struct branch *current_branch;\n-static const char *pushremote_name;\n-\n-static struct rewrites rewrites;\n-static struct rewrites rewrites_push;\n \n static int valid_remote(const struct remote *remote)\n {\n@@ -94,14 +67,16 @@ static void add_pushurl(struct remote *remote, const char *pushurl)\n \n static void add_pushurl_alias(struct remote *remote, const char *url)\n {\n-\tconst char *pushurl = alias_url(url, &rewrites_push);\n+\tconst char *pushurl =\n+\t\talias_url(url, &the_repository->remote_state->rewrites_push);\n \tif (pushurl != url)\n \t\tadd_pushurl(remote, pushurl);\n }\n \n static void add_url_alias(struct remote *remote, const char *url)\n {\n-\tadd_url(remote, alias_url(url, &rewrites));\n+\tadd_url(remote,\n+\t\talias_url(url, &the_repository->remote_state->rewrites));\n \tadd_pushurl_alias(remote, url);\n }\n \n@@ -129,8 +104,9 @@ static int remotes_hash_cmp(const void *unused_cmp_data,\n \n static inline void init_remotes_hash(void)\n {\n-\tif (!remotes_hash.cmpfn)\n-\t\thashmap_init(&remotes_hash, remotes_hash_cmp, NULL, 0);\n+\tif (!the_repository->remote_state->remotes_hash.cmpfn)\n+\t\thashmap_init(&the_repository->remote_state->remotes_hash,\n+\t\t\t     remotes_hash_cmp, NULL, 0);\n }\n \n static struct remote *make_remote(const char *name, int len)\n@@ -147,7 +123,8 @@ static struct remote *make_remote(const char *name, int len)\n \tlookup.len = len;\n \thashmap_entry_init(&lookup_entry, memhash(name, len));\n \n-\te = hashmap_get(&remotes_hash, &lookup_entry, &lookup);\n+\te = hashmap_get(&the_repository->remote_state->remotes_hash,\n+\t\t\t&lookup_entry, &lookup);\n \tif (e)\n \t\treturn container_of(e, struct remote, ent);\n \n@@ -158,15 +135,41 @@ static struct remote *make_remote(const char *name, int len)\n \trefspec_init(&ret->push, REFSPEC_PUSH);\n \trefspec_init(&ret->fetch, REFSPEC_FETCH);\n \n-\tALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);\n-\tremotes[remotes_nr++] = ret;\n+\tALLOC_GROW(the_repository->remote_state->remotes,\n+\t\t   the_repository->remote_state->remotes_nr + 1,\n+\t\t   the_repository->remote_state->remotes_alloc);\n+\tthe_repository->remote_state\n+\t\t->remotes[the_repository->remote_state->remotes_nr++] = ret;\n \n \thashmap_entry_init(&ret->ent, lookup_entry.hash);\n-\tif (hashmap_put_entry(&remotes_hash, ret, ent))\n+\tif (hashmap_put_entry(&the_repository->remote_state->remotes_hash, ret,\n+\t\t\t      ent))\n \t\tBUG(\"hashmap_put overwrote entry after hashmap_get returned NULL\");\n \treturn ret;\n }\n \n+static void remote_clear(struct remote *remote)\n+{\n+\tint i;\n+\n+\tfree((char *)remote->name);\n+\tfree((char *)remote->foreign_vcs);\n+\n+\tfor (i = 0; i < remote->url_nr; i++) {\n+\t\tfree((char *)remote->url[i]);\n+\t}\n+\tFREE_AND_NULL(remote->pushurl);\n+\n+\tfor (i = 0; i < remote->pushurl_nr; i++) {\n+\t\tfree((char *)remote->pushurl[i]);\n+\t}\n+\tFREE_AND_NULL(remote->pushurl);\n+\tfree((char *)remote->receivepack);\n+\tfree((char *)remote->uploadpack);\n+\tFREE_AND_NULL(remote->http_proxy);\n+\tFREE_AND_NULL(remote->http_proxy_authmethod);\n+}\n+\n static void add_merge(struct branch *branch, const char *name)\n {\n \tALLOC_GROW(branch->merge_name, branch->merge_nr + 1,\n@@ -179,15 +182,20 @@ static struct branch *make_branch(const char *name, size_t len)\n \tstruct branch *ret;\n \tint i;\n \n-\tfor (i = 0; i < branches_nr; i++) {\n-\t\tif (!strncmp(name, branches[i]->name, len) &&\n-\t\t    !branches[i]->name[len])\n-\t\t\treturn branches[i];\n+\tfor (i = 0; i < the_repository->remote_state->branches_nr; i++) {\n+\t\tif (!strncmp(name,\n+\t\t\t     the_repository->remote_state->branches[i]->name,\n+\t\t\t     len) &&\n+\t\t    !the_repository->remote_state->branches[i]->name[len])\n+\t\t\treturn the_repository->remote_state->branches[i];\n \t}\n \n-\tALLOC_GROW(branches, branches_nr + 1, branches_alloc);\n+\tALLOC_GROW(the_repository->remote_state->branches,\n+\t\t   the_repository->remote_state->branches_nr + 1,\n+\t\t   the_repository->remote_state->branches_alloc);\n \tCALLOC_ARRAY(ret, 1);\n-\tbranches[branches_nr++] = ret;\n+\tthe_repository->remote_state\n+\t\t->branches[the_repository->remote_state->branches_nr++] = ret;\n \tret->name = xstrndup(name, len);\n \tret->refname = xstrfmt(\"refs/heads/%s\", ret->name);\n \n@@ -327,12 +335,16 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\tif (!strcmp(subkey, \"insteadof\")) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n-\t\t\trewrite = make_rewrite(&rewrites, name, namelen);\n+\t\t\trewrite = make_rewrite(\n+\t\t\t\t&the_repository->remote_state->rewrites, name,\n+\t\t\t\tnamelen);\n \t\t\tadd_instead_of(rewrite, xstrdup(value));\n \t\t} else if (!strcmp(subkey, \"pushinsteadof\")) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n-\t\t\trewrite = make_rewrite(&rewrites_push, name, namelen);\n+\t\t\trewrite = make_rewrite(\n+\t\t\t\t&the_repository->remote_state->rewrites_push,\n+\t\t\t\tname, namelen);\n \t\t\tadd_instead_of(rewrite, xstrdup(value));\n \t\t}\n \t}\n@@ -342,7 +354,9 @@ static int handle_config(const char *key, const char *value, void *cb)\n \n \t/* Handle remote.* variables */\n \tif (!name && !strcmp(subkey, \"pushdefault\"))\n-\t\treturn git_config_string(&pushremote_name, key, value);\n+\t\treturn git_config_string(\n+\t\t\t&the_repository->remote_state->pushremote_name, key,\n+\t\t\tvalue);\n \n \tif (!name)\n \t\treturn 0;\n@@ -425,18 +439,34 @@ static int handle_config(const char *key, const char *value, void *cb)\n static void alias_all_urls(void)\n {\n \tint i, j;\n-\tfor (i = 0; i < remotes_nr; i++) {\n+\tfor (i = 0; i < the_repository->remote_state->remotes_nr; i++) {\n \t\tint add_pushurl_aliases;\n-\t\tif (!remotes[i])\n+\t\tif (!the_repository->remote_state->remotes[i])\n \t\t\tcontinue;\n-\t\tfor (j = 0; j < remotes[i]->pushurl_nr; j++) {\n-\t\t\tremotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j], &rewrites);\n+\t\tfor (j = 0;\n+\t\t     j < the_repository->remote_state->remotes[i]->pushurl_nr;\n+\t\t     j++) {\n+\t\t\tthe_repository->remote_state->remotes[i]->pushurl[j] =\n+\t\t\t\talias_url(\n+\t\t\t\t\tthe_repository->remote_state->remotes[i]\n+\t\t\t\t\t\t->pushurl[j],\n+\t\t\t\t\t&the_repository->remote_state->rewrites);\n \t\t}\n-\t\tadd_pushurl_aliases = remotes[i]->pushurl_nr == 0;\n-\t\tfor (j = 0; j < remotes[i]->url_nr; j++) {\n+\t\tadd_pushurl_aliases =\n+\t\t\tthe_repository->remote_state->remotes[i]->pushurl_nr ==\n+\t\t\t0;\n+\t\tfor (j = 0;\n+\t\t     j < the_repository->remote_state->remotes[i]->url_nr;\n+\t\t     j++) {\n \t\t\tif (add_pushurl_aliases)\n-\t\t\t\tadd_pushurl_alias(remotes[i], remotes[i]->url[j]);\n-\t\t\tremotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);\n+\t\t\t\tadd_pushurl_alias(\n+\t\t\t\t\tthe_repository->remote_state->remotes[i],\n+\t\t\t\t\tthe_repository->remote_state->remotes[i]\n+\t\t\t\t\t\t->url[j]);\n+\t\t\tthe_repository->remote_state->remotes[i]\n+\t\t\t\t->url[j] = alias_url(\n+\t\t\t\tthe_repository->remote_state->remotes[i]->url[j],\n+\t\t\t\t&the_repository->remote_state->rewrites);\n \t\t}\n \t}\n }\n@@ -450,12 +480,13 @@ static void read_config(void)\n \t\treturn;\n \tloaded = 1;\n \n-\tcurrent_branch = NULL;\n+\tthe_repository->remote_state->current_branch = NULL;\n \tif (startup_info->have_repository) {\n \t\tconst char *head_ref = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flag);\n \t\tif (head_ref && (flag & REF_ISSYMREF) &&\n \t\t    skip_prefix(head_ref, \"refs/heads/\", &head_ref)) {\n-\t\t\tcurrent_branch = make_branch(head_ref, strlen(head_ref));\n+\t\t\tthe_repository->remote_state->current_branch =\n+\t\t\t\tmake_branch(head_ref, strlen(head_ref));\n \t\t}\n \t}\n \tgit_config(handle_config, NULL);\n@@ -493,10 +524,10 @@ const char *pushremote_for_branch(struct branch *branch, int *explicit)\n \t\t\t*explicit = 1;\n \t\treturn branch->pushremote_name;\n \t}\n-\tif (pushremote_name) {\n+\tif (the_repository->remote_state->pushremote_name) {\n \t\tif (explicit)\n \t\t\t*explicit = 1;\n-\t\treturn pushremote_name;\n+\t\treturn the_repository->remote_state->pushremote_name;\n \t}\n \treturn remote_for_branch(branch, explicit);\n }\n@@ -534,7 +565,8 @@ static struct remote *remote_get_1(const char *name,\n \tif (name)\n \t\tname_given = 1;\n \telse\n-\t\tname = get_default(current_branch, &name_given);\n+\t\tname = get_default(the_repository->remote_state->current_branch,\n+\t\t\t\t   &name_given);\n \n \tret = make_remote(name, 0);\n \tif (valid_remote_nick(name) && have_git_dir()) {\n@@ -573,11 +605,13 @@ int for_each_remote(each_remote_fn fn, void *priv)\n {\n \tint i, result = 0;\n \tread_config();\n-\tfor (i = 0; i < remotes_nr && !result; i++) {\n-\t\tstruct remote *r = remotes[i];\n-\t\tif (!r)\n+\tfor (i = 0; i < the_repository->remote_state->remotes_nr && !result;\n+\t     i++) {\n+\t\tstruct remote *remote =\n+\t\t\tthe_repository->remote_state->remotes[i];\n+\t\tif (!remote)\n \t\t\tcontinue;\n-\t\tresult = fn(r, priv);\n+\t\tresult = fn(remote, priv);\n \t}\n \treturn result;\n }\n@@ -1685,7 +1719,7 @@ struct branch *branch_get(const char *name)\n \n \tread_config();\n \tif (!name || !*name || !strcmp(name, \"HEAD\"))\n-\t\tret = current_branch;\n+\t\tret = the_repository->remote_state->current_branch;\n \telse\n \t\tret = make_branch(name, strlen(name));\n \tset_merge(ret);\n@@ -2585,3 +2619,32 @@ void apply_push_cas(struct push_cas_option *cas,\n \t\t\tcheck_if_includes_upstream(ref);\n \t}\n }\n+\n+struct remote_state *remote_state_new(void)\n+{\n+\tstruct remote_state *r = xmalloc(sizeof(*r));\n+\n+\tmemset(r, 0, sizeof(*r));\n+\treturn r;\n+}\n+\n+void remote_state_clear(struct remote_state *remote_state)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < remote_state->remotes_nr; i++) {\n+\t\tremote_clear(remote_state->remotes[i]);\n+\t}\n+\tFREE_AND_NULL(remote_state->remotes);\n+\tremote_state->remotes_alloc = 0;\n+\tremote_state->remotes_nr = 0;\n+\n+\thashmap_clear_and_free(&remote_state->remotes_hash, struct remote, ent);\n+\n+\tfor (i = 0; i < remote_state->branches_nr; i++) {\n+\t\tFREE_AND_NULL(remote_state->branches[i]);\n+\t}\n+\tFREE_AND_NULL(remote_state->branches);\n+\tremote_state->branches_alloc = 0;\n+\tremote_state->branches_nr = 0;\n+}\ndiff --git a/remote.h b/remote.h\nindex 5a59198252..184d14af3d 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -23,6 +23,40 @@ enum {\n \tREMOTE_BRANCHES\n };\n \n+struct rewrite {\n+\tconst char *base;\n+\tsize_t baselen;\n+\tstruct counted_string *instead_of;\n+\tint instead_of_nr;\n+\tint instead_of_alloc;\n+};\n+struct rewrites {\n+\tstruct rewrite **rewrite;\n+\tint rewrite_alloc;\n+\tint rewrite_nr;\n+};\n+\n+struct remote_state {\n+\tint config_loaded;\n+\n+\tstruct remote **remotes;\n+\tint remotes_alloc;\n+\tint remotes_nr;\n+\tstruct hashmap remotes_hash;\n+\n+\tstruct branch **branches;\n+\tint branches_alloc;\n+\tint branches_nr;\n+\n+\tstruct branch *current_branch;\n+\tconst char *pushremote_name;\n+\n+\tstruct rewrites rewrites;\n+\tstruct rewrites rewrites_push;\n+};\n+void remote_state_clear(struct remote_state *remote_state);\n+struct remote_state *remote_state_new(void);\n+\n struct remote {\n \tstruct hashmap_entry ent;\n \ndiff --git a/repository.c b/repository.c\nindex c5b90ba93e..c7ea706c20 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -9,6 +9,7 @@\n #include \"config.h\"\n #include \"object.h\"\n #include \"lockfile.h\"\n+#include \"remote.h\"\n #include \"submodule-config.h\"\n #include \"sparse-index.h\"\n #include \"promisor-remote.h\"\n@@ -24,6 +25,7 @@ void initialize_the_repository(void)\n \n \tthe_repo.index = &the_index;\n \tthe_repo.objects = raw_object_store_new();\n+\tthe_repo.remote_state = remote_state_new();\n \tthe_repo.parsed_objects = parsed_object_pool_new();\n \n \trepo_set_hash_algo(&the_repo, GIT_HASH_SHA1);\n@@ -164,6 +166,7 @@ int repo_init(struct repository *repo,\n \n \trepo->objects = raw_object_store_new();\n \trepo->parsed_objects = parsed_object_pool_new();\n+\trepo->remote_state = remote_state_new();\n \n \tif (repo_init_gitdir(repo, gitdir))\n \t\tgoto error;\n@@ -270,6 +273,11 @@ void repo_clear(struct repository *repo)\n \t\tpromisor_remote_clear(repo->promisor_remote_config);\n \t\tFREE_AND_NULL(repo->promisor_remote_config);\n \t}\n+\n+\tif (repo->remote_state) {\n+\t\tremote_state_clear(repo->remote_state);\n+\t\tFREE_AND_NULL(repo->remote_state);\n+\t}\n }\n \n int repo_read_index(struct repository *repo)\ndiff --git a/repository.h b/repository.h\nindex a057653981..98f9583470 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -11,6 +11,7 @@ struct pathspec;\n struct raw_object_store;\n struct submodule_cache;\n struct promisor_remote_config;\n+struct remote_state;\n \n enum untracked_cache_setting {\n \tUNTRACKED_CACHE_KEEP,\n@@ -127,6 +128,9 @@ struct repository {\n \t */\n \tstruct index_state *index;\n \n+\t/* Repository's remotes and associated structures. */\n+\tstruct remote_state *remote_state;\n+\n \t/* Repository's current hash algorithm, as serialized on disk. */\n \tconst struct git_hash_algo *hash_algo;\n \n-- \n2.33.0.882.g93a45727a2-goog\n\n"},{"id":"438680","messageId":"20211013193127.76537-3-chooglen@google.com","threadId":"56659","inReplyTo":"20211013193127.76537-1-chooglen@google.com","subject":"[PATCH v2 2/3] remote: use remote_state parameter internally","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-13T19:31:26Z","receivedAt":"2021-10-13T19:31:40Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"In internal-facing functions, replace the_repository->remote_state with\na struct remote_state parameter, but do not change external-facing\nfunctions.\n\nAs a result, most static functions no longer reference\nthe_repository->remote_state. The exceptions are those that are used in\na way that depends on external-facing functions e.g. the callbacks to\nremote_get_1().\n\nSigned-off-by: Glen Choo <chooglen@google.com>\n---\n remote.c | 165 ++++++++++++++++++++++++++-----------------------------\n 1 file changed, 78 insertions(+), 87 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 29c29fcc3b..850ccd8eff 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -65,19 +65,19 @@ static void add_pushurl(struct remote *remote, const char *pushurl)\n \tremote->pushurl[remote->pushurl_nr++] = pushurl;\n }\n \n-static void add_pushurl_alias(struct remote *remote, const char *url)\n+static void add_pushurl_alias(struct remote_state *remote_state,\n+\t\t\t      struct remote *remote, const char *url)\n {\n-\tconst char *pushurl =\n-\t\talias_url(url, &the_repository->remote_state->rewrites_push);\n+\tconst char *pushurl = alias_url(url, &remote_state->rewrites_push);\n \tif (pushurl != url)\n \t\tadd_pushurl(remote, pushurl);\n }\n \n-static void add_url_alias(struct remote *remote, const char *url)\n+static void add_url_alias(struct remote_state *remote_state,\n+\t\t\t  struct remote *remote, const char *url)\n {\n-\tadd_url(remote,\n-\t\talias_url(url, &the_repository->remote_state->rewrites));\n-\tadd_pushurl_alias(remote, url);\n+\tadd_url(remote, alias_url(url, &remote_state->rewrites));\n+\tadd_pushurl_alias(remote_state, remote, url);\n }\n \n struct remotes_hash_key {\n@@ -102,14 +102,19 @@ static int remotes_hash_cmp(const void *unused_cmp_data,\n \t\treturn strcmp(a->name, b->name);\n }\n \n-static inline void init_remotes_hash(void)\n+/**\n+ * NEEDSWORK: Now that the hashmap is in a struct, this should probably\n+ * just be moved into remote_state_new().\n+ */\n+static inline void init_remotes_hash(struct remote_state *remote_state)\n {\n-\tif (!the_repository->remote_state->remotes_hash.cmpfn)\n-\t\thashmap_init(&the_repository->remote_state->remotes_hash,\n-\t\t\t     remotes_hash_cmp, NULL, 0);\n+\tif (!remote_state->remotes_hash.cmpfn)\n+\t\thashmap_init(&remote_state->remotes_hash, remotes_hash_cmp,\n+\t\t\t     NULL, 0);\n }\n \n-static struct remote *make_remote(const char *name, int len)\n+static struct remote *make_remote(struct remote_state *remote_state,\n+\t\t\t\t  const char *name, int len)\n {\n \tstruct remote *ret;\n \tstruct remotes_hash_key lookup;\n@@ -118,13 +123,12 @@ static struct remote *make_remote(const char *name, int len)\n \tif (!len)\n \t\tlen = strlen(name);\n \n-\tinit_remotes_hash();\n+\tinit_remotes_hash(remote_state);\n \tlookup.str = name;\n \tlookup.len = len;\n \thashmap_entry_init(&lookup_entry, memhash(name, len));\n \n-\te = hashmap_get(&the_repository->remote_state->remotes_hash,\n-\t\t\t&lookup_entry, &lookup);\n+\te = hashmap_get(&remote_state->remotes_hash, &lookup_entry, &lookup);\n \tif (e)\n \t\treturn container_of(e, struct remote, ent);\n \n@@ -135,15 +139,12 @@ static struct remote *make_remote(const char *name, int len)\n \trefspec_init(&ret->push, REFSPEC_PUSH);\n \trefspec_init(&ret->fetch, REFSPEC_FETCH);\n \n-\tALLOC_GROW(the_repository->remote_state->remotes,\n-\t\t   the_repository->remote_state->remotes_nr + 1,\n-\t\t   the_repository->remote_state->remotes_alloc);\n-\tthe_repository->remote_state\n-\t\t->remotes[the_repository->remote_state->remotes_nr++] = ret;\n+\tALLOC_GROW(remote_state->remotes, remote_state->remotes_nr + 1,\n+\t\t   remote_state->remotes_alloc);\n+\tremote_state->remotes[remote_state->remotes_nr++] = ret;\n \n \thashmap_entry_init(&ret->ent, lookup_entry.hash);\n-\tif (hashmap_put_entry(&the_repository->remote_state->remotes_hash, ret,\n-\t\t\t      ent))\n+\tif (hashmap_put_entry(&remote_state->remotes_hash, ret, ent))\n \t\tBUG(\"hashmap_put overwrote entry after hashmap_get returned NULL\");\n \treturn ret;\n }\n@@ -177,25 +178,22 @@ static void add_merge(struct branch *branch, const char *name)\n \tbranch->merge_name[branch->merge_nr++] = name;\n }\n \n-static struct branch *make_branch(const char *name, size_t len)\n+static struct branch *make_branch(struct remote_state *remote_state,\n+\t\t\t\t  const char *name, size_t len)\n {\n \tstruct branch *ret;\n \tint i;\n \n-\tfor (i = 0; i < the_repository->remote_state->branches_nr; i++) {\n-\t\tif (!strncmp(name,\n-\t\t\t     the_repository->remote_state->branches[i]->name,\n-\t\t\t     len) &&\n-\t\t    !the_repository->remote_state->branches[i]->name[len])\n-\t\t\treturn the_repository->remote_state->branches[i];\n+\tfor (i = 0; i < remote_state->branches_nr; i++) {\n+\t\tif (!strncmp(name, remote_state->branches[i]->name, len) &&\n+\t\t    !remote_state->branches[i]->name[len])\n+\t\t\treturn remote_state->branches[i];\n \t}\n \n-\tALLOC_GROW(the_repository->remote_state->branches,\n-\t\t   the_repository->remote_state->branches_nr + 1,\n-\t\t   the_repository->remote_state->branches_alloc);\n+\tALLOC_GROW(remote_state->branches, remote_state->branches_nr + 1,\n+\t\t   remote_state->branches_alloc);\n \tCALLOC_ARRAY(ret, 1);\n-\tthe_repository->remote_state\n-\t\t->branches[the_repository->remote_state->branches_nr++] = ret;\n+\tremote_state->branches[remote_state->branches_nr++] = ret;\n \tret->name = xstrndup(name, len);\n \tret->refname = xstrfmt(\"refs/heads/%s\", ret->name);\n \n@@ -237,7 +235,8 @@ static const char *skip_spaces(const char *s)\n \treturn s;\n }\n \n-static void read_remotes_file(struct remote *remote)\n+static void read_remotes_file(struct remote_state *remote_state,\n+\t\t\t      struct remote *remote)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tFILE *f = fopen_or_warn(git_path(\"remotes/%s\", remote->name), \"r\");\n@@ -252,7 +251,8 @@ static void read_remotes_file(struct remote *remote)\n \t\tstrbuf_rtrim(&buf);\n \n \t\tif (skip_prefix(buf.buf, \"URL:\", &v))\n-\t\t\tadd_url_alias(remote, xstrdup(skip_spaces(v)));\n+\t\t\tadd_url_alias(remote_state, remote,\n+\t\t\t\t      xstrdup(skip_spaces(v)));\n \t\telse if (skip_prefix(buf.buf, \"Push:\", &v))\n \t\t\trefspec_append(&remote->push, skip_spaces(v));\n \t\telse if (skip_prefix(buf.buf, \"Pull:\", &v))\n@@ -262,7 +262,8 @@ static void read_remotes_file(struct remote *remote)\n \tfclose(f);\n }\n \n-static void read_branches_file(struct remote *remote)\n+static void read_branches_file(struct remote_state *remote_state,\n+\t\t\t       struct remote *remote)\n {\n \tchar *frag;\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -294,7 +295,7 @@ static void read_branches_file(struct remote *remote)\n \telse\n \t\tfrag = (char *)git_default_branch_name(0);\n \n-\tadd_url_alias(remote, strbuf_detach(&buf, NULL));\n+\tadd_url_alias(remote_state, remote, strbuf_detach(&buf, NULL));\n \trefspec_appendf(&remote->fetch, \"refs/heads/%s:refs/heads/%s\",\n \t\t\tfrag, remote->name);\n \n@@ -313,10 +314,12 @@ static int handle_config(const char *key, const char *value, void *cb)\n \tconst char *subkey;\n \tstruct remote *remote;\n \tstruct branch *branch;\n+\tstruct remote_state *remote_state = cb;\n+\n \tif (parse_config_key(key, \"branch\", &name, &namelen, &subkey) >= 0) {\n \t\tif (!name)\n \t\t\treturn 0;\n-\t\tbranch = make_branch(name, namelen);\n+\t\tbranch = make_branch(remote_state, name, namelen);\n \t\tif (!strcmp(subkey, \"remote\")) {\n \t\t\treturn git_config_string(&branch->remote_name, key, value);\n \t\t} else if (!strcmp(subkey, \"pushremote\")) {\n@@ -335,16 +338,14 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\tif (!strcmp(subkey, \"insteadof\")) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n-\t\t\trewrite = make_rewrite(\n-\t\t\t\t&the_repository->remote_state->rewrites, name,\n-\t\t\t\tnamelen);\n+\t\t\trewrite = make_rewrite(&remote_state->rewrites, name,\n+\t\t\t\t\t       namelen);\n \t\t\tadd_instead_of(rewrite, xstrdup(value));\n \t\t} else if (!strcmp(subkey, \"pushinsteadof\")) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n-\t\t\trewrite = make_rewrite(\n-\t\t\t\t&the_repository->remote_state->rewrites_push,\n-\t\t\t\tname, namelen);\n+\t\t\trewrite = make_rewrite(&remote_state->rewrites_push,\n+\t\t\t\t\t       name, namelen);\n \t\t\tadd_instead_of(rewrite, xstrdup(value));\n \t\t}\n \t}\n@@ -354,9 +355,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \n \t/* Handle remote.* variables */\n \tif (!name && !strcmp(subkey, \"pushdefault\"))\n-\t\treturn git_config_string(\n-\t\t\t&the_repository->remote_state->pushremote_name, key,\n-\t\t\tvalue);\n+\t\treturn git_config_string(&remote_state->pushremote_name, key,\n+\t\t\t\t\t value);\n \n \tif (!name)\n \t\treturn 0;\n@@ -366,7 +366,7 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\t\tname);\n \t\treturn 0;\n \t}\n-\tremote = make_remote(name, namelen);\n+\tremote = make_remote(remote_state, name, namelen);\n \tremote->origin = REMOTE_CONFIG;\n \tif (current_config_scope() == CONFIG_SCOPE_LOCAL ||\n \t    current_config_scope() == CONFIG_SCOPE_WORKTREE)\n@@ -436,42 +436,32 @@ static int handle_config(const char *key, const char *value, void *cb)\n \treturn 0;\n }\n \n-static void alias_all_urls(void)\n+static void alias_all_urls(struct remote_state *remote_state)\n {\n \tint i, j;\n-\tfor (i = 0; i < the_repository->remote_state->remotes_nr; i++) {\n+\tfor (i = 0; i < remote_state->remotes_nr; i++) {\n \t\tint add_pushurl_aliases;\n-\t\tif (!the_repository->remote_state->remotes[i])\n+\t\tif (!remote_state->remotes[i])\n \t\t\tcontinue;\n-\t\tfor (j = 0;\n-\t\t     j < the_repository->remote_state->remotes[i]->pushurl_nr;\n-\t\t     j++) {\n-\t\t\tthe_repository->remote_state->remotes[i]->pushurl[j] =\n-\t\t\t\talias_url(\n-\t\t\t\t\tthe_repository->remote_state->remotes[i]\n-\t\t\t\t\t\t->pushurl[j],\n-\t\t\t\t\t&the_repository->remote_state->rewrites);\n+\t\tfor (j = 0; j < remote_state->remotes[i]->pushurl_nr; j++) {\n+\t\t\tremote_state->remotes[i]->pushurl[j] =\n+\t\t\t\talias_url(remote_state->remotes[i]->pushurl[j],\n+\t\t\t\t\t  &remote_state->rewrites);\n \t\t}\n-\t\tadd_pushurl_aliases =\n-\t\t\tthe_repository->remote_state->remotes[i]->pushurl_nr ==\n-\t\t\t0;\n-\t\tfor (j = 0;\n-\t\t     j < the_repository->remote_state->remotes[i]->url_nr;\n-\t\t     j++) {\n+\t\tadd_pushurl_aliases = remote_state->remotes[i]->pushurl_nr == 0;\n+\t\tfor (j = 0; j < remote_state->remotes[i]->url_nr; j++) {\n \t\t\tif (add_pushurl_aliases)\n \t\t\t\tadd_pushurl_alias(\n-\t\t\t\t\tthe_repository->remote_state->remotes[i],\n-\t\t\t\t\tthe_repository->remote_state->remotes[i]\n-\t\t\t\t\t\t->url[j]);\n-\t\t\tthe_repository->remote_state->remotes[i]\n-\t\t\t\t->url[j] = alias_url(\n-\t\t\t\tthe_repository->remote_state->remotes[i]->url[j],\n-\t\t\t\t&the_repository->remote_state->rewrites);\n+\t\t\t\t\tremote_state, remote_state->remotes[i],\n+\t\t\t\t\tremote_state->remotes[i]->url[j]);\n+\t\t\tremote_state->remotes[i]->url[j] =\n+\t\t\t\talias_url(remote_state->remotes[i]->url[j],\n+\t\t\t\t\t  &remote_state->rewrites);\n \t\t}\n \t}\n }\n \n-static void read_config(void)\n+static void read_config(struct remote_state *remote_state)\n {\n \tstatic int loaded;\n \tint flag;\n@@ -480,17 +470,17 @@ static void read_config(void)\n \t\treturn;\n \tloaded = 1;\n \n-\tthe_repository->remote_state->current_branch = NULL;\n+\tremote_state->current_branch = NULL;\n \tif (startup_info->have_repository) {\n \t\tconst char *head_ref = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flag);\n \t\tif (head_ref && (flag & REF_ISSYMREF) &&\n \t\t    skip_prefix(head_ref, \"refs/heads/\", &head_ref)) {\n-\t\t\tthe_repository->remote_state->current_branch =\n-\t\t\t\tmake_branch(head_ref, strlen(head_ref));\n+\t\t\tremote_state->current_branch = make_branch(\n+\t\t\t\tremote_state, head_ref, strlen(head_ref));\n \t\t}\n \t}\n-\tgit_config(handle_config, NULL);\n-\talias_all_urls();\n+\tgit_config(handle_config, remote_state);\n+\talias_all_urls(remote_state);\n }\n \n static int valid_remote_nick(const char *name)\n@@ -560,7 +550,7 @@ static struct remote *remote_get_1(const char *name,\n \tstruct remote *ret;\n \tint name_given = 0;\n \n-\tread_config();\n+\tread_config(the_repository->remote_state);\n \n \tif (name)\n \t\tname_given = 1;\n@@ -568,15 +558,15 @@ static struct remote *remote_get_1(const char *name,\n \t\tname = get_default(the_repository->remote_state->current_branch,\n \t\t\t\t   &name_given);\n \n-\tret = make_remote(name, 0);\n+\tret = make_remote(the_repository->remote_state, name, 0);\n \tif (valid_remote_nick(name) && have_git_dir()) {\n \t\tif (!valid_remote(ret))\n-\t\t\tread_remotes_file(ret);\n+\t\t\tread_remotes_file(the_repository->remote_state, ret);\n \t\tif (!valid_remote(ret))\n-\t\t\tread_branches_file(ret);\n+\t\t\tread_branches_file(the_repository->remote_state, ret);\n \t}\n \tif (name_given && !valid_remote(ret))\n-\t\tadd_url_alias(ret, name);\n+\t\tadd_url_alias(the_repository->remote_state, ret, name);\n \tif (!valid_remote(ret))\n \t\treturn NULL;\n \treturn ret;\n@@ -604,7 +594,7 @@ int remote_is_configured(struct remote *remote, int in_repo)\n int for_each_remote(each_remote_fn fn, void *priv)\n {\n \tint i, result = 0;\n-\tread_config();\n+\tread_config(the_repository->remote_state);\n \tfor (i = 0; i < the_repository->remote_state->remotes_nr && !result;\n \t     i++) {\n \t\tstruct remote *remote =\n@@ -1717,11 +1707,12 @@ struct branch *branch_get(const char *name)\n {\n \tstruct branch *ret;\n \n-\tread_config();\n+\tread_config(the_repository->remote_state);\n \tif (!name || !*name || !strcmp(name, \"HEAD\"))\n \t\tret = the_repository->remote_state->current_branch;\n \telse\n-\t\tret = make_branch(name, strlen(name));\n+\t\tret = make_branch(the_repository->remote_state, name,\n+\t\t\t\t  strlen(name));\n \tset_merge(ret);\n \treturn ret;\n }\n-- \n2.33.0.882.g93a45727a2-goog\n\n"},{"id":"438681","messageId":"20211013193127.76537-4-chooglen@google.com","threadId":"56659","inReplyTo":"20211013193127.76537-1-chooglen@google.com","subject":"[PATCH v2 3/3] remote: add struct repository parameter to external functions","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-13T19:31:27Z","receivedAt":"2021-10-13T19:31:41Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Finish plumbing remote_state by adding a struct repository\nparameter to repo_* functions. While this removes all references to\nthe_repository->remote_state, certain functions still use the_repository\nto parse oids.\n\nSigned-off-by: Glen Choo <chooglen@google.com>\n---\n remote.c | 111 ++++++++++++++++++++++++++++++-------------------------\n remote.h |  96 +++++++++++++++++++++++++++++++++++++++--------\n 2 files changed, 142 insertions(+), 65 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 850ccd8eff..be0889c53b 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -495,7 +495,8 @@ static int valid_remote_nick(const char *name)\n \treturn 1;\n }\n \n-const char *remote_for_branch(struct branch *branch, int *explicit)\n+const char *repo_remote_for_branch(struct repository *repo,\n+\t\t\t\t   struct branch *branch, int *explicit)\n {\n \tif (branch && branch->remote_name) {\n \t\tif (explicit)\n@@ -507,22 +508,24 @@ const char *remote_for_branch(struct branch *branch, int *explicit)\n \treturn \"origin\";\n }\n \n-const char *pushremote_for_branch(struct branch *branch, int *explicit)\n+const char *repo_pushremote_for_branch(struct repository *repo,\n+\t\t\t\t       struct branch *branch, int *explicit)\n {\n \tif (branch && branch->pushremote_name) {\n \t\tif (explicit)\n \t\t\t*explicit = 1;\n \t\treturn branch->pushremote_name;\n \t}\n-\tif (the_repository->remote_state->pushremote_name) {\n+\tif (repo->remote_state->pushremote_name) {\n \t\tif (explicit)\n \t\t\t*explicit = 1;\n-\t\treturn the_repository->remote_state->pushremote_name;\n+\t\treturn repo->remote_state->pushremote_name;\n \t}\n-\treturn remote_for_branch(branch, explicit);\n+\treturn repo_remote_for_branch(repo, branch, explicit);\n }\n \n-const char *remote_ref_for_branch(struct branch *branch, int for_push)\n+const char *repo_remote_ref_for_branch(struct repository *repo,\n+\t\t\t\t       struct branch *branch, int for_push)\n {\n \tif (branch) {\n \t\tif (!for_push) {\n@@ -530,9 +533,11 @@ const char *remote_ref_for_branch(struct branch *branch, int for_push)\n \t\t\t\treturn branch->merge_name[0];\n \t\t\t}\n \t\t} else {\n-\t\t\tconst char *dst, *remote_name =\n-\t\t\t\tpushremote_for_branch(branch, NULL);\n-\t\t\tstruct remote *remote = remote_get(remote_name);\n+\t\t\tconst char *dst,\n+\t\t\t\t*remote_name = repo_pushremote_for_branch(\n+\t\t\t\t\trepo, branch, NULL);\n+\t\t\tstruct remote *remote =\n+\t\t\t\trepo_remote_get(repo, remote_name);\n \n \t\t\tif (remote && remote->push.nr &&\n \t\t\t    (dst = apply_refspecs(&remote->push,\n@@ -544,42 +549,43 @@ const char *remote_ref_for_branch(struct branch *branch, int for_push)\n \treturn NULL;\n }\n \n-static struct remote *remote_get_1(const char *name,\n-\t\t\t\t   const char *(*get_default)(struct branch *, int *))\n+static struct remote *repo_remote_get_1(\n+\tstruct repository *repo, const char *name,\n+\tconst char *(*get_default)(struct repository *, struct branch *, int *))\n {\n \tstruct remote *ret;\n \tint name_given = 0;\n \n-\tread_config(the_repository->remote_state);\n+\tread_config(repo->remote_state);\n \n \tif (name)\n \t\tname_given = 1;\n \telse\n-\t\tname = get_default(the_repository->remote_state->current_branch,\n+\t\tname = get_default(repo, repo->remote_state->current_branch,\n \t\t\t\t   &name_given);\n \n-\tret = make_remote(the_repository->remote_state, name, 0);\n+\tret = make_remote(repo->remote_state, name, 0);\n \tif (valid_remote_nick(name) && have_git_dir()) {\n \t\tif (!valid_remote(ret))\n-\t\t\tread_remotes_file(the_repository->remote_state, ret);\n+\t\t\tread_remotes_file(repo->remote_state, ret);\n \t\tif (!valid_remote(ret))\n-\t\t\tread_branches_file(the_repository->remote_state, ret);\n+\t\t\tread_branches_file(repo->remote_state, ret);\n \t}\n \tif (name_given && !valid_remote(ret))\n-\t\tadd_url_alias(the_repository->remote_state, ret, name);\n+\t\tadd_url_alias(repo->remote_state, ret, name);\n \tif (!valid_remote(ret))\n \t\treturn NULL;\n \treturn ret;\n }\n \n-struct remote *remote_get(const char *name)\n+struct remote *repo_remote_get(struct repository *repo, const char *name)\n {\n-\treturn remote_get_1(name, remote_for_branch);\n+\treturn repo_remote_get_1(repo, name, repo_remote_for_branch);\n }\n \n-struct remote *pushremote_get(const char *name)\n+struct remote *repo_pushremote_get(struct repository *repo, const char *name)\n {\n-\treturn remote_get_1(name, pushremote_for_branch);\n+\treturn repo_remote_get_1(repo, name, repo_pushremote_for_branch);\n }\n \n int remote_is_configured(struct remote *remote, int in_repo)\n@@ -591,14 +597,12 @@ int remote_is_configured(struct remote *remote, int in_repo)\n \treturn !!remote->origin;\n }\n \n-int for_each_remote(each_remote_fn fn, void *priv)\n+int repo_for_each_remote(struct repository *repo, each_remote_fn fn, void *priv)\n {\n \tint i, result = 0;\n-\tread_config(the_repository->remote_state);\n-\tfor (i = 0; i < the_repository->remote_state->remotes_nr && !result;\n-\t     i++) {\n-\t\tstruct remote *remote =\n-\t\t\tthe_repository->remote_state->remotes[i];\n+\tread_config(repo->remote_state);\n+\tfor (i = 0; i < repo->remote_state->remotes_nr && !result; i++) {\n+\t\tstruct remote *remote = repo->remote_state->remotes[i];\n \t\tif (!remote)\n \t\t\tcontinue;\n \t\tresult = fn(remote, priv);\n@@ -1666,7 +1670,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\n \t}\n }\n \n-static void set_merge(struct branch *ret)\n+static void set_merge(struct repository *repository, struct branch *ret)\n {\n \tstruct remote *remote;\n \tchar *ref;\n@@ -1686,7 +1690,7 @@ static void set_merge(struct branch *ret)\n \t\treturn;\n \t}\n \n-\tremote = remote_get(ret->remote_name);\n+\tremote = repo_remote_get(repository, ret->remote_name);\n \n \tCALLOC_ARRAY(ret->merge, ret->merge_nr);\n \tfor (i = 0; i < ret->merge_nr; i++) {\n@@ -1703,17 +1707,16 @@ static void set_merge(struct branch *ret)\n \t}\n }\n \n-struct branch *branch_get(const char *name)\n+struct branch *repo_branch_get(struct repository *repo, const char *name)\n {\n \tstruct branch *ret;\n \n-\tread_config(the_repository->remote_state);\n+\tread_config(repo->remote_state);\n \tif (!name || !*name || !strcmp(name, \"HEAD\"))\n-\t\tret = the_repository->remote_state->current_branch;\n+\t\tret = repo->remote_state->current_branch;\n \telse\n-\t\tret = make_branch(the_repository->remote_state, name,\n-\t\t\t\t  strlen(name));\n-\tset_merge(ret);\n+\t\tret = make_branch(repo->remote_state, name, strlen(name));\n+\tset_merge(repo, ret);\n \treturn ret;\n }\n \n@@ -1743,7 +1746,8 @@ static const char *error_buf(struct strbuf *err, const char *fmt, ...)\n \treturn NULL;\n }\n \n-const char *branch_get_upstream(struct branch *branch, struct strbuf *err)\n+const char *repo_branch_get_upstream(struct repository *repo,\n+\t\t\t\t     struct branch *branch, struct strbuf *err)\n {\n \tif (!branch)\n \t\treturn error_buf(err, _(\"HEAD does not point to a branch\"));\n@@ -1784,11 +1788,14 @@ static const char *tracking_for_push_dest(struct remote *remote,\n \treturn ret;\n }\n \n-static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)\n+static const char *repo_branch_get_push_1(struct repository *repo,\n+\t\t\t\t\t  struct branch *branch,\n+\t\t\t\t\t  struct strbuf *err)\n {\n \tstruct remote *remote;\n \n-\tremote = remote_get(pushremote_for_branch(branch, NULL));\n+\tremote = repo_remote_get(repo, repo_pushremote_for_branch(repo, branch,\n+\t\t\t\t\t\t\t\t  NULL));\n \tif (!remote)\n \t\treturn error_buf(err,\n \t\t\t\t _(\"branch '%s' has no remote for pushing\"),\n@@ -1821,14 +1828,14 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)\n \t\treturn tracking_for_push_dest(remote, branch->refname, err);\n \n \tcase PUSH_DEFAULT_UPSTREAM:\n-\t\treturn branch_get_upstream(branch, err);\n+\t\treturn repo_branch_get_upstream(repo, branch, err);\n \n \tcase PUSH_DEFAULT_UNSPECIFIED:\n \tcase PUSH_DEFAULT_SIMPLE:\n \t\t{\n \t\t\tconst char *up, *cur;\n \n-\t\t\tup = branch_get_upstream(branch, err);\n+\t\t\tup = repo_branch_get_upstream(repo, branch, err);\n \t\t\tif (!up)\n \t\t\t\treturn NULL;\n \t\t\tcur = tracking_for_push_dest(remote, branch->refname, err);\n@@ -1844,13 +1851,15 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)\n \tBUG(\"unhandled push situation\");\n }\n \n-const char *branch_get_push(struct branch *branch, struct strbuf *err)\n+const char *repo_branch_get_push(struct repository *repo, struct branch *branch,\n+\t\t\t\t struct strbuf *err)\n {\n \tif (!branch)\n \t\treturn error_buf(err, _(\"HEAD does not point to a branch\"));\n \n \tif (!branch->push_tracking_ref)\n-\t\tbranch->push_tracking_ref = branch_get_push_1(branch, err);\n+\t\tbranch->push_tracking_ref =\n+\t\t\trepo_branch_get_push_1(repo, branch, err);\n \treturn branch->push_tracking_ref;\n }\n \n@@ -2103,15 +2112,16 @@ static int stat_branch_pair(const char *branch_name, const char *base,\n  * upstream defined, or ref does not exist).  Returns 0 if the commits are\n  * identical.  Returns 1 if commits are different.\n  */\n-int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n-\t\t       const char **tracking_name, int for_push,\n-\t\t       enum ahead_behind_flags abf)\n+int repo_stat_tracking_info(struct repository *repo, struct branch *branch,\n+\t\t\t    int *num_ours, int *num_theirs,\n+\t\t\t    const char **tracking_name, int for_push,\n+\t\t\t    enum ahead_behind_flags abf)\n {\n \tconst char *base;\n \n \t/* Cannot stat unless we are marked to build on top of somebody else. */\n-\tbase = for_push ? branch_get_push(branch, NULL) :\n-\t\tbranch_get_upstream(branch, NULL);\n+\tbase = for_push ? repo_branch_get_push(repo, branch, NULL) :\n+\t\t\t\trepo_branch_get_upstream(repo, branch, NULL);\n \tif (tracking_name)\n \t\t*tracking_name = base;\n \tif (!base)\n@@ -2123,15 +2133,16 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n /*\n  * Return true when there is anything to report, otherwise false.\n  */\n-int format_tracking_info(struct branch *branch, struct strbuf *sb,\n-\t\t\t enum ahead_behind_flags abf)\n+int repo_format_tracking_info(struct repository *repo, struct branch *branch,\n+\t\t\t      struct strbuf *sb, enum ahead_behind_flags abf)\n {\n \tint ours, theirs, sti;\n \tconst char *full_base;\n \tchar *base;\n \tint upstream_is_gone = 0;\n \n-\tsti = stat_tracking_info(branch, &ours, &theirs, &full_base, 0, abf);\n+\tsti = repo_stat_tracking_info(repo, branch, &ours, &theirs, &full_base,\n+\t\t\t\t      0, abf);\n \tif (sti < 0) {\n \t\tif (!full_base)\n \t\t\treturn 0;\ndiff --git a/remote.h b/remote.h\nindex 184d14af3d..a1cf86f973 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -117,15 +117,28 @@ struct remote {\n  * remote_get(NULL) will return the default remote, given the current branch\n  * and configuration.\n  */\n-struct remote *remote_get(const char *name);\n-\n-struct remote *pushremote_get(const char *name);\n+struct remote *repo_remote_get(struct repository *repo, const char *name);\n+static inline struct remote *remote_get(const char *name)\n+{\n+\treturn repo_remote_get(the_repository, name);\n+}\n+\n+struct remote *repo_pushremote_get(struct repository *repo, const char *name);\n+static inline struct remote *pushremote_get(const char *name)\n+{\n+\treturn repo_pushremote_get(the_repository, name);\n+}\n int remote_is_configured(struct remote *remote, int in_repo);\n \n typedef int each_remote_fn(struct remote *remote, void *priv);\n \n /* iterate through struct remotes */\n-int for_each_remote(each_remote_fn fn, void *priv);\n+int repo_for_each_remote(struct repository *repo, each_remote_fn fn,\n+\t\t\t void *priv);\n+static inline int for_each_remote(each_remote_fn fn, void *priv)\n+{\n+\treturn repo_for_each_remote(the_repository, fn, priv);\n+}\n \n int remote_has_url(struct remote *remote, const char *url);\n \n@@ -320,10 +333,35 @@ struct branch {\n \tconst char *push_tracking_ref;\n };\n \n-struct branch *branch_get(const char *name);\n-const char *remote_for_branch(struct branch *branch, int *explicit);\n-const char *pushremote_for_branch(struct branch *branch, int *explicit);\n-const char *remote_ref_for_branch(struct branch *branch, int for_push);\n+struct branch *repo_branch_get(struct repository *repo, const char *name);\n+static inline struct branch *branch_get(const char *name)\n+{\n+\treturn repo_branch_get(the_repository, name);\n+}\n+\n+const char *repo_remote_for_branch(struct repository *repo,\n+\t\t\t\t   struct branch *branch, int *explicit);\n+static inline const char *remote_for_branch(struct branch *branch,\n+\t\t\t\t\t    int *explicit)\n+{\n+\treturn repo_remote_for_branch(the_repository, branch, explicit);\n+}\n+\n+const char *repo_pushremote_for_branch(struct repository *repo,\n+\t\t\t\t       struct branch *branch, int *explicit);\n+static inline const char *pushremote_for_branch(struct branch *branch,\n+\t\t\t\t\t\tint *explicit)\n+{\n+\treturn repo_pushremote_for_branch(the_repository, branch, explicit);\n+}\n+\n+const char *repo_remote_ref_for_branch(struct repository *repo,\n+\t\t\t\t       struct branch *branch, int for_push);\n+static inline const char *remote_ref_for_branch(struct branch *branch,\n+\t\t\t\t\t\tint for_push)\n+{\n+\treturn repo_remote_ref_for_branch(the_repository, branch, for_push);\n+}\n \n /* returns true if the given branch has merge configuration given. */\n int branch_has_merge_config(struct branch *branch);\n@@ -339,7 +377,13 @@ int branch_merge_matches(struct branch *, int n, const char *);\n  * message is recorded there (if the function does not return NULL, then\n  * `err` is not touched).\n  */\n-const char *branch_get_upstream(struct branch *branch, struct strbuf *err);\n+const char *repo_branch_get_upstream(struct repository *repo,\n+\t\t\t\t     struct branch *branch, struct strbuf *err);\n+static inline const char *branch_get_upstream(struct branch *branch,\n+\t\t\t\t\t      struct strbuf *err)\n+{\n+\treturn repo_branch_get_upstream(the_repository, branch, err);\n+}\n \n /**\n  * Return the tracking branch that corresponds to the ref we would push to\n@@ -347,7 +391,13 @@ const char *branch_get_upstream(struct branch *branch, struct strbuf *err);\n  *\n  * The return value and `err` conventions match those of `branch_get_upstream`.\n  */\n-const char *branch_get_push(struct branch *branch, struct strbuf *err);\n+const char *repo_branch_get_push(struct repository *repo, struct branch *branch,\n+\t\t\t\t struct strbuf *err);\n+static inline const char *branch_get_push(struct branch *branch,\n+\t\t\t\t\t  struct strbuf *err)\n+{\n+\treturn repo_branch_get_push(the_repository, branch, err);\n+}\n \n /* Flags to match_refs. */\n enum match_refs_flags {\n@@ -366,11 +416,27 @@ enum ahead_behind_flags {\n };\n \n /* Reporting of tracking info */\n-int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n-\t\t       const char **upstream_name, int for_push,\n-\t\t       enum ahead_behind_flags abf);\n-int format_tracking_info(struct branch *branch, struct strbuf *sb,\n-\t\t\t enum ahead_behind_flags abf);\n+int repo_stat_tracking_info(struct repository *repo, struct branch *branch,\n+\t\t\t    int *num_ours, int *num_theirs,\n+\t\t\t    const char **upstream_name, int for_push,\n+\t\t\t    enum ahead_behind_flags abf);\n+static inline int stat_tracking_info(struct branch *branch, int *num_ours,\n+\t\t\t\t     int *num_theirs,\n+\t\t\t\t     const char **upstream_name, int for_push,\n+\t\t\t\t     enum ahead_behind_flags abf)\n+{\n+\treturn repo_stat_tracking_info(the_repository, branch, num_ours,\n+\t\t\t\t       num_theirs, upstream_name, for_push,\n+\t\t\t\t       abf);\n+}\n+\n+int repo_format_tracking_info(struct repository *repo, struct branch *branch,\n+\t\t\t      struct strbuf *sb, enum ahead_behind_flags abf);\n+static inline int format_tracking_info(struct branch *branch, struct strbuf *sb,\n+\t\t\t\t       enum ahead_behind_flags abf)\n+{\n+\treturn repo_format_tracking_info(the_repository, branch, sb, abf);\n+}\n \n struct ref *get_local_heads(void);\n /*\n-- \n2.33.0.882.g93a45727a2-goog\n\n"},{"id":"438686","messageId":"xmqqtuhkfzw8.fsf@gitster.g","threadId":"56659","inReplyTo":"20211013193127.76537-1-chooglen@google.com","subject":"Re: [PATCH v2 0/3] remote: replace static variables with struct remote_state","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-13T20:11:51Z","receivedAt":"2021-10-13T20:11:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n> While this concern is valid, I decided to keep this interface for a few\n> reasons:\n>\n> 1. The correct way of calling the function is 'obvious'.\n> 2. It is relatively easy to get the contained object (struct branch/remote)\n>    and its containing struct repository/remote_state (e.g. we don't pass\n>    struct branch or remote through long call chains). For \"struct\n>    branch\", callers usually get the branch from the repo and use it\n>    immediately. For \"struct remote\", we don't use container objects\n>    outside of static functions. If you are interested in seeing all of\n>    the call sites, you can see a sample commit in [3].\n> 3. The separation between container/contained objects allows us to\n>    reason better about what the correct interface is. e.g. we might be\n>    tempted to include a backpointer from struct branch to struct\n>    remote_state so that we can pass around struct branch and be\n>    confident that struct branch has all of the information it needs.\n\nI am not following.  None of the above reasons argue for forcing the\nfunctions that take contained object to also take its container.\n\n>    However, I believe that some questions *shouldn't* be answered by\n>    just struct branch. The prime example in this series is\n>    branch_get_push() - it conceptually answers 'What is the pushremote\n>    of this branch', but the behavior we want is closer to 'If\n>    configured, give me the pushremote for the branch. Otherwise, give me\n>    the default pushremote of the repo.'. This is arguably a relevant\n>    detail that should be exposed to callers.\n\nIt is a good example why such a function can just take an instance\nof branch, and the caller,\n\n (1) who does care about the fallback, can be assured that the logic\n     falls back to the correct repository; and\n\n (2) who does not care about the fallback and sees it a mere\n     implementation detail of \"I am on this branch; give me the\n     remote to push to\", do not have to know what, other than the\n     branch object, needs to be passed.\n\nif we explicitly record a branch object which repository it was\ntaken from.\n\nThere may be some other (real) reason where the resistance comes\nfrom, that you may not be telling us, though.  But in what was\ndescribed in the message I am responding to, I didn't see much\nconvincing reason to argue _for_ keeping the contained objects\nignorant of the container and forcing callers to pass both to\nfunctions that use both the container and contained to compute\nsomething.\n\nThanks.\n"},{"id":"438687","messageId":"xmqqlf2wfzfz.fsf@gitster.g","threadId":"56659","inReplyTo":"20211013193127.76537-2-chooglen@google.com","subject":"Re: [PATCH v2 1/3] remote: move static variables into per-repository struct","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-13T20:21:36Z","receivedAt":"2021-10-13T20:21:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n> remote.c does not work with non-the_repository because it stores its\n> state as static variables. To support non-the_repository, we can use a\n> per-repository struct for the remotes subsystem.\n>\n> Prepare for this change by defining a struct remote_state that holds\n> the remotes subsystem state and move the static variables of remote.c\n> into the_repository->remote_state.\n\nThat all sounds very sensible, but ...\n\n> diff --git a/remote.h b/remote.h\n> index 5a59198252..184d14af3d 100644\n> --- a/remote.h\n> +++ b/remote.h\n> @@ -23,6 +23,40 @@ enum {\n>  \tREMOTE_BRANCHES\n>  };\n>  \n> +struct rewrite {\n> +\tconst char *base;\n> +\tsize_t baselen;\n> +\tstruct counted_string *instead_of;\n> +\tint instead_of_nr;\n> +\tint instead_of_alloc;\n> +};\n> +struct rewrites {\n\nMissing a blank line between two type decls.\n\n> +\tstruct rewrite **rewrite;\n> +\tint rewrite_alloc;\n> +\tint rewrite_nr;\n> +};\n\nIt is a bit sad that we still have to keep \"struct rewrites\"; if we\nsee how .remotes and .branches are handled, we probably should have\nleft the <array, alloc, nr> 3-tuple for \"struct rewrite\" without an\nextra layer.  But this step is about moving things around and\nwrapping the set of existing static file-scope-global variables into\na structure, so such a restructuring would not belong here.  OK.\n\n> +struct remote_state {\n> +\tint config_loaded;\n> +\n> +\tstruct remote **remotes;\n> +\tint remotes_alloc;\n> +\tint remotes_nr;\n> +\tstruct hashmap remotes_hash;\n> +\n> +\tstruct branch **branches;\n> +\tint branches_alloc;\n> +\tint branches_nr;\n> +\n> +\tstruct branch *current_branch;\n> +\tconst char *pushremote_name;\n> +\n> +\tstruct rewrites rewrites;\n> +\tstruct rewrites rewrites_push;\n> +};\n> +void remote_state_clear(struct remote_state *remote_state);\n\nMissing a blank line after \"struct\" decl.\n\nThanks.\n"},{"id":"438688","messageId":"xmqqh7dkfzde.fsf@gitster.g","threadId":"56659","inReplyTo":"20211013193127.76537-3-chooglen@google.com","subject":"Re: [PATCH v2 2/3] remote: use remote_state parameter internally","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-13T20:23:09Z","receivedAt":"2021-10-13T20:23:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n> In internal-facing functions, replace the_repository->remote_state with\n> a struct remote_state parameter, but do not change external-facing\n> functions.\n\nOK.  Quite nice.\n\n"},{"id":"438689","messageId":"xmqqczo8fzbz.fsf@gitster.g","threadId":"56659","inReplyTo":"20211013193127.76537-4-chooglen@google.com","subject":"Re: [PATCH v2 3/3] remote: add struct repository parameter to external functions","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-13T20:24:00Z","receivedAt":"2021-10-13T20:24:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n> Finish plumbing remote_state by adding a struct repository\n> parameter to repo_* functions. While this removes all references to\n> the_repository->remote_state, certain functions still use the_repository\n> to parse oids.\n\nOK.\n"},{"id":"438690","messageId":"xmqq8rywfz6x.fsf@gitster.g","threadId":"56659","inReplyTo":"xmqqtuhkfzw8.fsf@gitster.g","subject":"Re: [PATCH v2 0/3] remote: replace static variables with struct remote_state","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-13T20:27:02Z","receivedAt":"2021-10-13T20:27:11Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> There may be some other (real) reason where the resistance comes\n> from, that you may not be telling us, though.  But in what was\n> described in the message I am responding to, I didn't see much\n> convincing reason to argue _for_ keeping the contained objects\n> ignorant of the container and forcing callers to pass both to\n> functions that use both the container and contained to compute\n> something.\n\nI am not you, so I can only speculate, but the real reason _could_\nbe that it makes it simpler to formulate steps 2 and 3 mechanically.\nAfter adding \"repo\" parameter to a function that used to take, say,\na \"branch\", in step 3, a future clean-up series could add a .repo\nmember to branch objects and remove the \"repo\" parameter from such\nfunction.\n\nI think that approach would make more work to get to the final\nstate, though.\n"},{"id":"438699","messageId":"kl6ltuhk1tdg.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"56659","inReplyTo":"xmqqtuhkfzw8.fsf@gitster.g","subject":"Re: [PATCH v2 0/3] remote: replace static variables with struct remote_state","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-13T21:56:27Z","receivedAt":"2021-10-13T21:56:33Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>>    However, I believe that some questions *shouldn't* be answered by\n>>    just struct branch. The prime example in this series is\n>>    branch_get_push() - it conceptually answers 'What is the pushremote\n>>    of this branch', but the behavior we want is closer to 'If\n>>    configured, give me the pushremote for the branch. Otherwise, give me\n>>    the default pushremote of the repo.'. This is arguably a relevant\n>>    detail that should be exposed to callers.\n>\n> It is a good example why such a function can just take an instance\n> of branch, and the caller,\n>\n>  (1) who does care about the fallback, can be assured that the logic\n>      falls back to the correct repository; and\n>\n>  (2) who does not care about the fallback and sees it a mere\n>      implementation detail of \"I am on this branch; give me the\n>      remote to push to\", do not have to know what, other than the\n>      branch object, needs to be passed.\n>\n> if we explicitly record a branch object which repository it was\n> taken from.\n>\n> There may be some other (real) reason where the resistance comes\n> from, that you may not be telling us, though.  But in what was\n> described in the message I am responding to, I didn't see much\n> convincing reason to argue _for_ keeping the contained objects\n> ignorant of the container and forcing callers to pass both to\n> functions that use both the container and contained to compute\n> something.\n\nMy primary line of thinking is that adding the backpointer from struct\nbranch to struct repository adds \"unnecessary\" coupling between the two\nobjects, which causes the more concrete problems of:\n\n* Inconsistency between other functions that use struct repository as a\n  \"context object\". We now find ourselves having to justify why branch\n  functions can bypass the context object using a special pointer,\n  whereas other functions and structs (say, struct object) cannot.\n* Interface misuse, where callers can now dereference branch->repository\n  even though it is meant to be internal. This is also a possible source\n  of inconsistency.\n* Extra memory consumption.\n\nA counterpoint to what I said is [1], where Jonathan eventually added\nthe backpointer from struct ref_store to struct repository, which does\ngive the nice benefits of referential integrity and abstraction that you\ncited. However in most of that series, struct ref_store is meant to be\nthe context object, but due to poor internal abstraction, ref_store ends\nup depending on repository in some form or another and thus the\nbackpointer is more defensible (as opposed to passing two context\nobjects). I do not think we are in the same position with struct branch;\nstruct branch seems sufficiently separated to me.\n\nI'm not opposed to adding a backpointer if it helps us get to a good\nfinal state, but I currently suspect that the final state still involves\npassing around struct repository as a context object.\n\n[1] https://lore.kernel.org/git/xmqqlf3gib0p.fsf@gitster.g/\n"},{"id":"438700","messageId":"kl6lr1co1t62.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"56659","inReplyTo":"xmqq8rywfz6x.fsf@gitster.g","subject":"Re: [PATCH v2 0/3] remote: replace static variables with struct remote_state","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-13T22:00:53Z","receivedAt":"2021-10-13T22:00:57Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I am not you, so I can only speculate, but the real reason _could_\n> be that it makes it simpler to formulate steps 2 and 3 mechanically.\n\nThis is somewhat true, however...\n\n> I think that approach would make more work to get to the final\n> state, though.\n\nI found that if I were to add the backpointer, it would have been better\nto add it early and avoid this extra work that you mention here :)\n"},{"id":"438714","messageId":"xmqqmtncebtb.fsf@gitster.g","threadId":"56659","inReplyTo":"kl6ltuhk1tdg.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v2 0/3] remote: replace static variables with struct remote_state","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-13T23:37:20Z","receivedAt":"2021-10-13T23:37:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n> My primary line of thinking is that adding the backpointer from struct\n> branch to struct repository adds \"unnecessary\" coupling between the two\n> objects,...\n\nmeaning, that the paragraphs below will explain why the reference is\nunnecessary?\n\n> ...which causes the more concrete problems of:\n\n> * Inconsistency between other functions that use struct repository as a\n>   \"context object\". We now find ourselves having to justify why branch\n>   functions can bypass the context object using a special pointer,\n>   whereas other functions and structs (say, struct object) cannot.\n\nGiven that the API has been evolved over time from a \"we deal only\nwith a single repository and everybody else can just fork into a\nseparate repository\" to \"now this function can work in the specified\nrepository\", it is understandable that some still do take repository\nas a separate parameter, even though the original counterpart that\ndid not take a repository pointer took an object that clearly can\nbelong to only one repository at a time.  They would need to be\ncleaned up, and they do not make a good excuse to add more of them,\nwhen we know we are dealing with objects that belong only to one\nrepository, like the \"remote.c\" contents we have been discussing.\n\nIOW, we need to start somewhere to clean things up, so either we\nteach multi-repository support to remote.c from day one, or we pass\na repository as a separate and redundant parameter, with the\nintention to later clean up.  I suspect that this piece is isolated\nenough that it is simpler to use as a starting point, and then the\ncallers into remote.c API can then be cleaned up, gradually extending\nthe circle.\n\nThe original counerpart may not have been about \"the other object\"\nto begin with (e.g. \"we have this string (not an object); resolve it\nto an object name in the context of the given repository\"), in which\ncase they are fine as they are, but I think most of the contents of\n\"remote.c\" do not fall into this category.\n\n> * Interface misuse, where callers can now dereference branch->repository\n>   even though it is meant to be internal. This is also a possible source\n>   of inconsistency.\n\nI do not think it is meant to be internal to begin with.  If we\ndeclare, after careful consideration, that an in-core branch object\ngiven by remote.c API always belongs to one and only one repository,\nthen following the branch->repository pointer should be the\ndocumented and kosher way to find out the repository by anybody who\nis given the branch object.  A function that takes repo and branch\nto pretend as if it can work with an inconsistent pair is an\ninvidation for the interface misuse, but I do not think it is a\nmisuse of the API for a codepath that obtained a branch instance\ninspecting what repository it came from.\n\nA function that takes a pair of remote and a branch object for\nwhatever reason may want to have an assert() that makes sure they\ncame from the same repository, by the way.\n\nAlso, some of the functions involved in this topic may need to say\n\"I have this string, please resolve it as an object name in the\ncontext of this repository\", and call a function that takes a\nrepository as \"context\".  The repository they use must be given by\ntheir caller somehow---is there any valid case where it must be\ndifferent from the other parameter (i.e. contained object) they are\ndealing with?  I do not think so.  So such a function would have to\nsay \"resolve this string in the context of the repository\nbranch->repo\" because the function was given an in-core branch\ninstance.\n\n> * Extra memory consumption.\n\nThis is true.  It however is the only valid excuse I can fully agree\nwith for being hesitant to have \"where did I come from\" information.\nWe do grow each in-core branch and remote instance by an 8-byte\npointer (but we reduce the stackframe consumption by 8-byte along\neach step in the callchain as we do not have to pass a repository as\na separate pointer parameter).\n\n> objects). I do not think we are in the same position with struct branch;\n> struct branch seems sufficiently separated to me.\n\nSufficiently separated in the sense that you take a branch from the\nparent repository and throwing it at a function with a pointer to an\nin-core repository for a submodule would make some sort of sense?  \n\nSorry, but I do not follow this part of your argument, at all.\n"},{"id":"438746","messageId":"kl6lmtnc1jpt.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"56659","inReplyTo":"xmqqmtncebtb.fsf@gitster.g","subject":"Re: [PATCH v2 0/3] remote: replace static variables with struct remote_state","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-14T01:25:02Z","receivedAt":"2021-10-14T01:25:11Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> IOW, we need to start somewhere to clean things up, so either we\n> teach multi-repository support to remote.c from day one, or we pass\n> a repository as a separate and redundant parameter, with the\n> intention to later clean up.\n\nI authored this topic with the assumption that passing a 'redundant'\ncontext object was the path forward, not something to clean up. I think\nthis explains most of the divergence in opinion.\n\n> ...I suspect that this piece is isolated\n> enough that it is simpler to use as a starting point, and then the\n> callers into remote.c API can then be cleaned up, gradually extending\n> the circle.\n\nAgree that this piece is a good place to start such work.\n\n> I do not think it is meant to be internal to begin with.  If we\n> declare, after careful consideration, that an in-core branch object\n> given by remote.c API always belongs to one and only one repository,\n> then following the branch->repository pointer should be the\n> documented and kosher way to find out the repository by anybody who\n> is given the branch object.\n\nIn the specific case of an in-core object that can refer only to one\nrepository, I think this is a fairly straightforward way of maintaining\nreferential integrity. However I don't think that the approach of\n\"contained object pointing to container\" generalizes well - if I were\npassed a struct remote_state and I needed to access its repository\nlater, is the ideal interface a backpointer, or is there a deeper\nproblem with how things are structured?\n\nIOW, whether or not we should use the backpointer seems to depend on the\nspecifics of each use case. It might be productive to decide on\nguidelines, especially with regards to repository.\n\n> ...A function that takes repo and branch\n> to pretend as if it can work with an inconsistent pair is an\n> invidation for the interface misuse.\n\nFair.\n\n>> objects). I do not think we are in the same position with struct branch;\n>> struct branch seems sufficiently separated to me.\n>\n> Sufficiently separated in the sense that you take a branch from the\n> parent repository and throwing it at a function with a pointer to an\n> in-core repository for a submodule would make some sort of sense?  \n\nI meant \"sufficiently separated\" in the sense that a struct branch is\nmeaningful to callers, even in the absence of a repository. Even in the\ncase where we *might* care about the repository e.g. getting a default\nfor remote_for_branch(), there are callers that make do with just\nbranch->remote_name.\n\nThis was meant to contrast to ref_store, which relies on its specific\nrepository for much of its internals and is difficult to separate. I am\nworried about the knock-on effects of taking a _relatively_ clean\nabstraction layer in struct branch and tying it back to the repository\nlayer.\n\nThat said, you have rightly noted that we should not pretend that a\nbranch from repo A can be thrown together with a pointer to repo B in\nany kind of meaningful way (or at least, not yet). A backpointer can\nprevent this.\n\nI'm not fully convinced either way so I'll take time to mull over it; I\nwould also like to start on the right foot with this subsystem.\n"},{"id":"438769","messageId":"kl6lk0if1pt3.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"56659","inReplyTo":"xmqqlf2wfzfz.fsf@gitster.g","subject":"Re: [PATCH v2 1/3] remote: move static variables into per-repository struct","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-14T17:25:44Z","receivedAt":"2021-10-14T17:25:49Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> +struct rewrite {\n>> +\tconst char *base;\n>> +\tsize_t baselen;\n>> +\tstruct counted_string *instead_of;\n>> +\tint instead_of_nr;\n>> +\tint instead_of_alloc;\n>> +};\n>> +struct rewrites {\n>\n> Missing a blank line between two type decls.\n\nThanks!\n>\n>> +\tstruct rewrite **rewrite;\n>> +\tint rewrite_alloc;\n>> +\tint rewrite_nr;\n>> +};\n>\n> It is a bit sad that we still have to keep \"struct rewrites\"; if we\n> see how .remotes and .branches are handled, we probably should have\n> left the <array, alloc, nr> 3-tuple for \"struct rewrite\" without an\n> extra layer.\n\nI considered doing this, but I found it non-trivial because some\nfunctions use \"struct rewrites\" without consideration for whether it is\nremote_state->rewrites or remote_state->rewrites_push i.e. the \"struct\nrewrites\" abstraction is doing its job.\n\n> ...such a restructuring would not belong here.  OK.\n\nBy \"here\", I assume you mean \"the restructuring doesn't belong in\nthis topic\" vs \"the restructuring belongs in this topic but not this\npatch\".\n\n>> +struct remote_state {\n>> +\tint config_loaded;\n>> +\n>> +\tstruct remote **remotes;\n>> +\tint remotes_alloc;\n>> +\tint remotes_nr;\n>> +\tstruct hashmap remotes_hash;\n>> +\n>> +\tstruct branch **branches;\n>> +\tint branches_alloc;\n>> +\tint branches_nr;\n>> +\n>> +\tstruct branch *current_branch;\n>> +\tconst char *pushremote_name;\n>> +\n>> +\tstruct rewrites rewrites;\n>> +\tstruct rewrites rewrites_push;\n>> +};\n>> +void remote_state_clear(struct remote_state *remote_state);\n>\n> Missing a blank line after \"struct\" decl.\n\nThanks!\n"},{"id":"438775","messageId":"xmqqr1cnbgmw.fsf@gitster.g","threadId":"56659","inReplyTo":"kl6lk0if1pt3.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v2 1/3] remote: move static variables into per-repository struct","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-14T18:33:43Z","receivedAt":"2021-10-14T18:33:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n> By \"here\", I assume you mean \"the restructuring doesn't belong in\n> this topic\" vs \"the restructuring belongs in this topic but not this\n> patch\".\n\nHmph, it is unclear which one you assumed ;-), but anyway I do not\nthink it would help to make such a clean-up as a part of this\nseries.\n\nAdding a pointer to the container to contained types, if done from\nthe beginning without disrupting the function signature (i.e. a\nfunction that takes a branch instance can now work on a branch that\nwas taken from any repository, by simply following branch->repo and\nusing it as a parameter to repo_foo_blah() calls it would make\ninstead of existing foo_blah() calls), may help reduce the size of\nthe series greatly, because you wouldn't have to touch may existing\ncallers.  That would make a great part of this topic.\n\nBut if done as a post clean-up step (i.e. first we sprinkle \"struct\nrepo *\" pointer as an additional parameter all over the functions\nthat take an object that clearly belongs to a repository, and then\nlater shrink the API to remove the extra pointer parameter by\nteaching the contained object which repository it belongs to), it\nwould probably not a good idea to have it as part of this series.\nWe'd instead stop at \"we add a new parameter to functions to tell\nin which repository it now should work\" in the topic itself.\n\nThe \"post clean-up\" series would be like \"the previous series made\nit 'working', now let's clean-up the API\" follow-up that is done\nseparately.  One benefit of going that route would be that it would\nallow us notice there are cases where contained object may have to\nbe used in the context of the container that is different from the\ncontainer it belongs to, before locking ourselves out of the\npossibility by limiting the API.  I personally do not think we'd\nreap that benefit as I do not think of a case where it makes sense\nto take a branch or a remote from a repository and use it in the\ncontext of another repository (hence my suggestion to teach objects\nthe repository they belong to), but if others think it is easier to\nproceed, then...\n\nThanks.\n\n"},{"id":"439049","messageId":"20211019224339.61881-1-chooglen@google.com","threadId":"56659","inReplyTo":"20211013193127.76537-1-chooglen@google.com","subject":"[PATCH v3 0/4] remote: replace static variables with struct remote_state","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-19T22:43:35Z","receivedAt":"2021-10-19T22:43:48Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"This series aims to make the remotes subsystem work with non-the_repository,\nwhich will allow submodule remotes to be accessed in-process, rather than\nthrough child processes. This is accomplished by creating a struct remote_state\nand adding it to struct repository.\n\nOne motivation for this is that it allows future submodule commands to run\nin-process. An example is an RFC series of mine [1], where I tried to implement\n\"git branch --recurse-submodules\" in-process but couldn't figure out how to read\nthe remotes of a submodule.\n\nv3 tightens up the interface issues discussed previously ([2] and other v2\ndiscussion) by adding a \"struct remote_state\" backpointer to \"struct branch\" and\n\"struct remote\". The most notable improvement is that we no longer accept\n\"struct {remote_state,repository}\" and \"struct {branch,remote}\" parameters at\nthe same time, thus it is impossible to pass the wrong (container, contained)\nobject pair. This is especially useful for branch_get_push_1(), where we\ntake a \"struct branch\" and need to get a related \"struct remote\".\n\nThe intention is that this backpointer is only meant to be used by the remotes\nsubsystem; it is meant to be an opaque implementation detail that no other\ncallers should touch.\n\nI attempted the initial suggestion to include a backpointer from branch->repo\ninstead of branch->remote_state and uploaded it to a branch in my own fork [3].\nHowever, this seems to be the wrong abstraction to me. I am not convinced that\nthe contained structs (branch, remote) need to (nor will ever need to) interact\nwith parts of the repository outside of remote_state. A symptom of this is that\ninternal code ends up using \"struct repository\" parameters instead of \"struct\nremote_state\" only to use repo->remote_state repeatedly, violating some of the\nnice layering that we have set up.\n\nThere is also a small YAGNI benefit of using a backpointer to the remote_state\ninstead of repository. If we eventually decide that branches should\npoint to their repository, we can make the change with\ns/remote_state/repo->remote_state, even if external callers start to rely on\nbranch->remote_state (even though I don't think they shouldn't rely on it!).\nThis property does not hold for branch->repo because external callers might be\nusing members other than repository->remote_state.\n\n[1] https://lore.kernel.org/git/20210921232529.81811-1-chooglen@google.com/\n[2] https://lore.kernel.org/git/xmqq4k9so15i.fsf@gitster.g/\n[3] https://github.com/git/git/commit/9527d9106ff5a41530e7bfa50a316af296144c99\n\nChanges since v2:\n* Add .remote_state to struct branch and struct remote, changing the\n  implementation appropriately.\n* In patch 2, properly consider the initialized state of remote_state.\n  In v2, I forgot to convert a static inside read_config() into a\n  private member of struct remote_state. Fix this.\n* In a new patch 3, add helper methods that get a remote via\n  remote_state and the remote name.\n* Move read_config(repo) calls to the external facing-functions. This keeps\n  \"struct repository\" away from the remote.c internals.\n\nChanges since v1:\n\n* In v1, we moved static variables into the_repository->remote_state in\n  two steps: static variables > static remote_state >\n  the_repository->remote_state. In v2, make this change in one step:\n  static variables > the_repository->remote_state.\n* Add more instances of repo_* that were missed.\n\nGlen Choo (4):\n  remote: move static variables into per-repository struct\n  remote: use remote_state parameter internally\n  remote: remove the_repository->remote_state from static methods\n  remote: add struct repository parameter to external functions\n\n remote.c     | 303 +++++++++++++++++++++++++++++++--------------------\n remote.h     |  72 +++++++++++-\n repository.c |   8 ++\n repository.h |   4 +\n 4 files changed, 262 insertions(+), 125 deletions(-)\n\nRange-diff against v2:\n1:  6972ba4dcb ! 1:  1f712c22b4 remote: move static variables into per-repository struct\n    @@ remote.h: enum {\n     +\tint instead_of_nr;\n     +\tint instead_of_alloc;\n     +};\n    ++\n     +struct rewrites {\n     +\tstruct rewrite **rewrite;\n     +\tint rewrite_alloc;\n    @@ remote.h: enum {\n     +};\n     +\n     +struct remote_state {\n    -+\tint config_loaded;\n    -+\n     +\tstruct remote **remotes;\n     +\tint remotes_alloc;\n     +\tint remotes_nr;\n    @@ remote.h: enum {\n     +\tstruct rewrites rewrites;\n     +\tstruct rewrites rewrites_push;\n     +};\n    ++\n     +void remote_state_clear(struct remote_state *remote_state);\n     +struct remote_state *remote_state_new(void);\n     +\n2:  71b1da4389 ! 2:  467247fa9c remote: use remote_state parameter internally\n    @@ Metadata\n      ## Commit message ##\n         remote: use remote_state parameter internally\n     \n    -    In internal-facing functions, replace the_repository->remote_state with\n    -    a struct remote_state parameter, but do not change external-facing\n    -    functions.\n    +    Introduce a struct remote_state member to structs that need to\n    +    'remember' their remote_state. Without changing external-facing\n    +    functions, replace the_repository->remote_state internally by using the\n    +    remote_state member where it is applicable i.e. when a function accepts\n    +    a struct that depends on the remote_state. If it is not applicable, add\n    +    a struct remote_state parameter instead.\n     \n    -    As a result, most static functions no longer reference\n    +    As a result, external-facing functions are still tied to the_repository,\n    +    but most static functions no longer reference\n         the_repository->remote_state. The exceptions are those that are used in\n         a way that depends on external-facing functions e.g. the callbacks to\n         remote_get_1().\n    @@ Commit message\n     \n      ## remote.c ##\n     @@ remote.c: static void add_pushurl(struct remote *remote, const char *pushurl)\n    - \tremote->pushurl[remote->pushurl_nr++] = pushurl;\n    - }\n    - \n    --static void add_pushurl_alias(struct remote *remote, const char *url)\n    -+static void add_pushurl_alias(struct remote_state *remote_state,\n    -+\t\t\t      struct remote *remote, const char *url)\n    + static void add_pushurl_alias(struct remote *remote, const char *url)\n      {\n    --\tconst char *pushurl =\n    + \tconst char *pushurl =\n     -\t\talias_url(url, &the_repository->remote_state->rewrites_push);\n    -+\tconst char *pushurl = alias_url(url, &remote_state->rewrites_push);\n    ++\t\talias_url(url, &remote->remote_state->rewrites_push);\n      \tif (pushurl != url)\n      \t\tadd_pushurl(remote, pushurl);\n      }\n      \n    --static void add_url_alias(struct remote *remote, const char *url)\n    -+static void add_url_alias(struct remote_state *remote_state,\n    -+\t\t\t  struct remote *remote, const char *url)\n    + static void add_url_alias(struct remote *remote, const char *url)\n      {\n     -\tadd_url(remote,\n     -\t\talias_url(url, &the_repository->remote_state->rewrites));\n    --\tadd_pushurl_alias(remote, url);\n    -+\tadd_url(remote, alias_url(url, &remote_state->rewrites));\n    -+\tadd_pushurl_alias(remote_state, remote, url);\n    ++\tadd_url(remote, alias_url(url, &remote->remote_state->rewrites));\n    + \tadd_pushurl_alias(remote, url);\n      }\n      \n    - struct remotes_hash_key {\n     @@ remote.c: static int remotes_hash_cmp(const void *unused_cmp_data,\n      \t\treturn strcmp(a->name, b->name);\n      }\n    @@ remote.c: static struct remote *make_remote(const char *name, int len)\n      \t\treturn container_of(e, struct remote, ent);\n      \n     @@ remote.c: static struct remote *make_remote(const char *name, int len)\n    + \tret->prune = -1;  /* unspecified */\n    + \tret->prune_tags = -1;  /* unspecified */\n    + \tret->name = xstrndup(name, len);\n    ++\tret->remote_state = remote_state;\n      \trefspec_init(&ret->push, REFSPEC_PUSH);\n      \trefspec_init(&ret->fetch, REFSPEC_FETCH);\n      \n    @@ remote.c: static void add_merge(struct branch *branch, const char *name)\n     +\tremote_state->branches[remote_state->branches_nr++] = ret;\n      \tret->name = xstrndup(name, len);\n      \tret->refname = xstrfmt(\"refs/heads/%s\", ret->name);\n    ++\tret->remote_state = remote_state;\n      \n    -@@ remote.c: static const char *skip_spaces(const char *s)\n    - \treturn s;\n    - }\n    - \n    --static void read_remotes_file(struct remote *remote)\n    -+static void read_remotes_file(struct remote_state *remote_state,\n    -+\t\t\t      struct remote *remote)\n    - {\n    - \tstruct strbuf buf = STRBUF_INIT;\n    - \tFILE *f = fopen_or_warn(git_path(\"remotes/%s\", remote->name), \"r\");\n    -@@ remote.c: static void read_remotes_file(struct remote *remote)\n    - \t\tstrbuf_rtrim(&buf);\n    - \n    - \t\tif (skip_prefix(buf.buf, \"URL:\", &v))\n    --\t\t\tadd_url_alias(remote, xstrdup(skip_spaces(v)));\n    -+\t\t\tadd_url_alias(remote_state, remote,\n    -+\t\t\t\t      xstrdup(skip_spaces(v)));\n    - \t\telse if (skip_prefix(buf.buf, \"Push:\", &v))\n    - \t\t\trefspec_append(&remote->push, skip_spaces(v));\n    - \t\telse if (skip_prefix(buf.buf, \"Pull:\", &v))\n    -@@ remote.c: static void read_remotes_file(struct remote *remote)\n    - \tfclose(f);\n    + \treturn ret;\n      }\n    - \n    --static void read_branches_file(struct remote *remote)\n    -+static void read_branches_file(struct remote_state *remote_state,\n    -+\t\t\t       struct remote *remote)\n    - {\n    - \tchar *frag;\n    - \tstruct strbuf buf = STRBUF_INIT;\n    -@@ remote.c: static void read_branches_file(struct remote *remote)\n    - \telse\n    - \t\tfrag = (char *)git_default_branch_name(0);\n    - \n    --\tadd_url_alias(remote, strbuf_detach(&buf, NULL));\n    -+\tadd_url_alias(remote_state, remote, strbuf_detach(&buf, NULL));\n    - \trefspec_appendf(&remote->fetch, \"refs/heads/%s:refs/heads/%s\",\n    - \t\t\tfrag, remote->name);\n    - \n     @@ remote.c: static int handle_config(const char *key, const char *value, void *cb)\n      \tconst char *subkey;\n      \tstruct remote *remote;\n    @@ remote.c: static int handle_config(const char *key, const char *value, void *cb)\n     -\t\t\t\t->url[j] = alias_url(\n     -\t\t\t\tthe_repository->remote_state->remotes[i]->url[j],\n     -\t\t\t\t&the_repository->remote_state->rewrites);\n    -+\t\t\t\t\tremote_state, remote_state->remotes[i],\n    ++\t\t\t\t\tremote_state->remotes[i],\n     +\t\t\t\t\tremote_state->remotes[i]->url[j]);\n     +\t\t\tremote_state->remotes[i]->url[j] =\n     +\t\t\t\talias_url(remote_state->remotes[i]->url[j],\n    @@ remote.c: static int handle_config(const char *key, const char *value, void *cb)\n      }\n      \n     -static void read_config(void)\n    -+static void read_config(struct remote_state *remote_state)\n    ++static void read_config(struct repository *repo)\n      {\n    - \tstatic int loaded;\n    +-\tstatic int loaded;\n      \tint flag;\n    -@@ remote.c: static void read_config(void)\n    + \n    +-\tif (loaded)\n    ++\tif (repo->remote_state->initialized)\n      \t\treturn;\n    - \tloaded = 1;\n    +-\tloaded = 1;\n    ++\trepo->remote_state->initialized = 1;\n      \n     -\tthe_repository->remote_state->current_branch = NULL;\n    -+\tremote_state->current_branch = NULL;\n    ++\trepo->remote_state->current_branch = NULL;\n      \tif (startup_info->have_repository) {\n    - \t\tconst char *head_ref = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flag);\n    +-\t\tconst char *head_ref = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flag);\n    ++\t\tconst char *head_ref = refs_resolve_ref_unsafe(\n    ++\t\t\tget_main_ref_store(repo), \"HEAD\", 0, NULL, &flag);\n      \t\tif (head_ref && (flag & REF_ISSYMREF) &&\n      \t\t    skip_prefix(head_ref, \"refs/heads/\", &head_ref)) {\n     -\t\t\tthe_repository->remote_state->current_branch =\n     -\t\t\t\tmake_branch(head_ref, strlen(head_ref));\n    -+\t\t\tremote_state->current_branch = make_branch(\n    -+\t\t\t\tremote_state, head_ref, strlen(head_ref));\n    ++\t\t\trepo->remote_state->current_branch = make_branch(\n    ++\t\t\t\trepo->remote_state, head_ref, strlen(head_ref));\n      \t\t}\n      \t}\n     -\tgit_config(handle_config, NULL);\n     -\talias_all_urls();\n    -+\tgit_config(handle_config, remote_state);\n    -+\talias_all_urls(remote_state);\n    ++\trepo_config(repo, handle_config, repo->remote_state);\n    ++\talias_all_urls(repo->remote_state);\n      }\n      \n      static int valid_remote_nick(const char *name)\n    +@@ remote.c: const char *pushremote_for_branch(struct branch *branch, int *explicit)\n    + \t\t\t*explicit = 1;\n    + \t\treturn branch->pushremote_name;\n    + \t}\n    +-\tif (the_repository->remote_state->pushremote_name) {\n    ++\tif (branch->remote_state->pushremote_name) {\n    + \t\tif (explicit)\n    + \t\t\t*explicit = 1;\n    +-\t\treturn the_repository->remote_state->pushremote_name;\n    ++\t\treturn branch->remote_state->pushremote_name;\n    + \t}\n    + \treturn remote_for_branch(branch, explicit);\n    + }\n     @@ remote.c: static struct remote *remote_get_1(const char *name,\n      \tstruct remote *ret;\n      \tint name_given = 0;\n      \n     -\tread_config();\n    -+\tread_config(the_repository->remote_state);\n    ++\tread_config(the_repository);\n      \n      \tif (name)\n      \t\tname_given = 1;\n    @@ remote.c: static struct remote *remote_get_1(const char *name,\n     +\tret = make_remote(the_repository->remote_state, name, 0);\n      \tif (valid_remote_nick(name) && have_git_dir()) {\n      \t\tif (!valid_remote(ret))\n    --\t\t\tread_remotes_file(ret);\n    -+\t\t\tread_remotes_file(the_repository->remote_state, ret);\n    - \t\tif (!valid_remote(ret))\n    --\t\t\tread_branches_file(ret);\n    -+\t\t\tread_branches_file(the_repository->remote_state, ret);\n    - \t}\n    - \tif (name_given && !valid_remote(ret))\n    --\t\tadd_url_alias(ret, name);\n    -+\t\tadd_url_alias(the_repository->remote_state, ret, name);\n    - \tif (!valid_remote(ret))\n    - \t\treturn NULL;\n    - \treturn ret;\n    + \t\t\tread_remotes_file(ret);\n     @@ remote.c: int remote_is_configured(struct remote *remote, int in_repo)\n      int for_each_remote(each_remote_fn fn, void *priv)\n      {\n      \tint i, result = 0;\n     -\tread_config();\n    -+\tread_config(the_repository->remote_state);\n    ++\tread_config(the_repository);\n      \tfor (i = 0; i < the_repository->remote_state->remotes_nr && !result;\n      \t     i++) {\n      \t\tstruct remote *remote =\n    @@ remote.c: struct branch *branch_get(const char *name)\n      \tstruct branch *ret;\n      \n     -\tread_config();\n    -+\tread_config(the_repository->remote_state);\n    ++\tread_config(the_repository);\n      \tif (!name || !*name || !strcmp(name, \"HEAD\"))\n      \t\tret = the_repository->remote_state->current_branch;\n      \telse\n    @@ remote.c: struct branch *branch_get(const char *name)\n      \tset_merge(ret);\n      \treturn ret;\n      }\n    +\n    + ## remote.h ##\n    +@@ remote.h: struct remote_state {\n    + \n    + \tstruct rewrites rewrites;\n    + \tstruct rewrites rewrites_push;\n    ++\n    ++\tint initialized;\n    + };\n    + \n    + void remote_state_clear(struct remote_state *remote_state);\n    +@@ remote.h: struct remote {\n    + \n    + \t/* The method used for authenticating against `http_proxy`. */\n    + \tchar *http_proxy_authmethod;\n    ++\n    ++\t/** The remote_state that this remote belongs to. This is only meant to\n    ++\t * be used by remote_* functions. */\n    ++\tstruct remote_state *remote_state;\n    + };\n    + \n    + /**\n    +@@ remote.h: struct branch {\n    + \tint merge_alloc;\n    + \n    + \tconst char *push_tracking_ref;\n    ++\n    ++\t/** The remote_state that this branch belongs to. This is only meant to\n    ++\t * be used by branch_* functions. */\n    ++\tstruct remote_state *remote_state;\n    + };\n    + \n    + struct branch *branch_get(const char *name);\n3:  ff12771f06 < -:  ---------- remote: add struct repository parameter to external functions\n-:  ---------- > 3:  10fbb84496 remote: remove the_repository->remote_state from static methods\n-:  ---------- > 4:  4013f74fd9 remote: add struct repository parameter to external functions\n-- \n2.33.GIT\n\n"},{"id":"439050","messageId":"20211019224339.61881-2-chooglen@google.com","threadId":"56659","inReplyTo":"20211019224339.61881-1-chooglen@google.com","subject":"[PATCH v3 1/4] remote: move static variables into per-repository struct","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-19T22:43:36Z","receivedAt":"2021-10-19T22:43:52Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"remote.c does not works with non-the_repository because it stores its\nstate as static variables. To support non-the_repository, we can use a\nper-repository struct for the remotes subsystem.\n\nPrepare for this change by defining a struct remote_state that holds\nthe remotes subsystem state and move the static variables of remote.c\ninto the_repository->remote_state.\n\nThis introduces no behavioral or API changes.\n\nSigned-off-by: Glen Choo <chooglen@google.com>\n---\n remote.c     | 187 ++++++++++++++++++++++++++++++++++-----------------\n remote.h     |  34 ++++++++++\n repository.c |   8 +++\n repository.h |   4 ++\n 4 files changed, 171 insertions(+), 62 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex f958543d70..29c29fcc3b 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -21,33 +21,6 @@ struct counted_string {\n \tsize_t len;\n \tconst char *s;\n };\n-struct rewrite {\n-\tconst char *base;\n-\tsize_t baselen;\n-\tstruct counted_string *instead_of;\n-\tint instead_of_nr;\n-\tint instead_of_alloc;\n-};\n-struct rewrites {\n-\tstruct rewrite **rewrite;\n-\tint rewrite_alloc;\n-\tint rewrite_nr;\n-};\n-\n-static struct remote **remotes;\n-static int remotes_alloc;\n-static int remotes_nr;\n-static struct hashmap remotes_hash;\n-\n-static struct branch **branches;\n-static int branches_alloc;\n-static int branches_nr;\n-\n-static struct branch *current_branch;\n-static const char *pushremote_name;\n-\n-static struct rewrites rewrites;\n-static struct rewrites rewrites_push;\n \n static int valid_remote(const struct remote *remote)\n {\n@@ -94,14 +67,16 @@ static void add_pushurl(struct remote *remote, const char *pushurl)\n \n static void add_pushurl_alias(struct remote *remote, const char *url)\n {\n-\tconst char *pushurl = alias_url(url, &rewrites_push);\n+\tconst char *pushurl =\n+\t\talias_url(url, &the_repository->remote_state->rewrites_push);\n \tif (pushurl != url)\n \t\tadd_pushurl(remote, pushurl);\n }\n \n static void add_url_alias(struct remote *remote, const char *url)\n {\n-\tadd_url(remote, alias_url(url, &rewrites));\n+\tadd_url(remote,\n+\t\talias_url(url, &the_repository->remote_state->rewrites));\n \tadd_pushurl_alias(remote, url);\n }\n \n@@ -129,8 +104,9 @@ static int remotes_hash_cmp(const void *unused_cmp_data,\n \n static inline void init_remotes_hash(void)\n {\n-\tif (!remotes_hash.cmpfn)\n-\t\thashmap_init(&remotes_hash, remotes_hash_cmp, NULL, 0);\n+\tif (!the_repository->remote_state->remotes_hash.cmpfn)\n+\t\thashmap_init(&the_repository->remote_state->remotes_hash,\n+\t\t\t     remotes_hash_cmp, NULL, 0);\n }\n \n static struct remote *make_remote(const char *name, int len)\n@@ -147,7 +123,8 @@ static struct remote *make_remote(const char *name, int len)\n \tlookup.len = len;\n \thashmap_entry_init(&lookup_entry, memhash(name, len));\n \n-\te = hashmap_get(&remotes_hash, &lookup_entry, &lookup);\n+\te = hashmap_get(&the_repository->remote_state->remotes_hash,\n+\t\t\t&lookup_entry, &lookup);\n \tif (e)\n \t\treturn container_of(e, struct remote, ent);\n \n@@ -158,15 +135,41 @@ static struct remote *make_remote(const char *name, int len)\n \trefspec_init(&ret->push, REFSPEC_PUSH);\n \trefspec_init(&ret->fetch, REFSPEC_FETCH);\n \n-\tALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);\n-\tremotes[remotes_nr++] = ret;\n+\tALLOC_GROW(the_repository->remote_state->remotes,\n+\t\t   the_repository->remote_state->remotes_nr + 1,\n+\t\t   the_repository->remote_state->remotes_alloc);\n+\tthe_repository->remote_state\n+\t\t->remotes[the_repository->remote_state->remotes_nr++] = ret;\n \n \thashmap_entry_init(&ret->ent, lookup_entry.hash);\n-\tif (hashmap_put_entry(&remotes_hash, ret, ent))\n+\tif (hashmap_put_entry(&the_repository->remote_state->remotes_hash, ret,\n+\t\t\t      ent))\n \t\tBUG(\"hashmap_put overwrote entry after hashmap_get returned NULL\");\n \treturn ret;\n }\n \n+static void remote_clear(struct remote *remote)\n+{\n+\tint i;\n+\n+\tfree((char *)remote->name);\n+\tfree((char *)remote->foreign_vcs);\n+\n+\tfor (i = 0; i < remote->url_nr; i++) {\n+\t\tfree((char *)remote->url[i]);\n+\t}\n+\tFREE_AND_NULL(remote->pushurl);\n+\n+\tfor (i = 0; i < remote->pushurl_nr; i++) {\n+\t\tfree((char *)remote->pushurl[i]);\n+\t}\n+\tFREE_AND_NULL(remote->pushurl);\n+\tfree((char *)remote->receivepack);\n+\tfree((char *)remote->uploadpack);\n+\tFREE_AND_NULL(remote->http_proxy);\n+\tFREE_AND_NULL(remote->http_proxy_authmethod);\n+}\n+\n static void add_merge(struct branch *branch, const char *name)\n {\n \tALLOC_GROW(branch->merge_name, branch->merge_nr + 1,\n@@ -179,15 +182,20 @@ static struct branch *make_branch(const char *name, size_t len)\n \tstruct branch *ret;\n \tint i;\n \n-\tfor (i = 0; i < branches_nr; i++) {\n-\t\tif (!strncmp(name, branches[i]->name, len) &&\n-\t\t    !branches[i]->name[len])\n-\t\t\treturn branches[i];\n+\tfor (i = 0; i < the_repository->remote_state->branches_nr; i++) {\n+\t\tif (!strncmp(name,\n+\t\t\t     the_repository->remote_state->branches[i]->name,\n+\t\t\t     len) &&\n+\t\t    !the_repository->remote_state->branches[i]->name[len])\n+\t\t\treturn the_repository->remote_state->branches[i];\n \t}\n \n-\tALLOC_GROW(branches, branches_nr + 1, branches_alloc);\n+\tALLOC_GROW(the_repository->remote_state->branches,\n+\t\t   the_repository->remote_state->branches_nr + 1,\n+\t\t   the_repository->remote_state->branches_alloc);\n \tCALLOC_ARRAY(ret, 1);\n-\tbranches[branches_nr++] = ret;\n+\tthe_repository->remote_state\n+\t\t->branches[the_repository->remote_state->branches_nr++] = ret;\n \tret->name = xstrndup(name, len);\n \tret->refname = xstrfmt(\"refs/heads/%s\", ret->name);\n \n@@ -327,12 +335,16 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\tif (!strcmp(subkey, \"insteadof\")) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n-\t\t\trewrite = make_rewrite(&rewrites, name, namelen);\n+\t\t\trewrite = make_rewrite(\n+\t\t\t\t&the_repository->remote_state->rewrites, name,\n+\t\t\t\tnamelen);\n \t\t\tadd_instead_of(rewrite, xstrdup(value));\n \t\t} else if (!strcmp(subkey, \"pushinsteadof\")) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n-\t\t\trewrite = make_rewrite(&rewrites_push, name, namelen);\n+\t\t\trewrite = make_rewrite(\n+\t\t\t\t&the_repository->remote_state->rewrites_push,\n+\t\t\t\tname, namelen);\n \t\t\tadd_instead_of(rewrite, xstrdup(value));\n \t\t}\n \t}\n@@ -342,7 +354,9 @@ static int handle_config(const char *key, const char *value, void *cb)\n \n \t/* Handle remote.* variables */\n \tif (!name && !strcmp(subkey, \"pushdefault\"))\n-\t\treturn git_config_string(&pushremote_name, key, value);\n+\t\treturn git_config_string(\n+\t\t\t&the_repository->remote_state->pushremote_name, key,\n+\t\t\tvalue);\n \n \tif (!name)\n \t\treturn 0;\n@@ -425,18 +439,34 @@ static int handle_config(const char *key, const char *value, void *cb)\n static void alias_all_urls(void)\n {\n \tint i, j;\n-\tfor (i = 0; i < remotes_nr; i++) {\n+\tfor (i = 0; i < the_repository->remote_state->remotes_nr; i++) {\n \t\tint add_pushurl_aliases;\n-\t\tif (!remotes[i])\n+\t\tif (!the_repository->remote_state->remotes[i])\n \t\t\tcontinue;\n-\t\tfor (j = 0; j < remotes[i]->pushurl_nr; j++) {\n-\t\t\tremotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j], &rewrites);\n+\t\tfor (j = 0;\n+\t\t     j < the_repository->remote_state->remotes[i]->pushurl_nr;\n+\t\t     j++) {\n+\t\t\tthe_repository->remote_state->remotes[i]->pushurl[j] =\n+\t\t\t\talias_url(\n+\t\t\t\t\tthe_repository->remote_state->remotes[i]\n+\t\t\t\t\t\t->pushurl[j],\n+\t\t\t\t\t&the_repository->remote_state->rewrites);\n \t\t}\n-\t\tadd_pushurl_aliases = remotes[i]->pushurl_nr == 0;\n-\t\tfor (j = 0; j < remotes[i]->url_nr; j++) {\n+\t\tadd_pushurl_aliases =\n+\t\t\tthe_repository->remote_state->remotes[i]->pushurl_nr ==\n+\t\t\t0;\n+\t\tfor (j = 0;\n+\t\t     j < the_repository->remote_state->remotes[i]->url_nr;\n+\t\t     j++) {\n \t\t\tif (add_pushurl_aliases)\n-\t\t\t\tadd_pushurl_alias(remotes[i], remotes[i]->url[j]);\n-\t\t\tremotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);\n+\t\t\t\tadd_pushurl_alias(\n+\t\t\t\t\tthe_repository->remote_state->remotes[i],\n+\t\t\t\t\tthe_repository->remote_state->remotes[i]\n+\t\t\t\t\t\t->url[j]);\n+\t\t\tthe_repository->remote_state->remotes[i]\n+\t\t\t\t->url[j] = alias_url(\n+\t\t\t\tthe_repository->remote_state->remotes[i]->url[j],\n+\t\t\t\t&the_repository->remote_state->rewrites);\n \t\t}\n \t}\n }\n@@ -450,12 +480,13 @@ static void read_config(void)\n \t\treturn;\n \tloaded = 1;\n \n-\tcurrent_branch = NULL;\n+\tthe_repository->remote_state->current_branch = NULL;\n \tif (startup_info->have_repository) {\n \t\tconst char *head_ref = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flag);\n \t\tif (head_ref && (flag & REF_ISSYMREF) &&\n \t\t    skip_prefix(head_ref, \"refs/heads/\", &head_ref)) {\n-\t\t\tcurrent_branch = make_branch(head_ref, strlen(head_ref));\n+\t\t\tthe_repository->remote_state->current_branch =\n+\t\t\t\tmake_branch(head_ref, strlen(head_ref));\n \t\t}\n \t}\n \tgit_config(handle_config, NULL);\n@@ -493,10 +524,10 @@ const char *pushremote_for_branch(struct branch *branch, int *explicit)\n \t\t\t*explicit = 1;\n \t\treturn branch->pushremote_name;\n \t}\n-\tif (pushremote_name) {\n+\tif (the_repository->remote_state->pushremote_name) {\n \t\tif (explicit)\n \t\t\t*explicit = 1;\n-\t\treturn pushremote_name;\n+\t\treturn the_repository->remote_state->pushremote_name;\n \t}\n \treturn remote_for_branch(branch, explicit);\n }\n@@ -534,7 +565,8 @@ static struct remote *remote_get_1(const char *name,\n \tif (name)\n \t\tname_given = 1;\n \telse\n-\t\tname = get_default(current_branch, &name_given);\n+\t\tname = get_default(the_repository->remote_state->current_branch,\n+\t\t\t\t   &name_given);\n \n \tret = make_remote(name, 0);\n \tif (valid_remote_nick(name) && have_git_dir()) {\n@@ -573,11 +605,13 @@ int for_each_remote(each_remote_fn fn, void *priv)\n {\n \tint i, result = 0;\n \tread_config();\n-\tfor (i = 0; i < remotes_nr && !result; i++) {\n-\t\tstruct remote *r = remotes[i];\n-\t\tif (!r)\n+\tfor (i = 0; i < the_repository->remote_state->remotes_nr && !result;\n+\t     i++) {\n+\t\tstruct remote *remote =\n+\t\t\tthe_repository->remote_state->remotes[i];\n+\t\tif (!remote)\n \t\t\tcontinue;\n-\t\tresult = fn(r, priv);\n+\t\tresult = fn(remote, priv);\n \t}\n \treturn result;\n }\n@@ -1685,7 +1719,7 @@ struct branch *branch_get(const char *name)\n \n \tread_config();\n \tif (!name || !*name || !strcmp(name, \"HEAD\"))\n-\t\tret = current_branch;\n+\t\tret = the_repository->remote_state->current_branch;\n \telse\n \t\tret = make_branch(name, strlen(name));\n \tset_merge(ret);\n@@ -2585,3 +2619,32 @@ void apply_push_cas(struct push_cas_option *cas,\n \t\t\tcheck_if_includes_upstream(ref);\n \t}\n }\n+\n+struct remote_state *remote_state_new(void)\n+{\n+\tstruct remote_state *r = xmalloc(sizeof(*r));\n+\n+\tmemset(r, 0, sizeof(*r));\n+\treturn r;\n+}\n+\n+void remote_state_clear(struct remote_state *remote_state)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < remote_state->remotes_nr; i++) {\n+\t\tremote_clear(remote_state->remotes[i]);\n+\t}\n+\tFREE_AND_NULL(remote_state->remotes);\n+\tremote_state->remotes_alloc = 0;\n+\tremote_state->remotes_nr = 0;\n+\n+\thashmap_clear_and_free(&remote_state->remotes_hash, struct remote, ent);\n+\n+\tfor (i = 0; i < remote_state->branches_nr; i++) {\n+\t\tFREE_AND_NULL(remote_state->branches[i]);\n+\t}\n+\tFREE_AND_NULL(remote_state->branches);\n+\tremote_state->branches_alloc = 0;\n+\tremote_state->branches_nr = 0;\n+}\ndiff --git a/remote.h b/remote.h\nindex 5a59198252..d21c035f1b 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -23,6 +23,40 @@ enum {\n \tREMOTE_BRANCHES\n };\n \n+struct rewrite {\n+\tconst char *base;\n+\tsize_t baselen;\n+\tstruct counted_string *instead_of;\n+\tint instead_of_nr;\n+\tint instead_of_alloc;\n+};\n+\n+struct rewrites {\n+\tstruct rewrite **rewrite;\n+\tint rewrite_alloc;\n+\tint rewrite_nr;\n+};\n+\n+struct remote_state {\n+\tstruct remote **remotes;\n+\tint remotes_alloc;\n+\tint remotes_nr;\n+\tstruct hashmap remotes_hash;\n+\n+\tstruct branch **branches;\n+\tint branches_alloc;\n+\tint branches_nr;\n+\n+\tstruct branch *current_branch;\n+\tconst char *pushremote_name;\n+\n+\tstruct rewrites rewrites;\n+\tstruct rewrites rewrites_push;\n+};\n+\n+void remote_state_clear(struct remote_state *remote_state);\n+struct remote_state *remote_state_new(void);\n+\n struct remote {\n \tstruct hashmap_entry ent;\n \ndiff --git a/repository.c b/repository.c\nindex c5b90ba93e..c7ea706c20 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -9,6 +9,7 @@\n #include \"config.h\"\n #include \"object.h\"\n #include \"lockfile.h\"\n+#include \"remote.h\"\n #include \"submodule-config.h\"\n #include \"sparse-index.h\"\n #include \"promisor-remote.h\"\n@@ -24,6 +25,7 @@ void initialize_the_repository(void)\n \n \tthe_repo.index = &the_index;\n \tthe_repo.objects = raw_object_store_new();\n+\tthe_repo.remote_state = remote_state_new();\n \tthe_repo.parsed_objects = parsed_object_pool_new();\n \n \trepo_set_hash_algo(&the_repo, GIT_HASH_SHA1);\n@@ -164,6 +166,7 @@ int repo_init(struct repository *repo,\n \n \trepo->objects = raw_object_store_new();\n \trepo->parsed_objects = parsed_object_pool_new();\n+\trepo->remote_state = remote_state_new();\n \n \tif (repo_init_gitdir(repo, gitdir))\n \t\tgoto error;\n@@ -270,6 +273,11 @@ void repo_clear(struct repository *repo)\n \t\tpromisor_remote_clear(repo->promisor_remote_config);\n \t\tFREE_AND_NULL(repo->promisor_remote_config);\n \t}\n+\n+\tif (repo->remote_state) {\n+\t\tremote_state_clear(repo->remote_state);\n+\t\tFREE_AND_NULL(repo->remote_state);\n+\t}\n }\n \n int repo_read_index(struct repository *repo)\ndiff --git a/repository.h b/repository.h\nindex a057653981..98f9583470 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -11,6 +11,7 @@ struct pathspec;\n struct raw_object_store;\n struct submodule_cache;\n struct promisor_remote_config;\n+struct remote_state;\n \n enum untracked_cache_setting {\n \tUNTRACKED_CACHE_KEEP,\n@@ -127,6 +128,9 @@ struct repository {\n \t */\n \tstruct index_state *index;\n \n+\t/* Repository's remotes and associated structures. */\n+\tstruct remote_state *remote_state;\n+\n \t/* Repository's current hash algorithm, as serialized on disk. */\n \tconst struct git_hash_algo *hash_algo;\n \n-- \n2.33.GIT\n\n"},{"id":"439051","messageId":"20211019224339.61881-3-chooglen@google.com","threadId":"56659","inReplyTo":"20211019224339.61881-1-chooglen@google.com","subject":"[PATCH v3 2/4] remote: use remote_state parameter internally","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-19T22:43:37Z","receivedAt":"2021-10-19T22:43:54Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Introduce a struct remote_state member to structs that need to\n'remember' their remote_state. Without changing external-facing\nfunctions, replace the_repository->remote_state internally by using the\nremote_state member where it is applicable i.e. when a function accepts\na struct that depends on the remote_state. If it is not applicable, add\na struct remote_state parameter instead.\n\nAs a result, external-facing functions are still tied to the_repository,\nbut most static functions no longer reference\nthe_repository->remote_state. The exceptions are those that are used in\na way that depends on external-facing functions e.g. the callbacks to\nremote_get_1().\n\nSigned-off-by: Glen Choo <chooglen@google.com>\n---\n remote.c | 153 ++++++++++++++++++++++++++-----------------------------\n remote.h |  10 ++++\n 2 files changed, 81 insertions(+), 82 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 29c29fcc3b..e3ca44f735 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -68,15 +68,14 @@ static void add_pushurl(struct remote *remote, const char *pushurl)\n static void add_pushurl_alias(struct remote *remote, const char *url)\n {\n \tconst char *pushurl =\n-\t\talias_url(url, &the_repository->remote_state->rewrites_push);\n+\t\talias_url(url, &remote->remote_state->rewrites_push);\n \tif (pushurl != url)\n \t\tadd_pushurl(remote, pushurl);\n }\n \n static void add_url_alias(struct remote *remote, const char *url)\n {\n-\tadd_url(remote,\n-\t\talias_url(url, &the_repository->remote_state->rewrites));\n+\tadd_url(remote, alias_url(url, &remote->remote_state->rewrites));\n \tadd_pushurl_alias(remote, url);\n }\n \n@@ -102,14 +101,19 @@ static int remotes_hash_cmp(const void *unused_cmp_data,\n \t\treturn strcmp(a->name, b->name);\n }\n \n-static inline void init_remotes_hash(void)\n+/**\n+ * NEEDSWORK: Now that the hashmap is in a struct, this should probably\n+ * just be moved into remote_state_new().\n+ */\n+static inline void init_remotes_hash(struct remote_state *remote_state)\n {\n-\tif (!the_repository->remote_state->remotes_hash.cmpfn)\n-\t\thashmap_init(&the_repository->remote_state->remotes_hash,\n-\t\t\t     remotes_hash_cmp, NULL, 0);\n+\tif (!remote_state->remotes_hash.cmpfn)\n+\t\thashmap_init(&remote_state->remotes_hash, remotes_hash_cmp,\n+\t\t\t     NULL, 0);\n }\n \n-static struct remote *make_remote(const char *name, int len)\n+static struct remote *make_remote(struct remote_state *remote_state,\n+\t\t\t\t  const char *name, int len)\n {\n \tstruct remote *ret;\n \tstruct remotes_hash_key lookup;\n@@ -118,13 +122,12 @@ static struct remote *make_remote(const char *name, int len)\n \tif (!len)\n \t\tlen = strlen(name);\n \n-\tinit_remotes_hash();\n+\tinit_remotes_hash(remote_state);\n \tlookup.str = name;\n \tlookup.len = len;\n \thashmap_entry_init(&lookup_entry, memhash(name, len));\n \n-\te = hashmap_get(&the_repository->remote_state->remotes_hash,\n-\t\t\t&lookup_entry, &lookup);\n+\te = hashmap_get(&remote_state->remotes_hash, &lookup_entry, &lookup);\n \tif (e)\n \t\treturn container_of(e, struct remote, ent);\n \n@@ -132,18 +135,16 @@ static struct remote *make_remote(const char *name, int len)\n \tret->prune = -1;  /* unspecified */\n \tret->prune_tags = -1;  /* unspecified */\n \tret->name = xstrndup(name, len);\n+\tret->remote_state = remote_state;\n \trefspec_init(&ret->push, REFSPEC_PUSH);\n \trefspec_init(&ret->fetch, REFSPEC_FETCH);\n \n-\tALLOC_GROW(the_repository->remote_state->remotes,\n-\t\t   the_repository->remote_state->remotes_nr + 1,\n-\t\t   the_repository->remote_state->remotes_alloc);\n-\tthe_repository->remote_state\n-\t\t->remotes[the_repository->remote_state->remotes_nr++] = ret;\n+\tALLOC_GROW(remote_state->remotes, remote_state->remotes_nr + 1,\n+\t\t   remote_state->remotes_alloc);\n+\tremote_state->remotes[remote_state->remotes_nr++] = ret;\n \n \thashmap_entry_init(&ret->ent, lookup_entry.hash);\n-\tif (hashmap_put_entry(&the_repository->remote_state->remotes_hash, ret,\n-\t\t\t      ent))\n+\tif (hashmap_put_entry(&remote_state->remotes_hash, ret, ent))\n \t\tBUG(\"hashmap_put overwrote entry after hashmap_get returned NULL\");\n \treturn ret;\n }\n@@ -177,27 +178,25 @@ static void add_merge(struct branch *branch, const char *name)\n \tbranch->merge_name[branch->merge_nr++] = name;\n }\n \n-static struct branch *make_branch(const char *name, size_t len)\n+static struct branch *make_branch(struct remote_state *remote_state,\n+\t\t\t\t  const char *name, size_t len)\n {\n \tstruct branch *ret;\n \tint i;\n \n-\tfor (i = 0; i < the_repository->remote_state->branches_nr; i++) {\n-\t\tif (!strncmp(name,\n-\t\t\t     the_repository->remote_state->branches[i]->name,\n-\t\t\t     len) &&\n-\t\t    !the_repository->remote_state->branches[i]->name[len])\n-\t\t\treturn the_repository->remote_state->branches[i];\n+\tfor (i = 0; i < remote_state->branches_nr; i++) {\n+\t\tif (!strncmp(name, remote_state->branches[i]->name, len) &&\n+\t\t    !remote_state->branches[i]->name[len])\n+\t\t\treturn remote_state->branches[i];\n \t}\n \n-\tALLOC_GROW(the_repository->remote_state->branches,\n-\t\t   the_repository->remote_state->branches_nr + 1,\n-\t\t   the_repository->remote_state->branches_alloc);\n+\tALLOC_GROW(remote_state->branches, remote_state->branches_nr + 1,\n+\t\t   remote_state->branches_alloc);\n \tCALLOC_ARRAY(ret, 1);\n-\tthe_repository->remote_state\n-\t\t->branches[the_repository->remote_state->branches_nr++] = ret;\n+\tremote_state->branches[remote_state->branches_nr++] = ret;\n \tret->name = xstrndup(name, len);\n \tret->refname = xstrfmt(\"refs/heads/%s\", ret->name);\n+\tret->remote_state = remote_state;\n \n \treturn ret;\n }\n@@ -313,10 +312,12 @@ static int handle_config(const char *key, const char *value, void *cb)\n \tconst char *subkey;\n \tstruct remote *remote;\n \tstruct branch *branch;\n+\tstruct remote_state *remote_state = cb;\n+\n \tif (parse_config_key(key, \"branch\", &name, &namelen, &subkey) >= 0) {\n \t\tif (!name)\n \t\t\treturn 0;\n-\t\tbranch = make_branch(name, namelen);\n+\t\tbranch = make_branch(remote_state, name, namelen);\n \t\tif (!strcmp(subkey, \"remote\")) {\n \t\t\treturn git_config_string(&branch->remote_name, key, value);\n \t\t} else if (!strcmp(subkey, \"pushremote\")) {\n@@ -335,16 +336,14 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\tif (!strcmp(subkey, \"insteadof\")) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n-\t\t\trewrite = make_rewrite(\n-\t\t\t\t&the_repository->remote_state->rewrites, name,\n-\t\t\t\tnamelen);\n+\t\t\trewrite = make_rewrite(&remote_state->rewrites, name,\n+\t\t\t\t\t       namelen);\n \t\t\tadd_instead_of(rewrite, xstrdup(value));\n \t\t} else if (!strcmp(subkey, \"pushinsteadof\")) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n-\t\t\trewrite = make_rewrite(\n-\t\t\t\t&the_repository->remote_state->rewrites_push,\n-\t\t\t\tname, namelen);\n+\t\t\trewrite = make_rewrite(&remote_state->rewrites_push,\n+\t\t\t\t\t       name, namelen);\n \t\t\tadd_instead_of(rewrite, xstrdup(value));\n \t\t}\n \t}\n@@ -354,9 +353,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \n \t/* Handle remote.* variables */\n \tif (!name && !strcmp(subkey, \"pushdefault\"))\n-\t\treturn git_config_string(\n-\t\t\t&the_repository->remote_state->pushremote_name, key,\n-\t\t\tvalue);\n+\t\treturn git_config_string(&remote_state->pushremote_name, key,\n+\t\t\t\t\t value);\n \n \tif (!name)\n \t\treturn 0;\n@@ -366,7 +364,7 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\t\tname);\n \t\treturn 0;\n \t}\n-\tremote = make_remote(name, namelen);\n+\tremote = make_remote(remote_state, name, namelen);\n \tremote->origin = REMOTE_CONFIG;\n \tif (current_config_scope() == CONFIG_SCOPE_LOCAL ||\n \t    current_config_scope() == CONFIG_SCOPE_WORKTREE)\n@@ -436,61 +434,51 @@ static int handle_config(const char *key, const char *value, void *cb)\n \treturn 0;\n }\n \n-static void alias_all_urls(void)\n+static void alias_all_urls(struct remote_state *remote_state)\n {\n \tint i, j;\n-\tfor (i = 0; i < the_repository->remote_state->remotes_nr; i++) {\n+\tfor (i = 0; i < remote_state->remotes_nr; i++) {\n \t\tint add_pushurl_aliases;\n-\t\tif (!the_repository->remote_state->remotes[i])\n+\t\tif (!remote_state->remotes[i])\n \t\t\tcontinue;\n-\t\tfor (j = 0;\n-\t\t     j < the_repository->remote_state->remotes[i]->pushurl_nr;\n-\t\t     j++) {\n-\t\t\tthe_repository->remote_state->remotes[i]->pushurl[j] =\n-\t\t\t\talias_url(\n-\t\t\t\t\tthe_repository->remote_state->remotes[i]\n-\t\t\t\t\t\t->pushurl[j],\n-\t\t\t\t\t&the_repository->remote_state->rewrites);\n+\t\tfor (j = 0; j < remote_state->remotes[i]->pushurl_nr; j++) {\n+\t\t\tremote_state->remotes[i]->pushurl[j] =\n+\t\t\t\talias_url(remote_state->remotes[i]->pushurl[j],\n+\t\t\t\t\t  &remote_state->rewrites);\n \t\t}\n-\t\tadd_pushurl_aliases =\n-\t\t\tthe_repository->remote_state->remotes[i]->pushurl_nr ==\n-\t\t\t0;\n-\t\tfor (j = 0;\n-\t\t     j < the_repository->remote_state->remotes[i]->url_nr;\n-\t\t     j++) {\n+\t\tadd_pushurl_aliases = remote_state->remotes[i]->pushurl_nr == 0;\n+\t\tfor (j = 0; j < remote_state->remotes[i]->url_nr; j++) {\n \t\t\tif (add_pushurl_aliases)\n \t\t\t\tadd_pushurl_alias(\n-\t\t\t\t\tthe_repository->remote_state->remotes[i],\n-\t\t\t\t\tthe_repository->remote_state->remotes[i]\n-\t\t\t\t\t\t->url[j]);\n-\t\t\tthe_repository->remote_state->remotes[i]\n-\t\t\t\t->url[j] = alias_url(\n-\t\t\t\tthe_repository->remote_state->remotes[i]->url[j],\n-\t\t\t\t&the_repository->remote_state->rewrites);\n+\t\t\t\t\tremote_state->remotes[i],\n+\t\t\t\t\tremote_state->remotes[i]->url[j]);\n+\t\t\tremote_state->remotes[i]->url[j] =\n+\t\t\t\talias_url(remote_state->remotes[i]->url[j],\n+\t\t\t\t\t  &remote_state->rewrites);\n \t\t}\n \t}\n }\n \n-static void read_config(void)\n+static void read_config(struct repository *repo)\n {\n-\tstatic int loaded;\n \tint flag;\n \n-\tif (loaded)\n+\tif (repo->remote_state->initialized)\n \t\treturn;\n-\tloaded = 1;\n+\trepo->remote_state->initialized = 1;\n \n-\tthe_repository->remote_state->current_branch = NULL;\n+\trepo->remote_state->current_branch = NULL;\n \tif (startup_info->have_repository) {\n-\t\tconst char *head_ref = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flag);\n+\t\tconst char *head_ref = refs_resolve_ref_unsafe(\n+\t\t\tget_main_ref_store(repo), \"HEAD\", 0, NULL, &flag);\n \t\tif (head_ref && (flag & REF_ISSYMREF) &&\n \t\t    skip_prefix(head_ref, \"refs/heads/\", &head_ref)) {\n-\t\t\tthe_repository->remote_state->current_branch =\n-\t\t\t\tmake_branch(head_ref, strlen(head_ref));\n+\t\t\trepo->remote_state->current_branch = make_branch(\n+\t\t\t\trepo->remote_state, head_ref, strlen(head_ref));\n \t\t}\n \t}\n-\tgit_config(handle_config, NULL);\n-\talias_all_urls();\n+\trepo_config(repo, handle_config, repo->remote_state);\n+\talias_all_urls(repo->remote_state);\n }\n \n static int valid_remote_nick(const char *name)\n@@ -524,10 +512,10 @@ const char *pushremote_for_branch(struct branch *branch, int *explicit)\n \t\t\t*explicit = 1;\n \t\treturn branch->pushremote_name;\n \t}\n-\tif (the_repository->remote_state->pushremote_name) {\n+\tif (branch->remote_state->pushremote_name) {\n \t\tif (explicit)\n \t\t\t*explicit = 1;\n-\t\treturn the_repository->remote_state->pushremote_name;\n+\t\treturn branch->remote_state->pushremote_name;\n \t}\n \treturn remote_for_branch(branch, explicit);\n }\n@@ -560,7 +548,7 @@ static struct remote *remote_get_1(const char *name,\n \tstruct remote *ret;\n \tint name_given = 0;\n \n-\tread_config();\n+\tread_config(the_repository);\n \n \tif (name)\n \t\tname_given = 1;\n@@ -568,7 +556,7 @@ static struct remote *remote_get_1(const char *name,\n \t\tname = get_default(the_repository->remote_state->current_branch,\n \t\t\t\t   &name_given);\n \n-\tret = make_remote(name, 0);\n+\tret = make_remote(the_repository->remote_state, name, 0);\n \tif (valid_remote_nick(name) && have_git_dir()) {\n \t\tif (!valid_remote(ret))\n \t\t\tread_remotes_file(ret);\n@@ -604,7 +592,7 @@ int remote_is_configured(struct remote *remote, int in_repo)\n int for_each_remote(each_remote_fn fn, void *priv)\n {\n \tint i, result = 0;\n-\tread_config();\n+\tread_config(the_repository);\n \tfor (i = 0; i < the_repository->remote_state->remotes_nr && !result;\n \t     i++) {\n \t\tstruct remote *remote =\n@@ -1717,11 +1705,12 @@ struct branch *branch_get(const char *name)\n {\n \tstruct branch *ret;\n \n-\tread_config();\n+\tread_config(the_repository);\n \tif (!name || !*name || !strcmp(name, \"HEAD\"))\n \t\tret = the_repository->remote_state->current_branch;\n \telse\n-\t\tret = make_branch(name, strlen(name));\n+\t\tret = make_branch(the_repository->remote_state, name,\n+\t\t\t\t  strlen(name));\n \tset_merge(ret);\n \treturn ret;\n }\ndiff --git a/remote.h b/remote.h\nindex d21c035f1b..d268a92ebb 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -52,6 +52,8 @@ struct remote_state {\n \n \tstruct rewrites rewrites;\n \tstruct rewrites rewrites_push;\n+\n+\tint initialized;\n };\n \n void remote_state_clear(struct remote_state *remote_state);\n@@ -110,6 +112,10 @@ struct remote {\n \n \t/* The method used for authenticating against `http_proxy`. */\n \tchar *http_proxy_authmethod;\n+\n+\t/** The remote_state that this remote belongs to. This is only meant to\n+\t * be used by remote_* functions. */\n+\tstruct remote_state *remote_state;\n };\n \n /**\n@@ -318,6 +324,10 @@ struct branch {\n \tint merge_alloc;\n \n \tconst char *push_tracking_ref;\n+\n+\t/** The remote_state that this branch belongs to. This is only meant to\n+\t * be used by branch_* functions. */\n+\tstruct remote_state *remote_state;\n };\n \n struct branch *branch_get(const char *name);\n-- \n2.33.GIT\n\n"},{"id":"439052","messageId":"20211019224339.61881-4-chooglen@google.com","threadId":"56659","inReplyTo":"20211019224339.61881-1-chooglen@google.com","subject":"[PATCH v3 3/4] remote: remove the_repository->remote_state from static methods","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-19T22:43:38Z","receivedAt":"2021-10-19T22:43:55Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Replace all remaining references of the_repository->remote_state in\nstatic methods with a struct remote_state parameter. To do so, introduce\na family of helper functions, \"remotes_*\", that behave like \"repo_*\",\nbut accept struct remote_state instead of struct repository, and move\nread_config() calls to non-static functions.\n\nSigned-off-by: Glen Choo <chooglen@google.com>\n---\n remote.c | 94 ++++++++++++++++++++++++++++++++------------------------\n 1 file changed, 53 insertions(+), 41 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex e3ca44f735..cf9cced5ed 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -505,6 +505,55 @@ const char *remote_for_branch(struct branch *branch, int *explicit)\n \treturn \"origin\";\n }\n \n+static struct remote *remotes_remote_get_1(\n+\tstruct remote_state *remote_state, const char *name,\n+\tconst char *(*get_default)(struct branch *, int *))\n+{\n+\tstruct remote *ret;\n+\tint name_given = 0;\n+\n+\tif (name)\n+\t\tname_given = 1;\n+\telse\n+\t\tname = get_default(remote_state->current_branch,\n+\t\t\t\t   &name_given);\n+\n+\tret = make_remote(remote_state, name, 0);\n+\tif (valid_remote_nick(name) && have_git_dir()) {\n+\t\tif (!valid_remote(ret))\n+\t\t\tread_remotes_file(ret);\n+\t\tif (!valid_remote(ret))\n+\t\t\tread_branches_file(ret);\n+\t}\n+\tif (name_given && !valid_remote(ret))\n+\t\tadd_url_alias(ret, name);\n+\tif (!valid_remote(ret))\n+\t\treturn NULL;\n+\treturn ret;\n+}\n+\n+static inline struct remote *remotes_remote_get(struct remote_state *remote_state, const char *name)\n+{\n+\treturn remotes_remote_get_1(remote_state, name, remote_for_branch);\n+}\n+\n+static inline struct remote *remotes_pushremote_get(struct remote_state *remote_state, const char *name)\n+{\n+\treturn remotes_remote_get_1(remote_state, name, pushremote_for_branch);\n+}\n+\n+struct remote *remote_get(const char *name)\n+{\n+\tread_config(the_repository);\n+\treturn remotes_remote_get(the_repository->remote_state, name);\n+}\n+\n+struct remote *pushremote_get(const char *name)\n+{\n+\tread_config(the_repository);\n+\treturn remotes_pushremote_get(the_repository->remote_state, name);\n+}\n+\n const char *pushremote_for_branch(struct branch *branch, int *explicit)\n {\n \tif (branch && branch->pushremote_name) {\n@@ -530,7 +579,8 @@ const char *remote_ref_for_branch(struct branch *branch, int for_push)\n \t\t} else {\n \t\t\tconst char *dst, *remote_name =\n \t\t\t\tpushremote_for_branch(branch, NULL);\n-\t\t\tstruct remote *remote = remote_get(remote_name);\n+\t\t\tstruct remote *remote =\n+\t\t\t\tremotes_remote_get(branch->remote_state, remote_name);\n \n \t\t\tif (remote && remote->push.nr &&\n \t\t\t    (dst = apply_refspecs(&remote->push,\n@@ -542,44 +592,6 @@ const char *remote_ref_for_branch(struct branch *branch, int for_push)\n \treturn NULL;\n }\n \n-static struct remote *remote_get_1(const char *name,\n-\t\t\t\t   const char *(*get_default)(struct branch *, int *))\n-{\n-\tstruct remote *ret;\n-\tint name_given = 0;\n-\n-\tread_config(the_repository);\n-\n-\tif (name)\n-\t\tname_given = 1;\n-\telse\n-\t\tname = get_default(the_repository->remote_state->current_branch,\n-\t\t\t\t   &name_given);\n-\n-\tret = make_remote(the_repository->remote_state, name, 0);\n-\tif (valid_remote_nick(name) && have_git_dir()) {\n-\t\tif (!valid_remote(ret))\n-\t\t\tread_remotes_file(ret);\n-\t\tif (!valid_remote(ret))\n-\t\t\tread_branches_file(ret);\n-\t}\n-\tif (name_given && !valid_remote(ret))\n-\t\tadd_url_alias(ret, name);\n-\tif (!valid_remote(ret))\n-\t\treturn NULL;\n-\treturn ret;\n-}\n-\n-struct remote *remote_get(const char *name)\n-{\n-\treturn remote_get_1(name, remote_for_branch);\n-}\n-\n-struct remote *pushremote_get(const char *name)\n-{\n-\treturn remote_get_1(name, pushremote_for_branch);\n-}\n-\n int remote_is_configured(struct remote *remote, int in_repo)\n {\n \tif (!remote)\n@@ -1684,7 +1696,7 @@ static void set_merge(struct branch *ret)\n \t\treturn;\n \t}\n \n-\tremote = remote_get(ret->remote_name);\n+\tremote = remotes_remote_get(ret->remote_state, ret->remote_name);\n \n \tCALLOC_ARRAY(ret->merge, ret->merge_nr);\n \tfor (i = 0; i < ret->merge_nr; i++) {\n@@ -1786,7 +1798,7 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)\n {\n \tstruct remote *remote;\n \n-\tremote = remote_get(pushremote_for_branch(branch, NULL));\n+\tremote = remotes_remote_get(branch->remote_state, pushremote_for_branch(branch, NULL));\n \tif (!remote)\n \t\treturn error_buf(err,\n \t\t\t\t _(\"branch '%s' has no remote for pushing\"),\n-- \n2.33.GIT\n\n"},{"id":"439053","messageId":"20211019224339.61881-5-chooglen@google.com","threadId":"56659","inReplyTo":"20211019224339.61881-1-chooglen@google.com","subject":"[PATCH v3 4/4] remote: add struct repository parameter to external functions","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-19T22:43:39Z","receivedAt":"2021-10-19T22:43:57Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Finish plumbing remote_state by adding a struct repository\nparameter to repo_* functions. While this removes all references to\nthe_repository->remote_state, certain functions still use the_repository\nto parse oids.\n\nSigned-off-by: Glen Choo <chooglen@google.com>\n---\n remote.c | 29 ++++++++++++++---------------\n remote.h | 28 +++++++++++++++++++++++-----\n 2 files changed, 37 insertions(+), 20 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex cf9cced5ed..179f0ff1ab 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -542,16 +542,16 @@ static inline struct remote *remotes_pushremote_get(struct remote_state *remote_\n \treturn remotes_remote_get_1(remote_state, name, pushremote_for_branch);\n }\n \n-struct remote *remote_get(const char *name)\n+struct remote *repo_remote_get(struct repository *repo, const char *name)\n {\n-\tread_config(the_repository);\n-\treturn remotes_remote_get(the_repository->remote_state, name);\n+\tread_config(repo);\n+\treturn remotes_remote_get(repo->remote_state, name);\n }\n \n-struct remote *pushremote_get(const char *name)\n+struct remote *repo_pushremote_get(struct repository *repo, const char *name)\n {\n-\tread_config(the_repository);\n-\treturn remotes_pushremote_get(the_repository->remote_state, name);\n+\tread_config(repo);\n+\treturn remotes_pushremote_get(repo->remote_state, name);\n }\n \n const char *pushremote_for_branch(struct branch *branch, int *explicit)\n@@ -601,14 +601,14 @@ int remote_is_configured(struct remote *remote, int in_repo)\n \treturn !!remote->origin;\n }\n \n-int for_each_remote(each_remote_fn fn, void *priv)\n+int repo_for_each_remote(struct repository *repo, each_remote_fn fn, void *priv)\n {\n \tint i, result = 0;\n-\tread_config(the_repository);\n-\tfor (i = 0; i < the_repository->remote_state->remotes_nr && !result;\n+\tread_config(repo);\n+\tfor (i = 0; i < repo->remote_state->remotes_nr && !result;\n \t     i++) {\n \t\tstruct remote *remote =\n-\t\t\tthe_repository->remote_state->remotes[i];\n+\t\t\trepo->remote_state->remotes[i];\n \t\tif (!remote)\n \t\t\tcontinue;\n \t\tresult = fn(remote, priv);\n@@ -1713,16 +1713,15 @@ static void set_merge(struct branch *ret)\n \t}\n }\n \n-struct branch *branch_get(const char *name)\n+struct branch *repo_branch_get(struct repository *repo, const char *name)\n {\n \tstruct branch *ret;\n \n-\tread_config(the_repository);\n+\tread_config(repo);\n \tif (!name || !*name || !strcmp(name, \"HEAD\"))\n-\t\tret = the_repository->remote_state->current_branch;\n+\t\tret = repo->remote_state->current_branch;\n \telse\n-\t\tret = make_branch(the_repository->remote_state, name,\n-\t\t\t\t  strlen(name));\n+\t\tret = make_branch(repo->remote_state, name, strlen(name));\n \tset_merge(ret);\n \treturn ret;\n }\ndiff --git a/remote.h b/remote.h\nindex d268a92ebb..e19cae2718 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -123,15 +123,28 @@ struct remote {\n  * remote_get(NULL) will return the default remote, given the current branch\n  * and configuration.\n  */\n-struct remote *remote_get(const char *name);\n-\n-struct remote *pushremote_get(const char *name);\n+struct remote *repo_remote_get(struct repository *repo, const char *name);\n+static inline struct remote *remote_get(const char *name)\n+{\n+\treturn repo_remote_get(the_repository, name);\n+}\n+\n+struct remote *repo_pushremote_get(struct repository *repo, const char *name);\n+static inline struct remote *pushremote_get(const char *name)\n+{\n+\treturn repo_pushremote_get(the_repository, name);\n+}\n int remote_is_configured(struct remote *remote, int in_repo);\n \n typedef int each_remote_fn(struct remote *remote, void *priv);\n \n /* iterate through struct remotes */\n-int for_each_remote(each_remote_fn fn, void *priv);\n+int repo_for_each_remote(struct repository *repo, each_remote_fn fn,\n+\t\t\t void *priv);\n+static inline int for_each_remote(each_remote_fn fn, void *priv)\n+{\n+\treturn repo_for_each_remote(the_repository, fn, priv);\n+}\n \n int remote_has_url(struct remote *remote, const char *url);\n \n@@ -330,7 +343,12 @@ struct branch {\n \tstruct remote_state *remote_state;\n };\n \n-struct branch *branch_get(const char *name);\n+struct branch *repo_branch_get(struct repository *repo, const char *name);\n+static inline struct branch *branch_get(const char *name)\n+{\n+\treturn repo_branch_get(the_repository, name);\n+}\n+\n const char *remote_for_branch(struct branch *branch, int *explicit);\n const char *pushremote_for_branch(struct branch *branch, int *explicit);\n const char *remote_ref_for_branch(struct branch *branch, int for_push);\n-- \n2.33.GIT\n\n"},{"id":"439135","messageId":"xmqqzgr3o4yw.fsf@gitster.g","threadId":"56659","inReplyTo":"20211019224339.61881-3-chooglen@google.com","subject":"Re: [PATCH v3 2/4] remote: use remote_state parameter internally","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-20T19:45:27Z","receivedAt":"2021-10-20T19:45:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n> Introduce a struct remote_state member to structs that need to\n> 'remember' their remote_state. Without changing external-facing\n> functions, replace the_repository->remote_state internally by using the\n> remote_state member where it is applicable i.e. when a function accepts\n> a struct that depends on the remote_state. If it is not applicable, add\n> a struct remote_state parameter instead.\n>\n> As a result, external-facing functions are still tied to the_repository,\n> but most static functions no longer reference\n> the_repository->remote_state. The exceptions are those that are used in\n> a way that depends on external-facing functions e.g. the callbacks to\n> remote_get_1().\n>\n> Signed-off-by: Glen Choo <chooglen@google.com>\n> ---\n>  remote.c | 153 ++++++++++++++++++++++++++-----------------------------\n>  remote.h |  10 ++++\n>  2 files changed, 81 insertions(+), 82 deletions(-)\n\nThis made my \"git push\" to k.org and other places over ssh segfault\nwhen their tip and what I am attempting to push are identical.  I\nhaven't spent more time than just to bisect the history down to\nidentify this commit as the possible culprit.\n\n\n"},{"id":"439136","messageId":"xmqqtuhbo2tn.fsf@gitster.g","threadId":"56659","inReplyTo":"xmqqzgr3o4yw.fsf@gitster.g","subject":"Re: [PATCH v3 2/4] remote: use remote_state parameter internally","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-20T20:31:48Z","receivedAt":"2021-10-20T20:31:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> This made my \"git push\" to k.org and other places over ssh segfault\n> when their tip and what I am attempting to push are identical.  I\n> haven't spent more time than just to bisect the history down to\n> identify this commit as the possible culprit.\n\n(gdb) bt\n#0  0x000055555579a785 in pushremote_for_branch (branch=0x0, explicit=0x7fffffffcf84)\n    at remote.c:564\n#1  0x000055555579a5c2 in remotes_remote_get_1 (remote_state=0x5555559782a0, name=0x0,\n    get_default=0x55555579a742 <pushremote_for_branch>) at remote.c:518\n#2  0x000055555579a6d0 in remotes_pushremote_get (remote_state=0x5555559782a0, name=0x0)\n    at remote.c:542\n#3  0x000055555579a740 in repo_pushremote_get (repo=0x555555974b80 <the_repo>, name=0x0)\n    at remote.c:554\n#4  0x000055555560aa9d in pushremote_get (name=0x0) at ./remote.h:135\n#5  0x000055555560c5ce in cmd_push (argc=0, argv=0x7fffffffdc70, prefix=0x0)\n    at builtin/push.c:611\n#6  0x000055555557396a in run_builtin (p=0x555555941f78 <commands+2136>, argc=3,\n    argv=0x7fffffffdc70) at git.c:461\n#7  0x0000555555573d79 in handle_builtin (argc=3, argv=0x7fffffffdc70) at git.c:713\n#8  0x0000555555573fe6 in run_argv (argcp=0x7fffffffdafc, argv=0x7fffffffdaf0) at git.c:780\n#9  0x000055555557448f in cmd_main (argc=3, argv=0x7fffffffdc70) at git.c:911\n#10 0x000055555565b2ae in main (argc=6, argv=0x7fffffffdc58) at common-main.c:52\n\nThe direct culprit is this part:\n\n    const char *pushremote_for_branch(struct branch *branch, int *explicit)\n    {\n            if (branch && branch->pushremote_name) {\n                    if (explicit)\n                            *explicit = 1;\n                    return branch->pushremote_name;\n            }\n            if (branch->remote_state->pushremote_name) {\n\nwhere the second if() statement used to check \"pushremote_name\", but\nnow unconditionally dereferences \"branch\".\n\nThe caller is remote_get_1(); this funciton was called with\n\"current_branch\", which can be NULL until you have a repository and\nyou've called read_config(), but otherwise shouldn't be.\n\nI think somebody is not setting up the remote_state correctly?  When\nthe user wants to just use the repository-wide pushremote, not\nhaving the current_branch would not matter, but if the pushremote\nfor the current branch is different from the repository-wide one,\nthe code would silently push to a wrong remote.\n\nIn any case, any public facing entry point, like pushremote_get()\nthat is directly called from cmd_push() with just a name, should\nauto vivify an instance of struct remote_state and populate its\nmembers as needed, I think, and in this particular case, I suspect\nthat it forgets to initialize the current_branch and other members\nby calling read_config(), just like other entry points like\nrepo_remote_get() do.\n\n\n"},{"id":"439146","messageId":"xmqqk0i7nyd3.fsf@gitster.g","threadId":"56659","inReplyTo":"xmqqtuhbo2tn.fsf@gitster.g","subject":"Re: [PATCH v3 2/4] remote: use remote_state parameter internally","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-20T22:08:08Z","receivedAt":"2021-10-20T22:08:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> In any case, any public facing entry point, like pushremote_get()\n> that is directly called from cmd_push() with just a name, should\n> auto vivify an instance of struct remote_state and populate its\n> members as needed, I think, and in this particular case, I suspect\n> that it forgets to initialize the current_branch and other members\n> by calling read_config(), just like other entry points like\n> repo_remote_get() do.\n\nI give up (for the day at least).  I am not quite sure where things\nare going wrong.\n\nThere are a few bare repositories (that hold preformatted\ndocumentation trees) next to my primary working tree, and I do\n\n\tfor topic in htmldocs manpages\n\tdo\n\t\tprintf \"%s: \" \"$topic\"\n\t\t( cd ../git-$topic.git && git push \"$@\") || exit\n\tdone\n\nwhere \"$@\" is often nothing, and the backtrace I showed was from\nsuch a push that uses \"no remote specified?  push to the origin\".\nThe configuration in these bare repositories do not have anything\nparticularly interesting.  It has three URLs, and pushes the same to\nall three.\n\n[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = true\n\n[remote \"origin\"]\n        url = gitolite.kernel.org:/pub/scm/git/git-htmldocs.git\n        url = github.com:gitster/git-htmldocs.git\n\turl = repo.or.cz:/srv/git/git-htmldocs.git\n        push = refs/heads/master:refs/heads/master\n"},{"id":"439574","messageId":"kl6l35opasd1.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"56659","inReplyTo":"xmqqtuhbo2tn.fsf@gitster.g","subject":"Re: [PATCH v3 2/4] remote: use remote_state parameter internally","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-25T18:09:46Z","receivedAt":"2021-10-25T18:09:52Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"\nSorry I wasn't able to get to this sooner, this will be my first\npriority.\n\nJunio C Hamano <gitster@pobox.com> writes:\n\n>> This made my \"git push\" to k.org and other places over ssh segfault\n>> when their tip and what I am attempting to push are identical.  I\n>> haven't spent more time than just to bisect the history down to\n>> identify this commit as the possible culprit.\n\nDoes this fail iff the tip and the attempted push are identical, or does\nit also fail for others sorts of pushes?\n\n> (gdb) bt\n> #0  0x000055555579a785 in pushremote_for_branch (branch=0x0, explicit=0x7fffffffcf84)\n>     at remote.c:564\n> #1  0x000055555579a5c2 in remotes_remote_get_1 (remote_state=0x5555559782a0, name=0x0,\n>     get_default=0x55555579a742 <pushremote_for_branch>) at remote.c:518\n> #2  0x000055555579a6d0 in remotes_pushremote_get (remote_state=0x5555559782a0, name=0x0)\n>     at remote.c:542\n> #3  0x000055555579a740 in repo_pushremote_get (repo=0x555555974b80 <the_repo>, name=0x0)\n>     at remote.c:554\n> #4  0x000055555560aa9d in pushremote_get (name=0x0) at ./remote.h:135\n> #5  0x000055555560c5ce in cmd_push (argc=0, argv=0x7fffffffdc70, prefix=0x0)\n>     at builtin/push.c:611\n> #6  0x000055555557396a in run_builtin (p=0x555555941f78 <commands+2136>, argc=3,\n>     argv=0x7fffffffdc70) at git.c:461\n> #7  0x0000555555573d79 in handle_builtin (argc=3, argv=0x7fffffffdc70) at git.c:713\n> #8  0x0000555555573fe6 in run_argv (argcp=0x7fffffffdafc, argv=0x7fffffffdaf0) at git.c:780\n> #9  0x000055555557448f in cmd_main (argc=3, argv=0x7fffffffdc70) at git.c:911\n> #10 0x000055555565b2ae in main (argc=6, argv=0x7fffffffdc58) at common-main.c:52\n>\n> The direct culprit is this part:\n>\n>     const char *pushremote_for_branch(struct branch *branch, int *explicit)\n>     {\n>             if (branch && branch->pushremote_name) {\n>                     if (explicit)\n>                             *explicit = 1;\n>                     return branch->pushremote_name;\n>             }\n>             if (branch->remote_state->pushremote_name) {\n>\n> where the second if() statement used to check \"pushremote_name\", but\n> now unconditionally dereferences \"branch\".\n>\n> The caller is remote_get_1(); this funciton was called with\n> \"current_branch\", which can be NULL until you have a repository and\n> you've called read_config(), but otherwise shouldn't be.\n\nThanks for helping to narrow the scope of the search :)\n\n> I think somebody is not setting up the remote_state correctly?  When\n> the user wants to just use the repository-wide pushremote, not\n> having the current_branch would not matter, but if the pushremote\n> for the current branch is different from the repository-wide one,\n> the code would silently push to a wrong remote.\n\nFor the_repository, remote_state should be initialized via\ninitialize_the_repository(), which is called in common-main.c. I assumed\nthat this would set up remote_state correctly. I will confirm whether or\nnot this is the cause.\n\n> In any case, any public facing entry point, like pushremote_get()\n> that is directly called from cmd_push() with just a name, should\n> auto vivify an instance of struct remote_state and populate its\n> members as needed, I think, and in this particular case, I suspect\n> that it forgets to initialize the current_branch and other members\n> by calling read_config(), just like other entry points like\n> repo_remote_get() do.\n\nI'm fairly certain that pushremote_get() calls read_config() correctly\nvia repo_pushremote_get():\n\n  struct remote *repo_pushremote_get(struct repository *repo, const char *name)\n  {\n    read_config(repo);\n    return remotes_pushremote_get(repo->remote_state, name);\n  }\n\nPerhaps the problem lies elsewhere, let me look into it further.\n"},{"id":"439583","messageId":"kl6lzgqwaocl.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"56659","inReplyTo":"kl6l35opasd1.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v3 2/4] remote: use remote_state parameter internally","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-25T19:36:26Z","receivedAt":"2021-10-25T20:12:35Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n>> The caller is remote_get_1(); this funciton was called with\n>> \"current_branch\", which can be NULL until you have a repository and\n>> you've called read_config(), but otherwise shouldn't be.\n\nOne possible culprit is working with detached HEAD, are you pushing with\ndetached HEAD?\n\n\"current_branch\" is allowed to be NULL when HEAD does not point to a\nbranch. From read_config():\n\n\tif (startup_info->have_repository) {\n\t\tconst char *head_ref = refs_resolve_ref_unsafe(\n\t\t\tget_main_ref_store(repo), \"HEAD\", 0, NULL, &flag);\n\t\tif (head_ref && (flag & REF_ISSYMREF) &&\n\t\t    skip_prefix(head_ref, \"refs/heads/\", &head_ref)) {\n\t\t\trepo->remote_state->current_branch = make_branch(\n\t\t\t\trepo->remote_state, head_ref, strlen(head_ref));\n\t\t}\n\t}\n\nThis condition fails in detached head and \"current_branch\" is not set:\n\n  head_ref && (flag & REF_ISSYMREF) && skip_prefix(head_ref, \"refs/heads/\", &head_ref)\n\n>> The direct culprit is this part:\n>>\n>>     const char *pushremote_for_branch(struct branch *branch, int *explicit)\n>>     {\n>>             if (branch && branch->pushremote_name) {\n>>                     if (explicit)\n>>                             *explicit = 1;\n>>                     return branch->pushremote_name;\n>>             }\n>>             if (branch->remote_state->pushremote_name) {\n>>\n>> where the second if() statement used to check \"pushremote_name\", but\n>> now unconditionally dereferences \"branch\".\n>>\nTo work with branch = NULL, it seems obvious that branch should be\nconditionally dereferenced in pushremote_for_branch, i.e.\n\n  - if (branch->remote_state->pushremote_name) {\n  + if (brnach && branch->remote_state->pushremote_name) {\n\nHowever, if you are not using detached HEAD, the problem might be\nelsewhere..\n"},{"id":"439586","messageId":"xmqqzgqwzvwx.fsf@gitster.g","threadId":"56659","inReplyTo":"kl6lzgqwaocl.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v3 2/4] remote: use remote_state parameter internally","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-25T20:33:50Z","receivedAt":"2021-10-25T20:33:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n> Glen Choo <chooglen@google.com> writes:\n>\n>>> The caller is remote_get_1(); this funciton was called with\n>>> \"current_branch\", which can be NULL until you have a repository and\n>>> you've called read_config(), but otherwise shouldn't be.\n>\n> One possible culprit is working with detached HEAD, are you pushing with\n> detached HEAD?\n>\n> \"current_branch\" is allowed to be NULL when HEAD does not point to a\n> branch.\n\nGood point.  It is a good justification to make the remote_state\navailable to the function, as branch==NULL that signals \"there is no\ncurrent branch in the repository\" cannot be dereferenced to get to\neither the repository or the remote_state, yet the function needs\naccess to remote_state even when branch==NULL.\n\nWhat \"branch\" is pushremote_for_branch() meant to take?  Is there a\ncaller that asks a hypothetical \"I know this is not a branch that is\nthe current branch in the repository, but to which remote would we\npush if this branch _were_ the current one?\" (and passes NULL to\nmean \"there is a checked out branch, but what happens if our HEAD\nwere detached?\") question?  Even if there isn't currently, do we\nwant to add such callers in the future?\n\nIf the answer is yes, then the function need to take both branch and\nremote_state as two separate parameters.  If the answer is no (i.e.\nwe never ask hypothetical questions, we just ask what we should do\nin the current, real, state), then the function can just take the\nremote_state and remote_state->branch being NULL would be used as a\nsignal that the HEAD is detached.  I suspect it is the former, as\nthis information is used in string-name-to-object translation for\n\"topic@{push}\" in object-name.c::interpret_branch_mark() function.\n\n> However, if you are not using detached HEAD, the problem might be\n> elsewhere..\n\nI just checked.  The repository the push is run in is bare and its\nHEAD is detached, pointing at a commit directly.\n"},{"id":"439613","messageId":"kl6lbl3c7lsa.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"56659","inReplyTo":"xmqqzgqwzvwx.fsf@gitster.g","subject":"Re: [PATCH v3 2/4] remote: use remote_state parameter internally","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-25T23:00:05Z","receivedAt":"2021-10-25T23:00:19Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I just checked.  The repository the push is run in is bare and its\n> HEAD is detached, pointing at a commit directly.\n\nThanks, I was able to reproduce the segfault using your config.\n\n>> \"current_branch\" is allowed to be NULL when HEAD does not point to a\n>> branch.\n>\n> Good point.  It is a good justification to make the remote_state\n> available to the function, as branch==NULL that signals \"there is no\n> current branch in the repository\" cannot be dereferenced to get to\n> either the repository or the remote_state, yet the function needs\n> access to remote_state even when branch==NULL.\n\nYes, I wish we had noticed this sooner in our discussion and the fault\nis mine. It seems that pushremote_for_branch() is a prime example of\n\"get the settings from the branch if possible, but default to the\ncorrect repository settings otherwise.\", which is difficult to express\nif remote_state is not available to the function.\n\n> What \"branch\" is pushremote_for_branch() meant to take?  Is there a\n> caller that asks a hypothetical \"I know this is not a branch that is\n> the current branch in the repository, but to which remote would we\n> push if this branch _were_ the current one?\" (and passes NULL to\n> mean \"there is a checked out branch, but what happens if our HEAD\n> were detached?\") question?  Even if there isn't currently, do we\n> want to add such callers in the future?\n>\n> If the answer is yes, then the function need to take both branch and\n> remote_state as two separate parameters.  If the answer is no (i.e.\n> we never ask hypothetical questions, we just ask what we should do\n> in the current, real, state), then the function can just take the\n> remote_state and remote_state->branch being NULL would be used as a\n> signal that the HEAD is detached.  I suspect it is the former, as\n> this information is used in string-name-to-object translation for\n> \"topic@{push}\" in object-name.c::interpret_branch_mark() function.\n\nI agree that the need for hypothetical \"what happens if HEAD were\ndetached?\" questions may arise, though I'm not sure if there are any\nright now. When we call branch_get(NULL), the expected return value is\nthe \"current_branch\". If there is no \"current_branch\" i.e. the return\nvalue of branch_get() is the NULL branch. A NULL branch is not usable -\nbranch_get_push() and branch_get_upstream() return error messages\nindicating \"HEAD does not point to a branch\". (these are the functions\nused by object-name.c::interpret_branch_mark()).\n\nGiven the convention of \"NULL branch == detached HEAD\", how do we\nproceed? Some options:\n\na) Represent detached HEAD with a non-NULL \"struct branch\". This will\n   let us continue using the remote_state backpointer, which makes many\n   interfaces clean, but is somewhat invasive, error-prone and it uses\n   \"struct branch\" for something that is not a branch, which is itself\n   an error-prone interface.\n\nb) Keep the backpointer, but add remote_state as a parameter to\n   pushremote_for_branch(). The least possible effort to fix the problem\n   and might be 'easy' but is inconsistent with the other functions and\n   is prone to misuse because the backpointer and parameter can be\n   different.\n\nc) Replace the backpointer with a remote_state parameter. Expressive and\n   fits the paradigm of \"defaulting to the repo when needed\", but\n   interfaces are repetitive and shifts the responsibility of\n   correctness to the caller (see v2).\n\nd) Default to the_repository in pushremote_for_branch(). Easy, but\n   incorrect in general.\n\ne) Some kind of reimagining of the remotes interfaces that doesn't\n   have this problem. One possible approach is to remove branches from\n   the remotes system altogether, since remotes are primarily concerned\n   with _branch tracking information_ and not really _branches_ per se;\n   perhaps we are being led astray by our terminology. If possible, this\n   is probably the most elegant long term solution, but it's\n   time-consuming and it's not clear how we will get there.\n\nCurrently, my preference is to go with (c). We can create a clear\nexpectation to callers that branch tracking information is not complete\nwithout a repository, thus a repository is always supplied, explicitly\nor not. If so, the remote_state parameter looks less like an\nimplementation detail, especially since a NULL branch is allowed.\n\nI know we have already considered and abandoned (c) after v2, but has\nyour opinion changed after considering the new information?\n"},{"id":"439614","messageId":"xmqqfssozk8r.fsf@gitster.g","threadId":"56659","inReplyTo":"kl6lbl3c7lsa.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v3 2/4] remote: use remote_state parameter internally","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-26T00:45:56Z","receivedAt":"2021-10-26T00:45:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n>> If the answer is yes, then the function need to take both branch and\n>> remote_state as two separate parameters.  If the answer is no (i.e.\n>> we never ask hypothetical questions, we just ask what we should do\n>> in the current, real, state), then the function can just take the\n>> remote_state and remote_state->branch being NULL would be used as a\n>> signal that the HEAD is detached.  I suspect it is the former, as\n>> this information is used in string-name-to-object translation for\n>> \"topic@{push}\" in object-name.c::interpret_branch_mark() function.\n>\n> I agree that the need for hypothetical \"what happens if HEAD were\n> detached?\" questions may arise, though I'm not sure if there are any\n> right now. When we call branch_get(NULL), the expected return value is\n> the \"current_branch\". If there is no \"current_branch\" i.e. the return\n> value of branch_get() is the NULL branch. A NULL branch is not usable -\n> branch_get_push() and branch_get_upstream() return error messages\n> indicating \"HEAD does not point to a branch\". (these are the functions\n> used by object-name.c::interpret_branch_mark()).\n>\n> Given the convention of \"NULL branch == detached HEAD\", how do we\n> proceed? Some options:\n>\n> a) Represent detached HEAD with a non-NULL \"struct branch\". This will\n>    let us continue using the remote_state backpointer, which makes many\n>    interfaces clean, but is somewhat invasive, error-prone and it uses\n>    \"struct branch\" for something that is not a branch, which is itself\n>    an error-prone interface.\n\nI'd rather not to go there.\n\n> b) Keep the backpointer, but add remote_state as a parameter to\n>    pushremote_for_branch(). The least possible effort to fix the problem\n>    and might be 'easy' but is inconsistent with the other functions and\n>    is prone to misuse because the backpointer and parameter can be\n>    different.\n\nMake the function take a remote_state as a parameter (instead of,\nnot in addition to, the branch parameter), and use the remote_state\nstructure to find which branch's branch specific configuration we\nwant to use by checking its current_branch member.\n\nI think that would be the best approach for \"no, we won't ask\nhypothetical question\" case.\n\nOn the other hand,...\n\n> c) Replace the backpointer with a remote_state parameter. Expressive and\n>    fits the paradigm of \"defaulting to the repo when needed\", but\n>    interfaces are repetitive and shifts the responsibility of\n>    correctness to the caller (see v2).\n\n... if we want to support the what-if callers, I\nthink the best approach would be a slight variant of c) above.\n\nThat is, pass branch and remote_state as two parameters, and when\nbranch is not NULL, barf if it is not among remote_state.branches[],\nto protect against nonsense combinations.\n\nThanks.\n"},{"id":"439618","messageId":"xmqqbl3czik5.fsf@gitster.g","threadId":"56659","inReplyTo":"xmqqfssozk8r.fsf@gitster.g","subject":"Re: [PATCH v3 2/4] remote: use remote_state parameter internally","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-26T01:22:18Z","receivedAt":"2021-10-26T01:22:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> a) Represent detached HEAD with a non-NULL \"struct branch\". This will\n>>    let us continue using the remote_state backpointer, which makes many\n>>    interfaces clean, but is somewhat invasive, error-prone and it uses\n>>    \"struct branch\" for something that is not a branch, which is itself\n>>    an error-prone interface.\n>\n> I'd rather not to go there.\n\nActually, I take half of that back, as I think this would be the\nbest direction in the longer term, and it conceptually is sound.\nAfter all, detached HEAD is not all that special--- when we ask for\nthe \"current branch\" and the HEAD happens to be detached, we treat\nit as a perfectly valid state and behave as if we are on that\nunnamed branch.  The state probably deserves to be represented as a\n\"struct branch\" instance.\n\nI do agree with you that this approach would involve significant\nshort-term pain, as a lot of existing code may say \"NULL is how we\nrepresent detached HEAD\" and they have to be identified and upgraded\nbefore the above plan calls for.\n\nSo, I tend to think that in the shorter term, perhaps a safer\nvariant of (c) that allows us to ask \"not-current@{push}\" would be a\ngood way to go, but when things have stabilized, we should revisit\nand see if it is feasible to represent a detached HEAD state with an\ninstance of \"struct branch\" and how simpler the interface would become\nwhen we did so.\n\nThanks.\n"},{"id":"439659","messageId":"kl6l7ddz7m54.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"56659","inReplyTo":"xmqqbl3czik5.fsf@gitster.g","subject":"Re: [PATCH v3 2/4] remote: use remote_state parameter internally","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-26T17:04:39Z","receivedAt":"2021-10-26T17:04:47Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> c) Replace the backpointer with a remote_state parameter. Expressive and\n>>    fits the paradigm of \"defaulting to the repo when needed\", but\n>>    interfaces are repetitive and shifts the responsibility of\n>>    correctness to the caller (see v2).\n> \n> ... if we want to support the what-if callers, I\n> think the best approach would be a slight variant of c) above.\n> \n> That is, pass branch and remote_state as two parameters, and when\n> branch is not NULL, barf if it is not among remote_state.branches[],\n> to protect against nonsense combinations.\n\nSounds reasonable to me. The resulting interface would look like the v2\none, but internally, this additional safety check will prevent misuse.\n\n>>> a) Represent detached HEAD with a non-NULL \"struct branch\". This will\n>>>    let us continue using the remote_state backpointer, which makes many\n>>>    interfaces clean, but is somewhat invasive, error-prone and it uses\n>>>    \"struct branch\" for something that is not a branch, which is itself\n>>>    an error-prone interface.\n>>\n>> I'd rather not to go there.\n>\n> Actually, I take half of that back, as I think this would be the\n> best direction in the longer term, and it conceptually is sound.\n> After all, detached HEAD is not all that special--- when we ask for\n> the \"current branch\" and the HEAD happens to be detached, we treat\n> it as a perfectly valid state and behave as if we are on that\n> unnamed branch.  The state probably deserves to be represented as a\n> \"struct branch\" instance.\n> [...] when things have stabilized, we should revisit\n> and see if it is feasible to represent a detached HEAD state with an\n> instance of \"struct branch\" and how simpler the interface would become\n> when we did so.\n\nThis \"longer term direction\" sounds like what I envisioned with (e). I\nagree that detached HEAD is a state that should be expressed with more\nthan just NULL, though I'm not sure that \"struct branch\" is the correct\nabstraction. No point bikeshedding now of course, we'll cross that\nbridge when we get there ;)\n"},{"id":"439707","messageId":"xmqq8ryfxkuj.fsf@gitster.g","threadId":"56659","inReplyTo":"kl6l7ddz7m54.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v3 2/4] remote: use remote_state parameter internally","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-27T02:28:04Z","receivedAt":"2021-10-27T02:28:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>>> c) Replace the backpointer with a remote_state parameter. Expressive and\n>>>    fits the paradigm of \"defaulting to the repo when needed\", but\n>>>    interfaces are repetitive and shifts the responsibility of\n>>>    correctness to the caller (see v2).\n>> \n>> ... if we want to support the what-if callers, I\n>> think the best approach would be a slight variant of c) above.\n>> \n>> That is, pass branch and remote_state as two parameters, and when\n>> branch is not NULL, barf if it is not among remote_state.branches[],\n>> to protect against nonsense combinations.\n>\n> Sounds reasonable to me. The resulting interface would look like the v2\n> one, but internally, this additional safety check will prevent misuse.\n\nHopefully.  Of course I think the implementation of the safety would\nactually be done, not by iterationg over branches[] array, but just\nchecking branch->remote_state == remote_state pointer equality.\n\n> This \"longer term direction\" sounds like what I envisioned with (e). I\n> agree that detached HEAD is a state that should be expressed with more\n> than just NULL, though I'm not sure that \"struct branch\" is the correct\n> abstraction. No point bikeshedding now of course, we'll cross that\n> bridge when we get there ;)\n\nI actually was hoping that the time to cross the bridge was now,\nthough ;-)\n"},{"id":"439801","messageId":"kl6l35om9wn9.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"56659","inReplyTo":"xmqq8ryfxkuj.fsf@gitster.g","subject":"Re: [PATCH v3 2/4] remote: use remote_state parameter internally","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-27T17:59:22Z","receivedAt":"2021-10-27T17:59:37Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Glen Choo <chooglen@google.com> writes:\n>\n>> Junio C Hamano <gitster@pobox.com> writes:\n>>\n>>>> c) Replace the backpointer with a remote_state parameter. Expressive and\n>>>>    fits the paradigm of \"defaulting to the repo when needed\", but\n>>>>    interfaces are repetitive and shifts the responsibility of\n>>>>    correctness to the caller (see v2).\n> Hopefully.  Of course I think the implementation of the safety would\n> actually be done, not by iterationg over branches[] array, but just\n> checking branch->remote_state == remote_state pointer equality.\n\nWith (c), I plan to eliminate the backpointer altogether, so such a\ncheck is not possible. However, I plan to add a branches_hash to\nremote_state in the same way that we have remotes_hash for remote_state.\n\n>> This \"longer term direction\" sounds like what I envisioned with (e). I\n>> agree that detached HEAD is a state that should be expressed with more\n>> than just NULL, though I'm not sure that \"struct branch\" is the correct\n>> abstraction. No point bikeshedding now of course, we'll cross that\n>> bridge when we get there ;)\n>\n> I actually was hoping that the time to cross the bridge was now,\n> though ;-)\n\nHm, well I don't want to get too lost in the weeds here and lose sight\nof the short-term objective. I have a few strands of thought, but\nnothing concrete enough to propose a full alternative:\n\n- It seems odd that the branches and the \"current_branch\" are handled by\n  the remotes system, perhaps it would be clearer to move it elsewhere.\n  Separating branches from \"branch remote-tracking configuration\" might\n  clarify our thinking.\n- branch_get(NULL) and branch_get(\"HEAD\") are not entirely coherent with\n  the idea of \"getting a branch by name\", perhaps we should just move\n  this functionality into a different function.\n- Similarly, variants of remote_for_branch() are misleading because they\n  behave inconsistently when branch is NULL. \n\nI might not want to take action on these ideas though, e.g.\nbranch_get(\"HEAD\") or remote_for_branch(NULL) are very convenient for\ncallers even though they behave slightly inconsisently. I'll propose a\nlonger term direction when I have a better grasp of the system and the\ntradeoffs.\n"},{"id":"439821","messageId":"xmqqcznqw7z8.fsf@gitster.g","threadId":"56659","inReplyTo":"kl6l35om9wn9.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v3 2/4] remote: use remote_state parameter internally","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-27T20:03:39Z","receivedAt":"2021-10-27T20:03:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n> I might not want to take action on these ideas though, e.g.\n> branch_get(\"HEAD\") or remote_for_branch(NULL) are very convenient\n> for callers even though they behave slightly inconsisently. I'll\n> propose a longer term direction when I have a better grasp of the\n> system and the tradeoffs.\n\nYup, that sounds like a good plan.\n"},{"id":"439910","messageId":"20211028183101.41013-1-chooglen@google.com","threadId":"56659","inReplyTo":"20211019224339.61881-1-chooglen@google.com","subject":"[PATCH v4 0/6] remote: replace static variables with struct remote_state","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-28T18:30:55Z","receivedAt":"2021-10-28T18:31:17Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"This series aims to make the remotes subsystem work with non-the_repository,\nwhich will allow submodule remotes to be accessed in-process, rather than\nthrough child processes. This is accomplished by creating a struct remote_state\nand adding it to struct repository.\n\nOne motivation for this is that it allows future submodule commands to run\nin-process. An example is an RFC series of mine [1], where I tried to implement\n\"git branch --recurse-submodules\" in-process but couldn't figure out how to read\nthe remotes of a submodule.\n\nv4 reverts the backpointer introduced in v3. In authoring v3, I had\noverlooked the fact that branch == NULL (representing detached HEAD) is\na valid argument to some branch_* functions and as a result, we cannot\nalways rely on branch->remote_state to tell us the remote_state of a\nbranch. This was not discovered because of a coding mistake in v3 where\nbranch was unconditionally dereferenced, even when it was null [2]. v4\nadds a test that checks the relevant behavior.\n\nThe resulting interface is similar to v2, but with Junio's proposed\nsafety check [3] - when branch + repository are passed as a pair we\ncheck that the branch belongs to the repository (i.e. it is in the\nrepository's remote_state struct). This check is only implemented for\nnon-static functions because the probability of misuse is much higher.\nIn static functions, this check is wasteful because we frequently\noperate on the remote_state + {branch, remote} pair in order to maintain\ndata consistency and the correct remote_state is often obvious from\ncontext.\n\nIn the long run, I believe that there is room for refactoring/interface changes\nas to avoid these internal correctness issues, but I think this is a good enough\nstarting point.\n\n[1] https://lore.kernel.org/git/20210921232529.81811-1-chooglen@google.com/\n[2] https://lore.kernel.org/git/xmqqtuhbo2tn.fsf@gitster.g\n[2] https://lore.kernel.org/git/xmqqfssozk8r.fsf@gitster.g\n\nChanges since v3:\n* Add a test case for pushing to a remote in detached HEAD. This test\n  would have caught the segfault that resulted in this reroll.\n* Remove the NEEDSWORK saying that init_remotes_hash() should be moved\n  into remote_state_new() and just do it.\n* Remove the backpointer to remote_state and add a remote_state\n  parameter instead.\n* In patch 4, add more remotes_* functions. These functions were not\n  needed in v3 because of the backpointer.\n* In patch 5, add a function that checks if a branch is in a repo. Add a\n  branch hashmap that makes this operation fast.\n* In patch 6, add more repo_* functions. These functions were not needed\n  in v3 because of the backpointer.\n\nChanges since v2:\n* Add .remote_state to struct branch and struct remote, changing the\n  implementation appropriately.\n* In patch 2, properly consider the initialized state of remote_state.\n  In v2, I forgot to convert a static inside read_config() into a\n  private member of struct remote_state. Fix this.\n* In a new patch 3, add helper methods that get a remote via\n  remote_state and the remote name.\n* Move read_config(repo) calls to the external facing-functions. This keeps\n  \"struct repository\" away from the remote.c internals.\n\nChanges since v1:\n* In v1, we moved static variables into the_repository->remote_state in\n  two steps: static variables > static remote_state >\n  the_repository->remote_state. In v2, make this change in one step:\n  static variables > the_repository->remote_state.\n* Add more instances of repo_* that were missed.\n\nGlen Choo (6):\n  t5516: add test case for pushing remote refspecs\n  remote: move static variables into per-repository struct\n  remote: use remote_state parameter internally\n  remote: remove the_repository->remote_state from static methods\n  remote: die if branch is not found in repository\n  remote: add struct repository parameter to external functions\n\n remote.c              | 406 +++++++++++++++++++++++++++++-------------\n remote.h              | 118 ++++++++++--\n repository.c          |   8 +\n repository.h          |   4 +\n t/t5516-fetch-push.sh |   9 +\n 5 files changed, 404 insertions(+), 141 deletions(-)\n\nRange-diff against v3:\n-:  ---------- > 1:  9b29ec27c6 t5516: add test case for pushing remote refspecs\n1:  1f712c22b4 ! 2:  ca9b5ab66a remote: move static variables into per-repository struct\n    @@ remote.c: static void add_pushurl(struct remote *remote, const char *pushurl)\n      }\n      \n     @@ remote.c: static int remotes_hash_cmp(const void *unused_cmp_data,\n    + \t\treturn strcmp(a->name, b->name);\n    + }\n      \n    - static inline void init_remotes_hash(void)\n    - {\n    +-static inline void init_remotes_hash(void)\n    +-{\n     -\tif (!remotes_hash.cmpfn)\n     -\t\thashmap_init(&remotes_hash, remotes_hash_cmp, NULL, 0);\n    -+\tif (!the_repository->remote_state->remotes_hash.cmpfn)\n    -+\t\thashmap_init(&the_repository->remote_state->remotes_hash,\n    -+\t\t\t     remotes_hash_cmp, NULL, 0);\n    - }\n    - \n    +-}\n    +-\n      static struct remote *make_remote(const char *name, int len)\n    + {\n    + \tstruct remote *ret;\n     @@ remote.c: static struct remote *make_remote(const char *name, int len)\n    + \tif (!len)\n    + \t\tlen = strlen(name);\n    + \n    +-\tinit_remotes_hash();\n    + \tlookup.str = name;\n      \tlookup.len = len;\n      \thashmap_entry_init(&lookup_entry, memhash(name, len));\n      \n    @@ remote.c: void apply_push_cas(struct push_cas_option *cas,\n     +\tstruct remote_state *r = xmalloc(sizeof(*r));\n     +\n     +\tmemset(r, 0, sizeof(*r));\n    ++\n    ++\thashmap_init(&r->remotes_hash, remotes_hash_cmp, NULL, 0);\n     +\treturn r;\n     +}\n     +\n2:  467247fa9c ! 3:  5d6a245cae remote: use remote_state parameter internally\n    @@ Metadata\n      ## Commit message ##\n         remote: use remote_state parameter internally\n     \n    -    Introduce a struct remote_state member to structs that need to\n    -    'remember' their remote_state. Without changing external-facing\n    -    functions, replace the_repository->remote_state internally by using the\n    -    remote_state member where it is applicable i.e. when a function accepts\n    -    a struct that depends on the remote_state. If it is not applicable, add\n    -    a struct remote_state parameter instead.\n    +    Without changing external-facing functions, replace\n    +    the_repository->remote_state internally by adding a struct remote_state\n    +    parameter.\n     \n         As a result, external-facing functions are still tied to the_repository,\n         but most static functions no longer reference\n    @@ Commit message\n     \n      ## remote.c ##\n     @@ remote.c: static void add_pushurl(struct remote *remote, const char *pushurl)\n    - static void add_pushurl_alias(struct remote *remote, const char *url)\n    + \tremote->pushurl[remote->pushurl_nr++] = pushurl;\n    + }\n    + \n    +-static void add_pushurl_alias(struct remote *remote, const char *url)\n    ++static void add_pushurl_alias(struct remote_state *remote_state,\n    ++\t\t\t      struct remote *remote, const char *url)\n      {\n    - \tconst char *pushurl =\n    +-\tconst char *pushurl =\n     -\t\talias_url(url, &the_repository->remote_state->rewrites_push);\n    -+\t\talias_url(url, &remote->remote_state->rewrites_push);\n    ++\tconst char *pushurl = alias_url(url, &remote_state->rewrites_push);\n      \tif (pushurl != url)\n      \t\tadd_pushurl(remote, pushurl);\n      }\n      \n    - static void add_url_alias(struct remote *remote, const char *url)\n    +-static void add_url_alias(struct remote *remote, const char *url)\n    ++static void add_url_alias(struct remote_state *remote_state,\n    ++\t\t\t  struct remote *remote, const char *url)\n      {\n     -\tadd_url(remote,\n     -\t\talias_url(url, &the_repository->remote_state->rewrites));\n    -+\tadd_url(remote, alias_url(url, &remote->remote_state->rewrites));\n    - \tadd_pushurl_alias(remote, url);\n    +-\tadd_pushurl_alias(remote, url);\n    ++\tadd_url(remote, alias_url(url, &remote_state->rewrites));\n    ++\tadd_pushurl_alias(remote_state, remote, url);\n      }\n      \n    + struct remotes_hash_key {\n     @@ remote.c: static int remotes_hash_cmp(const void *unused_cmp_data,\n      \t\treturn strcmp(a->name, b->name);\n      }\n      \n    --static inline void init_remotes_hash(void)\n    -+/**\n    -+ * NEEDSWORK: Now that the hashmap is in a struct, this should probably\n    -+ * just be moved into remote_state_new().\n    -+ */\n    -+static inline void init_remotes_hash(struct remote_state *remote_state)\n    - {\n    --\tif (!the_repository->remote_state->remotes_hash.cmpfn)\n    --\t\thashmap_init(&the_repository->remote_state->remotes_hash,\n    --\t\t\t     remotes_hash_cmp, NULL, 0);\n    -+\tif (!remote_state->remotes_hash.cmpfn)\n    -+\t\thashmap_init(&remote_state->remotes_hash, remotes_hash_cmp,\n    -+\t\t\t     NULL, 0);\n    - }\n    - \n     -static struct remote *make_remote(const char *name, int len)\n     +static struct remote *make_remote(struct remote_state *remote_state,\n     +\t\t\t\t  const char *name, int len)\n    @@ remote.c: static int remotes_hash_cmp(const void *unused_cmp_data,\n      \tstruct remote *ret;\n      \tstruct remotes_hash_key lookup;\n     @@ remote.c: static struct remote *make_remote(const char *name, int len)\n    - \tif (!len)\n    - \t\tlen = strlen(name);\n    - \n    --\tinit_remotes_hash();\n    -+\tinit_remotes_hash(remote_state);\n    - \tlookup.str = name;\n      \tlookup.len = len;\n      \thashmap_entry_init(&lookup_entry, memhash(name, len));\n      \n    @@ remote.c: static struct remote *make_remote(const char *name, int len)\n      \t\treturn container_of(e, struct remote, ent);\n      \n     @@ remote.c: static struct remote *make_remote(const char *name, int len)\n    - \tret->prune = -1;  /* unspecified */\n    - \tret->prune_tags = -1;  /* unspecified */\n    - \tret->name = xstrndup(name, len);\n    -+\tret->remote_state = remote_state;\n      \trefspec_init(&ret->push, REFSPEC_PUSH);\n      \trefspec_init(&ret->fetch, REFSPEC_FETCH);\n      \n    @@ remote.c: static void add_merge(struct branch *branch, const char *name)\n     +\tremote_state->branches[remote_state->branches_nr++] = ret;\n      \tret->name = xstrndup(name, len);\n      \tret->refname = xstrfmt(\"refs/heads/%s\", ret->name);\n    -+\tret->remote_state = remote_state;\n      \n    - \treturn ret;\n    +@@ remote.c: static const char *skip_spaces(const char *s)\n    + \treturn s;\n      }\n    + \n    +-static void read_remotes_file(struct remote *remote)\n    ++static void read_remotes_file(struct remote_state *remote_state,\n    ++\t\t\t      struct remote *remote)\n    + {\n    + \tstruct strbuf buf = STRBUF_INIT;\n    + \tFILE *f = fopen_or_warn(git_path(\"remotes/%s\", remote->name), \"r\");\n    +@@ remote.c: static void read_remotes_file(struct remote *remote)\n    + \t\tstrbuf_rtrim(&buf);\n    + \n    + \t\tif (skip_prefix(buf.buf, \"URL:\", &v))\n    +-\t\t\tadd_url_alias(remote, xstrdup(skip_spaces(v)));\n    ++\t\t\tadd_url_alias(remote_state, remote,\n    ++\t\t\t\t      xstrdup(skip_spaces(v)));\n    + \t\telse if (skip_prefix(buf.buf, \"Push:\", &v))\n    + \t\t\trefspec_append(&remote->push, skip_spaces(v));\n    + \t\telse if (skip_prefix(buf.buf, \"Pull:\", &v))\n    +@@ remote.c: static void read_remotes_file(struct remote *remote)\n    + \tfclose(f);\n    + }\n    + \n    +-static void read_branches_file(struct remote *remote)\n    ++static void read_branches_file(struct remote_state *remote_state,\n    ++\t\t\t       struct remote *remote)\n    + {\n    + \tchar *frag;\n    + \tstruct strbuf buf = STRBUF_INIT;\n    +@@ remote.c: static void read_branches_file(struct remote *remote)\n    + \telse\n    + \t\tfrag = (char *)git_default_branch_name(0);\n    + \n    +-\tadd_url_alias(remote, strbuf_detach(&buf, NULL));\n    ++\tadd_url_alias(remote_state, remote, strbuf_detach(&buf, NULL));\n    + \trefspec_appendf(&remote->fetch, \"refs/heads/%s:refs/heads/%s\",\n    + \t\t\tfrag, remote->name);\n    + \n     @@ remote.c: static int handle_config(const char *key, const char *value, void *cb)\n      \tconst char *subkey;\n      \tstruct remote *remote;\n    @@ remote.c: static int handle_config(const char *key, const char *value, void *cb)\n     -\t\t\t\t->url[j] = alias_url(\n     -\t\t\t\tthe_repository->remote_state->remotes[i]->url[j],\n     -\t\t\t\t&the_repository->remote_state->rewrites);\n    -+\t\t\t\t\tremote_state->remotes[i],\n    ++\t\t\t\t\tremote_state, remote_state->remotes[i],\n     +\t\t\t\t\tremote_state->remotes[i]->url[j]);\n     +\t\t\tremote_state->remotes[i]->url[j] =\n     +\t\t\t\talias_url(remote_state->remotes[i]->url[j],\n    @@ remote.c: static int handle_config(const char *key, const char *value, void *cb)\n      }\n      \n      static int valid_remote_nick(const char *name)\n    -@@ remote.c: const char *pushremote_for_branch(struct branch *branch, int *explicit)\n    - \t\t\t*explicit = 1;\n    - \t\treturn branch->pushremote_name;\n    - \t}\n    --\tif (the_repository->remote_state->pushremote_name) {\n    -+\tif (branch->remote_state->pushremote_name) {\n    - \t\tif (explicit)\n    - \t\t\t*explicit = 1;\n    --\t\treturn the_repository->remote_state->pushremote_name;\n    -+\t\treturn branch->remote_state->pushremote_name;\n    - \t}\n    - \treturn remote_for_branch(branch, explicit);\n    - }\n     @@ remote.c: static struct remote *remote_get_1(const char *name,\n      \tstruct remote *ret;\n      \tint name_given = 0;\n    @@ remote.c: static struct remote *remote_get_1(const char *name,\n     +\tret = make_remote(the_repository->remote_state, name, 0);\n      \tif (valid_remote_nick(name) && have_git_dir()) {\n      \t\tif (!valid_remote(ret))\n    - \t\t\tread_remotes_file(ret);\n    +-\t\t\tread_remotes_file(ret);\n    ++\t\t\tread_remotes_file(the_repository->remote_state, ret);\n    + \t\tif (!valid_remote(ret))\n    +-\t\t\tread_branches_file(ret);\n    ++\t\t\tread_branches_file(the_repository->remote_state, ret);\n    + \t}\n    + \tif (name_given && !valid_remote(ret))\n    +-\t\tadd_url_alias(ret, name);\n    ++\t\tadd_url_alias(the_repository->remote_state, ret, name);\n    + \tif (!valid_remote(ret))\n    + \t\treturn NULL;\n    + \treturn ret;\n     @@ remote.c: int remote_is_configured(struct remote *remote, int in_repo)\n      int for_each_remote(each_remote_fn fn, void *priv)\n      {\n    @@ remote.h: struct remote_state {\n      };\n      \n      void remote_state_clear(struct remote_state *remote_state);\n    -@@ remote.h: struct remote {\n    - \n    - \t/* The method used for authenticating against `http_proxy`. */\n    - \tchar *http_proxy_authmethod;\n    -+\n    -+\t/** The remote_state that this remote belongs to. This is only meant to\n    -+\t * be used by remote_* functions. */\n    -+\tstruct remote_state *remote_state;\n    - };\n    - \n    - /**\n    -@@ remote.h: struct branch {\n    - \tint merge_alloc;\n    - \n    - \tconst char *push_tracking_ref;\n    -+\n    -+\t/** The remote_state that this branch belongs to. This is only meant to\n    -+\t * be used by branch_* functions. */\n    -+\tstruct remote_state *remote_state;\n    - };\n    - \n    - struct branch *branch_get(const char *name);\n3:  10fbb84496 < -:  ---------- remote: remove the_repository->remote_state from static methods\n4:  4013f74fd9 < -:  ---------- remote: add struct repository parameter to external functions\n-:  ---------- > 4:  53f2e31f72 remote: remove the_repository->remote_state from static methods\n-:  ---------- > 5:  d3281c14eb remote: die if branch is not found in repository\n-:  ---------- > 6:  0974994cc6 remote: add struct repository parameter to external functions\n-- \n2.33.GIT\n\n"},{"id":"439909","messageId":"20211028183101.41013-2-chooglen@google.com","threadId":"56659","inReplyTo":"20211028183101.41013-1-chooglen@google.com","subject":"[PATCH v4 1/6] t5516: add test case for pushing remote refspecs","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-28T18:30:56Z","receivedAt":"2021-10-28T18:31:18Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"In detached HEAD, \"git push remote-name\" should push the refspecs in\nremote.remote-name.push. Since there is no test case that checks this\nbehavior, add one.\n\nSigned-off-by: Glen Choo <chooglen@google.com>\n---\n t/t5516-fetch-push.sh | 9 +++++++++\n 1 file changed, 9 insertions(+)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 8212ca56dc..d4c2f1b68f 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -541,6 +541,15 @@ do\n \n done\n \n+test_expect_success \"push to remote with detached HEAD and config remote.*.push = src:dest\" '\n+\tmk_test testrepo heads/main &&\n+\tgit checkout $the_first_commit &&\n+\ttest_config remote.there.url testrepo &&\n+\ttest_config remote.there.push refs/heads/main:refs/heads/main &&\n+\tgit push there &&\n+\tcheck_push_result testrepo $the_commit heads/main\n+'\n+\n test_expect_success 'push with remote.pushdefault' '\n \tmk_test up_repo heads/main &&\n \tmk_test down_repo heads/main &&\n-- \n2.33.GIT\n\n"},{"id":"439911","messageId":"20211028183101.41013-3-chooglen@google.com","threadId":"56659","inReplyTo":"20211028183101.41013-1-chooglen@google.com","subject":"[PATCH v4 2/6] remote: move static variables into per-repository struct","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-28T18:30:57Z","receivedAt":"2021-10-28T18:31:20Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"remote.c does not work with non-the_repository because it stores its\nstate as static variables. To support non-the_repository, we can use a\nper-repository struct for the remotes subsystem.\n\nPrepare for this change by defining a struct remote_state that holds\nthe remotes subsystem state and move the static variables of remote.c\ninto the_repository->remote_state.\n\nThis introduces no behavioral or API changes.\n\nSigned-off-by: Glen Choo <chooglen@google.com>\n---\n remote.c     | 191 +++++++++++++++++++++++++++++++++------------------\n remote.h     |  34 +++++++++\n repository.c |   8 +++\n repository.h |   4 ++\n 4 files changed, 170 insertions(+), 67 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex f958543d70..c2bb11242b 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -21,33 +21,6 @@ struct counted_string {\n \tsize_t len;\n \tconst char *s;\n };\n-struct rewrite {\n-\tconst char *base;\n-\tsize_t baselen;\n-\tstruct counted_string *instead_of;\n-\tint instead_of_nr;\n-\tint instead_of_alloc;\n-};\n-struct rewrites {\n-\tstruct rewrite **rewrite;\n-\tint rewrite_alloc;\n-\tint rewrite_nr;\n-};\n-\n-static struct remote **remotes;\n-static int remotes_alloc;\n-static int remotes_nr;\n-static struct hashmap remotes_hash;\n-\n-static struct branch **branches;\n-static int branches_alloc;\n-static int branches_nr;\n-\n-static struct branch *current_branch;\n-static const char *pushremote_name;\n-\n-static struct rewrites rewrites;\n-static struct rewrites rewrites_push;\n \n static int valid_remote(const struct remote *remote)\n {\n@@ -94,14 +67,16 @@ static void add_pushurl(struct remote *remote, const char *pushurl)\n \n static void add_pushurl_alias(struct remote *remote, const char *url)\n {\n-\tconst char *pushurl = alias_url(url, &rewrites_push);\n+\tconst char *pushurl =\n+\t\talias_url(url, &the_repository->remote_state->rewrites_push);\n \tif (pushurl != url)\n \t\tadd_pushurl(remote, pushurl);\n }\n \n static void add_url_alias(struct remote *remote, const char *url)\n {\n-\tadd_url(remote, alias_url(url, &rewrites));\n+\tadd_url(remote,\n+\t\talias_url(url, &the_repository->remote_state->rewrites));\n \tadd_pushurl_alias(remote, url);\n }\n \n@@ -127,12 +102,6 @@ static int remotes_hash_cmp(const void *unused_cmp_data,\n \t\treturn strcmp(a->name, b->name);\n }\n \n-static inline void init_remotes_hash(void)\n-{\n-\tif (!remotes_hash.cmpfn)\n-\t\thashmap_init(&remotes_hash, remotes_hash_cmp, NULL, 0);\n-}\n-\n static struct remote *make_remote(const char *name, int len)\n {\n \tstruct remote *ret;\n@@ -142,12 +111,12 @@ static struct remote *make_remote(const char *name, int len)\n \tif (!len)\n \t\tlen = strlen(name);\n \n-\tinit_remotes_hash();\n \tlookup.str = name;\n \tlookup.len = len;\n \thashmap_entry_init(&lookup_entry, memhash(name, len));\n \n-\te = hashmap_get(&remotes_hash, &lookup_entry, &lookup);\n+\te = hashmap_get(&the_repository->remote_state->remotes_hash,\n+\t\t\t&lookup_entry, &lookup);\n \tif (e)\n \t\treturn container_of(e, struct remote, ent);\n \n@@ -158,15 +127,41 @@ static struct remote *make_remote(const char *name, int len)\n \trefspec_init(&ret->push, REFSPEC_PUSH);\n \trefspec_init(&ret->fetch, REFSPEC_FETCH);\n \n-\tALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);\n-\tremotes[remotes_nr++] = ret;\n+\tALLOC_GROW(the_repository->remote_state->remotes,\n+\t\t   the_repository->remote_state->remotes_nr + 1,\n+\t\t   the_repository->remote_state->remotes_alloc);\n+\tthe_repository->remote_state\n+\t\t->remotes[the_repository->remote_state->remotes_nr++] = ret;\n \n \thashmap_entry_init(&ret->ent, lookup_entry.hash);\n-\tif (hashmap_put_entry(&remotes_hash, ret, ent))\n+\tif (hashmap_put_entry(&the_repository->remote_state->remotes_hash, ret,\n+\t\t\t      ent))\n \t\tBUG(\"hashmap_put overwrote entry after hashmap_get returned NULL\");\n \treturn ret;\n }\n \n+static void remote_clear(struct remote *remote)\n+{\n+\tint i;\n+\n+\tfree((char *)remote->name);\n+\tfree((char *)remote->foreign_vcs);\n+\n+\tfor (i = 0; i < remote->url_nr; i++) {\n+\t\tfree((char *)remote->url[i]);\n+\t}\n+\tFREE_AND_NULL(remote->pushurl);\n+\n+\tfor (i = 0; i < remote->pushurl_nr; i++) {\n+\t\tfree((char *)remote->pushurl[i]);\n+\t}\n+\tFREE_AND_NULL(remote->pushurl);\n+\tfree((char *)remote->receivepack);\n+\tfree((char *)remote->uploadpack);\n+\tFREE_AND_NULL(remote->http_proxy);\n+\tFREE_AND_NULL(remote->http_proxy_authmethod);\n+}\n+\n static void add_merge(struct branch *branch, const char *name)\n {\n \tALLOC_GROW(branch->merge_name, branch->merge_nr + 1,\n@@ -179,15 +174,20 @@ static struct branch *make_branch(const char *name, size_t len)\n \tstruct branch *ret;\n \tint i;\n \n-\tfor (i = 0; i < branches_nr; i++) {\n-\t\tif (!strncmp(name, branches[i]->name, len) &&\n-\t\t    !branches[i]->name[len])\n-\t\t\treturn branches[i];\n+\tfor (i = 0; i < the_repository->remote_state->branches_nr; i++) {\n+\t\tif (!strncmp(name,\n+\t\t\t     the_repository->remote_state->branches[i]->name,\n+\t\t\t     len) &&\n+\t\t    !the_repository->remote_state->branches[i]->name[len])\n+\t\t\treturn the_repository->remote_state->branches[i];\n \t}\n \n-\tALLOC_GROW(branches, branches_nr + 1, branches_alloc);\n+\tALLOC_GROW(the_repository->remote_state->branches,\n+\t\t   the_repository->remote_state->branches_nr + 1,\n+\t\t   the_repository->remote_state->branches_alloc);\n \tCALLOC_ARRAY(ret, 1);\n-\tbranches[branches_nr++] = ret;\n+\tthe_repository->remote_state\n+\t\t->branches[the_repository->remote_state->branches_nr++] = ret;\n \tret->name = xstrndup(name, len);\n \tret->refname = xstrfmt(\"refs/heads/%s\", ret->name);\n \n@@ -327,12 +327,16 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\tif (!strcmp(subkey, \"insteadof\")) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n-\t\t\trewrite = make_rewrite(&rewrites, name, namelen);\n+\t\t\trewrite = make_rewrite(\n+\t\t\t\t&the_repository->remote_state->rewrites, name,\n+\t\t\t\tnamelen);\n \t\t\tadd_instead_of(rewrite, xstrdup(value));\n \t\t} else if (!strcmp(subkey, \"pushinsteadof\")) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n-\t\t\trewrite = make_rewrite(&rewrites_push, name, namelen);\n+\t\t\trewrite = make_rewrite(\n+\t\t\t\t&the_repository->remote_state->rewrites_push,\n+\t\t\t\tname, namelen);\n \t\t\tadd_instead_of(rewrite, xstrdup(value));\n \t\t}\n \t}\n@@ -342,7 +346,9 @@ static int handle_config(const char *key, const char *value, void *cb)\n \n \t/* Handle remote.* variables */\n \tif (!name && !strcmp(subkey, \"pushdefault\"))\n-\t\treturn git_config_string(&pushremote_name, key, value);\n+\t\treturn git_config_string(\n+\t\t\t&the_repository->remote_state->pushremote_name, key,\n+\t\t\tvalue);\n \n \tif (!name)\n \t\treturn 0;\n@@ -425,18 +431,34 @@ static int handle_config(const char *key, const char *value, void *cb)\n static void alias_all_urls(void)\n {\n \tint i, j;\n-\tfor (i = 0; i < remotes_nr; i++) {\n+\tfor (i = 0; i < the_repository->remote_state->remotes_nr; i++) {\n \t\tint add_pushurl_aliases;\n-\t\tif (!remotes[i])\n+\t\tif (!the_repository->remote_state->remotes[i])\n \t\t\tcontinue;\n-\t\tfor (j = 0; j < remotes[i]->pushurl_nr; j++) {\n-\t\t\tremotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j], &rewrites);\n+\t\tfor (j = 0;\n+\t\t     j < the_repository->remote_state->remotes[i]->pushurl_nr;\n+\t\t     j++) {\n+\t\t\tthe_repository->remote_state->remotes[i]->pushurl[j] =\n+\t\t\t\talias_url(\n+\t\t\t\t\tthe_repository->remote_state->remotes[i]\n+\t\t\t\t\t\t->pushurl[j],\n+\t\t\t\t\t&the_repository->remote_state->rewrites);\n \t\t}\n-\t\tadd_pushurl_aliases = remotes[i]->pushurl_nr == 0;\n-\t\tfor (j = 0; j < remotes[i]->url_nr; j++) {\n+\t\tadd_pushurl_aliases =\n+\t\t\tthe_repository->remote_state->remotes[i]->pushurl_nr ==\n+\t\t\t0;\n+\t\tfor (j = 0;\n+\t\t     j < the_repository->remote_state->remotes[i]->url_nr;\n+\t\t     j++) {\n \t\t\tif (add_pushurl_aliases)\n-\t\t\t\tadd_pushurl_alias(remotes[i], remotes[i]->url[j]);\n-\t\t\tremotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);\n+\t\t\t\tadd_pushurl_alias(\n+\t\t\t\t\tthe_repository->remote_state->remotes[i],\n+\t\t\t\t\tthe_repository->remote_state->remotes[i]\n+\t\t\t\t\t\t->url[j]);\n+\t\t\tthe_repository->remote_state->remotes[i]\n+\t\t\t\t->url[j] = alias_url(\n+\t\t\t\tthe_repository->remote_state->remotes[i]->url[j],\n+\t\t\t\t&the_repository->remote_state->rewrites);\n \t\t}\n \t}\n }\n@@ -450,12 +472,13 @@ static void read_config(void)\n \t\treturn;\n \tloaded = 1;\n \n-\tcurrent_branch = NULL;\n+\tthe_repository->remote_state->current_branch = NULL;\n \tif (startup_info->have_repository) {\n \t\tconst char *head_ref = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flag);\n \t\tif (head_ref && (flag & REF_ISSYMREF) &&\n \t\t    skip_prefix(head_ref, \"refs/heads/\", &head_ref)) {\n-\t\t\tcurrent_branch = make_branch(head_ref, strlen(head_ref));\n+\t\t\tthe_repository->remote_state->current_branch =\n+\t\t\t\tmake_branch(head_ref, strlen(head_ref));\n \t\t}\n \t}\n \tgit_config(handle_config, NULL);\n@@ -493,10 +516,10 @@ const char *pushremote_for_branch(struct branch *branch, int *explicit)\n \t\t\t*explicit = 1;\n \t\treturn branch->pushremote_name;\n \t}\n-\tif (pushremote_name) {\n+\tif (the_repository->remote_state->pushremote_name) {\n \t\tif (explicit)\n \t\t\t*explicit = 1;\n-\t\treturn pushremote_name;\n+\t\treturn the_repository->remote_state->pushremote_name;\n \t}\n \treturn remote_for_branch(branch, explicit);\n }\n@@ -534,7 +557,8 @@ static struct remote *remote_get_1(const char *name,\n \tif (name)\n \t\tname_given = 1;\n \telse\n-\t\tname = get_default(current_branch, &name_given);\n+\t\tname = get_default(the_repository->remote_state->current_branch,\n+\t\t\t\t   &name_given);\n \n \tret = make_remote(name, 0);\n \tif (valid_remote_nick(name) && have_git_dir()) {\n@@ -573,11 +597,13 @@ int for_each_remote(each_remote_fn fn, void *priv)\n {\n \tint i, result = 0;\n \tread_config();\n-\tfor (i = 0; i < remotes_nr && !result; i++) {\n-\t\tstruct remote *r = remotes[i];\n-\t\tif (!r)\n+\tfor (i = 0; i < the_repository->remote_state->remotes_nr && !result;\n+\t     i++) {\n+\t\tstruct remote *remote =\n+\t\t\tthe_repository->remote_state->remotes[i];\n+\t\tif (!remote)\n \t\t\tcontinue;\n-\t\tresult = fn(r, priv);\n+\t\tresult = fn(remote, priv);\n \t}\n \treturn result;\n }\n@@ -1685,7 +1711,7 @@ struct branch *branch_get(const char *name)\n \n \tread_config();\n \tif (!name || !*name || !strcmp(name, \"HEAD\"))\n-\t\tret = current_branch;\n+\t\tret = the_repository->remote_state->current_branch;\n \telse\n \t\tret = make_branch(name, strlen(name));\n \tset_merge(ret);\n@@ -2585,3 +2611,34 @@ void apply_push_cas(struct push_cas_option *cas,\n \t\t\tcheck_if_includes_upstream(ref);\n \t}\n }\n+\n+struct remote_state *remote_state_new(void)\n+{\n+\tstruct remote_state *r = xmalloc(sizeof(*r));\n+\n+\tmemset(r, 0, sizeof(*r));\n+\n+\thashmap_init(&r->remotes_hash, remotes_hash_cmp, NULL, 0);\n+\treturn r;\n+}\n+\n+void remote_state_clear(struct remote_state *remote_state)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < remote_state->remotes_nr; i++) {\n+\t\tremote_clear(remote_state->remotes[i]);\n+\t}\n+\tFREE_AND_NULL(remote_state->remotes);\n+\tremote_state->remotes_alloc = 0;\n+\tremote_state->remotes_nr = 0;\n+\n+\thashmap_clear_and_free(&remote_state->remotes_hash, struct remote, ent);\n+\n+\tfor (i = 0; i < remote_state->branches_nr; i++) {\n+\t\tFREE_AND_NULL(remote_state->branches[i]);\n+\t}\n+\tFREE_AND_NULL(remote_state->branches);\n+\tremote_state->branches_alloc = 0;\n+\tremote_state->branches_nr = 0;\n+}\ndiff --git a/remote.h b/remote.h\nindex 5a59198252..d21c035f1b 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -23,6 +23,40 @@ enum {\n \tREMOTE_BRANCHES\n };\n \n+struct rewrite {\n+\tconst char *base;\n+\tsize_t baselen;\n+\tstruct counted_string *instead_of;\n+\tint instead_of_nr;\n+\tint instead_of_alloc;\n+};\n+\n+struct rewrites {\n+\tstruct rewrite **rewrite;\n+\tint rewrite_alloc;\n+\tint rewrite_nr;\n+};\n+\n+struct remote_state {\n+\tstruct remote **remotes;\n+\tint remotes_alloc;\n+\tint remotes_nr;\n+\tstruct hashmap remotes_hash;\n+\n+\tstruct branch **branches;\n+\tint branches_alloc;\n+\tint branches_nr;\n+\n+\tstruct branch *current_branch;\n+\tconst char *pushremote_name;\n+\n+\tstruct rewrites rewrites;\n+\tstruct rewrites rewrites_push;\n+};\n+\n+void remote_state_clear(struct remote_state *remote_state);\n+struct remote_state *remote_state_new(void);\n+\n struct remote {\n \tstruct hashmap_entry ent;\n \ndiff --git a/repository.c b/repository.c\nindex c5b90ba93e..c7ea706c20 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -9,6 +9,7 @@\n #include \"config.h\"\n #include \"object.h\"\n #include \"lockfile.h\"\n+#include \"remote.h\"\n #include \"submodule-config.h\"\n #include \"sparse-index.h\"\n #include \"promisor-remote.h\"\n@@ -24,6 +25,7 @@ void initialize_the_repository(void)\n \n \tthe_repo.index = &the_index;\n \tthe_repo.objects = raw_object_store_new();\n+\tthe_repo.remote_state = remote_state_new();\n \tthe_repo.parsed_objects = parsed_object_pool_new();\n \n \trepo_set_hash_algo(&the_repo, GIT_HASH_SHA1);\n@@ -164,6 +166,7 @@ int repo_init(struct repository *repo,\n \n \trepo->objects = raw_object_store_new();\n \trepo->parsed_objects = parsed_object_pool_new();\n+\trepo->remote_state = remote_state_new();\n \n \tif (repo_init_gitdir(repo, gitdir))\n \t\tgoto error;\n@@ -270,6 +273,11 @@ void repo_clear(struct repository *repo)\n \t\tpromisor_remote_clear(repo->promisor_remote_config);\n \t\tFREE_AND_NULL(repo->promisor_remote_config);\n \t}\n+\n+\tif (repo->remote_state) {\n+\t\tremote_state_clear(repo->remote_state);\n+\t\tFREE_AND_NULL(repo->remote_state);\n+\t}\n }\n \n int repo_read_index(struct repository *repo)\ndiff --git a/repository.h b/repository.h\nindex a057653981..98f9583470 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -11,6 +11,7 @@ struct pathspec;\n struct raw_object_store;\n struct submodule_cache;\n struct promisor_remote_config;\n+struct remote_state;\n \n enum untracked_cache_setting {\n \tUNTRACKED_CACHE_KEEP,\n@@ -127,6 +128,9 @@ struct repository {\n \t */\n \tstruct index_state *index;\n \n+\t/* Repository's remotes and associated structures. */\n+\tstruct remote_state *remote_state;\n+\n \t/* Repository's current hash algorithm, as serialized on disk. */\n \tconst struct git_hash_algo *hash_algo;\n \n-- \n2.33.GIT\n\n"},{"id":"439912","messageId":"20211028183101.41013-4-chooglen@google.com","threadId":"56659","inReplyTo":"20211028183101.41013-1-chooglen@google.com","subject":"[PATCH v4 3/6] remote: use remote_state parameter internally","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-28T18:30:58Z","receivedAt":"2021-10-28T18:31:22Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Without changing external-facing functions, replace\nthe_repository->remote_state internally by adding a struct remote_state\nparameter.\n\nAs a result, external-facing functions are still tied to the_repository,\nbut most static functions no longer reference\nthe_repository->remote_state. The exceptions are those that are used in\na way that depends on external-facing functions e.g. the callbacks to\nremote_get_1().\n\nSigned-off-by: Glen Choo <chooglen@google.com>\n---\n remote.c | 159 +++++++++++++++++++++++++------------------------------\n remote.h |   2 +\n 2 files changed, 75 insertions(+), 86 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex c2bb11242b..48374cc0e2 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -65,19 +65,19 @@ static void add_pushurl(struct remote *remote, const char *pushurl)\n \tremote->pushurl[remote->pushurl_nr++] = pushurl;\n }\n \n-static void add_pushurl_alias(struct remote *remote, const char *url)\n+static void add_pushurl_alias(struct remote_state *remote_state,\n+\t\t\t      struct remote *remote, const char *url)\n {\n-\tconst char *pushurl =\n-\t\talias_url(url, &the_repository->remote_state->rewrites_push);\n+\tconst char *pushurl = alias_url(url, &remote_state->rewrites_push);\n \tif (pushurl != url)\n \t\tadd_pushurl(remote, pushurl);\n }\n \n-static void add_url_alias(struct remote *remote, const char *url)\n+static void add_url_alias(struct remote_state *remote_state,\n+\t\t\t  struct remote *remote, const char *url)\n {\n-\tadd_url(remote,\n-\t\talias_url(url, &the_repository->remote_state->rewrites));\n-\tadd_pushurl_alias(remote, url);\n+\tadd_url(remote, alias_url(url, &remote_state->rewrites));\n+\tadd_pushurl_alias(remote_state, remote, url);\n }\n \n struct remotes_hash_key {\n@@ -102,7 +102,8 @@ static int remotes_hash_cmp(const void *unused_cmp_data,\n \t\treturn strcmp(a->name, b->name);\n }\n \n-static struct remote *make_remote(const char *name, int len)\n+static struct remote *make_remote(struct remote_state *remote_state,\n+\t\t\t\t  const char *name, int len)\n {\n \tstruct remote *ret;\n \tstruct remotes_hash_key lookup;\n@@ -115,8 +116,7 @@ static struct remote *make_remote(const char *name, int len)\n \tlookup.len = len;\n \thashmap_entry_init(&lookup_entry, memhash(name, len));\n \n-\te = hashmap_get(&the_repository->remote_state->remotes_hash,\n-\t\t\t&lookup_entry, &lookup);\n+\te = hashmap_get(&remote_state->remotes_hash, &lookup_entry, &lookup);\n \tif (e)\n \t\treturn container_of(e, struct remote, ent);\n \n@@ -127,15 +127,12 @@ static struct remote *make_remote(const char *name, int len)\n \trefspec_init(&ret->push, REFSPEC_PUSH);\n \trefspec_init(&ret->fetch, REFSPEC_FETCH);\n \n-\tALLOC_GROW(the_repository->remote_state->remotes,\n-\t\t   the_repository->remote_state->remotes_nr + 1,\n-\t\t   the_repository->remote_state->remotes_alloc);\n-\tthe_repository->remote_state\n-\t\t->remotes[the_repository->remote_state->remotes_nr++] = ret;\n+\tALLOC_GROW(remote_state->remotes, remote_state->remotes_nr + 1,\n+\t\t   remote_state->remotes_alloc);\n+\tremote_state->remotes[remote_state->remotes_nr++] = ret;\n \n \thashmap_entry_init(&ret->ent, lookup_entry.hash);\n-\tif (hashmap_put_entry(&the_repository->remote_state->remotes_hash, ret,\n-\t\t\t      ent))\n+\tif (hashmap_put_entry(&remote_state->remotes_hash, ret, ent))\n \t\tBUG(\"hashmap_put overwrote entry after hashmap_get returned NULL\");\n \treturn ret;\n }\n@@ -169,25 +166,22 @@ static void add_merge(struct branch *branch, const char *name)\n \tbranch->merge_name[branch->merge_nr++] = name;\n }\n \n-static struct branch *make_branch(const char *name, size_t len)\n+static struct branch *make_branch(struct remote_state *remote_state,\n+\t\t\t\t  const char *name, size_t len)\n {\n \tstruct branch *ret;\n \tint i;\n \n-\tfor (i = 0; i < the_repository->remote_state->branches_nr; i++) {\n-\t\tif (!strncmp(name,\n-\t\t\t     the_repository->remote_state->branches[i]->name,\n-\t\t\t     len) &&\n-\t\t    !the_repository->remote_state->branches[i]->name[len])\n-\t\t\treturn the_repository->remote_state->branches[i];\n+\tfor (i = 0; i < remote_state->branches_nr; i++) {\n+\t\tif (!strncmp(name, remote_state->branches[i]->name, len) &&\n+\t\t    !remote_state->branches[i]->name[len])\n+\t\t\treturn remote_state->branches[i];\n \t}\n \n-\tALLOC_GROW(the_repository->remote_state->branches,\n-\t\t   the_repository->remote_state->branches_nr + 1,\n-\t\t   the_repository->remote_state->branches_alloc);\n+\tALLOC_GROW(remote_state->branches, remote_state->branches_nr + 1,\n+\t\t   remote_state->branches_alloc);\n \tCALLOC_ARRAY(ret, 1);\n-\tthe_repository->remote_state\n-\t\t->branches[the_repository->remote_state->branches_nr++] = ret;\n+\tremote_state->branches[remote_state->branches_nr++] = ret;\n \tret->name = xstrndup(name, len);\n \tret->refname = xstrfmt(\"refs/heads/%s\", ret->name);\n \n@@ -229,7 +223,8 @@ static const char *skip_spaces(const char *s)\n \treturn s;\n }\n \n-static void read_remotes_file(struct remote *remote)\n+static void read_remotes_file(struct remote_state *remote_state,\n+\t\t\t      struct remote *remote)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tFILE *f = fopen_or_warn(git_path(\"remotes/%s\", remote->name), \"r\");\n@@ -244,7 +239,8 @@ static void read_remotes_file(struct remote *remote)\n \t\tstrbuf_rtrim(&buf);\n \n \t\tif (skip_prefix(buf.buf, \"URL:\", &v))\n-\t\t\tadd_url_alias(remote, xstrdup(skip_spaces(v)));\n+\t\t\tadd_url_alias(remote_state, remote,\n+\t\t\t\t      xstrdup(skip_spaces(v)));\n \t\telse if (skip_prefix(buf.buf, \"Push:\", &v))\n \t\t\trefspec_append(&remote->push, skip_spaces(v));\n \t\telse if (skip_prefix(buf.buf, \"Pull:\", &v))\n@@ -254,7 +250,8 @@ static void read_remotes_file(struct remote *remote)\n \tfclose(f);\n }\n \n-static void read_branches_file(struct remote *remote)\n+static void read_branches_file(struct remote_state *remote_state,\n+\t\t\t       struct remote *remote)\n {\n \tchar *frag;\n \tstruct strbuf buf = STRBUF_INIT;\n@@ -286,7 +283,7 @@ static void read_branches_file(struct remote *remote)\n \telse\n \t\tfrag = (char *)git_default_branch_name(0);\n \n-\tadd_url_alias(remote, strbuf_detach(&buf, NULL));\n+\tadd_url_alias(remote_state, remote, strbuf_detach(&buf, NULL));\n \trefspec_appendf(&remote->fetch, \"refs/heads/%s:refs/heads/%s\",\n \t\t\tfrag, remote->name);\n \n@@ -305,10 +302,12 @@ static int handle_config(const char *key, const char *value, void *cb)\n \tconst char *subkey;\n \tstruct remote *remote;\n \tstruct branch *branch;\n+\tstruct remote_state *remote_state = cb;\n+\n \tif (parse_config_key(key, \"branch\", &name, &namelen, &subkey) >= 0) {\n \t\tif (!name)\n \t\t\treturn 0;\n-\t\tbranch = make_branch(name, namelen);\n+\t\tbranch = make_branch(remote_state, name, namelen);\n \t\tif (!strcmp(subkey, \"remote\")) {\n \t\t\treturn git_config_string(&branch->remote_name, key, value);\n \t\t} else if (!strcmp(subkey, \"pushremote\")) {\n@@ -327,16 +326,14 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\tif (!strcmp(subkey, \"insteadof\")) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n-\t\t\trewrite = make_rewrite(\n-\t\t\t\t&the_repository->remote_state->rewrites, name,\n-\t\t\t\tnamelen);\n+\t\t\trewrite = make_rewrite(&remote_state->rewrites, name,\n+\t\t\t\t\t       namelen);\n \t\t\tadd_instead_of(rewrite, xstrdup(value));\n \t\t} else if (!strcmp(subkey, \"pushinsteadof\")) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n-\t\t\trewrite = make_rewrite(\n-\t\t\t\t&the_repository->remote_state->rewrites_push,\n-\t\t\t\tname, namelen);\n+\t\t\trewrite = make_rewrite(&remote_state->rewrites_push,\n+\t\t\t\t\t       name, namelen);\n \t\t\tadd_instead_of(rewrite, xstrdup(value));\n \t\t}\n \t}\n@@ -346,9 +343,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \n \t/* Handle remote.* variables */\n \tif (!name && !strcmp(subkey, \"pushdefault\"))\n-\t\treturn git_config_string(\n-\t\t\t&the_repository->remote_state->pushremote_name, key,\n-\t\t\tvalue);\n+\t\treturn git_config_string(&remote_state->pushremote_name, key,\n+\t\t\t\t\t value);\n \n \tif (!name)\n \t\treturn 0;\n@@ -358,7 +354,7 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\t\tname);\n \t\treturn 0;\n \t}\n-\tremote = make_remote(name, namelen);\n+\tremote = make_remote(remote_state, name, namelen);\n \tremote->origin = REMOTE_CONFIG;\n \tif (current_config_scope() == CONFIG_SCOPE_LOCAL ||\n \t    current_config_scope() == CONFIG_SCOPE_WORKTREE)\n@@ -428,61 +424,51 @@ static int handle_config(const char *key, const char *value, void *cb)\n \treturn 0;\n }\n \n-static void alias_all_urls(void)\n+static void alias_all_urls(struct remote_state *remote_state)\n {\n \tint i, j;\n-\tfor (i = 0; i < the_repository->remote_state->remotes_nr; i++) {\n+\tfor (i = 0; i < remote_state->remotes_nr; i++) {\n \t\tint add_pushurl_aliases;\n-\t\tif (!the_repository->remote_state->remotes[i])\n+\t\tif (!remote_state->remotes[i])\n \t\t\tcontinue;\n-\t\tfor (j = 0;\n-\t\t     j < the_repository->remote_state->remotes[i]->pushurl_nr;\n-\t\t     j++) {\n-\t\t\tthe_repository->remote_state->remotes[i]->pushurl[j] =\n-\t\t\t\talias_url(\n-\t\t\t\t\tthe_repository->remote_state->remotes[i]\n-\t\t\t\t\t\t->pushurl[j],\n-\t\t\t\t\t&the_repository->remote_state->rewrites);\n+\t\tfor (j = 0; j < remote_state->remotes[i]->pushurl_nr; j++) {\n+\t\t\tremote_state->remotes[i]->pushurl[j] =\n+\t\t\t\talias_url(remote_state->remotes[i]->pushurl[j],\n+\t\t\t\t\t  &remote_state->rewrites);\n \t\t}\n-\t\tadd_pushurl_aliases =\n-\t\t\tthe_repository->remote_state->remotes[i]->pushurl_nr ==\n-\t\t\t0;\n-\t\tfor (j = 0;\n-\t\t     j < the_repository->remote_state->remotes[i]->url_nr;\n-\t\t     j++) {\n+\t\tadd_pushurl_aliases = remote_state->remotes[i]->pushurl_nr == 0;\n+\t\tfor (j = 0; j < remote_state->remotes[i]->url_nr; j++) {\n \t\t\tif (add_pushurl_aliases)\n \t\t\t\tadd_pushurl_alias(\n-\t\t\t\t\tthe_repository->remote_state->remotes[i],\n-\t\t\t\t\tthe_repository->remote_state->remotes[i]\n-\t\t\t\t\t\t->url[j]);\n-\t\t\tthe_repository->remote_state->remotes[i]\n-\t\t\t\t->url[j] = alias_url(\n-\t\t\t\tthe_repository->remote_state->remotes[i]->url[j],\n-\t\t\t\t&the_repository->remote_state->rewrites);\n+\t\t\t\t\tremote_state, remote_state->remotes[i],\n+\t\t\t\t\tremote_state->remotes[i]->url[j]);\n+\t\t\tremote_state->remotes[i]->url[j] =\n+\t\t\t\talias_url(remote_state->remotes[i]->url[j],\n+\t\t\t\t\t  &remote_state->rewrites);\n \t\t}\n \t}\n }\n \n-static void read_config(void)\n+static void read_config(struct repository *repo)\n {\n-\tstatic int loaded;\n \tint flag;\n \n-\tif (loaded)\n+\tif (repo->remote_state->initialized)\n \t\treturn;\n-\tloaded = 1;\n+\trepo->remote_state->initialized = 1;\n \n-\tthe_repository->remote_state->current_branch = NULL;\n+\trepo->remote_state->current_branch = NULL;\n \tif (startup_info->have_repository) {\n-\t\tconst char *head_ref = resolve_ref_unsafe(\"HEAD\", 0, NULL, &flag);\n+\t\tconst char *head_ref = refs_resolve_ref_unsafe(\n+\t\t\tget_main_ref_store(repo), \"HEAD\", 0, NULL, &flag);\n \t\tif (head_ref && (flag & REF_ISSYMREF) &&\n \t\t    skip_prefix(head_ref, \"refs/heads/\", &head_ref)) {\n-\t\t\tthe_repository->remote_state->current_branch =\n-\t\t\t\tmake_branch(head_ref, strlen(head_ref));\n+\t\t\trepo->remote_state->current_branch = make_branch(\n+\t\t\t\trepo->remote_state, head_ref, strlen(head_ref));\n \t\t}\n \t}\n-\tgit_config(handle_config, NULL);\n-\talias_all_urls();\n+\trepo_config(repo, handle_config, repo->remote_state);\n+\talias_all_urls(repo->remote_state);\n }\n \n static int valid_remote_nick(const char *name)\n@@ -552,7 +538,7 @@ static struct remote *remote_get_1(const char *name,\n \tstruct remote *ret;\n \tint name_given = 0;\n \n-\tread_config();\n+\tread_config(the_repository);\n \n \tif (name)\n \t\tname_given = 1;\n@@ -560,15 +546,15 @@ static struct remote *remote_get_1(const char *name,\n \t\tname = get_default(the_repository->remote_state->current_branch,\n \t\t\t\t   &name_given);\n \n-\tret = make_remote(name, 0);\n+\tret = make_remote(the_repository->remote_state, name, 0);\n \tif (valid_remote_nick(name) && have_git_dir()) {\n \t\tif (!valid_remote(ret))\n-\t\t\tread_remotes_file(ret);\n+\t\t\tread_remotes_file(the_repository->remote_state, ret);\n \t\tif (!valid_remote(ret))\n-\t\t\tread_branches_file(ret);\n+\t\t\tread_branches_file(the_repository->remote_state, ret);\n \t}\n \tif (name_given && !valid_remote(ret))\n-\t\tadd_url_alias(ret, name);\n+\t\tadd_url_alias(the_repository->remote_state, ret, name);\n \tif (!valid_remote(ret))\n \t\treturn NULL;\n \treturn ret;\n@@ -596,7 +582,7 @@ int remote_is_configured(struct remote *remote, int in_repo)\n int for_each_remote(each_remote_fn fn, void *priv)\n {\n \tint i, result = 0;\n-\tread_config();\n+\tread_config(the_repository);\n \tfor (i = 0; i < the_repository->remote_state->remotes_nr && !result;\n \t     i++) {\n \t\tstruct remote *remote =\n@@ -1709,11 +1695,12 @@ struct branch *branch_get(const char *name)\n {\n \tstruct branch *ret;\n \n-\tread_config();\n+\tread_config(the_repository);\n \tif (!name || !*name || !strcmp(name, \"HEAD\"))\n \t\tret = the_repository->remote_state->current_branch;\n \telse\n-\t\tret = make_branch(name, strlen(name));\n+\t\tret = make_branch(the_repository->remote_state, name,\n+\t\t\t\t  strlen(name));\n \tset_merge(ret);\n \treturn ret;\n }\ndiff --git a/remote.h b/remote.h\nindex d21c035f1b..85a730b8ef 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -52,6 +52,8 @@ struct remote_state {\n \n \tstruct rewrites rewrites;\n \tstruct rewrites rewrites_push;\n+\n+\tint initialized;\n };\n \n void remote_state_clear(struct remote_state *remote_state);\n-- \n2.33.GIT\n\n"},{"id":"439913","messageId":"20211028183101.41013-5-chooglen@google.com","threadId":"56659","inReplyTo":"20211028183101.41013-1-chooglen@google.com","subject":"[PATCH v4 4/6] remote: remove the_repository->remote_state from static methods","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-28T18:30:59Z","receivedAt":"2021-10-28T18:31:25Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Replace all remaining references of the_repository->remote_state in\nstatic functions with a struct remote_state parameter.\n\nTo do so, move read_config() calls to non-static functions and create a\nfamily of static functions, \"remotes_*\", that behave like \"repo_*\", but\naccept struct remote_state instead of struct repository. In the case\nwhere a static function calls a non-static function, replace the\nnon-static function with its \"remotes_*\" equivalent.\n\nSigned-off-by: Glen Choo <chooglen@google.com>\n---\n remote.c | 96 +++++++++++++++++++++++++++++++++++++++++---------------\n 1 file changed, 71 insertions(+), 25 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 48374cc0e2..88697e7e6d 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -483,7 +483,9 @@ static int valid_remote_nick(const char *name)\n \treturn 1;\n }\n \n-const char *remote_for_branch(struct branch *branch, int *explicit)\n+static const char *remotes_remote_for_branch(struct remote_state *remote_state,\n+\t\t\t\t\t     struct branch *branch,\n+\t\t\t\t\t     int *explicit)\n {\n \tif (branch && branch->remote_name) {\n \t\tif (explicit)\n@@ -495,32 +497,55 @@ const char *remote_for_branch(struct branch *branch, int *explicit)\n \treturn \"origin\";\n }\n \n-const char *pushremote_for_branch(struct branch *branch, int *explicit)\n+const char *remote_for_branch(struct branch *branch, int *explicit)\n+{\n+\tread_config(the_repository);\n+\treturn remotes_remote_for_branch(the_repository->remote_state, branch,\n+\t\t\t\t\t explicit);\n+}\n+\n+static const char *\n+remotes_pushremote_for_branch(struct remote_state *remote_state,\n+\t\t\t      struct branch *branch, int *explicit)\n {\n \tif (branch && branch->pushremote_name) {\n \t\tif (explicit)\n \t\t\t*explicit = 1;\n \t\treturn branch->pushremote_name;\n \t}\n-\tif (the_repository->remote_state->pushremote_name) {\n+\tif (remote_state->pushremote_name) {\n \t\tif (explicit)\n \t\t\t*explicit = 1;\n-\t\treturn the_repository->remote_state->pushremote_name;\n+\t\treturn remote_state->pushremote_name;\n \t}\n-\treturn remote_for_branch(branch, explicit);\n+\treturn remotes_remote_for_branch(remote_state, branch, explicit);\n }\n \n+const char *pushremote_for_branch(struct branch *branch, int *explicit)\n+{\n+\tread_config(the_repository);\n+\treturn remotes_pushremote_for_branch(the_repository->remote_state,\n+\t\t\t\t\t     branch, explicit);\n+}\n+\n+static struct remote *remotes_remote_get(struct remote_state *remote_state,\n+\t\t\t\t\t const char *name);\n+\n const char *remote_ref_for_branch(struct branch *branch, int for_push)\n {\n+\tread_config(the_repository);\n \tif (branch) {\n \t\tif (!for_push) {\n \t\t\tif (branch->merge_nr) {\n \t\t\t\treturn branch->merge_name[0];\n \t\t\t}\n \t\t} else {\n-\t\t\tconst char *dst, *remote_name =\n-\t\t\t\tpushremote_for_branch(branch, NULL);\n-\t\t\tstruct remote *remote = remote_get(remote_name);\n+\t\t\tconst char *dst,\n+\t\t\t\t*remote_name = remotes_pushremote_for_branch(\n+\t\t\t\t\tthe_repository->remote_state, branch,\n+\t\t\t\t\tNULL);\n+\t\t\tstruct remote *remote = remotes_remote_get(\n+\t\t\t\tthe_repository->remote_state, remote_name);\n \n \t\t\tif (remote && remote->push.nr &&\n \t\t\t    (dst = apply_refspecs(&remote->push,\n@@ -532,42 +557,58 @@ const char *remote_ref_for_branch(struct branch *branch, int for_push)\n \treturn NULL;\n }\n \n-static struct remote *remote_get_1(const char *name,\n-\t\t\t\t   const char *(*get_default)(struct branch *, int *))\n+static struct remote *\n+remotes_remote_get_1(struct remote_state *remote_state, const char *name,\n+\t\t     const char *(*get_default)(struct remote_state *,\n+\t\t\t\t\t\tstruct branch *, int *))\n {\n \tstruct remote *ret;\n \tint name_given = 0;\n \n-\tread_config(the_repository);\n-\n \tif (name)\n \t\tname_given = 1;\n \telse\n-\t\tname = get_default(the_repository->remote_state->current_branch,\n+\t\tname = get_default(remote_state, remote_state->current_branch,\n \t\t\t\t   &name_given);\n \n-\tret = make_remote(the_repository->remote_state, name, 0);\n+\tret = make_remote(remote_state, name, 0);\n \tif (valid_remote_nick(name) && have_git_dir()) {\n \t\tif (!valid_remote(ret))\n-\t\t\tread_remotes_file(the_repository->remote_state, ret);\n+\t\t\tread_remotes_file(remote_state, ret);\n \t\tif (!valid_remote(ret))\n-\t\t\tread_branches_file(the_repository->remote_state, ret);\n+\t\t\tread_branches_file(remote_state, ret);\n \t}\n \tif (name_given && !valid_remote(ret))\n-\t\tadd_url_alias(the_repository->remote_state, ret, name);\n+\t\tadd_url_alias(remote_state, ret, name);\n \tif (!valid_remote(ret))\n \t\treturn NULL;\n \treturn ret;\n }\n \n+static inline struct remote *\n+remotes_remote_get(struct remote_state *remote_state, const char *name)\n+{\n+\treturn remotes_remote_get_1(remote_state, name,\n+\t\t\t\t    remotes_remote_for_branch);\n+}\n+\n struct remote *remote_get(const char *name)\n {\n-\treturn remote_get_1(name, remote_for_branch);\n+\tread_config(the_repository);\n+\treturn remotes_remote_get(the_repository->remote_state, name);\n+}\n+\n+static inline struct remote *\n+remotes_pushremote_get(struct remote_state *remote_state, const char *name)\n+{\n+\treturn remotes_remote_get_1(remote_state, name,\n+\t\t\t\t    remotes_pushremote_for_branch);\n }\n \n struct remote *pushremote_get(const char *name)\n {\n-\treturn remote_get_1(name, pushremote_for_branch);\n+\tread_config(the_repository);\n+\treturn remotes_pushremote_get(the_repository->remote_state, name);\n }\n \n int remote_is_configured(struct remote *remote, int in_repo)\n@@ -1654,7 +1695,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\n \t}\n }\n \n-static void set_merge(struct branch *ret)\n+static void set_merge(struct remote_state *remote_state, struct branch *ret)\n {\n \tstruct remote *remote;\n \tchar *ref;\n@@ -1674,7 +1715,7 @@ static void set_merge(struct branch *ret)\n \t\treturn;\n \t}\n \n-\tremote = remote_get(ret->remote_name);\n+\tremote = remotes_remote_get(remote_state, ret->remote_name);\n \n \tCALLOC_ARRAY(ret->merge, ret->merge_nr);\n \tfor (i = 0; i < ret->merge_nr; i++) {\n@@ -1701,7 +1742,7 @@ struct branch *branch_get(const char *name)\n \telse\n \t\tret = make_branch(the_repository->remote_state, name,\n \t\t\t\t  strlen(name));\n-\tset_merge(ret);\n+\tset_merge(the_repository->remote_state, ret);\n \treturn ret;\n }\n \n@@ -1772,11 +1813,14 @@ static const char *tracking_for_push_dest(struct remote *remote,\n \treturn ret;\n }\n \n-static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)\n+static const char *branch_get_push_1(struct remote_state *remote_state,\n+\t\t\t\t     struct branch *branch, struct strbuf *err)\n {\n \tstruct remote *remote;\n \n-\tremote = remote_get(pushremote_for_branch(branch, NULL));\n+\tremote = remotes_remote_get(\n+\t\tremote_state,\n+\t\tremotes_pushremote_for_branch(remote_state, branch, NULL));\n \tif (!remote)\n \t\treturn error_buf(err,\n \t\t\t\t _(\"branch '%s' has no remote for pushing\"),\n@@ -1834,11 +1878,13 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)\n \n const char *branch_get_push(struct branch *branch, struct strbuf *err)\n {\n+\tread_config(the_repository);\n \tif (!branch)\n \t\treturn error_buf(err, _(\"HEAD does not point to a branch\"));\n \n \tif (!branch->push_tracking_ref)\n-\t\tbranch->push_tracking_ref = branch_get_push_1(branch, err);\n+\t\tbranch->push_tracking_ref = branch_get_push_1(\n+\t\t\tthe_repository->remote_state, branch, err);\n \treturn branch->push_tracking_ref;\n }\n \n-- \n2.33.GIT\n\n"},{"id":"439914","messageId":"20211028183101.41013-6-chooglen@google.com","threadId":"56659","inReplyTo":"20211028183101.41013-1-chooglen@google.com","subject":"[PATCH v4 5/6] remote: die if branch is not found in repository","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-28T18:31:00Z","receivedAt":"2021-10-28T18:31:26Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"In a subsequent commit, we would like external-facing functions to be\nable to accept \"struct repository\" and \"struct branch\" as a pair. This\nis useful for functions like pushremote_for_branch(), which need to take\nvalues from the remote_state and branch, even if branch == NULL.\nHowever, a caller may supply an unrelated repository and branch, which\nis not supported behavior.\n\nTo prevent misuse, add a die_on_missing_branch() helper function that\ndies if a given branch is not from a given repository. Speed up the\nexistence check by using a new branches_hash hashmap to remote_state,\nand use the hashmap to remove the branch array iteration in\nmake_branch().\n\nLike read_config(), die_on_missing_branch() is only called from\nnon-static functions; static functions are less prone to misuse because\nthey have strong conventions for keeping remote_state and branch in\nsync.\n\nSigned-off-by: Glen Choo <chooglen@google.com>\n---\n remote.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++-----\n remote.h |  2 ++\n 2 files changed, 69 insertions(+), 6 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 88697e7e6d..2b5166dee0 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -166,17 +166,66 @@ static void add_merge(struct branch *branch, const char *name)\n \tbranch->merge_name[branch->merge_nr++] = name;\n }\n \n+struct branches_hash_key {\n+\tconst char *str;\n+\tint len;\n+};\n+\n+static int branches_hash_cmp(const void *unused_cmp_data,\n+\t\t\t     const struct hashmap_entry *eptr,\n+\t\t\t     const struct hashmap_entry *entry_or_key,\n+\t\t\t     const void *keydata)\n+{\n+\tconst struct branch *a, *b;\n+\tconst struct branches_hash_key *key = keydata;\n+\n+\ta = container_of(eptr, const struct branch, ent);\n+\tb = container_of(entry_or_key, const struct branch, ent);\n+\n+\tif (key)\n+\t\treturn strncmp(a->name, key->str, key->len) ||\n+\t\t       a->name[key->len];\n+\telse\n+\t\treturn strcmp(a->name, b->name);\n+}\n+\n+static struct branch *find_branch(struct remote_state *remote_state,\n+\t\t\t\t  const char *name, size_t len)\n+{\n+\tstruct branches_hash_key lookup;\n+\tstruct hashmap_entry lookup_entry, *e;\n+\n+\tif (!len)\n+\t\tlen = strlen(name);\n+\n+\tlookup.str = name;\n+\tlookup.len = len;\n+\thashmap_entry_init(&lookup_entry, memhash(name, len));\n+\n+\te = hashmap_get(&remote_state->branches_hash, &lookup_entry, &lookup);\n+\tif (e)\n+\t\treturn container_of(e, struct branch, ent);\n+\n+\treturn NULL;\n+}\n+\n+static void die_on_missing_branch(struct repository *repo,\n+\t\t\t\t  struct branch *branch)\n+{\n+\t/* branch == NULL is always valid because it represents detached HEAD. */\n+\tif (branch &&\n+\t    branch != find_branch(repo->remote_state, branch->name, 0))\n+\t\tdie(\"branch %s was not found in the repository\", branch->name);\n+}\n+\n static struct branch *make_branch(struct remote_state *remote_state,\n \t\t\t\t  const char *name, size_t len)\n {\n \tstruct branch *ret;\n-\tint i;\n \n-\tfor (i = 0; i < remote_state->branches_nr; i++) {\n-\t\tif (!strncmp(name, remote_state->branches[i]->name, len) &&\n-\t\t    !remote_state->branches[i]->name[len])\n-\t\t\treturn remote_state->branches[i];\n-\t}\n+\tret = find_branch(remote_state, name, len);\n+\tif (ret)\n+\t\treturn ret;\n \n \tALLOC_GROW(remote_state->branches, remote_state->branches_nr + 1,\n \t\t   remote_state->branches_alloc);\n@@ -185,6 +234,9 @@ static struct branch *make_branch(struct remote_state *remote_state,\n \tret->name = xstrndup(name, len);\n \tret->refname = xstrfmt(\"refs/heads/%s\", ret->name);\n \n+\thashmap_entry_init(&ret->ent, memhash(name, len));\n+\tif (hashmap_put_entry(&remote_state->branches_hash, ret, ent))\n+\t\tBUG(\"hashmap_put overwrote entry after hashmap_get returned NULL\");\n \treturn ret;\n }\n \n@@ -500,6 +552,8 @@ static const char *remotes_remote_for_branch(struct remote_state *remote_state,\n const char *remote_for_branch(struct branch *branch, int *explicit)\n {\n \tread_config(the_repository);\n+\tdie_on_missing_branch(the_repository, branch);\n+\n \treturn remotes_remote_for_branch(the_repository->remote_state, branch,\n \t\t\t\t\t explicit);\n }\n@@ -524,6 +578,8 @@ remotes_pushremote_for_branch(struct remote_state *remote_state,\n const char *pushremote_for_branch(struct branch *branch, int *explicit)\n {\n \tread_config(the_repository);\n+\tdie_on_missing_branch(the_repository, branch);\n+\n \treturn remotes_pushremote_for_branch(the_repository->remote_state,\n \t\t\t\t\t     branch, explicit);\n }\n@@ -534,6 +590,8 @@ static struct remote *remotes_remote_get(struct remote_state *remote_state,\n const char *remote_ref_for_branch(struct branch *branch, int for_push)\n {\n \tread_config(the_repository);\n+\tdie_on_missing_branch(the_repository, branch);\n+\n \tif (branch) {\n \t\tif (!for_push) {\n \t\t\tif (branch->merge_nr) {\n@@ -1879,6 +1937,8 @@ static const char *branch_get_push_1(struct remote_state *remote_state,\n const char *branch_get_push(struct branch *branch, struct strbuf *err)\n {\n \tread_config(the_repository);\n+\tdie_on_missing_branch(the_repository, branch);\n+\n \tif (!branch)\n \t\treturn error_buf(err, _(\"HEAD does not point to a branch\"));\n \n@@ -2652,6 +2712,7 @@ struct remote_state *remote_state_new(void)\n \tmemset(r, 0, sizeof(*r));\n \n \thashmap_init(&r->remotes_hash, remotes_hash_cmp, NULL, 0);\n+\thashmap_init(&r->branches_hash, branches_hash_cmp, NULL, 0);\n \treturn r;\n }\n \ndiff --git a/remote.h b/remote.h\nindex 85a730b8ef..b205830ac3 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -46,6 +46,7 @@ struct remote_state {\n \tstruct branch **branches;\n \tint branches_alloc;\n \tint branches_nr;\n+\tstruct hashmap branches_hash;\n \n \tstruct branch *current_branch;\n \tconst char *pushremote_name;\n@@ -292,6 +293,7 @@ int remote_find_tracking(struct remote *remote, struct refspec_item *refspec);\n  * branch_get(name) for \"refs/heads/{name}\", or with branch_get(NULL) for HEAD.\n  */\n struct branch {\n+\tstruct hashmap_entry ent;\n \n \t/* The short name of the branch. */\n \tconst char *name;\n-- \n2.33.GIT\n\n"},{"id":"439915","messageId":"20211028183101.41013-7-chooglen@google.com","threadId":"56659","inReplyTo":"20211028183101.41013-1-chooglen@google.com","subject":"[PATCH v4 6/6] remote: add struct repository parameter to external functions","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-10-28T18:31:01Z","receivedAt":"2021-10-28T18:31:28Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Finish plumbing remote_state by adding a struct repository\nparameter to repo_* functions. This removes all references to\nthe_repository->remote_state in remote.c - explicit references in the\nfunction bodies and implicit references via calls to non-repo_*\nfunctions. However, certain functions still use the_repository to parse\noids.\n\nWhere a non-static function has the option to call either the static\n\"remotes_*\" variant or the non-static \"repo_*\" variant, use \"remotes_*\"\nin order to be consistent with the static functions. A desirable\nside-effect of this is that it minimizes the number of calls to\nread_config().\n\nSigned-off-by: Glen Choo <chooglen@google.com>\n---\n remote.c | 85 ++++++++++++++++++++++++++++----------------------------\n remote.h | 80 ++++++++++++++++++++++++++++++++++++++++++----------\n 2 files changed, 109 insertions(+), 56 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 2b5166dee0..f7d9a47faa 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -549,13 +549,13 @@ static const char *remotes_remote_for_branch(struct remote_state *remote_state,\n \treturn \"origin\";\n }\n \n-const char *remote_for_branch(struct branch *branch, int *explicit)\n+const char *repo_remote_for_branch(struct repository *repo, struct branch *branch, int *explicit)\n {\n-\tread_config(the_repository);\n-\tdie_on_missing_branch(the_repository, branch);\n+\tread_config(repo);\n+\tdie_on_missing_branch(repo, branch);\n \n-\treturn remotes_remote_for_branch(the_repository->remote_state, branch,\n-\t\t\t\t\t explicit);\n+\treturn remotes_remote_for_branch(repo->remote_state, branch,\n+\t\t\t\t\t     explicit);\n }\n \n static const char *\n@@ -575,22 +575,22 @@ remotes_pushremote_for_branch(struct remote_state *remote_state,\n \treturn remotes_remote_for_branch(remote_state, branch, explicit);\n }\n \n-const char *pushremote_for_branch(struct branch *branch, int *explicit)\n+const char *repo_pushremote_for_branch(struct repository *repo, struct branch *branch, int *explicit)\n {\n-\tread_config(the_repository);\n-\tdie_on_missing_branch(the_repository, branch);\n+\tread_config(repo);\n+\tdie_on_missing_branch(repo, branch);\n \n-\treturn remotes_pushremote_for_branch(the_repository->remote_state,\n+\treturn remotes_pushremote_for_branch(repo->remote_state,\n \t\t\t\t\t     branch, explicit);\n }\n \n static struct remote *remotes_remote_get(struct remote_state *remote_state,\n \t\t\t\t\t const char *name);\n \n-const char *remote_ref_for_branch(struct branch *branch, int for_push)\n+const char *repo_remote_ref_for_branch(struct repository *repo, struct branch *branch, int for_push)\n {\n-\tread_config(the_repository);\n-\tdie_on_missing_branch(the_repository, branch);\n+\tread_config(repo);\n+\tdie_on_missing_branch(repo, branch);\n \n \tif (branch) {\n \t\tif (!for_push) {\n@@ -600,10 +600,10 @@ const char *remote_ref_for_branch(struct branch *branch, int for_push)\n \t\t} else {\n \t\t\tconst char *dst,\n \t\t\t\t*remote_name = remotes_pushremote_for_branch(\n-\t\t\t\t\tthe_repository->remote_state, branch,\n+\t\t\t\t\trepo->remote_state, branch,\n \t\t\t\t\tNULL);\n \t\t\tstruct remote *remote = remotes_remote_get(\n-\t\t\t\tthe_repository->remote_state, remote_name);\n+\t\t\t\trepo->remote_state, remote_name);\n \n \t\t\tif (remote && remote->push.nr &&\n \t\t\t    (dst = apply_refspecs(&remote->push,\n@@ -650,10 +650,10 @@ remotes_remote_get(struct remote_state *remote_state, const char *name)\n \t\t\t\t    remotes_remote_for_branch);\n }\n \n-struct remote *remote_get(const char *name)\n+struct remote *repo_remote_get(struct repository *repo, const char *name)\n {\n-\tread_config(the_repository);\n-\treturn remotes_remote_get(the_repository->remote_state, name);\n+\tread_config(repo);\n+\treturn remotes_remote_get(repo->remote_state, name);\n }\n \n static inline struct remote *\n@@ -663,10 +663,10 @@ remotes_pushremote_get(struct remote_state *remote_state, const char *name)\n \t\t\t\t    remotes_pushremote_for_branch);\n }\n \n-struct remote *pushremote_get(const char *name)\n+struct remote *repo_pushremote_get(struct repository *repo, const char *name)\n {\n-\tread_config(the_repository);\n-\treturn remotes_pushremote_get(the_repository->remote_state, name);\n+\tread_config(repo);\n+\treturn remotes_pushremote_get(repo->remote_state, name);\n }\n \n int remote_is_configured(struct remote *remote, int in_repo)\n@@ -678,14 +678,14 @@ int remote_is_configured(struct remote *remote, int in_repo)\n \treturn !!remote->origin;\n }\n \n-int for_each_remote(each_remote_fn fn, void *priv)\n+int repo_for_each_remote(struct repository *repo, each_remote_fn fn, void *priv)\n {\n \tint i, result = 0;\n-\tread_config(the_repository);\n-\tfor (i = 0; i < the_repository->remote_state->remotes_nr && !result;\n+\tread_config(repo);\n+\tfor (i = 0; i < repo->remote_state->remotes_nr && !result;\n \t     i++) {\n \t\tstruct remote *remote =\n-\t\t\tthe_repository->remote_state->remotes[i];\n+\t\t\trepo->remote_state->remotes[i];\n \t\tif (!remote)\n \t\t\tcontinue;\n \t\tresult = fn(remote, priv);\n@@ -1790,17 +1790,16 @@ static void set_merge(struct remote_state *remote_state, struct branch *ret)\n \t}\n }\n \n-struct branch *branch_get(const char *name)\n+struct branch *repo_branch_get(struct repository *repo, const char *name)\n {\n \tstruct branch *ret;\n \n-\tread_config(the_repository);\n+\tread_config(repo);\n \tif (!name || !*name || !strcmp(name, \"HEAD\"))\n-\t\tret = the_repository->remote_state->current_branch;\n+\t\tret = repo->remote_state->current_branch;\n \telse\n-\t\tret = make_branch(the_repository->remote_state, name,\n-\t\t\t\t  strlen(name));\n-\tset_merge(the_repository->remote_state, ret);\n+\t\tret = make_branch(repo->remote_state, name, strlen(name));\n+\tset_merge(repo->remote_state, ret);\n \treturn ret;\n }\n \n@@ -1934,17 +1933,17 @@ static const char *branch_get_push_1(struct remote_state *remote_state,\n \tBUG(\"unhandled push situation\");\n }\n \n-const char *branch_get_push(struct branch *branch, struct strbuf *err)\n+const char *repo_branch_get_push(struct repository *repo, struct branch *branch, struct strbuf *err)\n {\n-\tread_config(the_repository);\n-\tdie_on_missing_branch(the_repository, branch);\n+\tread_config(repo);\n+\tdie_on_missing_branch(repo, branch);\n \n \tif (!branch)\n \t\treturn error_buf(err, _(\"HEAD does not point to a branch\"));\n \n \tif (!branch->push_tracking_ref)\n \t\tbranch->push_tracking_ref = branch_get_push_1(\n-\t\t\tthe_repository->remote_state, branch, err);\n+\t\t\trepo->remote_state, branch, err);\n \treturn branch->push_tracking_ref;\n }\n \n@@ -2197,15 +2196,16 @@ static int stat_branch_pair(const char *branch_name, const char *base,\n  * upstream defined, or ref does not exist).  Returns 0 if the commits are\n  * identical.  Returns 1 if commits are different.\n  */\n-int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n-\t\t       const char **tracking_name, int for_push,\n-\t\t       enum ahead_behind_flags abf)\n+int repo_stat_tracking_info(struct repository *repo, struct branch *branch,\n+\t\t\t    int *num_ours, int *num_theirs,\n+\t\t\t    const char **tracking_name, int for_push,\n+\t\t\t    enum ahead_behind_flags abf)\n {\n \tconst char *base;\n \n \t/* Cannot stat unless we are marked to build on top of somebody else. */\n-\tbase = for_push ? branch_get_push(branch, NULL) :\n-\t\tbranch_get_upstream(branch, NULL);\n+\tbase = for_push ? repo_branch_get_push(repo, branch, NULL) :\n+\t\t\t\tbranch_get_upstream(branch, NULL);\n \tif (tracking_name)\n \t\t*tracking_name = base;\n \tif (!base)\n@@ -2217,15 +2217,16 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n /*\n  * Return true when there is anything to report, otherwise false.\n  */\n-int format_tracking_info(struct branch *branch, struct strbuf *sb,\n-\t\t\t enum ahead_behind_flags abf)\n+int repo_format_tracking_info(struct repository *repo, struct branch *branch,\n+\t\t\t      struct strbuf *sb, enum ahead_behind_flags abf)\n {\n \tint ours, theirs, sti;\n \tconst char *full_base;\n \tchar *base;\n \tint upstream_is_gone = 0;\n \n-\tsti = stat_tracking_info(branch, &ours, &theirs, &full_base, 0, abf);\n+\tsti = repo_stat_tracking_info(repo, branch, &ours, &theirs, &full_base,\n+\t\t\t\t      0, abf);\n \tif (sti < 0) {\n \t\tif (!full_base)\n \t\t\treturn 0;\ndiff --git a/remote.h b/remote.h\nindex b205830ac3..9b06afd285 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -120,15 +120,28 @@ struct remote {\n  * remote_get(NULL) will return the default remote, given the current branch\n  * and configuration.\n  */\n-struct remote *remote_get(const char *name);\n-\n-struct remote *pushremote_get(const char *name);\n+struct remote *repo_remote_get(struct repository *repo, const char *name);\n+static inline struct remote *remote_get(const char *name)\n+{\n+\treturn repo_remote_get(the_repository, name);\n+}\n+\n+struct remote *repo_pushremote_get(struct repository *repo, const char *name);\n+static inline struct remote *pushremote_get(const char *name)\n+{\n+\treturn repo_pushremote_get(the_repository, name);\n+}\n int remote_is_configured(struct remote *remote, int in_repo);\n \n typedef int each_remote_fn(struct remote *remote, void *priv);\n \n /* iterate through struct remotes */\n-int for_each_remote(each_remote_fn fn, void *priv);\n+int repo_for_each_remote(struct repository *repo, each_remote_fn fn,\n+\t\t\t void *priv);\n+static inline int for_each_remote(each_remote_fn fn, void *priv)\n+{\n+\treturn repo_for_each_remote(the_repository, fn, priv);\n+}\n \n int remote_has_url(struct remote *remote, const char *url);\n \n@@ -324,10 +337,29 @@ struct branch {\n \tconst char *push_tracking_ref;\n };\n \n-struct branch *branch_get(const char *name);\n-const char *remote_for_branch(struct branch *branch, int *explicit);\n-const char *pushremote_for_branch(struct branch *branch, int *explicit);\n-const char *remote_ref_for_branch(struct branch *branch, int for_push);\n+struct branch *repo_branch_get(struct repository *repo, const char *name);\n+static inline struct branch *branch_get(const char *name)\n+{\n+\treturn repo_branch_get(the_repository, name);\n+}\n+\n+const char *repo_remote_for_branch(struct repository *repo, struct branch *branch, int *explicit);\n+static inline const char *remote_for_branch(struct branch *branch, int *explicit)\n+{\n+\treturn repo_remote_for_branch(the_repository, branch, explicit);\n+}\n+\n+const char *repo_pushremote_for_branch(struct repository *repo, struct branch *branch, int *explicit);\n+static inline const char *pushremote_for_branch(struct branch *branch, int *explicit)\n+{\n+\treturn repo_pushremote_for_branch(the_repository, branch, explicit);\n+}\n+\n+const char *repo_remote_ref_for_branch(struct repository *repo, struct branch *branch, int for_push);\n+static inline const char *remote_ref_for_branch(struct branch *branch, int for_push)\n+{\n+\treturn repo_remote_ref_for_branch(the_repository, branch, for_push);\n+}\n \n /* returns true if the given branch has merge configuration given. */\n int branch_has_merge_config(struct branch *branch);\n@@ -351,7 +383,11 @@ const char *branch_get_upstream(struct branch *branch, struct strbuf *err);\n  *\n  * The return value and `err` conventions match those of `branch_get_upstream`.\n  */\n-const char *branch_get_push(struct branch *branch, struct strbuf *err);\n+const char *repo_branch_get_push(struct repository *repo, struct branch *branch, struct strbuf *err);\n+static inline const char *branch_get_push(struct branch *branch, struct strbuf *err)\n+{\n+\treturn repo_branch_get_push(the_repository, branch, err);\n+}\n \n /* Flags to match_refs. */\n enum match_refs_flags {\n@@ -370,11 +406,27 @@ enum ahead_behind_flags {\n };\n \n /* Reporting of tracking info */\n-int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,\n-\t\t       const char **upstream_name, int for_push,\n-\t\t       enum ahead_behind_flags abf);\n-int format_tracking_info(struct branch *branch, struct strbuf *sb,\n-\t\t\t enum ahead_behind_flags abf);\n+int repo_stat_tracking_info(struct repository *repo, struct branch *branch,\n+\t\t\t    int *num_ours, int *num_theirs,\n+\t\t\t    const char **upstream_name, int for_push,\n+\t\t\t    enum ahead_behind_flags abf);\n+static inline int stat_tracking_info(struct branch *branch, int *num_ours,\n+\t\t\t\t     int *num_theirs,\n+\t\t\t\t     const char **upstream_name, int for_push,\n+\t\t\t\t     enum ahead_behind_flags abf)\n+{\n+\treturn repo_stat_tracking_info(the_repository, branch, num_ours,\n+\t\t\t\t       num_theirs, upstream_name, for_push,\n+\t\t\t\t       abf);\n+}\n+\n+int repo_format_tracking_info(struct repository *repo, struct branch *branch,\n+\t\t\t      struct strbuf *sb, enum ahead_behind_flags abf);\n+static inline int format_tracking_info(struct branch *branch, struct strbuf *sb,\n+\t\t\t\t       enum ahead_behind_flags abf)\n+{\n+\treturn repo_format_tracking_info(the_repository, branch, sb, abf);\n+}\n \n struct ref *get_local_heads(void);\n /*\n-- \n2.33.GIT\n\n"},{"id":"439933","messageId":"xmqq1r44rjiu.fsf@gitster.g","threadId":"56659","inReplyTo":"20211028183101.41013-2-chooglen@google.com","subject":"Re: [PATCH v4 1/6] t5516: add test case for pushing remote refspecs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-10-28T20:17:45Z","receivedAt":"2021-10-28T20:17:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Glen Choo <chooglen@google.com> writes:\n\n> In detached HEAD, \"git push remote-name\" should push the refspecs in\n> remote.remote-name.push. Since there is no test case that checks this\n> behavior, add one.\n\nMakes sense.\n\n>\n> Signed-off-by: Glen Choo <chooglen@google.com>\n> ---\n>  t/t5516-fetch-push.sh | 9 +++++++++\n>  1 file changed, 9 insertions(+)\n>\n> diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\n> index 8212ca56dc..d4c2f1b68f 100755\n> --- a/t/t5516-fetch-push.sh\n> +++ b/t/t5516-fetch-push.sh\n> @@ -541,6 +541,15 @@ do\n>  \n>  done\n>  \n> +test_expect_success \"push to remote with detached HEAD and config remote.*.push = src:dest\" '\n> +\tmk_test testrepo heads/main &&\n> +\tgit checkout $the_first_commit &&\n> +\ttest_config remote.there.url testrepo &&\n> +\ttest_config remote.there.push refs/heads/main:refs/heads/main &&\n> +\tgit push there &&\n> +\tcheck_push_result testrepo $the_commit heads/main\n> +'\n> +\n>  test_expect_success 'push with remote.pushdefault' '\n>  \tmk_test up_repo heads/main &&\n>  \tmk_test down_repo heads/main &&\n"},{"id":"440950","messageId":"kl6ly25utf6j.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"56659","inReplyTo":"20211028183101.41013-1-chooglen@google.com","subject":"Re: [PATCH v4 0/6] remote: replace static variables with struct remote_state","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-11-12T00:01:40Z","receivedAt":"2021-11-12T00:01:44Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"\nHi everyone! I'd appreciate thoughts on the design presented here. I\nthink Junio and I have explored a good part of the short-term design\nspace, but it would be great for others to chime in - as Junio mentioned\nin [1], this series isn't exactly sufficiently reviewed yet.\n\nFor whoever is taking a look, here are some pieces that may be worth\npaying special attention to:\n\n* [2] Junio expresses concern that struct branch depends on struct\n  repository and accepting both of them as parameters might be unsafe. A\n  back pointer from branch->repository might solve this.\n  \n* [3] Glen and Junio discover that branch_get() can accept NULL and return\n  NULL. accepting NULL means \"give me the current branch\", returning\n  NULL means \"detached HEAD\".\n  \n* [4] Glen questions the design of the current API and wonders what an\n  optimal API might look like. Junio and Glen agree that we can remove\n  global variables now and rethink the API later.\n  \n\n[1] https://lore.kernel.org/git/xmqqlf1ujo4g.fsf@gitster.g/\n[2] https://lore.kernel.org/git/xmqqr1cnbgmw.fsf@gitster.g/\n[3] https://lore.kernel.org/git/xmqqzgqwzvwx.fsf@gitster.g/\n[4] https://lore.kernel.org/git/kl6l35om9wn9.fsf@chooglen-macbookpro.roam.corp.google.com/\n"},{"id":"441153","messageId":"20211115184252.1077901-1-jonathantanmy@google.com","threadId":"56659","inReplyTo":"20211028183101.41013-2-chooglen@google.com","subject":"Re: [PATCH v4 1/6] t5516: add test case for pushing remote refspecs","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2021-11-15T18:42:52Z","receivedAt":"2021-11-15T20:32:19Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"> In detached HEAD, \"git push remote-name\" should push the refspecs in\n> remote.remote-name.push. Since there is no test case that checks this\n> behavior, add one.\n\nThis is technically true, but reading the documentation, it seems that\nthis should also happen when we're not in detached HEAD. Maybe write:\n\n  \"git push remote-name\" (that is, with no refspec given on the command\n  line) should push the refspecs in remote.remote-name.push. There is no\n  test case that checks this behavior in detached HEAD, so add one.\n\nThe test itself looks OK.\n"},{"id":"441156","messageId":"20211115184821.1079331-1-jonathantanmy@google.com","threadId":"56659","inReplyTo":"20211028183101.41013-5-chooglen@google.com","subject":"Re: [PATCH v4 4/6] remote: remove the_repository->remote_state from static methods","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2021-11-15T18:48:21Z","receivedAt":"2021-11-15T20:32:25Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"> Replace all remaining references of the_repository->remote_state in\n> static functions with a struct remote_state parameter.\n> \n> To do so, move read_config() calls to non-static functions and create a\n> family of static functions, \"remotes_*\", that behave like \"repo_*\", but\n> accept struct remote_state instead of struct repository. In the case\n> where a static function calls a non-static function, replace the\n> non-static function with its \"remotes_*\" equivalent.\n\nIn patches 2-4, the same lines have been changed a few times, so much so\nthat I found it easier to review if I squashed 2-4 together. I'm not\nsure what the opinions of other reviewers are, but I think it's better\nif patches 2-4 were combined into one patch.\n\nThe only confusing part is the moving of read_config(), but that is\ndescribed in this commit message - it's moved to the outer (non-static)\nfunctions so that the inner functions do not need to have a pointer to\nthe repo, only to remote_state.\n\nSo, patches 2-4 look good. I think they should be combined, but I'm OK\nif they're left separate too.\n"},{"id":"441159","messageId":"kl6llf1ptc4j.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"56659","inReplyTo":"20211115184252.1077901-1-jonathantanmy@google.com","subject":"Re: [PATCH v4 1/6] t5516: add test case for pushing remote refspecs","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-11-15T20:09:00Z","receivedAt":"2021-11-15T20:34:35Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Jonathan Tan <jonathantanmy@google.com> writes:\n\n> This is technically true, but reading the documentation, it seems that\n> this should also happen when we're not in detached HEAD. Maybe write:\n>\n>   \"git push remote-name\" (that is, with no refspec given on the command\n>   line) should push the refspecs in remote.remote-name.push. There is no\n>   test case that checks this behavior in detached HEAD, so add one.\n\nAh, you are right. I was under the wrong impression that refspec\ndefaults to HEAD if HEAD points to a branch.\n\nI will adopt your wording, thanks.\n"},{"id":"441171","messageId":"kl6lilwtt7or.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"56659","inReplyTo":"20211115185545.1082815-1-jonathantanmy@google.com","subject":"Re: [PATCH v4 6/6] remote: add struct repository parameter to external functions","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-11-15T21:44:52Z","receivedAt":"2021-11-15T22:58:40Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Jonathan Tan <jonathantanmy@google.com> writes:\n\n> I don't feel as confident about this patch as I do about the patches\n> that remove the global variables - as you said, some functions might use\n> the_repository to parse OIDs (or do other things). I think that part of\n> this patch should go into the next series you have planned (as you\n> describe in the original cover letter [1], when you make future\n> submodule commands be run in-process), so that they can be tested.\n> (Removing the global variables was more of a mechanical code change, so\n> it was easier to see what's going on by just looking at \"before\" and\n> \"after\".)\n\nThanks! I think that thematically, this patch belongs in this series.\nBut that is not worth leaving the code untested, so I will move this\npatch into my future series.\n"},{"id":"441208","messageId":"kl6lo86ltc9a.fsf@chooglen-macbookpro.roam.corp.google.com","threadId":"56659","inReplyTo":"20211115185029.1081720-1-jonathantanmy@google.com","subject":"Re: [PATCH v4 5/6] remote: die if branch is not found in repository","fromName":"Glen Choo","fromEmail":"chooglen@google.com","sentAt":"2021-11-15T20:06:09Z","receivedAt":"2021-11-16T00:23:22Z","isPatch":true,"sender":{"key":"glencbz@gmail.com","avatar":"https://avatars.githubusercontent.com/u/58092771?v=4"},"body":"Jonathan Tan <jonathantanmy@google.com> writes:\n\n>> To prevent misuse, add a die_on_missing_branch() helper function that\n>> dies if a given branch is not from a given repository. Speed up the\n>> existence check by using a new branches_hash hashmap to remote_state,\n>> and use the hashmap to remove the branch array iteration in\n>> make_branch().\n> This makes sense, but how often would this be called?\n\nThis affects most of remote.h branches API (branch_get(),\nbranch_get_upstream(), branch_get_push()). From what I can tell, I don't\nthink these are called very frequently.\n\n> Couldn't we just iterate over the array (instead of making a hashmap)?\n> If speed is important, I think we could just sort the array and do a\n> binary search.\n\nThe primary reason I used a hashmap is to be consistent with struct\nremote (which also uses a hashmap). One possible argument in your favor\nis that remotes are often looked up by name often (and justify the\nhashmap), whereas branches are not looked up by name as often (and don't\njustify a hashmap).\n\nI say _justify_, but I don't see significant drawbacks to using a\nhashmap here. I suspect that there is an advantage to binary search that\nyou haven't made explicit yet? Could you share your thought process to\nhelp inform the decision?\n"},{"id":"441213","messageId":"20211115185545.1082815-1-jonathantanmy@google.com","threadId":"56659","inReplyTo":"20211028183101.41013-7-chooglen@google.com","subject":"Re: [PATCH v4 6/6] remote: add struct repository parameter to external functions","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2021-11-15T18:55:45Z","receivedAt":"2021-11-16T00:29:08Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"> Finish plumbing remote_state by adding a struct repository\n> parameter to repo_* functions. This removes all references to\n> the_repository->remote_state in remote.c - explicit references in the\n> function bodies and implicit references via calls to non-repo_*\n> functions. However, certain functions still use the_repository to parse\n> oids.\n> \n> Where a non-static function has the option to call either the static\n> \"remotes_*\" variant or the non-static \"repo_*\" variant, use \"remotes_*\"\n> in order to be consistent with the static functions. A desirable\n> side-effect of this is that it minimizes the number of calls to\n> read_config().\n\nI don't feel as confident about this patch as I do about the patches\nthat remove the global variables - as you said, some functions might use\nthe_repository to parse OIDs (or do other things). I think that part of\nthis patch should go into the next series you have planned (as you\ndescribe in the original cover letter [1], when you make future\nsubmodule commands be run in-process), so that they can be tested.\n(Removing the global variables was more of a mechanical code change, so\nit was easier to see what's going on by just looking at \"before\" and\n\"after\".)\n\nOverall this patch series looks good with only minor comments from me,\nexcept that I think that this patch (patch 6) should be dropped.\n\n[1] https://lore.kernel.org/git/pull.1103.git.git.1633633635.gitgitgadget@gmail.com/\n"},{"id":"441215","messageId":"20211115185029.1081720-1-jonathantanmy@google.com","threadId":"56659","inReplyTo":"20211028183101.41013-6-chooglen@google.com","subject":"Re: [PATCH v4 5/6] remote: die if branch is not found in repository","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2021-11-15T18:50:29Z","receivedAt":"2021-11-16T00:29:56Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"> In a subsequent commit, we would like external-facing functions to be\n> able to accept \"struct repository\" and \"struct branch\" as a pair. This\n> is useful for functions like pushremote_for_branch(), which need to take\n> values from the remote_state and branch, even if branch == NULL.\n> However, a caller may supply an unrelated repository and branch, which\n> is not supported behavior.\n> \n> To prevent misuse, add a die_on_missing_branch() helper function that\n> dies if a given branch is not from a given repository. Speed up the\n> existence check by using a new branches_hash hashmap to remote_state,\n> and use the hashmap to remove the branch array iteration in\n> make_branch().\n> \n> Like read_config(), die_on_missing_branch() is only called from\n> non-static functions; static functions are less prone to misuse because\n> they have strong conventions for keeping remote_state and branch in\n> sync.\n\nThis makes sense, but how often would this be called? Couldn't we just\niterate over the array (instead of making a hashmap)? If speed is\nimportant, I think we could just sort the array and do a binary search.\n"},{"id":"441337","messageId":"20211116174553.2300029-1-jonathantanmy@google.com","threadId":"56659","inReplyTo":"kl6lo86ltc9a.fsf@chooglen-macbookpro.roam.corp.google.com","subject":"Re: [PATCH v4 5/6] remote: die if branch is not found in repository","fromName":"Jonathan Tan","fromEmail":"jonathantanmy@google.com","sentAt":"2021-11-16T17:45:53Z","receivedAt":"2021-11-16T17:45:58Z","isPatch":true,"sender":{"key":"jonathantanmy@fastmail.com","avatar":null},"body":"Glen Choo <chooglen@google.com> writes:\n> > Couldn't we just iterate over the array (instead of making a hashmap)?\n> > If speed is important, I think we could just sort the array and do a\n> > binary search.\n> \n> The primary reason I used a hashmap is to be consistent with struct\n> remote (which also uses a hashmap). One possible argument in your favor\n> is that remotes are often looked up by name often (and justify the\n> hashmap), whereas branches are not looked up by name as often (and don't\n> justify a hashmap).\n> \n> I say _justify_, but I don't see significant drawbacks to using a\n> hashmap here. I suspect that there is an advantage to binary search that\n> you haven't made explicit yet? Could you share your thought process to\n> help inform the decision?\n\nThe main drawback is that branches are now stored in 2 ways - as the\n\"branches\" array in struct remote_state and as this new \"branches_hash\".\nI think we should avoid storing the same data twice unless we really\nneed to, and I don't think there is a need here.\n\nAs for hashmap vs array (say, if we were thinking of removing the array\nand putting in a hashmap instead), I would still prefer the array\n(sorted if needed) just for the simplicity, but I wouldn't feel as\nstrongly about this since there is no duplication here.\n"}]}