{"thread":{"id":"48495","subject":"[PATCH 00/35] refactoring refspecs","startedAt":"2018-05-14T21:56:40Z","lastAt":"2018-06-05T20:20:15Z","messageCount":112,"participants":["Brandon Williams","Stefan Beller","Junio C Hamano","Ævar Arnfjörð Bjarmason","Jonathan Nieder","Martin Ågren"],"isPatch":true,"patchVersion":1,"patchTotal":35},"messages":[{"id":"347627","messageId":"20180514215626.164960-1-bmwill@google.com","threadId":"48495","inReplyTo":null,"subject":"[PATCH 00/35] refactoring refspecs","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:55:51Z","receivedAt":"2018-05-14T21:56:40Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"When working on protocol v2 I noticed that working with refspecs was a\nlittle difficult because of the various api's that existed.  Some\nfunctions expected an array of \"const char *\" while others expected an\narray of \"struct refspec\".  In all cases a length parameter needed to be\npassed as a parameter as well.  This makes working with refspecs a\nlittle challenging because of the different expectations different parts\nof the code base have.\n\nThis series refactors how refspecs are handled through out the code base\nby renaming the existing struct refspec to refspec_item and introducing\na new 'struct refspec' which is a container of refspec_items, much like\nhow a pathspec contains pathspec_items.  This simplifies many callers\nand makes handling pathspecs a bit easier.\n\nI have some follow on work which I'll build on top of this series, but\nsince this was already getting a bit lengthy at 35 patches I'll save\nthat for another time.\n\nBrandon Williams (35):\n  refspec: move refspec parsing logic into its own file\n  refspec: factor out parsing a single refspec\n  refspec: rename struct refspec to struct refspec_item\n  refspec: introduce struct refspec\n  refspec: convert valid_fetch_refspec to use parse_refspec\n  submodule--helper: convert push_check to use struct refspec\n  pull: convert get_tracking_branch to use refspec_item_init\n  transport: convert transport_push to use struct refspec\n  remote: convert check_push_refs to use struct refspec\n  remote: convert match_push_refs to use struct refspec\n  clone: convert cmd_clone to use refspec_item_init\n  fast-export: convert to use struct refspec\n  remote: convert push refspecs to struct refspec\n  remote: convert fetch refspecs to struct refspec\n  transport-helper: convert to use struct refspec\n  fetch: convert fetch_one to use struct refspec\n  fetch: convert refmap to use struct refspec\n  refspec: remove the deprecated functions\n  fetch: convert do_fetch to take a struct refspec\n  fetch: convert get_ref_map to take a struct refspec\n  fetch: convert prune_refs to take a struct refspec\n  remote: convert get_stale_heads to take a struct refspec\n  remote: convert apply_refspecs to take a struct refspec\n  remote: convert query_refspecs to take a struct refspec\n  remote: convert get_ref_match to take a struct refspec\n  remote: convert match_explicit_refs to take a struct refspec\n  push: check for errors earlier\n  push: convert to use struct refspec\n  transport: convert transport_push to take a struct refspec\n  send-pack: store refspecs in a struct refspec\n  transport: remove transport_verify_remote_names\n  http-push: store refspecs in a struct refspec\n  remote: convert match_push_refs to take a struct refspec\n  remote: convert check_push_refs to take a struct refspec\n  submodule: convert push_unpushed_submodules to take a struct refspec\n\n Makefile                    |   1 +\n branch.c                    |   7 +-\n builtin/clone.c             |  13 +-\n builtin/fast-export.c       |  22 +--\n builtin/fetch.c             | 128 +++++++-------\n builtin/merge.c             |   1 +\n builtin/pull.c              |   9 +-\n builtin/push.c              |  80 ++++-----\n builtin/remote.c            |  37 ++--\n builtin/send-pack.c         |  24 +--\n builtin/submodule--helper.c |  14 +-\n checkout.c                  |   5 +-\n http-push.c                 |  18 +-\n refspec.c                   | 194 +++++++++++++++++++++\n refspec.h                   |  44 +++++\n remote.c                    | 324 ++++++++----------------------------\n remote.h                    |  48 ++----\n submodule.c                 |  19 +--\n submodule.h                 |   3 +-\n transport-helper.c          |  39 ++---\n transport.c                 |  51 ++----\n transport.h                 |   4 +-\n 22 files changed, 514 insertions(+), 571 deletions(-)\n create mode 100644 refspec.c\n create mode 100644 refspec.h\n\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347628","messageId":"20180514215626.164960-2-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 01/35] refspec: move refspec parsing logic into its own file","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:55:52Z","receivedAt":"2018-05-14T21:56:44Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"In preperation for performing a refactor on refspec related code, move\nthe refspec parsing logic into its own file.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n Makefile                    |   1 +\n branch.c                    |   1 +\n builtin/clone.c             |   1 +\n builtin/fast-export.c       |   1 +\n builtin/fetch.c             |   1 +\n builtin/merge.c             |   1 +\n builtin/pull.c              |   1 +\n builtin/push.c              |   1 +\n builtin/remote.c            |   1 +\n builtin/submodule--helper.c |   1 +\n checkout.c                  |   1 +\n refspec.c                   | 168 ++++++++++++++++++++++++++++++++++++\n refspec.h                   |  23 +++++\n remote.c                    | 165 +----------------------------------\n remote.h                    |  20 -----\n transport-helper.c          |   1 +\n transport.c                 |   1 +\n 17 files changed, 205 insertions(+), 184 deletions(-)\n create mode 100644 refspec.c\n create mode 100644 refspec.h\n\ndiff --git a/Makefile b/Makefile\nindex ad880d1fc..4bca65383 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -928,6 +928,7 @@ LIB_OBJS += refs/files-backend.o\n LIB_OBJS += refs/iterator.o\n LIB_OBJS += refs/packed-backend.o\n LIB_OBJS += refs/ref-cache.o\n+LIB_OBJS += refspec.o\n LIB_OBJS += ref-filter.o\n LIB_OBJS += remote.o\n LIB_OBJS += replace-object.o\ndiff --git a/branch.c b/branch.c\nindex 2672054f0..32ccefc6b 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -3,6 +3,7 @@\n #include \"config.h\"\n #include \"branch.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"remote.h\"\n #include \"commit.h\"\n #include \"worktree.h\"\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 84f1473d1..6d1614ed3 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -14,6 +14,7 @@\n #include \"parse-options.h\"\n #include \"fetch-pack.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"unpack-trees.h\"\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 530df12f0..a13b7c8ef 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -7,6 +7,7 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"commit.h\"\n #include \"object.h\"\n #include \"tag.h\"\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 7ee83ac0f..1fce68e9a 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -5,6 +5,7 @@\n #include \"config.h\"\n #include \"repository.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"commit.h\"\n #include \"builtin.h\"\n #include \"string-list.h\"\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 9db5a2cf1..c362cfe90 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -14,6 +14,7 @@\n #include \"run-command.h\"\n #include \"diff.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"commit.h\"\n #include \"diffcore.h\"\n #include \"revision.h\"\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 71aac5005..6247c956d 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -15,6 +15,7 @@\n #include \"remote.h\"\n #include \"dir.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"revision.h\"\n #include \"submodule.h\"\n #include \"submodule-config.h\"\ndiff --git a/builtin/push.c b/builtin/push.c\nindex ac3705370..fa65999b2 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -4,6 +4,7 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"run-command.h\"\n #include \"builtin.h\"\n #include \"remote.h\"\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 8708e584e..c49513995 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -7,6 +7,7 @@\n #include \"strbuf.h\"\n #include \"run-command.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"argv-array.h\"\n \n static const char * const builtin_remote_usage[] = {\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex c2403a915..6ab032acb 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -12,6 +12,7 @@\n #include \"run-command.h\"\n #include \"remote.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"connect.h\"\n #include \"revision.h\"\n #include \"diffcore.h\"\ndiff --git a/checkout.c b/checkout.c\nindex ac42630f7..193ba8567 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -1,5 +1,6 @@\n #include \"cache.h\"\n #include \"remote.h\"\n+#include \"refspec.h\"\n #include \"checkout.h\"\n \n struct tracking_name_data {\ndiff --git a/refspec.c b/refspec.c\nnew file mode 100644\nindex 000000000..ecb0bdff3\n--- /dev/null\n+++ b/refspec.c\n@@ -0,0 +1,168 @@\n+#include \"cache.h\"\n+#include \"refs.h\"\n+#include \"refspec.h\"\n+\n+static struct refspec s_tag_refspec = {\n+\t0,\n+\t1,\n+\t0,\n+\t0,\n+\t\"refs/tags/*\",\n+\t\"refs/tags/*\"\n+};\n+\n+/* See TAG_REFSPEC for the string version */\n+const struct refspec *tag_refspec = &s_tag_refspec;\n+\n+static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n+{\n+\tint i;\n+\tstruct refspec *rs = xcalloc(nr_refspec, sizeof(*rs));\n+\n+\tfor (i = 0; i < nr_refspec; i++) {\n+\t\tsize_t llen;\n+\t\tint is_glob;\n+\t\tconst char *lhs, *rhs;\n+\t\tint flags;\n+\n+\t\tis_glob = 0;\n+\n+\t\tlhs = refspec[i];\n+\t\tif (*lhs == '+') {\n+\t\t\trs[i].force = 1;\n+\t\t\tlhs++;\n+\t\t}\n+\n+\t\trhs = strrchr(lhs, ':');\n+\n+\t\t/*\n+\t\t * Before going on, special case \":\" (or \"+:\") as a refspec\n+\t\t * for pushing matching refs.\n+\t\t */\n+\t\tif (!fetch && rhs == lhs && rhs[1] == '\\0') {\n+\t\t\trs[i].matching = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (rhs) {\n+\t\t\tsize_t rlen = strlen(++rhs);\n+\t\t\tis_glob = (1 <= rlen && strchr(rhs, '*'));\n+\t\t\trs[i].dst = xstrndup(rhs, rlen);\n+\t\t}\n+\n+\t\tllen = (rhs ? (rhs - lhs - 1) : strlen(lhs));\n+\t\tif (1 <= llen && memchr(lhs, '*', llen)) {\n+\t\t\tif ((rhs && !is_glob) || (!rhs && fetch))\n+\t\t\t\tgoto invalid;\n+\t\t\tis_glob = 1;\n+\t\t} else if (rhs && is_glob) {\n+\t\t\tgoto invalid;\n+\t\t}\n+\n+\t\trs[i].pattern = is_glob;\n+\t\trs[i].src = xstrndup(lhs, llen);\n+\t\tflags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);\n+\n+\t\tif (fetch) {\n+\t\t\tstruct object_id unused;\n+\n+\t\t\t/* LHS */\n+\t\t\tif (!*rs[i].src)\n+\t\t\t\t; /* empty is ok; it means \"HEAD\" */\n+\t\t\telse if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused))\n+\t\t\t\trs[i].exact_sha1 = 1; /* ok */\n+\t\t\telse if (!check_refname_format(rs[i].src, flags))\n+\t\t\t\t; /* valid looking ref is ok */\n+\t\t\telse\n+\t\t\t\tgoto invalid;\n+\t\t\t/* RHS */\n+\t\t\tif (!rs[i].dst)\n+\t\t\t\t; /* missing is ok; it is the same as empty */\n+\t\t\telse if (!*rs[i].dst)\n+\t\t\t\t; /* empty is ok; it means \"do not store\" */\n+\t\t\telse if (!check_refname_format(rs[i].dst, flags))\n+\t\t\t\t; /* valid looking ref is ok */\n+\t\t\telse\n+\t\t\t\tgoto invalid;\n+\t\t} else {\n+\t\t\t/*\n+\t\t\t * LHS\n+\t\t\t * - empty is allowed; it means delete.\n+\t\t\t * - when wildcarded, it must be a valid looking ref.\n+\t\t\t * - otherwise, it must be an extended SHA-1, but\n+\t\t\t *   there is no existing way to validate this.\n+\t\t\t */\n+\t\t\tif (!*rs[i].src)\n+\t\t\t\t; /* empty is ok */\n+\t\t\telse if (is_glob) {\n+\t\t\t\tif (check_refname_format(rs[i].src, flags))\n+\t\t\t\t\tgoto invalid;\n+\t\t\t}\n+\t\t\telse\n+\t\t\t\t; /* anything goes, for now */\n+\t\t\t/*\n+\t\t\t * RHS\n+\t\t\t * - missing is allowed, but LHS then must be a\n+\t\t\t *   valid looking ref.\n+\t\t\t * - empty is not allowed.\n+\t\t\t * - otherwise it must be a valid looking ref.\n+\t\t\t */\n+\t\t\tif (!rs[i].dst) {\n+\t\t\t\tif (check_refname_format(rs[i].src, flags))\n+\t\t\t\t\tgoto invalid;\n+\t\t\t} else if (!*rs[i].dst) {\n+\t\t\t\tgoto invalid;\n+\t\t\t} else {\n+\t\t\t\tif (check_refname_format(rs[i].dst, flags))\n+\t\t\t\t\tgoto invalid;\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn rs;\n+\n+ invalid:\n+\tif (verify) {\n+\t\t/*\n+\t\t * nr_refspec must be greater than zero and i must be valid\n+\t\t * since it is only possible to reach this point from within\n+\t\t * the for loop above.\n+\t\t */\n+\t\tfree_refspec(i+1, rs);\n+\t\treturn NULL;\n+\t}\n+\tdie(\"Invalid refspec '%s'\", refspec[i]);\n+}\n+\n+int valid_fetch_refspec(const char *fetch_refspec_str)\n+{\n+\tstruct refspec *refspec;\n+\n+\trefspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);\n+\tfree_refspec(1, refspec);\n+\treturn !!refspec;\n+}\n+\n+struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)\n+{\n+\treturn parse_refspec_internal(nr_refspec, refspec, 1, 0);\n+}\n+\n+struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)\n+{\n+\treturn parse_refspec_internal(nr_refspec, refspec, 0, 0);\n+}\n+\n+void free_refspec(int nr_refspec, struct refspec *refspec)\n+{\n+\tint i;\n+\n+\tif (!refspec)\n+\t\treturn;\n+\n+\tfor (i = 0; i < nr_refspec; i++) {\n+\t\tfree(refspec[i].src);\n+\t\tfree(refspec[i].dst);\n+\t}\n+\tfree(refspec);\n+}\n+\ndiff --git a/refspec.h b/refspec.h\nnew file mode 100644\nindex 000000000..b1db91918\n--- /dev/null\n+++ b/refspec.h\n@@ -0,0 +1,23 @@\n+#ifndef REFSPEC_H\n+#define REFSPEC_H\n+\n+#define TAG_REFSPEC \"refs/tags/*:refs/tags/*\"\n+const struct refspec *tag_refspec;\n+\n+struct refspec {\n+\tunsigned force : 1;\n+\tunsigned pattern : 1;\n+\tunsigned matching : 1;\n+\tunsigned exact_sha1 : 1;\n+\n+\tchar *src;\n+\tchar *dst;\n+};\n+\n+int valid_fetch_refspec(const char *refspec);\n+struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);\n+struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);\n+\n+void free_refspec(int nr_refspec, struct refspec *refspec);\n+\n+#endif /* REFSPEC_H */\ndiff --git a/remote.c b/remote.c\nindex 91eb010ca..4d67c061a 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -2,6 +2,7 @@\n #include \"config.h\"\n #include \"remote.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"commit.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n@@ -13,18 +14,6 @@\n \n enum map_direction { FROM_SRC, FROM_DST };\n \n-static struct refspec s_tag_refspec = {\n-\t0,\n-\t1,\n-\t0,\n-\t0,\n-\t\"refs/tags/*\",\n-\t\"refs/tags/*\"\n-};\n-\n-/* See TAG_REFSPEC for the string version */\n-const struct refspec *tag_refspec = &s_tag_refspec;\n-\n struct counted_string {\n \tsize_t len;\n \tconst char *s;\n@@ -499,158 +488,6 @@ static void read_config(void)\n \talias_all_urls();\n }\n \n-static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n-{\n-\tint i;\n-\tstruct refspec *rs = xcalloc(nr_refspec, sizeof(*rs));\n-\n-\tfor (i = 0; i < nr_refspec; i++) {\n-\t\tsize_t llen;\n-\t\tint is_glob;\n-\t\tconst char *lhs, *rhs;\n-\t\tint flags;\n-\n-\t\tis_glob = 0;\n-\n-\t\tlhs = refspec[i];\n-\t\tif (*lhs == '+') {\n-\t\t\trs[i].force = 1;\n-\t\t\tlhs++;\n-\t\t}\n-\n-\t\trhs = strrchr(lhs, ':');\n-\n-\t\t/*\n-\t\t * Before going on, special case \":\" (or \"+:\") as a refspec\n-\t\t * for pushing matching refs.\n-\t\t */\n-\t\tif (!fetch && rhs == lhs && rhs[1] == '\\0') {\n-\t\t\trs[i].matching = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\tif (rhs) {\n-\t\t\tsize_t rlen = strlen(++rhs);\n-\t\t\tis_glob = (1 <= rlen && strchr(rhs, '*'));\n-\t\t\trs[i].dst = xstrndup(rhs, rlen);\n-\t\t}\n-\n-\t\tllen = (rhs ? (rhs - lhs - 1) : strlen(lhs));\n-\t\tif (1 <= llen && memchr(lhs, '*', llen)) {\n-\t\t\tif ((rhs && !is_glob) || (!rhs && fetch))\n-\t\t\t\tgoto invalid;\n-\t\t\tis_glob = 1;\n-\t\t} else if (rhs && is_glob) {\n-\t\t\tgoto invalid;\n-\t\t}\n-\n-\t\trs[i].pattern = is_glob;\n-\t\trs[i].src = xstrndup(lhs, llen);\n-\t\tflags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);\n-\n-\t\tif (fetch) {\n-\t\t\tstruct object_id unused;\n-\n-\t\t\t/* LHS */\n-\t\t\tif (!*rs[i].src)\n-\t\t\t\t; /* empty is ok; it means \"HEAD\" */\n-\t\t\telse if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused))\n-\t\t\t\trs[i].exact_sha1 = 1; /* ok */\n-\t\t\telse if (!check_refname_format(rs[i].src, flags))\n-\t\t\t\t; /* valid looking ref is ok */\n-\t\t\telse\n-\t\t\t\tgoto invalid;\n-\t\t\t/* RHS */\n-\t\t\tif (!rs[i].dst)\n-\t\t\t\t; /* missing is ok; it is the same as empty */\n-\t\t\telse if (!*rs[i].dst)\n-\t\t\t\t; /* empty is ok; it means \"do not store\" */\n-\t\t\telse if (!check_refname_format(rs[i].dst, flags))\n-\t\t\t\t; /* valid looking ref is ok */\n-\t\t\telse\n-\t\t\t\tgoto invalid;\n-\t\t} else {\n-\t\t\t/*\n-\t\t\t * LHS\n-\t\t\t * - empty is allowed; it means delete.\n-\t\t\t * - when wildcarded, it must be a valid looking ref.\n-\t\t\t * - otherwise, it must be an extended SHA-1, but\n-\t\t\t *   there is no existing way to validate this.\n-\t\t\t */\n-\t\t\tif (!*rs[i].src)\n-\t\t\t\t; /* empty is ok */\n-\t\t\telse if (is_glob) {\n-\t\t\t\tif (check_refname_format(rs[i].src, flags))\n-\t\t\t\t\tgoto invalid;\n-\t\t\t}\n-\t\t\telse\n-\t\t\t\t; /* anything goes, for now */\n-\t\t\t/*\n-\t\t\t * RHS\n-\t\t\t * - missing is allowed, but LHS then must be a\n-\t\t\t *   valid looking ref.\n-\t\t\t * - empty is not allowed.\n-\t\t\t * - otherwise it must be a valid looking ref.\n-\t\t\t */\n-\t\t\tif (!rs[i].dst) {\n-\t\t\t\tif (check_refname_format(rs[i].src, flags))\n-\t\t\t\t\tgoto invalid;\n-\t\t\t} else if (!*rs[i].dst) {\n-\t\t\t\tgoto invalid;\n-\t\t\t} else {\n-\t\t\t\tif (check_refname_format(rs[i].dst, flags))\n-\t\t\t\t\tgoto invalid;\n-\t\t\t}\n-\t\t}\n-\t}\n-\treturn rs;\n-\n- invalid:\n-\tif (verify) {\n-\t\t/*\n-\t\t * nr_refspec must be greater than zero and i must be valid\n-\t\t * since it is only possible to reach this point from within\n-\t\t * the for loop above.\n-\t\t */\n-\t\tfree_refspec(i+1, rs);\n-\t\treturn NULL;\n-\t}\n-\tdie(\"Invalid refspec '%s'\", refspec[i]);\n-}\n-\n-int valid_fetch_refspec(const char *fetch_refspec_str)\n-{\n-\tstruct refspec *refspec;\n-\n-\trefspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);\n-\tfree_refspec(1, refspec);\n-\treturn !!refspec;\n-}\n-\n-struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)\n-{\n-\treturn parse_refspec_internal(nr_refspec, refspec, 1, 0);\n-}\n-\n-struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)\n-{\n-\treturn parse_refspec_internal(nr_refspec, refspec, 0, 0);\n-}\n-\n-void free_refspec(int nr_refspec, struct refspec *refspec)\n-{\n-\tint i;\n-\n-\tif (!refspec)\n-\t\treturn;\n-\n-\tfor (i = 0; i < nr_refspec; i++) {\n-\t\tfree(refspec[i].src);\n-\t\tfree(refspec[i].dst);\n-\t}\n-\tfree(refspec);\n-}\n-\n static int valid_remote_nick(const char *name)\n {\n \tif (!name[0] || is_dot_or_dotdot(name))\ndiff --git a/remote.h b/remote.h\nindex 2b3180f94..386ced901 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -68,18 +68,6 @@ int for_each_remote(each_remote_fn fn, void *priv);\n \n int remote_has_url(struct remote *remote, const char *url);\n \n-struct refspec {\n-\tunsigned force : 1;\n-\tunsigned pattern : 1;\n-\tunsigned matching : 1;\n-\tunsigned exact_sha1 : 1;\n-\n-\tchar *src;\n-\tchar *dst;\n-};\n-\n-extern const struct refspec *tag_refspec;\n-\n struct ref {\n \tstruct ref *next;\n \tstruct object_id old_oid;\n@@ -175,12 +163,6 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);\n  */\n struct ref *ref_remove_duplicates(struct ref *ref_map);\n \n-int valid_fetch_refspec(const char *refspec);\n-struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);\n-extern struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);\n-\n-void free_refspec(int nr_refspec, struct refspec *refspec);\n-\n extern int query_refspecs(struct refspec *specs, int nr, struct refspec *query);\n char *apply_refspecs(struct refspec *refspecs, int nr_refspec,\n \t\t     const char *name);\n@@ -313,8 +295,6 @@ extern int parseopt_push_cas_option(const struct option *, const char *arg, int\n extern int is_empty_cas(const struct push_cas_option *);\n void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);\n \n-#define TAG_REFSPEC \"refs/tags/*:refs/tags/*\"\n-\n void add_prune_tags_to_fetch_refspec(struct remote *remote);\n \n #endif\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 11f1055b4..b99e1cce9 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -11,6 +11,7 @@\n #include \"sigchain.h\"\n #include \"argv-array.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"transport-internal.h\"\n #include \"protocol.h\"\n \ndiff --git a/transport.c b/transport.c\nindex 37410d8aa..2cf63d18b 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -11,6 +11,7 @@\n #include \"bundle.h\"\n #include \"dir.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"branch.h\"\n #include \"url.h\"\n #include \"submodule.h\"\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347629","messageId":"20180514215626.164960-3-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 02/35] refspec: factor out parsing a single refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:55:53Z","receivedAt":"2018-05-14T21:56:45Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Factor out the logic which parses a single refspec into its own\nfunction.  This makes it easier to reuse this logic in a future patch.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n refspec.c | 195 +++++++++++++++++++++++++++++-------------------------\n 1 file changed, 104 insertions(+), 91 deletions(-)\n\ndiff --git a/refspec.c b/refspec.c\nindex ecb0bdff3..3cfcbd37d 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -14,110 +14,123 @@ static struct refspec s_tag_refspec = {\n /* See TAG_REFSPEC for the string version */\n const struct refspec *tag_refspec = &s_tag_refspec;\n \n-static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n+/*\n+ * Parses 'refspec' and populates 'rs'.  returns 1 if successful and 0 if the\n+ * refspec is invalid.\n+ */\n+static int parse_refspec(struct refspec *rs, const char *refspec, int fetch)\n {\n-\tint i;\n-\tstruct refspec *rs = xcalloc(nr_refspec, sizeof(*rs));\n+\tsize_t llen;\n+\tint is_glob;\n+\tconst char *lhs, *rhs;\n+\tint flags;\n \n-\tfor (i = 0; i < nr_refspec; i++) {\n-\t\tsize_t llen;\n-\t\tint is_glob;\n-\t\tconst char *lhs, *rhs;\n-\t\tint flags;\n+\tis_glob = 0;\n \n-\t\tis_glob = 0;\n+\tlhs = refspec;\n+\tif (*lhs == '+') {\n+\t\trs->force = 1;\n+\t\tlhs++;\n+\t}\n \n-\t\tlhs = refspec[i];\n-\t\tif (*lhs == '+') {\n-\t\t\trs[i].force = 1;\n-\t\t\tlhs++;\n-\t\t}\n+\trhs = strrchr(lhs, ':');\n \n-\t\trhs = strrchr(lhs, ':');\n+\t/*\n+\t * Before going on, special case \":\" (or \"+:\") as a refspec\n+\t * for pushing matching refs.\n+\t */\n+\tif (!fetch && rhs == lhs && rhs[1] == '\\0') {\n+\t\trs->matching = 1;\n+\t\treturn 1;\n+\t}\n \n+\tif (rhs) {\n+\t\tsize_t rlen = strlen(++rhs);\n+\t\tis_glob = (1 <= rlen && strchr(rhs, '*'));\n+\t\trs->dst = xstrndup(rhs, rlen);\n+\t}\n+\n+\tllen = (rhs ? (rhs - lhs - 1) : strlen(lhs));\n+\tif (1 <= llen && memchr(lhs, '*', llen)) {\n+\t\tif ((rhs && !is_glob) || (!rhs && fetch))\n+\t\t\treturn 0;\n+\t\tis_glob = 1;\n+\t} else if (rhs && is_glob) {\n+\t\treturn 0;\n+\t}\n+\n+\trs->pattern = is_glob;\n+\trs->src = xstrndup(lhs, llen);\n+\tflags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);\n+\n+\tif (fetch) {\n+\t\tstruct object_id unused;\n+\n+\t\t/* LHS */\n+\t\tif (!*rs->src)\n+\t\t\t; /* empty is ok; it means \"HEAD\" */\n+\t\telse if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs->src, &unused))\n+\t\t\trs->exact_sha1 = 1; /* ok */\n+\t\telse if (!check_refname_format(rs->src, flags))\n+\t\t\t; /* valid looking ref is ok */\n+\t\telse\n+\t\t\treturn 0;\n+\t\t/* RHS */\n+\t\tif (!rs->dst)\n+\t\t\t; /* missing is ok; it is the same as empty */\n+\t\telse if (!*rs->dst)\n+\t\t\t; /* empty is ok; it means \"do not store\" */\n+\t\telse if (!check_refname_format(rs->dst, flags))\n+\t\t\t; /* valid looking ref is ok */\n+\t\telse\n+\t\t\treturn 0;\n+\t} else {\n \t\t/*\n-\t\t * Before going on, special case \":\" (or \"+:\") as a refspec\n-\t\t * for pushing matching refs.\n+\t\t * LHS\n+\t\t * - empty is allowed; it means delete.\n+\t\t * - when wildcarded, it must be a valid looking ref.\n+\t\t * - otherwise, it must be an extended SHA-1, but\n+\t\t *   there is no existing way to validate this.\n \t\t */\n-\t\tif (!fetch && rhs == lhs && rhs[1] == '\\0') {\n-\t\t\trs[i].matching = 1;\n-\t\t\tcontinue;\n+\t\tif (!*rs->src)\n+\t\t\t; /* empty is ok */\n+\t\telse if (is_glob) {\n+\t\t\tif (check_refname_format(rs->src, flags))\n+\t\t\t\treturn 0;\n \t\t}\n-\n-\t\tif (rhs) {\n-\t\t\tsize_t rlen = strlen(++rhs);\n-\t\t\tis_glob = (1 <= rlen && strchr(rhs, '*'));\n-\t\t\trs[i].dst = xstrndup(rhs, rlen);\n+\t\telse\n+\t\t\t; /* anything goes, for now */\n+\t\t/*\n+\t\t * RHS\n+\t\t * - missing is allowed, but LHS then must be a\n+\t\t *   valid looking ref.\n+\t\t * - empty is not allowed.\n+\t\t * - otherwise it must be a valid looking ref.\n+\t\t */\n+\t\tif (!rs->dst) {\n+\t\t\tif (check_refname_format(rs->src, flags))\n+\t\t\t\treturn 0;\n+\t\t} else if (!*rs->dst) {\n+\t\t\treturn 0;\n+\t\t} else {\n+\t\t\tif (check_refname_format(rs->dst, flags))\n+\t\t\t\treturn 0;\n \t\t}\n+\t}\n \n-\t\tllen = (rhs ? (rhs - lhs - 1) : strlen(lhs));\n-\t\tif (1 <= llen && memchr(lhs, '*', llen)) {\n-\t\t\tif ((rhs && !is_glob) || (!rhs && fetch))\n-\t\t\t\tgoto invalid;\n-\t\t\tis_glob = 1;\n-\t\t} else if (rhs && is_glob) {\n-\t\t\tgoto invalid;\n-\t\t}\n+\treturn 1;\n+}\n \n-\t\trs[i].pattern = is_glob;\n-\t\trs[i].src = xstrndup(lhs, llen);\n-\t\tflags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);\n-\n-\t\tif (fetch) {\n-\t\t\tstruct object_id unused;\n-\n-\t\t\t/* LHS */\n-\t\t\tif (!*rs[i].src)\n-\t\t\t\t; /* empty is ok; it means \"HEAD\" */\n-\t\t\telse if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused))\n-\t\t\t\trs[i].exact_sha1 = 1; /* ok */\n-\t\t\telse if (!check_refname_format(rs[i].src, flags))\n-\t\t\t\t; /* valid looking ref is ok */\n-\t\t\telse\n-\t\t\t\tgoto invalid;\n-\t\t\t/* RHS */\n-\t\t\tif (!rs[i].dst)\n-\t\t\t\t; /* missing is ok; it is the same as empty */\n-\t\t\telse if (!*rs[i].dst)\n-\t\t\t\t; /* empty is ok; it means \"do not store\" */\n-\t\t\telse if (!check_refname_format(rs[i].dst, flags))\n-\t\t\t\t; /* valid looking ref is ok */\n-\t\t\telse\n-\t\t\t\tgoto invalid;\n-\t\t} else {\n-\t\t\t/*\n-\t\t\t * LHS\n-\t\t\t * - empty is allowed; it means delete.\n-\t\t\t * - when wildcarded, it must be a valid looking ref.\n-\t\t\t * - otherwise, it must be an extended SHA-1, but\n-\t\t\t *   there is no existing way to validate this.\n-\t\t\t */\n-\t\t\tif (!*rs[i].src)\n-\t\t\t\t; /* empty is ok */\n-\t\t\telse if (is_glob) {\n-\t\t\t\tif (check_refname_format(rs[i].src, flags))\n-\t\t\t\t\tgoto invalid;\n-\t\t\t}\n-\t\t\telse\n-\t\t\t\t; /* anything goes, for now */\n-\t\t\t/*\n-\t\t\t * RHS\n-\t\t\t * - missing is allowed, but LHS then must be a\n-\t\t\t *   valid looking ref.\n-\t\t\t * - empty is not allowed.\n-\t\t\t * - otherwise it must be a valid looking ref.\n-\t\t\t */\n-\t\t\tif (!rs[i].dst) {\n-\t\t\t\tif (check_refname_format(rs[i].src, flags))\n-\t\t\t\t\tgoto invalid;\n-\t\t\t} else if (!*rs[i].dst) {\n-\t\t\t\tgoto invalid;\n-\t\t\t} else {\n-\t\t\t\tif (check_refname_format(rs[i].dst, flags))\n-\t\t\t\t\tgoto invalid;\n-\t\t\t}\n-\t\t}\n+static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n+{\n+\tint i;\n+\tstruct refspec *rs = xcalloc(nr_refspec, sizeof(*rs));\n+\n+\tfor (i = 0; i < nr_refspec; i++) {\n+\t\tif (!parse_refspec(&rs[i], refspec[i], fetch))\n+\t\t\tgoto invalid;\n \t}\n+\n \treturn rs;\n \n  invalid:\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347630","messageId":"20180514215626.164960-4-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 03/35] refspec: rename struct refspec to struct refspec_item","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:55:54Z","receivedAt":"2018-05-14T21:56:50Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"In preperation for introducing an abstraction around a collection of\nrefspecs (much like how a 'struct pathspec' is a collection of 'struct\npathspec_item's) rename the existing 'struct refspec' to 'struct\nrefspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n branch.c                    |  6 ++---\n builtin/clone.c             |  4 +--\n builtin/fast-export.c       |  4 +--\n builtin/fetch.c             | 12 ++++-----\n builtin/pull.c              |  2 +-\n builtin/push.c              |  4 +--\n builtin/remote.c            |  8 +++---\n builtin/submodule--helper.c |  4 +--\n checkout.c                  |  4 +--\n refspec.c                   | 19 +++++++-------\n refspec.h                   | 10 ++++----\n remote.c                    | 50 ++++++++++++++++++-------------------\n remote.h                    | 16 ++++++------\n transport-helper.c          |  2 +-\n transport.c                 |  4 +--\n 15 files changed, 74 insertions(+), 75 deletions(-)\n\ndiff --git a/branch.c b/branch.c\nindex 32ccefc6b..f967c98f6 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -9,7 +9,7 @@\n #include \"worktree.h\"\n \n struct tracking {\n-\tstruct refspec spec;\n+\tstruct refspec_item spec;\n \tchar *src;\n \tconst char *remote;\n \tint matches;\n@@ -219,8 +219,8 @@ int validate_new_branchname(const char *name, struct strbuf *ref, int force)\n static int check_tracking_branch(struct remote *remote, void *cb_data)\n {\n \tchar *tracking_branch = cb_data;\n-\tstruct refspec query;\n-\tmemset(&query, 0, sizeof(struct refspec));\n+\tstruct refspec_item query;\n+\tmemset(&query, 0, sizeof(struct refspec_item));\n \tquery.dst = tracking_branch;\n \treturn !remote_find_tracking(remote, &query);\n }\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 6d1614ed3..854088a3a 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -547,7 +547,7 @@ static struct ref *find_remote_branch(const struct ref *refs, const char *branch\n }\n \n static struct ref *wanted_peer_refs(const struct ref *refs,\n-\t\tstruct refspec *refspec)\n+\t\tstruct refspec_item *refspec)\n {\n \tstruct ref *head = copy_ref(find_ref_by_name(refs, \"HEAD\"));\n \tstruct ref *local_refs = head;\n@@ -895,7 +895,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tint err = 0, complete_refs_before_fetch = 1;\n \tint submodule_progress;\n \n-\tstruct refspec *refspec;\n+\tstruct refspec_item *refspec;\n \tconst char *fetch_pattern;\n \n \tfetch_if_missing = 0;\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex a13b7c8ef..6f105dc79 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -36,7 +36,7 @@ static int use_done_feature;\n static int no_data;\n static int full_tree;\n static struct string_list extra_refs = STRING_LIST_INIT_NODUP;\n-static struct refspec *refspecs;\n+static struct refspec_item *refspecs;\n static int refspecs_nr;\n static int anonymize;\n \n@@ -979,7 +979,7 @@ static void handle_deletes(void)\n {\n \tint i;\n \tfor (i = 0; i < refspecs_nr; i++) {\n-\t\tstruct refspec *refspec = &refspecs[i];\n+\t\tstruct refspec_item *refspec = &refspecs[i];\n \t\tif (*refspec->src)\n \t\t\tcontinue;\n \ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 1fce68e9a..745020a10 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -203,7 +203,7 @@ static void add_merge_config(struct ref **head,\n \n \tfor (i = 0; i < branch->merge_nr; i++) {\n \t\tstruct ref *rm, **old_tail = *tail;\n-\t\tstruct refspec refspec;\n+\t\tstruct refspec_item refspec;\n \n \t\tfor (rm = *head; rm; rm = rm->next) {\n \t\t\tif (branch_merge_matches(branch, i, rm->name)) {\n@@ -340,7 +340,7 @@ static void find_non_local_tags(struct transport *transport,\n }\n \n static struct ref *get_ref_map(struct transport *transport,\n-\t\t\t       struct refspec *refspecs, int refspec_count,\n+\t\t\t       struct refspec_item *refspecs, int refspec_count,\n \t\t\t       int tags, int *autotags)\n {\n \tint i;\n@@ -371,7 +371,7 @@ static struct ref *get_ref_map(struct transport *transport,\n \targv_array_clear(&ref_prefixes);\n \n \tif (refspec_count) {\n-\t\tstruct refspec *fetch_refspec;\n+\t\tstruct refspec_item *fetch_refspec;\n \t\tint fetch_refspec_nr;\n \n \t\tfor (i = 0; i < refspec_count; i++) {\n@@ -965,7 +965,7 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)\n \treturn ret;\n }\n \n-static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,\n+static int prune_refs(struct refspec_item *refs, int ref_count, struct ref *ref_map,\n \t\tconst char *raw_url)\n {\n \tint url_len, i, result = 0;\n@@ -1115,7 +1115,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)\n }\n \n static int do_fetch(struct transport *transport,\n-\t\t    struct refspec *refs, int ref_count)\n+\t\t    struct refspec_item *refs, int ref_count)\n {\n \tstruct string_list existing_refs = STRING_LIST_INIT_DUP;\n \tstruct ref *ref_map;\n@@ -1357,7 +1357,7 @@ static inline void fetch_one_setup_partial(struct remote *remote)\n static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok)\n {\n \tstatic const char **refs = NULL;\n-\tstruct refspec *refspec;\n+\tstruct refspec_item *refspec;\n \tint ref_nr = 0;\n \tint j = 0;\n \tint exit_code;\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 6247c956d..5a79deae5 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -676,7 +676,7 @@ static const char *get_upstream_branch(const char *remote)\n  */\n static const char *get_tracking_branch(const char *remote, const char *refspec)\n {\n-\tstruct refspec *spec;\n+\tstruct refspec_item *spec;\n \tconst char *spec_src;\n \tconst char *merge_branch;\n \ndiff --git a/builtin/push.c b/builtin/push.c\nindex fa65999b2..00d81fb1d 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -80,8 +80,8 @@ static const char *map_refspec(const char *ref,\n \t\treturn ref;\n \n \tif (remote->push) {\n-\t\tstruct refspec query;\n-\t\tmemset(&query, 0, sizeof(struct refspec));\n+\t\tstruct refspec_item query;\n+\t\tmemset(&query, 0, sizeof(struct refspec_item));\n \t\tquery.src = matched->name;\n \t\tif (!query_refspecs(remote->push, remote->push_refspec_nr, &query) &&\n \t\t    query.dst) {\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex c49513995..d9da82dc8 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -442,7 +442,7 @@ static int get_push_ref_states_noquery(struct ref_states *states)\n \t\tinfo->dest = xstrdup(item->string);\n \t}\n \tfor (i = 0; i < remote->push_refspec_nr; i++) {\n-\t\tstruct refspec *spec = remote->push + i;\n+\t\tstruct refspec_item *spec = remote->push + i;\n \t\tif (spec->matching)\n \t\t\titem = string_list_append(&states->push, _(\"(matching)\"));\n \t\telse if (strlen(spec->src))\n@@ -462,7 +462,7 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat\n {\n \tstruct ref *ref, *matches;\n \tstruct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;\n-\tstruct refspec refspec;\n+\tstruct refspec_item refspec;\n \n \trefspec.force = 0;\n \trefspec.pattern = 1;\n@@ -515,7 +515,7 @@ static int add_branch_for_removal(const char *refname,\n \tconst struct object_id *oid, int flags, void *cb_data)\n {\n \tstruct branches_for_remote *branches = cb_data;\n-\tstruct refspec refspec;\n+\tstruct refspec_item refspec;\n \tstruct known_remote *kr;\n \n \tmemset(&refspec, 0, sizeof(refspec));\n@@ -834,7 +834,7 @@ static int append_ref_to_tracked_list(const char *refname,\n \tconst struct object_id *oid, int flags, void *cb_data)\n {\n \tstruct ref_states *states = cb_data;\n-\tstruct refspec refspec;\n+\tstruct refspec_item refspec;\n \n \tif (flags & REF_ISSYMREF)\n \t\treturn 0;\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 6ab032acb..c0c4db007 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1746,11 +1746,11 @@ static int push_check(int argc, const char **argv, const char *prefix)\n \tif (argc > 2) {\n \t\tint i, refspec_nr = argc - 2;\n \t\tstruct ref *local_refs = get_local_heads();\n-\t\tstruct refspec *refspec = parse_push_refspec(refspec_nr,\n+\t\tstruct refspec_item *refspec = parse_push_refspec(refspec_nr,\n \t\t\t\t\t\t\t     argv + 2);\n \n \t\tfor (i = 0; i < refspec_nr; i++) {\n-\t\t\tstruct refspec *rs = refspec + i;\n+\t\t\tstruct refspec_item *rs = refspec + i;\n \n \t\t\tif (rs->pattern || rs->matching)\n \t\t\t\tcontinue;\ndiff --git a/checkout.c b/checkout.c\nindex 193ba8567..bdefc888b 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -13,8 +13,8 @@ struct tracking_name_data {\n static int check_tracking_name(struct remote *remote, void *cb_data)\n {\n \tstruct tracking_name_data *cb = cb_data;\n-\tstruct refspec query;\n-\tmemset(&query, 0, sizeof(struct refspec));\n+\tstruct refspec_item query;\n+\tmemset(&query, 0, sizeof(struct refspec_item));\n \tquery.src = cb->src_ref;\n \tif (remote_find_tracking(remote, &query) ||\n \t    get_oid(query.dst, cb->dst_oid)) {\ndiff --git a/refspec.c b/refspec.c\nindex 3cfcbd37d..cef513ad8 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -2,7 +2,7 @@\n #include \"refs.h\"\n #include \"refspec.h\"\n \n-static struct refspec s_tag_refspec = {\n+static struct refspec_item s_tag_refspec = {\n \t0,\n \t1,\n \t0,\n@@ -12,13 +12,13 @@ static struct refspec s_tag_refspec = {\n };\n \n /* See TAG_REFSPEC for the string version */\n-const struct refspec *tag_refspec = &s_tag_refspec;\n+const struct refspec_item *tag_refspec = &s_tag_refspec;\n \n /*\n  * Parses 'refspec' and populates 'rs'.  returns 1 if successful and 0 if the\n  * refspec is invalid.\n  */\n-static int parse_refspec(struct refspec *rs, const char *refspec, int fetch)\n+static int parse_refspec(struct refspec_item *rs, const char *refspec, int fetch)\n {\n \tsize_t llen;\n \tint is_glob;\n@@ -121,10 +121,10 @@ static int parse_refspec(struct refspec *rs, const char *refspec, int fetch)\n \treturn 1;\n }\n \n-static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n+static struct refspec_item *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n {\n \tint i;\n-\tstruct refspec *rs = xcalloc(nr_refspec, sizeof(*rs));\n+\tstruct refspec_item *rs = xcalloc(nr_refspec, sizeof(*rs));\n \n \tfor (i = 0; i < nr_refspec; i++) {\n \t\tif (!parse_refspec(&rs[i], refspec[i], fetch))\n@@ -148,24 +148,24 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp\n \n int valid_fetch_refspec(const char *fetch_refspec_str)\n {\n-\tstruct refspec *refspec;\n+\tstruct refspec_item *refspec;\n \n \trefspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);\n \tfree_refspec(1, refspec);\n \treturn !!refspec;\n }\n \n-struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)\n+struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec)\n {\n \treturn parse_refspec_internal(nr_refspec, refspec, 1, 0);\n }\n \n-struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)\n+struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec)\n {\n \treturn parse_refspec_internal(nr_refspec, refspec, 0, 0);\n }\n \n-void free_refspec(int nr_refspec, struct refspec *refspec)\n+void free_refspec(int nr_refspec, struct refspec_item *refspec)\n {\n \tint i;\n \n@@ -178,4 +178,3 @@ void free_refspec(int nr_refspec, struct refspec *refspec)\n \t}\n \tfree(refspec);\n }\n-\ndiff --git a/refspec.h b/refspec.h\nindex b1db91918..173cea882 100644\n--- a/refspec.h\n+++ b/refspec.h\n@@ -2,9 +2,9 @@\n #define REFSPEC_H\n \n #define TAG_REFSPEC \"refs/tags/*:refs/tags/*\"\n-const struct refspec *tag_refspec;\n+const struct refspec_item *tag_refspec;\n \n-struct refspec {\n+struct refspec_item {\n \tunsigned force : 1;\n \tunsigned pattern : 1;\n \tunsigned matching : 1;\n@@ -15,9 +15,9 @@ struct refspec {\n };\n \n int valid_fetch_refspec(const char *refspec);\n-struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);\n-struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);\n+struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec);\n+struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec);\n \n-void free_refspec(int nr_refspec, struct refspec *refspec);\n+void free_refspec(int nr_refspec, struct refspec_item *refspec);\n \n #endif /* REFSPEC_H */\ndiff --git a/remote.c b/remote.c\nindex 4d67c061a..89820c476 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -97,7 +97,7 @@ void add_prune_tags_to_fetch_refspec(struct remote *remote)\n {\n \tint nr = remote->fetch_refspec_nr;\n \tint bufsize = nr  + 1;\n-\tint size = sizeof(struct refspec);\n+\tint size = sizeof(struct refspec_item);\n \n \tremote->fetch = xrealloc(remote->fetch, size  * bufsize);\n \tmemcpy(&remote->fetch[nr], tag_refspec, size);\n@@ -724,7 +724,7 @@ static int match_name_with_pattern(const char *key, const char *name,\n \treturn ret;\n }\n \n-static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct refspec *query, struct string_list *results)\n+static void query_refspecs_multiple(struct refspec_item *refs, int ref_count, struct refspec_item *query, struct string_list *results)\n {\n \tint i;\n \tint find_src = !query->src;\n@@ -733,7 +733,7 @@ static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct\n \t\terror(\"query_refspecs_multiple: need either src or dst\");\n \n \tfor (i = 0; i < ref_count; i++) {\n-\t\tstruct refspec *refspec = &refs[i];\n+\t\tstruct refspec_item *refspec = &refs[i];\n \t\tconst char *key = find_src ? refspec->dst : refspec->src;\n \t\tconst char *value = find_src ? refspec->src : refspec->dst;\n \t\tconst char *needle = find_src ? query->dst : query->src;\n@@ -750,7 +750,7 @@ static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct\n \t}\n }\n \n-int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)\n+int query_refspecs(struct refspec_item *refs, int ref_count, struct refspec_item *query)\n {\n \tint i;\n \tint find_src = !query->src;\n@@ -761,7 +761,7 @@ int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)\n \t\treturn error(\"query_refspecs: need either src or dst\");\n \n \tfor (i = 0; i < ref_count; i++) {\n-\t\tstruct refspec *refspec = &refs[i];\n+\t\tstruct refspec_item *refspec = &refs[i];\n \t\tconst char *key = find_src ? refspec->dst : refspec->src;\n \t\tconst char *value = find_src ? refspec->src : refspec->dst;\n \n@@ -781,12 +781,12 @@ int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)\n \treturn -1;\n }\n \n-char *apply_refspecs(struct refspec *refspecs, int nr_refspec,\n+char *apply_refspecs(struct refspec_item *refspecs, int nr_refspec,\n \t\t     const char *name)\n {\n-\tstruct refspec query;\n+\tstruct refspec_item query;\n \n-\tmemset(&query, 0, sizeof(struct refspec));\n+\tmemset(&query, 0, sizeof(struct refspec_item));\n \tquery.src = (char *)name;\n \n \tif (query_refspecs(refspecs, nr_refspec, &query))\n@@ -795,7 +795,7 @@ char *apply_refspecs(struct refspec *refspecs, int nr_refspec,\n \treturn query.dst;\n }\n \n-int remote_find_tracking(struct remote *remote, struct refspec *refspec)\n+int remote_find_tracking(struct remote *remote, struct refspec_item *refspec)\n {\n \treturn query_refspecs(remote->fetch, remote->fetch_refspec_nr, refspec);\n }\n@@ -1004,7 +1004,7 @@ static char *guess_ref(const char *name, struct ref *peer)\n }\n \n static int match_explicit_lhs(struct ref *src,\n-\t\t\t      struct refspec *rs,\n+\t\t\t      struct refspec_item *rs,\n \t\t\t      struct ref **match,\n \t\t\t      int *allocated_match)\n {\n@@ -1030,7 +1030,7 @@ static int match_explicit_lhs(struct ref *src,\n \n static int match_explicit(struct ref *src, struct ref *dst,\n \t\t\t  struct ref ***dst_tail,\n-\t\t\t  struct refspec *rs)\n+\t\t\t  struct refspec_item *rs)\n {\n \tstruct ref *matched_src, *matched_dst;\n \tint allocated_src;\n@@ -1099,7 +1099,7 @@ static int match_explicit(struct ref *src, struct ref *dst,\n }\n \n static int match_explicit_refs(struct ref *src, struct ref *dst,\n-\t\t\t       struct ref ***dst_tail, struct refspec *rs,\n+\t\t\t       struct ref ***dst_tail, struct refspec_item *rs,\n \t\t\t       int rs_nr)\n {\n \tint i, errs;\n@@ -1108,10 +1108,10 @@ static int match_explicit_refs(struct ref *src, struct ref *dst,\n \treturn errs;\n }\n \n-static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref *ref,\n-\t\tint send_mirror, int direction, const struct refspec **ret_pat)\n+static char *get_ref_match(const struct refspec_item *rs, int rs_nr, const struct ref *ref,\n+\t\tint send_mirror, int direction, const struct refspec_item **ret_pat)\n {\n-\tconst struct refspec *pat;\n+\tconst struct refspec_item *pat;\n \tchar *name;\n \tint i;\n \tint matching_refs = -1;\n@@ -1282,12 +1282,12 @@ static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)\n  */\n int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)\n {\n-\tstruct refspec *refspec = parse_push_refspec(nr_refspec, refspec_names);\n+\tstruct refspec_item *refspec = parse_push_refspec(nr_refspec, refspec_names);\n \tint ret = 0;\n \tint i;\n \n \tfor (i = 0; i < nr_refspec; i++) {\n-\t\tstruct refspec *rs = refspec + i;\n+\t\tstruct refspec_item *rs = refspec + i;\n \n \t\tif (rs->pattern || rs->matching)\n \t\t\tcontinue;\n@@ -1310,7 +1310,7 @@ int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)\n int match_push_refs(struct ref *src, struct ref **dst,\n \t\t    int nr_refspec, const char **refspec, int flags)\n {\n-\tstruct refspec *rs;\n+\tstruct refspec_item *rs;\n \tint send_all = flags & MATCH_REFS_ALL;\n \tint send_mirror = flags & MATCH_REFS_MIRROR;\n \tint send_prune = flags & MATCH_REFS_PRUNE;\n@@ -1330,7 +1330,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \tfor (ref = src; ref; ref = ref->next) {\n \t\tstruct string_list_item *dst_item;\n \t\tstruct ref *dst_peer;\n-\t\tconst struct refspec *pat = NULL;\n+\t\tconst struct refspec_item *pat = NULL;\n \t\tchar *dst_name;\n \n \t\tdst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat);\n@@ -1686,7 +1686,7 @@ static int ignore_symref_update(const char *refname)\n  * local symbolic ref.\n  */\n static struct ref *get_expanded_map(const struct ref *remote_refs,\n-\t\t\t\t    const struct refspec *refspec)\n+\t\t\t\t    const struct refspec_item *refspec)\n {\n \tconst struct ref *ref;\n \tstruct ref *ret = NULL;\n@@ -1751,7 +1751,7 @@ static struct ref *get_local_ref(const char *name)\n }\n \n int get_fetch_map(const struct ref *remote_refs,\n-\t\t  const struct refspec *refspec,\n+\t\t  const struct refspec_item *refspec,\n \t\t  struct ref ***tail,\n \t\t  int missing_ok)\n {\n@@ -2089,7 +2089,7 @@ struct ref *guess_remote_head(const struct ref *head,\n struct stale_heads_info {\n \tstruct string_list *ref_names;\n \tstruct ref **stale_refs_tail;\n-\tstruct refspec *refs;\n+\tstruct refspec_item *refs;\n \tint ref_count;\n };\n \n@@ -2098,9 +2098,9 @@ static int get_stale_heads_cb(const char *refname, const struct object_id *oid,\n {\n \tstruct stale_heads_info *info = cb_data;\n \tstruct string_list matches = STRING_LIST_INIT_DUP;\n-\tstruct refspec query;\n+\tstruct refspec_item query;\n \tint i, stale = 1;\n-\tmemset(&query, 0, sizeof(struct refspec));\n+\tmemset(&query, 0, sizeof(struct refspec_item));\n \tquery.dst = (char *)refname;\n \n \tquery_refspecs_multiple(info->refs, info->ref_count, &query, &matches);\n@@ -2131,7 +2131,7 @@ static int get_stale_heads_cb(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n-struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map)\n+struct ref *get_stale_heads(struct refspec_item *refs, int ref_count, struct ref *fetch_map)\n {\n \tstruct ref *ref, *stale_refs = NULL;\n \tstruct string_list ref_names = STRING_LIST_INIT_NODUP;\ndiff --git a/remote.h b/remote.h\nindex 386ced901..3657bd43d 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -28,12 +28,12 @@ struct remote {\n \tint pushurl_alloc;\n \n \tconst char **push_refspec;\n-\tstruct refspec *push;\n+\tstruct refspec_item *push;\n \tint push_refspec_nr;\n \tint push_refspec_alloc;\n \n \tconst char **fetch_refspec;\n-\tstruct refspec *fetch;\n+\tstruct refspec_item *fetch;\n \tint fetch_refspec_nr;\n \tint fetch_refspec_alloc;\n \n@@ -163,8 +163,8 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);\n  */\n struct ref *ref_remove_duplicates(struct ref *ref_map);\n \n-extern int query_refspecs(struct refspec *specs, int nr, struct refspec *query);\n-char *apply_refspecs(struct refspec *refspecs, int nr_refspec,\n+extern int query_refspecs(struct refspec_item *specs, int nr, struct refspec_item *query);\n+char *apply_refspecs(struct refspec_item *refspecs, int nr_refspec,\n \t\t     const char *name);\n \n int check_push_refs(struct ref *src, int nr_refspec, const char **refspec);\n@@ -185,7 +185,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\n  * missing_ok is usually false, but when we are adding branch.$name.merge\n  * it is Ok if the branch is not at the remote anymore.\n  */\n-int get_fetch_map(const struct ref *remote_refs, const struct refspec *refspec,\n+int get_fetch_map(const struct ref *remote_refs, const struct refspec_item *refspec,\n \t\t  struct ref ***tail, int missing_ok);\n \n struct ref *get_remote_ref(const struct ref *remote_refs, const char *name);\n@@ -193,7 +193,7 @@ struct ref *get_remote_ref(const struct ref *remote_refs, const char *name);\n /*\n  * For the given remote, reads the refspec's src and sets the other fields.\n  */\n-int remote_find_tracking(struct remote *remote, struct refspec *refspec);\n+int remote_find_tracking(struct remote *remote, struct refspec_item *refspec);\n \n struct branch {\n \tconst char *name;\n@@ -203,7 +203,7 @@ struct branch {\n \tconst char *pushremote_name;\n \n \tconst char **merge_name;\n-\tstruct refspec **merge;\n+\tstruct refspec_item **merge;\n \tint merge_nr;\n \tint merge_alloc;\n \n@@ -272,7 +272,7 @@ struct ref *guess_remote_head(const struct ref *head,\n \t\t\t      int all);\n \n /* Return refs which no longer exist on remote */\n-struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map);\n+struct ref *get_stale_heads(struct refspec_item *refs, int ref_count, struct ref *fetch_map);\n \n /*\n  * Compare-and-swap\ndiff --git a/transport-helper.c b/transport-helper.c\nindex b99e1cce9..b156a37e7 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -36,7 +36,7 @@ struct helper_data {\n \tchar *export_marks;\n \tchar *import_marks;\n \t/* These go from remote name (as in \"list\") to private name */\n-\tstruct refspec *refspecs;\n+\tstruct refspec_item *refspecs;\n \tint refspec_nr;\n \t/* Transport options for fetch-pack/send-pack (should one of\n \t * those be invoked).\ndiff --git a/transport.c b/transport.c\nindex 2cf63d18b..3ad4d37dc 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -390,7 +390,7 @@ int transport_refs_pushed(struct ref *ref)\n \n void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose)\n {\n-\tstruct refspec rs;\n+\tstruct refspec_item rs;\n \n \tif (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE)\n \t\treturn;\n@@ -1111,7 +1111,7 @@ int transport_push(struct transport *transport,\n \t\tint porcelain = flags & TRANSPORT_PUSH_PORCELAIN;\n \t\tint pretend = flags & TRANSPORT_PUSH_DRY_RUN;\n \t\tint push_ret, ret, err;\n-\t\tstruct refspec *tmp_rs;\n+\t\tstruct refspec_item *tmp_rs;\n \t\tstruct argv_array ref_prefixes = ARGV_ARRAY_INIT;\n \t\tint i;\n \n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347631","messageId":"20180514215626.164960-5-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 04/35] refspec: introduce struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:55:55Z","receivedAt":"2018-05-14T21:56:54Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Introduce 'struct refspec', an abstraction around a collection of\n'struct refspec_item's much like how 'struct pathspec' holds a\ncollection of 'struct pathspec_item's.\n\nA refspec struct also contains an array of the original refspec strings\nwhich will be used to facilitate the migration to using this new\nabstraction throughout the code base.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n refspec.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\n refspec.h | 25 ++++++++++++++++++++++\n 2 files changed, 89 insertions(+)\n\ndiff --git a/refspec.c b/refspec.c\nindex cef513ad8..2b898c922 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -178,3 +178,67 @@ void free_refspec(int nr_refspec, struct refspec_item *refspec)\n \t}\n \tfree(refspec);\n }\n+\n+void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)\n+{\n+\tmemset(item, 0, sizeof(*item));\n+\n+\tif (!parse_refspec(item, refspec, fetch))\n+\t\tdie(\"Invalid refspec '%s'\", refspec);\n+}\n+\n+void refspec_item_clear(struct refspec_item *item)\n+{\n+\tFREE_AND_NULL(item->src);\n+\tFREE_AND_NULL(item->dst);\n+\titem->force = 0;\n+\titem->pattern = 0;\n+\titem->matching = 0;\n+\titem->exact_sha1 = 0;\n+}\n+\n+void refspec_init(struct refspec *rs, int fetch)\n+{\n+\tmemset(rs, 0, sizeof(*rs));\n+\trs->fetch = fetch;\n+}\n+\n+void refspec_append(struct refspec *rs, const char *refspec)\n+{\n+\tstruct refspec_item item;\n+\n+\trefspec_item_init(&item, refspec, rs->fetch);\n+\n+\tALLOC_GROW(rs->items, rs->nr + 1, rs->alloc);\n+\trs->items[rs->nr++] = item;\n+\n+\tALLOC_GROW(rs->raw, rs->raw_nr + 1, rs->raw_alloc);\n+\trs->raw[rs->raw_nr++] = xstrdup(refspec);\n+}\n+\n+void refspec_appendn(struct refspec *rs, const char **refspecs, int nr)\n+{\n+\tint i;\n+\tfor (i = 0; i < nr; i++)\n+\t\trefspec_append(rs, refspecs[i]);\n+}\n+\n+void refspec_clear(struct refspec *rs)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < rs->nr; i++)\n+\t\trefspec_item_clear(&rs->items[i]);\n+\n+\tFREE_AND_NULL(rs->items);\n+\trs->alloc = 0;\n+\trs->nr = 0;\n+\n+\tfor (i = 0; i < rs->raw_nr; i++)\n+\t\tfree((char *)rs->raw[i]);\n+\tFREE_AND_NULL(rs->raw);\n+\trs->raw_alloc = 0;\n+\trs->raw_nr = 0;\n+\n+\trs->fetch = 0;\n+}\ndiff --git a/refspec.h b/refspec.h\nindex 173cea882..f6fb251f3 100644\n--- a/refspec.h\n+++ b/refspec.h\n@@ -20,4 +20,29 @@ struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec);\n \n void free_refspec(int nr_refspec, struct refspec_item *refspec);\n \n+#define REFSPEC_FETCH 1\n+#define REFSPEC_PUSH 0\n+\n+#define REFSPEC_INIT_FETCH { .fetch = REFSPEC_FETCH }\n+#define REFSPEC_INIT_PUSH { .fetch = REFSPEC_PUSH }\n+\n+struct refspec {\n+\tstruct refspec_item *items;\n+\tint alloc;\n+\tint nr;\n+\n+\tconst char **raw;\n+\tint raw_alloc;\n+\tint raw_nr;\n+\n+\tint fetch;\n+};\n+\n+void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch);\n+void refspec_item_clear(struct refspec_item *item);\n+void refspec_init(struct refspec *rs, int fetch);\n+void refspec_append(struct refspec *rs, const char *refspec);\n+void refspec_appendn(struct refspec *rs, const char **refspecs, int nr);\n+void refspec_clear(struct refspec *rs);\n+\n #endif /* REFSPEC_H */\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347632","messageId":"20180514215626.164960-7-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 06/35] submodule--helper: convert push_check to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:55:57Z","receivedAt":"2018-05-14T21:56:56Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'push_check()' to use 'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/submodule--helper.c | 13 +++++++------\n 1 file changed, 7 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex c0c4db007..88a149a2c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1744,13 +1744,14 @@ static int push_check(int argc, const char **argv, const char *prefix)\n \n \t/* Check the refspec */\n \tif (argc > 2) {\n-\t\tint i, refspec_nr = argc - 2;\n+\t\tint i;\n \t\tstruct ref *local_refs = get_local_heads();\n-\t\tstruct refspec_item *refspec = parse_push_refspec(refspec_nr,\n-\t\t\t\t\t\t\t     argv + 2);\n+\t\tstruct refspec refspec = REFSPEC_INIT_PUSH;\n \n-\t\tfor (i = 0; i < refspec_nr; i++) {\n-\t\t\tstruct refspec_item *rs = refspec + i;\n+\t\trefspec_appendn(&refspec, argv + 2, argc - 2);\n+\n+\t\tfor (i = 0; i < refspec.nr; i++) {\n+\t\t\tconst struct refspec_item *rs = &refspec.items[i];\n \n \t\t\tif (rs->pattern || rs->matching)\n \t\t\t\tcontinue;\n@@ -1777,7 +1778,7 @@ static int push_check(int argc, const char **argv, const char *prefix)\n \t\t\t\t    rs->src);\n \t\t\t}\n \t\t}\n-\t\tfree_refspec(refspec_nr, refspec);\n+\t\trefspec_clear(&refspec);\n \t}\n \tfree(head);\n \n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347633","messageId":"20180514215626.164960-8-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 07/35] pull: convert get_tracking_branch to use refspec_item_init","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:55:58Z","receivedAt":"2018-05-14T21:57:00Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'get_tracking_branch()' to use 'refspec_item_init()' instead of\nthe old 'parse_fetch_refspec()' function.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/pull.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 5a79deae5..09575fd23 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -676,12 +676,12 @@ static const char *get_upstream_branch(const char *remote)\n  */\n static const char *get_tracking_branch(const char *remote, const char *refspec)\n {\n-\tstruct refspec_item *spec;\n+\tstruct refspec_item spec;\n \tconst char *spec_src;\n \tconst char *merge_branch;\n \n-\tspec = parse_fetch_refspec(1, &refspec);\n-\tspec_src = spec->src;\n+\trefspec_item_init(&spec, refspec, REFSPEC_FETCH);\n+\tspec_src = spec.src;\n \tif (!*spec_src || !strcmp(spec_src, \"HEAD\"))\n \t\tspec_src = \"HEAD\";\n \telse if (skip_prefix(spec_src, \"heads/\", &spec_src))\n@@ -701,7 +701,7 @@ static const char *get_tracking_branch(const char *remote, const char *refspec)\n \t} else\n \t\tmerge_branch = NULL;\n \n-\tfree_refspec(1, spec);\n+\trefspec_item_clear(&spec);\n \treturn merge_branch;\n }\n \n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347634","messageId":"20180514215626.164960-6-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 05/35] refspec: convert valid_fetch_refspec to use parse_refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:55:56Z","receivedAt":"2018-05-14T21:57:03Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'valid_fetch_refspec()' to use the new 'parse_refspec()'\nfunction to only parse a single refspec an eliminate an allocation.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n refspec.c | 17 ++++++++---------\n refspec.h |  3 ++-\n 2 files changed, 10 insertions(+), 10 deletions(-)\n\ndiff --git a/refspec.c b/refspec.c\nindex 2b898c922..98c99cd84 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -146,15 +146,6 @@ static struct refspec_item *parse_refspec_internal(int nr_refspec, const char **\n \tdie(\"Invalid refspec '%s'\", refspec[i]);\n }\n \n-int valid_fetch_refspec(const char *fetch_refspec_str)\n-{\n-\tstruct refspec_item *refspec;\n-\n-\trefspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);\n-\tfree_refspec(1, refspec);\n-\treturn !!refspec;\n-}\n-\n struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec)\n {\n \treturn parse_refspec_internal(nr_refspec, refspec, 1, 0);\n@@ -242,3 +233,11 @@ void refspec_clear(struct refspec *rs)\n \n \trs->fetch = 0;\n }\n+\n+int valid_fetch_refspec(const char *fetch_refspec_str)\n+{\n+\tstruct refspec_item refspec;\n+\tint ret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n+\trefspec_item_clear(&refspec);\n+\treturn ret;\n+}\ndiff --git a/refspec.h b/refspec.h\nindex f6fb251f3..b009440c0 100644\n--- a/refspec.h\n+++ b/refspec.h\n@@ -14,7 +14,6 @@ struct refspec_item {\n \tchar *dst;\n };\n \n-int valid_fetch_refspec(const char *refspec);\n struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec);\n struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec);\n \n@@ -45,4 +44,6 @@ void refspec_append(struct refspec *rs, const char *refspec);\n void refspec_appendn(struct refspec *rs, const char **refspecs, int nr);\n void refspec_clear(struct refspec *rs);\n \n+int valid_fetch_refspec(const char *refspec);\n+\n #endif /* REFSPEC_H */\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347635","messageId":"20180514215626.164960-9-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 08/35] transport: convert transport_push to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:55:59Z","receivedAt":"2018-05-14T21:57:04Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert the logic in 'transport_push()' which calculates a list of\nref-prefixes to use 'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n transport.c | 17 +++++++++--------\n 1 file changed, 9 insertions(+), 8 deletions(-)\n\ndiff --git a/transport.c b/transport.c\nindex 3ad4d37dc..181db4d4d 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1111,21 +1111,22 @@ int transport_push(struct transport *transport,\n \t\tint porcelain = flags & TRANSPORT_PUSH_PORCELAIN;\n \t\tint pretend = flags & TRANSPORT_PUSH_DRY_RUN;\n \t\tint push_ret, ret, err;\n-\t\tstruct refspec_item *tmp_rs;\n+\t\tstruct refspec tmp_rs = REFSPEC_INIT_PUSH;\n \t\tstruct argv_array ref_prefixes = ARGV_ARRAY_INIT;\n \t\tint i;\n \n \t\tif (check_push_refs(local_refs, refspec_nr, refspec) < 0)\n \t\t\treturn -1;\n \n-\t\ttmp_rs = parse_push_refspec(refspec_nr, refspec);\n-\t\tfor (i = 0; i < refspec_nr; i++) {\n+\t\trefspec_appendn(&tmp_rs, refspec, refspec_nr);\n+\t\tfor (i = 0; i < tmp_rs.nr; i++) {\n+\t\t\tconst struct refspec_item *item = &tmp_rs.items[i];\n \t\t\tconst char *prefix = NULL;\n \n-\t\t\tif (tmp_rs[i].dst)\n-\t\t\t\tprefix = tmp_rs[i].dst;\n-\t\t\telse if (tmp_rs[i].src && !tmp_rs[i].exact_sha1)\n-\t\t\t\tprefix = tmp_rs[i].src;\n+\t\t\tif (item->dst)\n+\t\t\t\tprefix = item->dst;\n+\t\t\telse if (item->src && !item->exact_sha1)\n+\t\t\t\tprefix = item->src;\n \n \t\t\tif (prefix) {\n \t\t\t\tconst char *glob = strchr(prefix, '*');\n@@ -1142,7 +1143,7 @@ int transport_push(struct transport *transport,\n \t\t\t\t\t\t\t       &ref_prefixes);\n \n \t\targv_array_clear(&ref_prefixes);\n-\t\tfree_refspec(refspec_nr, tmp_rs);\n+\t\trefspec_clear(&tmp_rs);\n \n \t\tif (flags & TRANSPORT_PUSH_ALL)\n \t\t\tmatch_flags |= MATCH_REFS_ALL;\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347636","messageId":"20180514215626.164960-10-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 09/35] remote: convert check_push_refs to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:00Z","receivedAt":"2018-05-14T21:57:08Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'check_push_refs()' to use 'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n remote.c | 10 ++++++----\n 1 file changed, 6 insertions(+), 4 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 89820c476..191855118 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1282,12 +1282,14 @@ static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)\n  */\n int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)\n {\n-\tstruct refspec_item *refspec = parse_push_refspec(nr_refspec, refspec_names);\n+\tstruct refspec refspec = REFSPEC_INIT_PUSH;\n \tint ret = 0;\n \tint i;\n \n-\tfor (i = 0; i < nr_refspec; i++) {\n-\t\tstruct refspec_item *rs = refspec + i;\n+\trefspec_appendn(&refspec, refspec_names, nr_refspec);\n+\n+\tfor (i = 0; i < refspec.nr; i++) {\n+\t\tstruct refspec_item *rs = &refspec.items[i];\n \n \t\tif (rs->pattern || rs->matching)\n \t\t\tcontinue;\n@@ -1295,7 +1297,7 @@ int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)\n \t\tret |= match_explicit_lhs(src, rs, NULL, NULL);\n \t}\n \n-\tfree_refspec(nr_refspec, refspec);\n+\trefspec_clear(&refspec);\n \treturn ret;\n }\n \n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347637","messageId":"20180514215626.164960-15-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 14/35] remote: convert fetch refspecs to struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:05Z","receivedAt":"2018-05-14T21:57:17Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert the set of fetch refspecs stored in 'struct remote' to use\n'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c  | 20 ++++++++++----------\n builtin/remote.c | 18 +++++++++---------\n remote.c         | 24 ++++++------------------\n remote.h         |  5 +----\n 4 files changed, 26 insertions(+), 41 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 745020a10..30083d4bc 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -407,8 +407,8 @@ static struct ref *get_ref_map(struct transport *transport,\n \t\t\tfetch_refspec = parse_fetch_refspec(refmap_nr, refmap_array);\n \t\t\tfetch_refspec_nr = refmap_nr;\n \t\t} else {\n-\t\t\tfetch_refspec = transport->remote->fetch;\n-\t\t\tfetch_refspec_nr = transport->remote->fetch_refspec_nr;\n+\t\t\tfetch_refspec = transport->remote->fetch.items;\n+\t\t\tfetch_refspec_nr = transport->remote->fetch.nr;\n \t\t}\n \n \t\tfor (i = 0; i < fetch_refspec_nr; i++)\n@@ -421,16 +421,16 @@ static struct ref *get_ref_map(struct transport *transport,\n \t\tstruct branch *branch = branch_get(NULL);\n \t\tint has_merge = branch_has_merge_config(branch);\n \t\tif (remote &&\n-\t\t    (remote->fetch_refspec_nr ||\n+\t\t    (remote->fetch.nr ||\n \t\t     /* Note: has_merge implies non-NULL branch->remote_name */\n \t\t     (has_merge && !strcmp(branch->remote_name, remote->name)))) {\n-\t\t\tfor (i = 0; i < remote->fetch_refspec_nr; i++) {\n-\t\t\t\tget_fetch_map(remote_refs, &remote->fetch[i], &tail, 0);\n-\t\t\t\tif (remote->fetch[i].dst &&\n-\t\t\t\t    remote->fetch[i].dst[0])\n+\t\t\tfor (i = 0; i < remote->fetch.nr; i++) {\n+\t\t\t\tget_fetch_map(remote_refs, &remote->fetch.items[i], &tail, 0);\n+\t\t\t\tif (remote->fetch.items[i].dst &&\n+\t\t\t\t    remote->fetch.items[i].dst[0])\n \t\t\t\t\t*autotags = 1;\n \t\t\t\tif (!i && !has_merge && ref_map &&\n-\t\t\t\t    !remote->fetch[0].pattern)\n+\t\t\t\t    !remote->fetch.items[0].pattern)\n \t\t\t\t\tref_map->fetch_head_status = FETCH_HEAD_MERGE;\n \t\t\t}\n \t\t\t/*\n@@ -1166,8 +1166,8 @@ static int do_fetch(struct transport *transport,\n \t\tif (ref_count) {\n \t\t\tprune_refs(refs, ref_count, ref_map, transport->url);\n \t\t} else {\n-\t\t\tprune_refs(transport->remote->fetch,\n-\t\t\t\t   transport->remote->fetch_refspec_nr,\n+\t\t\tprune_refs(transport->remote->fetch.items,\n+\t\t\t\t   transport->remote->fetch.nr,\n \t\t\t\t   ref_map,\n \t\t\t\t   transport->url);\n \t\t}\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex fb84729d6..94dfcb78b 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -333,10 +333,10 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat\n \tstruct ref *ref, *stale_refs;\n \tint i;\n \n-\tfor (i = 0; i < states->remote->fetch_refspec_nr; i++)\n-\t\tif (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1))\n+\tfor (i = 0; i < states->remote->fetch.nr; i++)\n+\t\tif (get_fetch_map(remote_refs, &states->remote->fetch.items[i], &tail, 1))\n \t\t\tdie(_(\"Could not get fetch map for refspec %s\"),\n-\t\t\t\tstates->remote->fetch_refspec[i]);\n+\t\t\t\tstates->remote->fetch.raw[i]);\n \n \tstates->new_refs.strdup_strings = 1;\n \tstates->tracked.strdup_strings = 1;\n@@ -347,8 +347,8 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat\n \t\telse\n \t\t\tstring_list_append(&states->tracked, abbrev_branch(ref->name));\n \t}\n-\tstale_refs = get_stale_heads(states->remote->fetch,\n-\t\t\t\t     states->remote->fetch_refspec_nr, fetch_map);\n+\tstale_refs = get_stale_heads(states->remote->fetch.items,\n+\t\t\t\t     states->remote->fetch.nr, fetch_map);\n \tfor (ref = stale_refs; ref; ref = ref->next) {\n \t\tstruct string_list_item *item =\n \t\t\tstring_list_append(&states->stale, abbrev_branch(ref->name));\n@@ -590,8 +590,8 @@ static int migrate_file(struct remote *remote)\n \t\tgit_config_set_multivar(buf.buf, remote->push.raw[i], \"^$\", 0);\n \tstrbuf_reset(&buf);\n \tstrbuf_addf(&buf, \"remote.%s.fetch\", remote->name);\n-\tfor (i = 0; i < remote->fetch_refspec_nr; i++)\n-\t\tgit_config_set_multivar(buf.buf, remote->fetch_refspec[i], \"^$\", 0);\n+\tfor (i = 0; i < remote->fetch.raw_nr; i++)\n+\t\tgit_config_set_multivar(buf.buf, remote->fetch.raw[i], \"^$\", 0);\n \tif (remote->origin == REMOTE_REMOTES)\n \t\tunlink_or_warn(git_path(\"remotes/%s\", remote->name));\n \telse if (remote->origin == REMOTE_BRANCHES)\n@@ -646,11 +646,11 @@ static int mv(int argc, const char **argv)\n \tstrbuf_addf(&buf, \"remote.%s.fetch\", rename.new_name);\n \tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n \tstrbuf_addf(&old_remote_context, \":refs/remotes/%s/\", rename.old_name);\n-\tfor (i = 0; i < oldremote->fetch_refspec_nr; i++) {\n+\tfor (i = 0; i < oldremote->fetch.raw_nr; i++) {\n \t\tchar *ptr;\n \n \t\tstrbuf_reset(&buf2);\n-\t\tstrbuf_addstr(&buf2, oldremote->fetch_refspec[i]);\n+\t\tstrbuf_addstr(&buf2, oldremote->fetch.raw[i]);\n \t\tptr = strstr(buf2.buf, old_remote_context.buf);\n \t\tif (ptr) {\n \t\t\trefspec_updated = 1;\ndiff --git a/remote.c b/remote.c\nindex 090110c37..db1e4edb7 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -84,21 +84,12 @@ static void add_push_refspec(struct remote *remote, const char *ref)\n \n static void add_fetch_refspec(struct remote *remote, const char *ref)\n {\n-\tALLOC_GROW(remote->fetch_refspec,\n-\t\t   remote->fetch_refspec_nr + 1,\n-\t\t   remote->fetch_refspec_alloc);\n-\tremote->fetch_refspec[remote->fetch_refspec_nr++] = ref;\n+\trefspec_append(&remote->fetch, ref);\n }\n \n void add_prune_tags_to_fetch_refspec(struct remote *remote)\n {\n-\tint nr = remote->fetch_refspec_nr;\n-\tint bufsize = nr  + 1;\n-\tint size = sizeof(struct refspec_item);\n-\n-\tremote->fetch = xrealloc(remote->fetch, size  * bufsize);\n-\tmemcpy(&remote->fetch[nr], tag_refspec, size);\n-\tadd_fetch_refspec(remote, xstrdup(TAG_REFSPEC));\n+\trefspec_append(&remote->fetch, TAG_REFSPEC);\n }\n \n static void add_url(struct remote *remote, const char *url)\n@@ -174,6 +165,7 @@ static struct remote *make_remote(const char *name, int len)\n \tret->prune_tags = -1;  /* unspecified */\n \tret->name = xstrndup(name, 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@@ -580,7 +572,6 @@ static struct remote *remote_get_1(const char *name,\n \t\tadd_url_alias(ret, name);\n \tif (!valid_remote(ret))\n \t\treturn NULL;\n-\tret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec);\n \treturn ret;\n }\n \n@@ -611,9 +602,6 @@ int for_each_remote(each_remote_fn fn, void *priv)\n \t\tstruct remote *r = remotes[i];\n \t\tif (!r)\n \t\t\tcontinue;\n-\t\tif (!r->fetch)\n-\t\t\tr->fetch = parse_fetch_refspec(r->fetch_refspec_nr,\n-\t\t\t\t\t\t       r->fetch_refspec);\n \t\tresult = fn(r, priv);\n \t}\n \treturn result;\n@@ -792,7 +780,7 @@ char *apply_refspecs(struct refspec_item *refspecs, int nr_refspec,\n \n int remote_find_tracking(struct remote *remote, struct refspec_item *refspec)\n {\n-\treturn query_refspecs(remote->fetch, remote->fetch_refspec_nr, refspec);\n+\treturn query_refspecs(remote->fetch.items, remote->fetch.nr, refspec);\n }\n \n static struct ref *alloc_ref_with_prefix(const char *prefix, size_t prefixlen,\n@@ -1590,7 +1578,7 @@ static const char *tracking_for_push_dest(struct remote *remote,\n {\n \tchar *ret;\n \n-\tret = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);\n+\tret = apply_refspecs(remote->fetch.items, remote->fetch.nr, refname);\n \tif (!ret)\n \t\treturn error_buf(err,\n \t\t\t\t _(\"push destination '%s' on remote '%s' has no local tracking branch\"),\n@@ -2224,7 +2212,7 @@ static int remote_tracking(struct remote *remote, const char *refname,\n {\n \tchar *dst;\n \n-\tdst = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);\n+\tdst = apply_refspecs(remote->fetch.items, remote->fetch.nr, refname);\n \tif (!dst)\n \t\treturn -1; /* no tracking ref for refname at remote */\n \tif (read_ref(dst, oid))\ndiff --git a/remote.h b/remote.h\nindex 637fc5d0c..e7d00fe2a 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -30,10 +30,7 @@ struct remote {\n \n \tstruct refspec push;\n \n-\tconst char **fetch_refspec;\n-\tstruct refspec_item *fetch;\n-\tint fetch_refspec_nr;\n-\tint fetch_refspec_alloc;\n+\tstruct refspec fetch;\n \n \t/*\n \t * -1 to never fetch tags\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347638","messageId":"20180514215626.164960-16-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 15/35] transport-helper: convert to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:06Z","receivedAt":"2018-05-14T21:57:22Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert the refspecs in transport-helper.c to be stored in a\n'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n transport-helper.c | 38 ++++++++++++--------------------------\n 1 file changed, 12 insertions(+), 26 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex b156a37e7..33f51ebfc 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -36,8 +36,7 @@ struct helper_data {\n \tchar *export_marks;\n \tchar *import_marks;\n \t/* These go from remote name (as in \"list\") to private name */\n-\tstruct refspec_item *refspecs;\n-\tint refspec_nr;\n+\tstruct refspec rs;\n \t/* Transport options for fetch-pack/send-pack (should one of\n \t * those be invoked).\n \t */\n@@ -107,9 +106,6 @@ static struct child_process *get_helper(struct transport *transport)\n \tstruct helper_data *data = transport->data;\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct child_process *helper;\n-\tconst char **refspecs = NULL;\n-\tint refspec_nr = 0;\n-\tint refspec_alloc = 0;\n \tint duped;\n \tint code;\n \n@@ -139,6 +135,7 @@ static struct child_process *get_helper(struct transport *transport)\n \n \tdata->helper = helper;\n \tdata->no_disconnect_req = 0;\n+\trefspec_init(&data->rs, REFSPEC_FETCH);\n \n \t/*\n \t * Open the output as FILE* so strbuf_getline_*() family of\n@@ -184,11 +181,8 @@ static struct child_process *get_helper(struct transport *transport)\n \t\t\tdata->export = 1;\n \t\telse if (!strcmp(capname, \"check-connectivity\"))\n \t\t\tdata->check_connectivity = 1;\n-\t\telse if (!data->refspecs && skip_prefix(capname, \"refspec \", &arg)) {\n-\t\t\tALLOC_GROW(refspecs,\n-\t\t\t\t   refspec_nr + 1,\n-\t\t\t\t   refspec_alloc);\n-\t\t\trefspecs[refspec_nr++] = xstrdup(arg);\n+\t\telse if (skip_prefix(capname, \"refspec \", &arg)) {\n+\t\t\trefspec_append(&data->rs, arg);\n \t\t} else if (!strcmp(capname, \"connect\")) {\n \t\t\tdata->connect = 1;\n \t\t} else if (!strcmp(capname, \"stateless-connect\")) {\n@@ -207,14 +201,7 @@ static struct child_process *get_helper(struct transport *transport)\n \t\t\t    capname);\n \t\t}\n \t}\n-\tif (refspecs) {\n-\t\tint i;\n-\t\tdata->refspec_nr = refspec_nr;\n-\t\tdata->refspecs = parse_fetch_refspec(refspec_nr, refspecs);\n-\t\tfor (i = 0; i < refspec_nr; i++)\n-\t\t\tfree((char *)refspecs[i]);\n-\t\tfree(refspecs);\n-\t} else if (data->import || data->bidi_import || data->export) {\n+\tif (!data->rs.nr && (data->import || data->bidi_import || data->export)) {\n \t\twarning(\"This remote helper should implement refspec capability.\");\n \t}\n \tstrbuf_release(&buf);\n@@ -378,8 +365,7 @@ static int release_helper(struct transport *transport)\n {\n \tint res = 0;\n \tstruct helper_data *data = transport->data;\n-\tfree_refspec(data->refspec_nr, data->refspecs);\n-\tdata->refspecs = NULL;\n+\trefspec_clear(&data->rs);\n \tres = disconnect_helper(transport);\n \tfree(transport->data);\n \treturn res;\n@@ -536,8 +522,8 @@ static int fetch_with_import(struct transport *transport,\n \t\tif (posn->status & REF_STATUS_UPTODATE)\n \t\t\tcontinue;\n \t\tname = posn->symref ? posn->symref : posn->name;\n-\t\tif (data->refspecs)\n-\t\t\tprivate = apply_refspecs(data->refspecs, data->refspec_nr, name);\n+\t\tif (data->rs.nr)\n+\t\t\tprivate = apply_refspecs(data->rs.items, data->rs.nr, name);\n \t\telse\n \t\t\tprivate = xstrdup(name);\n \t\tif (private) {\n@@ -815,11 +801,11 @@ static int push_update_refs_status(struct helper_data *data,\n \t\tif (push_update_ref_status(&buf, &ref, remote_refs))\n \t\t\tcontinue;\n \n-\t\tif (flags & TRANSPORT_PUSH_DRY_RUN || !data->refspecs || data->no_private_update)\n+\t\tif (flags & TRANSPORT_PUSH_DRY_RUN || !data->rs.nr || data->no_private_update)\n \t\t\tcontinue;\n \n \t\t/* propagate back the update to the remote namespace */\n-\t\tprivate = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);\n+\t\tprivate = apply_refspecs(data->rs.items, data->rs.nr, ref->name);\n \t\tif (!private)\n \t\t\tcontinue;\n \t\tupdate_ref(\"update by helper\", private, &ref->new_oid, NULL,\n@@ -939,7 +925,7 @@ static int push_refs_with_export(struct transport *transport,\n \tstruct string_list revlist_args = STRING_LIST_INIT_DUP;\n \tstruct strbuf buf = STRBUF_INIT;\n \n-\tif (!data->refspecs)\n+\tif (!data->rs.nr)\n \t\tdie(\"remote-helper doesn't support push; refspec needed\");\n \n \tset_common_push_options(transport, data->name, flags);\n@@ -956,7 +942,7 @@ static int push_refs_with_export(struct transport *transport,\n \t\tchar *private;\n \t\tstruct object_id oid;\n \n-\t\tprivate = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);\n+\t\tprivate = apply_refspecs(data->rs.items, data->rs.nr, ref->name);\n \t\tif (private && !get_oid(private, &oid)) {\n \t\t\tstrbuf_addf(&buf, \"^%s\", private);\n \t\t\tstring_list_append_nodup(&revlist_args,\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347639","messageId":"20180514215626.164960-18-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 17/35] fetch: convert refmap to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:08Z","receivedAt":"2018-05-14T21:57:23Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert the refmap in builtin/fetch.c to be stored in a\n'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c | 17 +++++++----------\n 1 file changed, 7 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 769f9d2be..e2f2f290e 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -60,8 +60,7 @@ static const char *submodule_prefix = \"\";\n static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;\n static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;\n static int shown_url = 0;\n-static int refmap_alloc, refmap_nr;\n-static const char **refmap_array;\n+static struct refspec refmap = REFSPEC_INIT_FETCH;\n static struct list_objects_filter_options filter_options;\n \n static int git_fetch_config(const char *k, const char *v, void *cb)\n@@ -108,14 +107,12 @@ static int gitmodules_fetch_config(const char *var, const char *value, void *cb)\n \n static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)\n {\n-\tALLOC_GROW(refmap_array, refmap_nr + 1, refmap_alloc);\n-\n \t/*\n \t * \"git fetch --refmap='' origin foo\"\n \t * can be used to tell the command not to store anywhere\n \t */\n-\tif (*arg)\n-\t\trefmap_array[refmap_nr++] = arg;\n+\trefspec_append(&refmap, arg);\n+\n \treturn 0;\n }\n \n@@ -403,9 +400,9 @@ static struct ref *get_ref_map(struct transport *transport,\n \t\t * by ref_remove_duplicates() in favor of one of these\n \t\t * opportunistic entries with FETCH_HEAD_IGNORE.\n \t\t */\n-\t\tif (refmap_array) {\n-\t\t\tfetch_refspec = parse_fetch_refspec(refmap_nr, refmap_array);\n-\t\t\tfetch_refspec_nr = refmap_nr;\n+\t\tif (refmap.nr) {\n+\t\t\tfetch_refspec = refmap.items;\n+\t\t\tfetch_refspec_nr = refmap.nr;\n \t\t} else {\n \t\t\tfetch_refspec = transport->remote->fetch.items;\n \t\t\tfetch_refspec_nr = transport->remote->fetch.nr;\n@@ -413,7 +410,7 @@ static struct ref *get_ref_map(struct transport *transport,\n \n \t\tfor (i = 0; i < fetch_refspec_nr; i++)\n \t\t\tget_fetch_map(ref_map, &fetch_refspec[i], &oref_tail, 1);\n-\t} else if (refmap_array) {\n+\t} else if (refmap.nr) {\n \t\tdie(\"--refmap option is only meaningful with command-line refspec(s).\");\n \t} else {\n \t\t/* Use the defaults */\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347640","messageId":"20180514215626.164960-19-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 18/35] refspec: remove the deprecated functions","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:09Z","receivedAt":"2018-05-14T21:57:26Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Now that there are no callers of 'parse_push_refspec()',\n'parse_fetch_refspec()', and 'free_refspec()', remove these\nfunctions.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n refspec.c | 49 -------------------------------------------------\n refspec.h |  5 -----\n 2 files changed, 54 deletions(-)\n\ndiff --git a/refspec.c b/refspec.c\nindex 98c99cd84..7953a70d0 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -121,55 +121,6 @@ static int parse_refspec(struct refspec_item *rs, const char *refspec, int fetch\n \treturn 1;\n }\n \n-static struct refspec_item *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n-{\n-\tint i;\n-\tstruct refspec_item *rs = xcalloc(nr_refspec, sizeof(*rs));\n-\n-\tfor (i = 0; i < nr_refspec; i++) {\n-\t\tif (!parse_refspec(&rs[i], refspec[i], fetch))\n-\t\t\tgoto invalid;\n-\t}\n-\n-\treturn rs;\n-\n- invalid:\n-\tif (verify) {\n-\t\t/*\n-\t\t * nr_refspec must be greater than zero and i must be valid\n-\t\t * since it is only possible to reach this point from within\n-\t\t * the for loop above.\n-\t\t */\n-\t\tfree_refspec(i+1, rs);\n-\t\treturn NULL;\n-\t}\n-\tdie(\"Invalid refspec '%s'\", refspec[i]);\n-}\n-\n-struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec)\n-{\n-\treturn parse_refspec_internal(nr_refspec, refspec, 1, 0);\n-}\n-\n-struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec)\n-{\n-\treturn parse_refspec_internal(nr_refspec, refspec, 0, 0);\n-}\n-\n-void free_refspec(int nr_refspec, struct refspec_item *refspec)\n-{\n-\tint i;\n-\n-\tif (!refspec)\n-\t\treturn;\n-\n-\tfor (i = 0; i < nr_refspec; i++) {\n-\t\tfree(refspec[i].src);\n-\t\tfree(refspec[i].dst);\n-\t}\n-\tfree(refspec);\n-}\n-\n void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)\n {\n \tmemset(item, 0, sizeof(*item));\ndiff --git a/refspec.h b/refspec.h\nindex b009440c0..374f8ea63 100644\n--- a/refspec.h\n+++ b/refspec.h\n@@ -14,11 +14,6 @@ struct refspec_item {\n \tchar *dst;\n };\n \n-struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec);\n-struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec);\n-\n-void free_refspec(int nr_refspec, struct refspec_item *refspec);\n-\n #define REFSPEC_FETCH 1\n #define REFSPEC_PUSH 0\n \n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347641","messageId":"20180514215626.164960-20-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 19/35] fetch: convert do_fetch to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:10Z","receivedAt":"2018-05-14T21:57:29Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'do_fetch()' to take a 'struct refspec' as a parameter instead\nof a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c | 10 +++++-----\n 1 file changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex e2f2f290e..733feb19c 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1112,7 +1112,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)\n }\n \n static int do_fetch(struct transport *transport,\n-\t\t    struct refspec_item *refs, int ref_count)\n+\t\t    struct refspec *rs)\n {\n \tstruct string_list existing_refs = STRING_LIST_INIT_DUP;\n \tstruct ref *ref_map;\n@@ -1136,7 +1136,7 @@ static int do_fetch(struct transport *transport,\n \t\t\tgoto cleanup;\n \t}\n \n-\tref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);\n+\tref_map = get_ref_map(transport, rs->items, rs->nr, tags, &autotags);\n \tif (!update_head_ok)\n \t\tcheck_not_current_branch(ref_map);\n \n@@ -1160,8 +1160,8 @@ static int do_fetch(struct transport *transport,\n \t\t * explicitly (via command line or configuration); we\n \t\t * don't care whether --tags was specified.\n \t\t */\n-\t\tif (ref_count) {\n-\t\t\tprune_refs(refs, ref_count, ref_map, transport->url);\n+\t\tif (rs->nr) {\n+\t\t\tprune_refs(rs->items, rs->nr, ref_map, transport->url);\n \t\t} else {\n \t\t\tprune_refs(transport->remote->fetch.items,\n \t\t\t\t   transport->remote->fetch.nr,\n@@ -1410,7 +1410,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru\n \n \tsigchain_push_common(unlock_pack_on_signal);\n \tatexit(unlock_pack);\n-\texit_code = do_fetch(gtransport, rs.items, rs.nr);\n+\texit_code = do_fetch(gtransport, &rs);\n \trefspec_clear(&rs);\n \ttransport_disconnect(gtransport);\n \tgtransport = NULL;\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347642","messageId":"20180514215626.164960-23-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 22/35] remote: convert get_stale_heads to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:13Z","receivedAt":"2018-05-14T21:57:39Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'get_stale_heads()' to take a 'struct refspec' as a parameter instead\nof a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c  |  2 +-\n builtin/remote.c |  3 +--\n remote.c         | 18 +++++++++---------\n remote.h         |  2 +-\n 4 files changed, 12 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex eebeebfd8..af7064dce 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -963,7 +963,7 @@ static int prune_refs(struct refspec *rs, struct ref *ref_map,\n \t\t      const char *raw_url)\n {\n \tint url_len, i, result = 0;\n-\tstruct ref *ref, *stale_refs = get_stale_heads(rs->items, rs->nr, ref_map);\n+\tstruct ref *ref, *stale_refs = get_stale_heads(rs, ref_map);\n \tchar *url;\n \tint summary_width = transport_summary_width(stale_refs);\n \tconst char *dangling_msg = dry_run\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 94dfcb78b..b8e66589f 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -347,8 +347,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat\n \t\telse\n \t\t\tstring_list_append(&states->tracked, abbrev_branch(ref->name));\n \t}\n-\tstale_refs = get_stale_heads(states->remote->fetch.items,\n-\t\t\t\t     states->remote->fetch.nr, fetch_map);\n+\tstale_refs = get_stale_heads(&states->remote->fetch, fetch_map);\n \tfor (ref = stale_refs; ref; ref = ref->next) {\n \t\tstruct string_list_item *item =\n \t\t\tstring_list_append(&states->stale, abbrev_branch(ref->name));\ndiff --git a/remote.c b/remote.c\nindex db1e4edb7..f2e97c545 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -707,7 +707,9 @@ static int match_name_with_pattern(const char *key, const char *name,\n \treturn ret;\n }\n \n-static void query_refspecs_multiple(struct refspec_item *refs, int ref_count, struct refspec_item *query, struct string_list *results)\n+static void query_refspecs_multiple(struct refspec *rs,\n+\t\t\t\t    struct refspec_item *query,\n+\t\t\t\t    struct string_list *results)\n {\n \tint i;\n \tint find_src = !query->src;\n@@ -715,8 +717,8 @@ static void query_refspecs_multiple(struct refspec_item *refs, int ref_count, st\n \tif (find_src && !query->dst)\n \t\terror(\"query_refspecs_multiple: need either src or dst\");\n \n-\tfor (i = 0; i < ref_count; i++) {\n-\t\tstruct refspec_item *refspec = &refs[i];\n+\tfor (i = 0; i < rs->nr; i++) {\n+\t\tstruct refspec_item *refspec = &rs->items[i];\n \t\tconst char *key = find_src ? refspec->dst : refspec->src;\n \t\tconst char *value = find_src ? refspec->src : refspec->dst;\n \t\tconst char *needle = find_src ? query->dst : query->src;\n@@ -2077,8 +2079,7 @@ struct ref *guess_remote_head(const struct ref *head,\n struct stale_heads_info {\n \tstruct string_list *ref_names;\n \tstruct ref **stale_refs_tail;\n-\tstruct refspec_item *refs;\n-\tint ref_count;\n+\tstruct refspec *rs;\n };\n \n static int get_stale_heads_cb(const char *refname, const struct object_id *oid,\n@@ -2091,7 +2092,7 @@ static int get_stale_heads_cb(const char *refname, const struct object_id *oid,\n \tmemset(&query, 0, sizeof(struct refspec_item));\n \tquery.dst = (char *)refname;\n \n-\tquery_refspecs_multiple(info->refs, info->ref_count, &query, &matches);\n+\tquery_refspecs_multiple(info->rs, &query, &matches);\n \tif (matches.nr == 0)\n \t\tgoto clean_exit; /* No matches */\n \n@@ -2119,7 +2120,7 @@ static int get_stale_heads_cb(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n-struct ref *get_stale_heads(struct refspec_item *refs, int ref_count, struct ref *fetch_map)\n+struct ref *get_stale_heads(struct refspec *rs, struct ref *fetch_map)\n {\n \tstruct ref *ref, *stale_refs = NULL;\n \tstruct string_list ref_names = STRING_LIST_INIT_NODUP;\n@@ -2127,8 +2128,7 @@ struct ref *get_stale_heads(struct refspec_item *refs, int ref_count, struct ref\n \n \tinfo.ref_names = &ref_names;\n \tinfo.stale_refs_tail = &stale_refs;\n-\tinfo.refs = refs;\n-\tinfo.ref_count = ref_count;\n+\tinfo.rs = rs;\n \tfor (ref = fetch_map; ref; ref = ref->next)\n \t\tstring_list_append(&ref_names, ref->name);\n \tstring_list_sort(&ref_names);\ndiff --git a/remote.h b/remote.h\nindex e7d00fe2a..5ac7536f5 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -267,7 +267,7 @@ struct ref *guess_remote_head(const struct ref *head,\n \t\t\t      int all);\n \n /* Return refs which no longer exist on remote */\n-struct ref *get_stale_heads(struct refspec_item *refs, int ref_count, struct ref *fetch_map);\n+struct ref *get_stale_heads(struct refspec *rs, struct ref *fetch_map);\n \n /*\n  * Compare-and-swap\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347643","messageId":"20180514215626.164960-25-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 24/35] remote: convert query_refspecs to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:15Z","receivedAt":"2018-05-14T21:57:41Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'query_refspecs()' to take a 'struct refspec' as a parameter instead\nof a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/push.c |  3 +--\n remote.c       | 10 +++++-----\n remote.h       |  2 +-\n 3 files changed, 7 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 509dc6677..6b7e45890 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -83,8 +83,7 @@ static const char *map_refspec(const char *ref,\n \t\tstruct refspec_item query;\n \t\tmemset(&query, 0, sizeof(struct refspec_item));\n \t\tquery.src = matched->name;\n-\t\tif (!query_refspecs(remote->push.items, remote->push.nr, &query) &&\n-\t\t    query.dst) {\n+\t\tif (!query_refspecs(&remote->push, &query) && query.dst) {\n \t\t\tstruct strbuf buf = STRBUF_INIT;\n \t\t\tstrbuf_addf(&buf, \"%s%s:%s\",\n \t\t\t\t    query.force ? \"+\" : \"\",\ndiff --git a/remote.c b/remote.c\nindex 54297de3d..3d7bc7504 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -734,7 +734,7 @@ static void query_refspecs_multiple(struct refspec *rs,\n \t}\n }\n \n-int query_refspecs(struct refspec_item *refs, int ref_count, struct refspec_item *query)\n+int query_refspecs(struct refspec *rs, struct refspec_item *query)\n {\n \tint i;\n \tint find_src = !query->src;\n@@ -744,8 +744,8 @@ int query_refspecs(struct refspec_item *refs, int ref_count, struct refspec_item\n \tif (find_src && !query->dst)\n \t\treturn error(\"query_refspecs: need either src or dst\");\n \n-\tfor (i = 0; i < ref_count; i++) {\n-\t\tstruct refspec_item *refspec = &refs[i];\n+\tfor (i = 0; i < rs->nr; i++) {\n+\t\tstruct refspec_item *refspec = &rs->items[i];\n \t\tconst char *key = find_src ? refspec->dst : refspec->src;\n \t\tconst char *value = find_src ? refspec->src : refspec->dst;\n \n@@ -772,7 +772,7 @@ char *apply_refspecs(struct refspec *rs, const char *name)\n \tmemset(&query, 0, sizeof(struct refspec_item));\n \tquery.src = (char *)name;\n \n-\tif (query_refspecs(rs->items, rs->nr, &query))\n+\tif (query_refspecs(rs, &query))\n \t\treturn NULL;\n \n \treturn query.dst;\n@@ -780,7 +780,7 @@ char *apply_refspecs(struct refspec *rs, const char *name)\n \n int remote_find_tracking(struct remote *remote, struct refspec_item *refspec)\n {\n-\treturn query_refspecs(remote->fetch.items, remote->fetch.nr, refspec);\n+\treturn query_refspecs(&remote->fetch, refspec);\n }\n \n static struct ref *alloc_ref_with_prefix(const char *prefix, size_t prefixlen,\ndiff --git a/remote.h b/remote.h\nindex 4e7590b55..edcfc3600 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -158,7 +158,7 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);\n  */\n struct ref *ref_remove_duplicates(struct ref *ref_map);\n \n-extern int query_refspecs(struct refspec_item *specs, int nr, struct refspec_item *query);\n+int query_refspecs(struct refspec *rs, struct refspec_item *query);\n char *apply_refspecs(struct refspec *rs, const char *name);\n \n int check_push_refs(struct ref *src, int nr_refspec, const char **refspec);\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347644","messageId":"20180514215626.164960-26-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 25/35] remote: convert get_ref_match to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:16Z","receivedAt":"2018-05-14T21:57:45Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'get_ref_match()' to take a 'struct refspec' as a parameter\ninstead of a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n remote.c | 26 ++++++++++++++------------\n 1 file changed, 14 insertions(+), 12 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 3d7bc7504..0879ee587 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1091,27 +1091,29 @@ static int match_explicit_refs(struct ref *src, struct ref *dst,\n \treturn errs;\n }\n \n-static char *get_ref_match(const struct refspec_item *rs, int rs_nr, const struct ref *ref,\n-\t\tint send_mirror, int direction, const struct refspec_item **ret_pat)\n+static char *get_ref_match(const struct refspec *rs, const struct ref *ref,\n+\t\t\t   int send_mirror, int direction,\n+\t\t\t   const struct refspec_item **ret_pat)\n {\n \tconst struct refspec_item *pat;\n \tchar *name;\n \tint i;\n \tint matching_refs = -1;\n-\tfor (i = 0; i < rs_nr; i++) {\n-\t\tif (rs[i].matching &&\n-\t\t    (matching_refs == -1 || rs[i].force)) {\n+\tfor (i = 0; i < rs->nr; i++) {\n+\t\tconst struct refspec_item *item = &rs->items[i];\n+\t\tif (item->matching &&\n+\t\t    (matching_refs == -1 || item->force)) {\n \t\t\tmatching_refs = i;\n \t\t\tcontinue;\n \t\t}\n \n-\t\tif (rs[i].pattern) {\n-\t\t\tconst char *dst_side = rs[i].dst ? rs[i].dst : rs[i].src;\n+\t\tif (item->pattern) {\n+\t\t\tconst char *dst_side = item->dst ? item->dst : item->src;\n \t\t\tint match;\n \t\t\tif (direction == FROM_SRC)\n-\t\t\t\tmatch = match_name_with_pattern(rs[i].src, ref->name, dst_side, &name);\n+\t\t\t\tmatch = match_name_with_pattern(item->src, ref->name, dst_side, &name);\n \t\t\telse\n-\t\t\t\tmatch = match_name_with_pattern(dst_side, ref->name, rs[i].src, &name);\n+\t\t\t\tmatch = match_name_with_pattern(dst_side, ref->name, item->src, &name);\n \t\t\tif (match) {\n \t\t\t\tmatching_refs = i;\n \t\t\t\tbreak;\n@@ -1121,7 +1123,7 @@ static char *get_ref_match(const struct refspec_item *rs, int rs_nr, const struc\n \tif (matching_refs == -1)\n \t\treturn NULL;\n \n-\tpat = rs + matching_refs;\n+\tpat = &rs->items[matching_refs];\n \tif (pat->matching) {\n \t\t/*\n \t\t * \"matching refs\"; traditionally we pushed everything\n@@ -1318,7 +1320,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\tconst struct refspec_item *pat = NULL;\n \t\tchar *dst_name;\n \n-\t\tdst_name = get_ref_match(rs.items, rs.nr, ref, send_mirror, FROM_SRC, &pat);\n+\t\tdst_name = get_ref_match(&rs, ref, send_mirror, FROM_SRC, &pat);\n \t\tif (!dst_name)\n \t\t\tcontinue;\n \n@@ -1367,7 +1369,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\t\t\t/* We're already sending something to this ref. */\n \t\t\t\tcontinue;\n \n-\t\t\tsrc_name = get_ref_match(rs.items, rs.nr, ref, send_mirror, FROM_DST, NULL);\n+\t\t\tsrc_name = get_ref_match(&rs, ref, send_mirror, FROM_DST, NULL);\n \t\t\tif (src_name) {\n \t\t\t\tif (!src_ref_index.nr)\n \t\t\t\t\tprepare_ref_index(&src_ref_index, src);\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347645","messageId":"20180514215626.164960-27-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 26/35] remote: convert match_explicit_refs to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:17Z","receivedAt":"2018-05-14T21:57:45Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'match_explicit_refs()' to take a 'struct refspec' as a\nparameter instead of a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n remote.c | 9 ++++-----\n 1 file changed, 4 insertions(+), 5 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 0879ee587..73d462f24 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1082,12 +1082,11 @@ static int match_explicit(struct ref *src, struct ref *dst,\n }\n \n static int match_explicit_refs(struct ref *src, struct ref *dst,\n-\t\t\t       struct ref ***dst_tail, struct refspec_item *rs,\n-\t\t\t       int rs_nr)\n+\t\t\t       struct ref ***dst_tail, struct refspec *rs)\n {\n \tint i, errs;\n-\tfor (i = errs = 0; i < rs_nr; i++)\n-\t\terrs += match_explicit(src, dst, dst_tail, &rs[i]);\n+\tfor (i = errs = 0; i < rs->nr; i++)\n+\t\terrs += match_explicit(src, dst, dst_tail, &rs->items[i]);\n \treturn errs;\n }\n \n@@ -1311,7 +1310,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\trefspec = default_refspec;\n \t}\n \trefspec_appendn(&rs, refspec, nr_refspec);\n-\terrs = match_explicit_refs(src, *dst, &dst_tail, rs.items, rs.nr);\n+\terrs = match_explicit_refs(src, *dst, &dst_tail, &rs);\n \n \t/* pick the remainder */\n \tfor (ref = src; ref; ref = ref->next) {\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347646","messageId":"20180514215626.164960-28-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 27/35] push: check for errors earlier","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:18Z","receivedAt":"2018-05-14T21:57:50Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Move the error checking for using the \"--mirror\", \"--all\", and \"--tags\"\noptions earlier and explicitly check for the presence of the flags\ninstead of checking for a side-effect of the flag.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/push.c | 31 ++++++++++++++-----------------\n 1 file changed, 14 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 6b7e45890..df5df6c0d 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -417,23 +417,6 @@ static int do_push(const char *repo, int flags,\n \tif (push_options->nr)\n \t\tflags |= TRANSPORT_PUSH_OPTIONS;\n \n-\tif ((flags & TRANSPORT_PUSH_ALL) && refspec) {\n-\t\tif (!strcmp(*refspec, \"refs/tags/*\"))\n-\t\t\treturn error(_(\"--all and --tags are incompatible\"));\n-\t\treturn error(_(\"--all can't be combined with refspecs\"));\n-\t}\n-\n-\tif ((flags & TRANSPORT_PUSH_MIRROR) && refspec) {\n-\t\tif (!strcmp(*refspec, \"refs/tags/*\"))\n-\t\t\treturn error(_(\"--mirror and --tags are incompatible\"));\n-\t\treturn error(_(\"--mirror can't be combined with refspecs\"));\n-\t}\n-\n-\tif ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==\n-\t\t\t\t(TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {\n-\t\treturn error(_(\"--all and --mirror are incompatible\"));\n-\t}\n-\n \tif (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {\n \t\tif (remote->push.raw_nr) {\n \t\t\trefspec = remote->push.raw;\n@@ -625,6 +608,20 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"--delete is incompatible with --all, --mirror and --tags\"));\n \tif (deleterefs && argc < 2)\n \t\tdie(_(\"--delete doesn't make sense without any refs\"));\n+\tif (flags & TRANSPORT_PUSH_ALL) {\n+\t\tif (tags)\n+\t\t\tdie(_(\"--all and --tags are incompatible\"));\n+\t\tif (argc >= 2)\n+\t\t\tdie(_(\"--all can't be combined with refspecs\"));\n+\t}\n+\tif (flags & TRANSPORT_PUSH_MIRROR) {\n+\t\tif (tags)\n+\t\t\tdie(_(\"--mirror and --tags are incompatible\"));\n+\t\tif (argc >= 2)\n+\t\t\tdie(_(\"--mirror can't be combined with refspecs\"));\n+\t}\n+\tif ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR))\n+\t\tdie(_(\"--all and --mirror are incompatible\"));\n \n \tif (recurse_submodules == RECURSE_SUBMODULES_CHECK)\n \t\tflags |= TRANSPORT_RECURSE_SUBMODULES_CHECK;\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347647","messageId":"20180514215626.164960-33-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 32/35] http-push: store refspecs in a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:23Z","receivedAt":"2018-05-14T21:58:01Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert http-push.c to store refspecs in a 'struct refspec' instead of\nin an array of 'const char *'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n http-push.c | 17 ++++++++---------\n 1 file changed, 8 insertions(+), 9 deletions(-)\n\ndiff --git a/http-push.c b/http-push.c\nindex f308ce019..a724ef03f 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1692,8 +1692,7 @@ int cmd_main(int argc, const char **argv)\n {\n \tstruct transfer_request *request;\n \tstruct transfer_request *next_request;\n-\tint nr_refspec = 0;\n-\tconst char **refspec = NULL;\n+\tstruct refspec rs = REFSPEC_INIT_PUSH;\n \tstruct remote_lock *ref_lock = NULL;\n \tstruct remote_lock *info_ref_lock = NULL;\n \tstruct rev_info revs;\n@@ -1756,8 +1755,7 @@ int cmd_main(int argc, const char **argv)\n \t\t\t}\n \t\t\tcontinue;\n \t\t}\n-\t\trefspec = argv;\n-\t\tnr_refspec = argc - i;\n+\t\trefspec_appendn(&rs, argv, argc - i);\n \t\tbreak;\n \t}\n \n@@ -1768,7 +1766,7 @@ int cmd_main(int argc, const char **argv)\n \tif (!repo->url)\n \t\tusage(http_push_usage);\n \n-\tif (delete_branch && nr_refspec != 1)\n+\tif (delete_branch && rs.nr != 1)\n \t\tdie(\"You must specify only one branch name when deleting a remote branch\");\n \n \tsetup_git_directory();\n@@ -1814,18 +1812,19 @@ int cmd_main(int argc, const char **argv)\n \n \t/* Remove a remote branch if -d or -D was specified */\n \tif (delete_branch) {\n-\t\tif (delete_remote_branch(refspec[0], force_delete) == -1) {\n+\t\tconst char *branch = rs.items[i].src;\n+\t\tif (delete_remote_branch(branch, force_delete) == -1) {\n \t\t\tfprintf(stderr, \"Unable to delete remote branch %s\\n\",\n-\t\t\t\trefspec[0]);\n+\t\t\t\tbranch);\n \t\t\tif (helper_status)\n-\t\t\t\tprintf(\"error %s cannot remove\\n\", refspec[0]);\n+\t\t\t\tprintf(\"error %s cannot remove\\n\", branch);\n \t\t}\n \t\tgoto cleanup;\n \t}\n \n \t/* match them up */\n \tif (match_push_refs(local_refs, &remote_refs,\n-\t\t\t    nr_refspec, (const char **) refspec, push_all)) {\n+\t\t\t    rs.raw_nr, rs.raw, push_all)) {\n \t\trc = -1;\n \t\tgoto cleanup;\n \t}\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347648","messageId":"20180514215626.164960-34-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 33/35] remote: convert match_push_refs to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:24Z","receivedAt":"2018-05-14T21:58:05Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'match_push_refs()' to take a 'struct refspec' as a parameter\ninstead of an array of 'const char *'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/remote.c    |  3 +--\n builtin/send-pack.c |  2 +-\n http-push.c         |  3 +--\n remote.c            | 21 ++++++++-------------\n remote.h            |  2 +-\n transport.c         |  4 +---\n 6 files changed, 13 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex b8e66589f..b84175cc6 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -387,8 +387,7 @@ static int get_push_ref_states(const struct ref *remote_refs,\n \tlocal_refs = get_local_heads();\n \tpush_map = copy_ref_list(remote_refs);\n \n-\tmatch_push_refs(local_refs, &push_map, remote->push.raw_nr,\n-\t\t\tremote->push.raw, MATCH_REFS_NONE);\n+\tmatch_push_refs(local_refs, &push_map, &remote->push, MATCH_REFS_NONE);\n \n \tstates->push.strdup_strings = 1;\n \tfor (ref = push_map; ref; ref = ref->next) {\ndiff --git a/builtin/send-pack.c b/builtin/send-pack.c\nindex 7c34bf467..4923b1058 100644\n--- a/builtin/send-pack.c\n+++ b/builtin/send-pack.c\n@@ -275,7 +275,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \t\tflags |= MATCH_REFS_MIRROR;\n \n \t/* match them up */\n-\tif (match_push_refs(local_refs, &remote_refs, rs.raw_nr, rs.raw, flags))\n+\tif (match_push_refs(local_refs, &remote_refs, &rs, flags))\n \t\treturn -1;\n \n \tif (!is_empty_cas(&cas))\ndiff --git a/http-push.c b/http-push.c\nindex a724ef03f..ea5af6227 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1823,8 +1823,7 @@ int cmd_main(int argc, const char **argv)\n \t}\n \n \t/* match them up */\n-\tif (match_push_refs(local_refs, &remote_refs,\n-\t\t\t    rs.raw_nr, rs.raw, push_all)) {\n+\tif (match_push_refs(local_refs, &remote_refs, &rs, push_all)) {\n \t\trc = -1;\n \t\tgoto cleanup;\n \t}\ndiff --git a/remote.c b/remote.c\nindex 73d462f24..0e882d812 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1294,23 +1294,20 @@ int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)\n  * dst (e.g. pushing to a new branch, done in match_explicit_refs).\n  */\n int match_push_refs(struct ref *src, struct ref **dst,\n-\t\t    int nr_refspec, const char **refspec, int flags)\n+\t\t    struct refspec *rs, int flags)\n {\n-\tstruct refspec rs = REFSPEC_INIT_PUSH;\n \tint send_all = flags & MATCH_REFS_ALL;\n \tint send_mirror = flags & MATCH_REFS_MIRROR;\n \tint send_prune = flags & MATCH_REFS_PRUNE;\n \tint errs;\n-\tstatic const char *default_refspec[] = { \":\", NULL };\n \tstruct ref *ref, **dst_tail = tail_ref(dst);\n \tstruct string_list dst_ref_index = STRING_LIST_INIT_NODUP;\n \n-\tif (!nr_refspec) {\n-\t\tnr_refspec = 1;\n-\t\trefspec = default_refspec;\n-\t}\n-\trefspec_appendn(&rs, refspec, nr_refspec);\n-\terrs = match_explicit_refs(src, *dst, &dst_tail, &rs);\n+\t/* If no refspec is provided, use the default \":\" */\n+\tif (!rs->nr)\n+\t\trefspec_append(rs, \":\");\n+\n+\terrs = match_explicit_refs(src, *dst, &dst_tail, rs);\n \n \t/* pick the remainder */\n \tfor (ref = src; ref; ref = ref->next) {\n@@ -1319,7 +1316,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\tconst struct refspec_item *pat = NULL;\n \t\tchar *dst_name;\n \n-\t\tdst_name = get_ref_match(&rs, ref, send_mirror, FROM_SRC, &pat);\n+\t\tdst_name = get_ref_match(rs, ref, send_mirror, FROM_SRC, &pat);\n \t\tif (!dst_name)\n \t\t\tcontinue;\n \n@@ -1368,7 +1365,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\t\t\t/* We're already sending something to this ref. */\n \t\t\t\tcontinue;\n \n-\t\t\tsrc_name = get_ref_match(&rs, ref, send_mirror, FROM_DST, NULL);\n+\t\t\tsrc_name = get_ref_match(rs, ref, send_mirror, FROM_DST, NULL);\n \t\t\tif (src_name) {\n \t\t\t\tif (!src_ref_index.nr)\n \t\t\t\t\tprepare_ref_index(&src_ref_index, src);\n@@ -1381,8 +1378,6 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\tstring_list_clear(&src_ref_index, 0);\n \t}\n \n-\trefspec_clear(&rs);\n-\n \tif (errs)\n \t\treturn -1;\n \treturn 0;\ndiff --git a/remote.h b/remote.h\nindex edcfc3600..d5b5f24ac 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -163,7 +163,7 @@ char *apply_refspecs(struct refspec *rs, const char *name);\n \n int check_push_refs(struct ref *src, int nr_refspec, const char **refspec);\n int match_push_refs(struct ref *src, struct ref **dst,\n-\t\t    int nr_refspec, const char **refspec, int all);\n+\t\t    struct refspec *rs, int flags);\n void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\n \tint force_update);\n \ndiff --git a/transport.c b/transport.c\nindex fe96c0b80..24a97d9e8 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1127,10 +1127,8 @@ int transport_push(struct transport *transport,\n \t\tif (flags & TRANSPORT_PUSH_FOLLOW_TAGS)\n \t\t\tmatch_flags |= MATCH_REFS_FOLLOW_TAGS;\n \n-\t\tif (match_push_refs(local_refs, &remote_refs,\n-\t\t\t\t    rs->raw_nr, rs->raw, match_flags)) {\n+\t\tif (match_push_refs(local_refs, &remote_refs, rs, match_flags))\n \t\t\treturn -1;\n-\t\t}\n \n \t\tif (transport->smart_options &&\n \t\t    transport->smart_options->cas &&\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347649","messageId":"20180514215626.164960-35-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 34/35] remote: convert check_push_refs to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:25Z","receivedAt":"2018-05-14T21:58:09Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'check_push_refs()' to take a 'struct refspec' as a parameter\ninstead of an array of 'const char *'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n remote.c    | 14 +++++---------\n remote.h    |  2 +-\n transport.c |  2 +-\n 3 files changed, 7 insertions(+), 11 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 0e882d812..8e6522f4d 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1264,24 +1264,20 @@ static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)\n  * but we can catch some errors early before even talking to the\n  * remote side.\n  */\n-int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)\n+int check_push_refs(struct ref *src, struct refspec *rs)\n {\n-\tstruct refspec refspec = REFSPEC_INIT_PUSH;\n \tint ret = 0;\n \tint i;\n \n-\trefspec_appendn(&refspec, refspec_names, nr_refspec);\n-\n-\tfor (i = 0; i < refspec.nr; i++) {\n-\t\tstruct refspec_item *rs = &refspec.items[i];\n+\tfor (i = 0; i < rs->nr; i++) {\n+\t\tstruct refspec_item *item = &rs->items[i];\n \n-\t\tif (rs->pattern || rs->matching)\n+\t\tif (item->pattern || item->matching)\n \t\t\tcontinue;\n \n-\t\tret |= match_explicit_lhs(src, rs, NULL, NULL);\n+\t\tret |= match_explicit_lhs(src, item, NULL, NULL);\n \t}\n \n-\trefspec_clear(&refspec);\n \treturn ret;\n }\n \ndiff --git a/remote.h b/remote.h\nindex d5b5f24ac..9014f707f 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -161,7 +161,7 @@ struct ref *ref_remove_duplicates(struct ref *ref_map);\n int query_refspecs(struct refspec *rs, struct refspec_item *query);\n char *apply_refspecs(struct refspec *rs, const char *name);\n \n-int check_push_refs(struct ref *src, int nr_refspec, const char **refspec);\n+int check_push_refs(struct ref *src, struct refspec *rs);\n int match_push_refs(struct ref *src, struct ref **dst,\n \t\t    struct refspec *rs, int flags);\n void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\ndiff --git a/transport.c b/transport.c\nindex 24a97d9e8..e32bc320c 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1090,7 +1090,7 @@ int transport_push(struct transport *transport,\n \t\tstruct argv_array ref_prefixes = ARGV_ARRAY_INIT;\n \t\tint i;\n \n-\t\tif (check_push_refs(local_refs, rs->raw_nr, rs->raw) < 0)\n+\t\tif (check_push_refs(local_refs, rs) < 0)\n \t\t\treturn -1;\n \n \t\tfor (i = 0; i < rs->nr; i++) {\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347650","messageId":"20180514215626.164960-36-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 35/35] submodule: convert push_unpushed_submodules to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:26Z","receivedAt":"2018-05-14T21:58:10Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'push_unpushed_submodules()' to take a 'struct refspec' as a\nparameter instead of an array of 'const char *'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n submodule.c | 19 +++++++++----------\n submodule.h |  3 ++-\n transport.c |  2 +-\n 3 files changed, 12 insertions(+), 12 deletions(-)\n\ndiff --git a/submodule.c b/submodule.c\nindex 74d35b257..cdeadd80e 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -968,7 +968,7 @@ int find_unpushed_submodules(struct oid_array *commits,\n \n static int push_submodule(const char *path,\n \t\t\t  const struct remote *remote,\n-\t\t\t  const char **refspec, int refspec_nr,\n+\t\t\t  const struct refspec *rs,\n \t\t\t  const struct string_list *push_options,\n \t\t\t  int dry_run)\n {\n@@ -991,8 +991,8 @@ static int push_submodule(const char *path,\n \t\tif (remote->origin != REMOTE_UNCONFIGURED) {\n \t\t\tint i;\n \t\t\targv_array_push(&cp.args, remote->name);\n-\t\t\tfor (i = 0; i < refspec_nr; i++)\n-\t\t\t\targv_array_push(&cp.args, refspec[i]);\n+\t\t\tfor (i = 0; i < rs->raw_nr; i++)\n+\t\t\t\targv_array_push(&cp.args, rs->raw[i]);\n \t\t}\n \n \t\tprepare_submodule_repo_env(&cp.env_array);\n@@ -1013,7 +1013,7 @@ static int push_submodule(const char *path,\n  */\n static void submodule_push_check(const char *path, const char *head,\n \t\t\t\t const struct remote *remote,\n-\t\t\t\t const char **refspec, int refspec_nr)\n+\t\t\t\t const struct refspec *rs)\n {\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tint i;\n@@ -1023,8 +1023,8 @@ static void submodule_push_check(const char *path, const char *head,\n \targv_array_push(&cp.args, head);\n \targv_array_push(&cp.args, remote->name);\n \n-\tfor (i = 0; i < refspec_nr; i++)\n-\t\targv_array_push(&cp.args, refspec[i]);\n+\tfor (i = 0; i < rs->raw_nr; i++)\n+\t\targv_array_push(&cp.args, rs->raw[i]);\n \n \tprepare_submodule_repo_env(&cp.env_array);\n \tcp.git_cmd = 1;\n@@ -1043,7 +1043,7 @@ static void submodule_push_check(const char *path, const char *head,\n \n int push_unpushed_submodules(struct oid_array *commits,\n \t\t\t     const struct remote *remote,\n-\t\t\t     const char **refspec, int refspec_nr,\n+\t\t\t     const struct refspec *rs,\n \t\t\t     const struct string_list *push_options,\n \t\t\t     int dry_run)\n {\n@@ -1069,8 +1069,7 @@ int push_unpushed_submodules(struct oid_array *commits,\n \n \t\tfor (i = 0; i < needs_pushing.nr; i++)\n \t\t\tsubmodule_push_check(needs_pushing.items[i].string,\n-\t\t\t\t\t     head, remote,\n-\t\t\t\t\t     refspec, refspec_nr);\n+\t\t\t\t\t     head, remote, rs);\n \t\tfree(head);\n \t}\n \n@@ -1078,7 +1077,7 @@ int push_unpushed_submodules(struct oid_array *commits,\n \tfor (i = 0; i < needs_pushing.nr; i++) {\n \t\tconst char *path = needs_pushing.items[i].string;\n \t\tfprintf(stderr, \"Pushing submodule '%s'\\n\", path);\n-\t\tif (!push_submodule(path, remote, refspec, refspec_nr,\n+\t\tif (!push_submodule(path, remote, rs,\n \t\t\t\t    push_options, dry_run)) {\n \t\t\tfprintf(stderr, \"Unable to push submodule '%s'\\n\", path);\n \t\t\tret = 0;\ndiff --git a/submodule.h b/submodule.h\nindex e5526f6aa..aae0c9c8f 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -100,9 +100,10 @@ extern int submodule_touches_in_range(struct object_id *a,\n extern int find_unpushed_submodules(struct oid_array *commits,\n \t\t\t\t    const char *remotes_name,\n \t\t\t\t    struct string_list *needs_pushing);\n+struct refspec;\n extern int push_unpushed_submodules(struct oid_array *commits,\n \t\t\t\t    const struct remote *remote,\n-\t\t\t\t    const char **refspec, int refspec_nr,\n+\t\t\t\t    const struct refspec *rs,\n \t\t\t\t    const struct string_list *push_options,\n \t\t\t\t    int dry_run);\n /*\ndiff --git a/transport.c b/transport.c\nindex e32bc320c..7e0b9abba 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1157,7 +1157,7 @@ int transport_push(struct transport *transport,\n \n \t\t\tif (!push_unpushed_submodules(&commits,\n \t\t\t\t\t\t      transport->remote,\n-\t\t\t\t\t\t      rs->raw, rs->raw_nr,\n+\t\t\t\t\t\t      rs,\n \t\t\t\t\t\t      transport->push_options,\n \t\t\t\t\t\t      pretend)) {\n \t\t\t\toid_array_clear(&commits);\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347651","messageId":"20180514215626.164960-32-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 31/35] transport: remove transport_verify_remote_names","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:22Z","receivedAt":"2018-05-14T21:58:14Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Remove 'transprot_verify_remote_names()' because all callers have\nmigrated to using 'struct refspec' which performs the same checks in\n'parse_refspec()'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/send-pack.c |  2 --\n transport.c         | 24 ------------------------\n transport.h         |  2 --\n 3 files changed, 28 deletions(-)\n\ndiff --git a/builtin/send-pack.c b/builtin/send-pack.c\nindex ef512616f..7c34bf467 100644\n--- a/builtin/send-pack.c\n+++ b/builtin/send-pack.c\n@@ -265,8 +265,6 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \t\tBUG(\"unknown protocol version\");\n \t}\n \n-\ttransport_verify_remote_names(rs.raw_nr, rs.raw);\n-\n \tlocal_refs = get_local_heads();\n \n \tflags = MATCH_REFS_NONE;\ndiff --git a/transport.c b/transport.c\nindex a89f17744..fe96c0b80 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -619,29 +619,6 @@ void transport_print_push_status(const char *dest, struct ref *refs,\n \tfree(head);\n }\n \n-void transport_verify_remote_names(int nr_heads, const char **heads)\n-{\n-\tint i;\n-\n-\tfor (i = 0; i < nr_heads; i++) {\n-\t\tconst char *local = heads[i];\n-\t\tconst char *remote = strrchr(heads[i], ':');\n-\n-\t\tif (*local == '+')\n-\t\t\tlocal++;\n-\n-\t\t/* A matching refspec is okay.  */\n-\t\tif (remote == local && remote[1] == '\\0')\n-\t\t\tcontinue;\n-\n-\t\tremote = remote ? (remote + 1) : local;\n-\t\tif (check_refname_format(remote,\n-\t\t\t\tREFNAME_ALLOW_ONELEVEL|REFNAME_REFSPEC_PATTERN))\n-\t\t\tdie(\"remote part of refspec is not a valid name in %s\",\n-\t\t\t\theads[i]);\n-\t}\n-}\n-\n static int git_transport_push(struct transport *transport, struct ref *remote_refs, int flags)\n {\n \tstruct git_transport_data *data = transport->data;\n@@ -1097,7 +1074,6 @@ int transport_push(struct transport *transport,\n \t\t   unsigned int *reject_reasons)\n {\n \t*reject_reasons = 0;\n-\ttransport_verify_remote_names(rs->raw_nr, rs->raw);\n \n \tif (transport_color_config() < 0)\n \t\treturn -1;\ndiff --git a/transport.h b/transport.h\nindex e2c809af4..bac085ce0 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -227,8 +227,6 @@ int transport_helper_init(struct transport *transport, const char *name);\n int bidirectional_transfer_loop(int input, int output);\n \n /* common methods used by transport.c and builtin/send-pack.c */\n-void transport_verify_remote_names(int nr_heads, const char **heads);\n-\n void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose);\n \n int transport_refs_pushed(struct ref *ref);\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347652","messageId":"20180514215626.164960-29-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 28/35] push: convert to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:19Z","receivedAt":"2018-05-14T21:58:51Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert the refspecs in builtin/push.c to be stored in a 'struct\nrefspec' instead of being stored in a list of 'struct refspec_item's.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/push.c | 38 +++++++++++++++-----------------------\n 1 file changed, 15 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/push.c b/builtin/push.c\nindex df5df6c0d..ef42979d1 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -57,19 +57,10 @@ static enum transport_family family;\n \n static struct push_cas_option cas;\n \n-static const char **refspec;\n-static int refspec_nr;\n-static int refspec_alloc;\n+static struct refspec rs = REFSPEC_INIT_PUSH;\n \n static struct string_list push_options_config = STRING_LIST_INIT_DUP;\n \n-static void add_refspec(const char *ref)\n-{\n-\trefspec_nr++;\n-\tALLOC_GROW(refspec, refspec_nr, refspec_alloc);\n-\trefspec[refspec_nr-1] = ref;\n-}\n-\n static const char *map_refspec(const char *ref,\n \t\t\t       struct remote *remote, struct ref *local_refs)\n {\n@@ -138,7 +129,7 @@ static void set_refspecs(const char **refs, int nr, const char *repo)\n \t\t\t}\n \t\t\tref = map_refspec(ref, remote, local_refs);\n \t\t}\n-\t\tadd_refspec(ref);\n+\t\trefspec_append(&rs, ref);\n \t}\n }\n \n@@ -226,7 +217,7 @@ static void setup_push_upstream(struct remote *remote, struct branch *branch,\n \t}\n \n \tstrbuf_addf(&refspec, \"%s:%s\", branch->refname, branch->merge[0]->src);\n-\tadd_refspec(refspec.buf);\n+\trefspec_append(&rs, refspec.buf);\n }\n \n static void setup_push_current(struct remote *remote, struct branch *branch)\n@@ -236,7 +227,7 @@ static void setup_push_current(struct remote *remote, struct branch *branch)\n \tif (!branch)\n \t\tdie(_(message_detached_head_die), remote->name);\n \tstrbuf_addf(&refspec, \"%s:%s\", branch->refname, branch->refname);\n-\tadd_refspec(refspec.buf);\n+\trefspec_append(&rs, refspec.buf);\n }\n \n static int is_workflow_triangular(struct remote *remote)\n@@ -253,7 +244,7 @@ static void setup_default_push_refspecs(struct remote *remote)\n \tswitch (push_default) {\n \tdefault:\n \tcase PUSH_DEFAULT_MATCHING:\n-\t\tadd_refspec(\":\");\n+\t\trefspec_append(&rs, \":\");\n \t\tbreak;\n \n \tcase PUSH_DEFAULT_UNSPECIFIED:\n@@ -341,7 +332,8 @@ static void advise_ref_needs_force(void)\n \tadvise(_(message_advice_ref_needs_force));\n }\n \n-static int push_with_options(struct transport *transport, int flags)\n+static int push_with_options(struct transport *transport, struct refspec *rs,\n+\t\t\t     int flags)\n {\n \tint err;\n \tunsigned int reject_reasons;\n@@ -363,7 +355,7 @@ static int push_with_options(struct transport *transport, int flags)\n \n \tif (verbosity > 0)\n \t\tfprintf(stderr, _(\"Pushing to %s\\n\"), transport->url);\n-\terr = transport_push(transport, refspec_nr, refspec, flags,\n+\terr = transport_push(transport, rs->raw_nr, rs->raw, flags,\n \t\t\t     &reject_reasons);\n \tif (err != 0) {\n \t\tfprintf(stderr, \"%s\", push_get_color(PUSH_COLOR_ERROR));\n@@ -397,6 +389,7 @@ static int do_push(const char *repo, int flags,\n \tstruct remote *remote = pushremote_get(repo);\n \tconst char **url;\n \tint url_nr;\n+\tstruct refspec *push_refspec = &rs;\n \n \tif (!remote) {\n \t\tif (repo)\n@@ -417,10 +410,9 @@ static int do_push(const char *repo, int flags,\n \tif (push_options->nr)\n \t\tflags |= TRANSPORT_PUSH_OPTIONS;\n \n-\tif (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {\n-\t\tif (remote->push.raw_nr) {\n-\t\t\trefspec = remote->push.raw;\n-\t\t\trefspec_nr = remote->push.raw_nr;\n+\tif (!push_refspec->nr && !(flags & TRANSPORT_PUSH_ALL)) {\n+\t\tif (remote->push.nr) {\n+\t\t\tpush_refspec = &remote->push;\n \t\t} else if (!(flags & TRANSPORT_PUSH_MIRROR))\n \t\t\tsetup_default_push_refspecs(remote);\n \t}\n@@ -432,7 +424,7 @@ static int do_push(const char *repo, int flags,\n \t\t\t\ttransport_get(remote, url[i]);\n \t\t\tif (flags & TRANSPORT_PUSH_OPTIONS)\n \t\t\t\ttransport->push_options = push_options;\n-\t\t\tif (push_with_options(transport, flags))\n+\t\t\tif (push_with_options(transport, push_refspec, flags))\n \t\t\t\terrs++;\n \t\t}\n \t} else {\n@@ -440,7 +432,7 @@ static int do_push(const char *repo, int flags,\n \t\t\ttransport_get(remote, NULL);\n \t\tif (flags & TRANSPORT_PUSH_OPTIONS)\n \t\t\ttransport->push_options = push_options;\n-\t\tif (push_with_options(transport, flags))\n+\t\tif (push_with_options(transport, push_refspec, flags))\n \t\t\terrs++;\n \t}\n \treturn !!errs;\n@@ -631,7 +623,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tflags |= TRANSPORT_RECURSE_SUBMODULES_ONLY;\n \n \tif (tags)\n-\t\tadd_refspec(\"refs/tags/*\");\n+\t\trefspec_append(&rs, \"refs/tags/*\");\n \n \tif (argc > 0) {\n \t\trepo = argv[0];\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347653","messageId":"20180514215626.164960-31-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 30/35] send-pack: store refspecs in a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:21Z","receivedAt":"2018-05-14T21:58:53Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert send-pack.c to store refspecs in a 'struct refspec' instead of\nas an array of 'const char *'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/send-pack.c | 24 +++++++-----------------\n 1 file changed, 7 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/send-pack.c b/builtin/send-pack.c\nindex b5427f75e..ef512616f 100644\n--- a/builtin/send-pack.c\n+++ b/builtin/send-pack.c\n@@ -126,8 +126,7 @@ static int send_pack_config(const char *k, const char *v, void *cb)\n \n int cmd_send_pack(int argc, const char **argv, const char *prefix)\n {\n-\tint i, nr_refspecs = 0;\n-\tconst char **refspecs = NULL;\n+\tstruct refspec rs = REFSPEC_INIT_PUSH;\n \tconst char *remote_name = NULL;\n \tstruct remote *remote = NULL;\n \tconst char *dest = NULL;\n@@ -189,8 +188,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options, send_pack_usage, 0);\n \tif (argc > 0) {\n \t\tdest = argv[0];\n-\t\trefspecs = (const char **)(argv + 1);\n-\t\tnr_refspecs = argc - 1;\n+\t\trefspec_appendn(&rs, argv + 1, argc - 1);\n \t}\n \n \tif (!dest)\n@@ -209,31 +207,23 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \targs.push_options = push_options.nr ? &push_options : NULL;\n \n \tif (from_stdin) {\n-\t\tstruct argv_array all_refspecs = ARGV_ARRAY_INIT;\n-\n-\t\tfor (i = 0; i < nr_refspecs; i++)\n-\t\t\targv_array_push(&all_refspecs, refspecs[i]);\n-\n \t\tif (args.stateless_rpc) {\n \t\t\tconst char *buf;\n \t\t\twhile ((buf = packet_read_line(0, NULL)))\n-\t\t\t\targv_array_push(&all_refspecs, buf);\n+\t\t\t\trefspec_append(&rs, buf);\n \t\t} else {\n \t\t\tstruct strbuf line = STRBUF_INIT;\n \t\t\twhile (strbuf_getline(&line, stdin) != EOF)\n-\t\t\t\targv_array_push(&all_refspecs, line.buf);\n+\t\t\t\trefspec_append(&rs, line.buf);\n \t\t\tstrbuf_release(&line);\n \t\t}\n-\n-\t\trefspecs = all_refspecs.argv;\n-\t\tnr_refspecs = all_refspecs.argc;\n \t}\n \n \t/*\n \t * --all and --mirror are incompatible; neither makes sense\n \t * with any refspecs.\n \t */\n-\tif ((nr_refspecs > 0 && (send_all || args.send_mirror)) ||\n+\tif ((rs.nr > 0 && (send_all || args.send_mirror)) ||\n \t    (send_all && args.send_mirror))\n \t\tusage_with_options(send_pack_usage, options);\n \n@@ -275,7 +265,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \t\tBUG(\"unknown protocol version\");\n \t}\n \n-\ttransport_verify_remote_names(nr_refspecs, refspecs);\n+\ttransport_verify_remote_names(rs.raw_nr, rs.raw);\n \n \tlocal_refs = get_local_heads();\n \n@@ -287,7 +277,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \t\tflags |= MATCH_REFS_MIRROR;\n \n \t/* match them up */\n-\tif (match_push_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags))\n+\tif (match_push_refs(local_refs, &remote_refs, rs.raw_nr, rs.raw, flags))\n \t\treturn -1;\n \n \tif (!is_empty_cas(&cas))\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347654","messageId":"20180514215626.164960-30-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 29/35] transport: convert transport_push to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:20Z","receivedAt":"2018-05-14T21:58:57Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'transport_push()' to take a 'struct refspec' as a\nparameter instead of an array of strings which represent\nrefspecs.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/push.c |  3 +--\n transport.c    | 17 +++++++----------\n transport.h    |  2 +-\n 3 files changed, 9 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/push.c b/builtin/push.c\nindex ef42979d1..9cd8e8cd5 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -355,8 +355,7 @@ static int push_with_options(struct transport *transport, struct refspec *rs,\n \n \tif (verbosity > 0)\n \t\tfprintf(stderr, _(\"Pushing to %s\\n\"), transport->url);\n-\terr = transport_push(transport, rs->raw_nr, rs->raw, flags,\n-\t\t\t     &reject_reasons);\n+\terr = transport_push(transport, rs, flags, &reject_reasons);\n \tif (err != 0) {\n \t\tfprintf(stderr, \"%s\", push_get_color(PUSH_COLOR_ERROR));\n \t\terror(_(\"failed to push some refs to '%s'\"), transport->url);\ndiff --git a/transport.c b/transport.c\nindex 181db4d4d..a89f17744 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1093,11 +1093,11 @@ static int run_pre_push_hook(struct transport *transport,\n }\n \n int transport_push(struct transport *transport,\n-\t\t   int refspec_nr, const char **refspec, int flags,\n+\t\t   struct refspec *rs, int flags,\n \t\t   unsigned int *reject_reasons)\n {\n \t*reject_reasons = 0;\n-\ttransport_verify_remote_names(refspec_nr, refspec);\n+\ttransport_verify_remote_names(rs->raw_nr, rs->raw);\n \n \tif (transport_color_config() < 0)\n \t\treturn -1;\n@@ -1111,16 +1111,14 @@ int transport_push(struct transport *transport,\n \t\tint porcelain = flags & TRANSPORT_PUSH_PORCELAIN;\n \t\tint pretend = flags & TRANSPORT_PUSH_DRY_RUN;\n \t\tint push_ret, ret, err;\n-\t\tstruct refspec tmp_rs = REFSPEC_INIT_PUSH;\n \t\tstruct argv_array ref_prefixes = ARGV_ARRAY_INIT;\n \t\tint i;\n \n-\t\tif (check_push_refs(local_refs, refspec_nr, refspec) < 0)\n+\t\tif (check_push_refs(local_refs, rs->raw_nr, rs->raw) < 0)\n \t\t\treturn -1;\n \n-\t\trefspec_appendn(&tmp_rs, refspec, refspec_nr);\n-\t\tfor (i = 0; i < tmp_rs.nr; i++) {\n-\t\t\tconst struct refspec_item *item = &tmp_rs.items[i];\n+\t\tfor (i = 0; i < rs->nr; i++) {\n+\t\t\tconst struct refspec_item *item = &rs->items[i];\n \t\t\tconst char *prefix = NULL;\n \n \t\t\tif (item->dst)\n@@ -1143,7 +1141,6 @@ int transport_push(struct transport *transport,\n \t\t\t\t\t\t\t       &ref_prefixes);\n \n \t\targv_array_clear(&ref_prefixes);\n-\t\trefspec_clear(&tmp_rs);\n \n \t\tif (flags & TRANSPORT_PUSH_ALL)\n \t\t\tmatch_flags |= MATCH_REFS_ALL;\n@@ -1155,7 +1152,7 @@ int transport_push(struct transport *transport,\n \t\t\tmatch_flags |= MATCH_REFS_FOLLOW_TAGS;\n \n \t\tif (match_push_refs(local_refs, &remote_refs,\n-\t\t\t\t    refspec_nr, refspec, match_flags)) {\n+\t\t\t\t    rs->raw_nr, rs->raw, match_flags)) {\n \t\t\treturn -1;\n \t\t}\n \n@@ -1186,7 +1183,7 @@ int transport_push(struct transport *transport,\n \n \t\t\tif (!push_unpushed_submodules(&commits,\n \t\t\t\t\t\t      transport->remote,\n-\t\t\t\t\t\t      refspec, refspec_nr,\n+\t\t\t\t\t\t      rs->raw, rs->raw_nr,\n \t\t\t\t\t\t      transport->push_options,\n \t\t\t\t\t\t      pretend)) {\n \t\t\t\toid_array_clear(&commits);\ndiff --git a/transport.h b/transport.h\nindex e783cfa07..e2c809af4 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -197,7 +197,7 @@ void transport_set_verbosity(struct transport *transport, int verbosity,\n #define REJECT_NEEDS_FORCE     0x10\n \n int transport_push(struct transport *connection,\n-\t\t   int refspec_nr, const char **refspec, int flags,\n+\t\t   struct refspec *rs, int flags,\n \t\t   unsigned int * reject_reasons);\n \n /*\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347655","messageId":"20180514215626.164960-24-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 23/35] remote: convert apply_refspecs to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:14Z","receivedAt":"2018-05-14T21:59:02Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'apply_refspecs()' to take a 'struct refspec' as a parameter instead\nof a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fast-export.c |  2 +-\n remote.c              | 15 ++++++---------\n remote.h              |  3 +--\n transport-helper.c    |  6 +++---\n 4 files changed, 11 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 143999738..41fe49e4d 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -831,7 +831,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)\n \n \t\tif (refspecs.nr) {\n \t\t\tchar *private;\n-\t\t\tprivate = apply_refspecs(refspecs.items, refspecs.nr, full_name);\n+\t\t\tprivate = apply_refspecs(&refspecs, full_name);\n \t\t\tif (private) {\n \t\t\t\tfree(full_name);\n \t\t\t\tfull_name = private;\ndiff --git a/remote.c b/remote.c\nindex f2e97c545..54297de3d 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -534,8 +534,7 @@ const char *remote_ref_for_branch(struct branch *branch, int for_push,\n \t\t\tstruct remote *remote = remote_get(remote_name);\n \n \t\t\tif (remote && remote->push.nr &&\n-\t\t\t    (dst = apply_refspecs(remote->push.items,\n-\t\t\t\t\t\t  remote->push.nr,\n+\t\t\t    (dst = apply_refspecs(&remote->push,\n \t\t\t\t\t\t  branch->refname))) {\n \t\t\t\tif (explicit)\n \t\t\t\t\t*explicit = 1;\n@@ -766,15 +765,14 @@ int query_refspecs(struct refspec_item *refs, int ref_count, struct refspec_item\n \treturn -1;\n }\n \n-char *apply_refspecs(struct refspec_item *refspecs, int nr_refspec,\n-\t\t     const char *name)\n+char *apply_refspecs(struct refspec *rs, const char *name)\n {\n \tstruct refspec_item query;\n \n \tmemset(&query, 0, sizeof(struct refspec_item));\n \tquery.src = (char *)name;\n \n-\tif (query_refspecs(refspecs, nr_refspec, &query))\n+\tif (query_refspecs(rs->items, rs->nr, &query))\n \t\treturn NULL;\n \n \treturn query.dst;\n@@ -1580,7 +1578,7 @@ static const char *tracking_for_push_dest(struct remote *remote,\n {\n \tchar *ret;\n \n-\tret = apply_refspecs(remote->fetch.items, remote->fetch.nr, refname);\n+\tret = apply_refspecs(&remote->fetch, refname);\n \tif (!ret)\n \t\treturn error_buf(err,\n \t\t\t\t _(\"push destination '%s' on remote '%s' has no local tracking branch\"),\n@@ -1602,8 +1600,7 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)\n \t\tchar *dst;\n \t\tconst char *ret;\n \n-\t\tdst = apply_refspecs(remote->push.items, remote->push.nr,\n-\t\t\t\t     branch->refname);\n+\t\tdst = apply_refspecs(&remote->push, branch->refname);\n \t\tif (!dst)\n \t\t\treturn error_buf(err,\n \t\t\t\t\t _(\"push refspecs for '%s' do not include '%s'\"),\n@@ -2212,7 +2209,7 @@ static int remote_tracking(struct remote *remote, const char *refname,\n {\n \tchar *dst;\n \n-\tdst = apply_refspecs(remote->fetch.items, remote->fetch.nr, refname);\n+\tdst = apply_refspecs(&remote->fetch, refname);\n \tif (!dst)\n \t\treturn -1; /* no tracking ref for refname at remote */\n \tif (read_ref(dst, oid))\ndiff --git a/remote.h b/remote.h\nindex 5ac7536f5..4e7590b55 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -159,8 +159,7 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);\n struct ref *ref_remove_duplicates(struct ref *ref_map);\n \n extern int query_refspecs(struct refspec_item *specs, int nr, struct refspec_item *query);\n-char *apply_refspecs(struct refspec_item *refspecs, int nr_refspec,\n-\t\t     const char *name);\n+char *apply_refspecs(struct refspec *rs, const char *name);\n \n int check_push_refs(struct ref *src, int nr_refspec, const char **refspec);\n int match_push_refs(struct ref *src, struct ref **dst,\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 33f51ebfc..1f8ff7e94 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -523,7 +523,7 @@ static int fetch_with_import(struct transport *transport,\n \t\t\tcontinue;\n \t\tname = posn->symref ? posn->symref : posn->name;\n \t\tif (data->rs.nr)\n-\t\t\tprivate = apply_refspecs(data->rs.items, data->rs.nr, name);\n+\t\t\tprivate = apply_refspecs(&data->rs, name);\n \t\telse\n \t\t\tprivate = xstrdup(name);\n \t\tif (private) {\n@@ -805,7 +805,7 @@ static int push_update_refs_status(struct helper_data *data,\n \t\t\tcontinue;\n \n \t\t/* propagate back the update to the remote namespace */\n-\t\tprivate = apply_refspecs(data->rs.items, data->rs.nr, ref->name);\n+\t\tprivate = apply_refspecs(&data->rs, ref->name);\n \t\tif (!private)\n \t\t\tcontinue;\n \t\tupdate_ref(\"update by helper\", private, &ref->new_oid, NULL,\n@@ -942,7 +942,7 @@ static int push_refs_with_export(struct transport *transport,\n \t\tchar *private;\n \t\tstruct object_id oid;\n \n-\t\tprivate = apply_refspecs(data->rs.items, data->rs.nr, ref->name);\n+\t\tprivate = apply_refspecs(&data->rs, ref->name);\n \t\tif (private && !get_oid(private, &oid)) {\n \t\t\tstrbuf_addf(&buf, \"^%s\", private);\n \t\t\tstring_list_append_nodup(&revlist_args,\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347656","messageId":"20180514215626.164960-22-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 21/35] fetch: convert prune_refs to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:12Z","receivedAt":"2018-05-14T21:59:09Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'prune_refs()' to take a 'struct refspec' as a parameter instead\nof a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c | 11 +++++------\n 1 file changed, 5 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 86a7f103f..eebeebfd8 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -959,11 +959,11 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)\n \treturn ret;\n }\n \n-static int prune_refs(struct refspec_item *refs, int ref_count, struct ref *ref_map,\n-\t\tconst char *raw_url)\n+static int prune_refs(struct refspec *rs, struct ref *ref_map,\n+\t\t      const char *raw_url)\n {\n \tint url_len, i, result = 0;\n-\tstruct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);\n+\tstruct ref *ref, *stale_refs = get_stale_heads(rs->items, rs->nr, ref_map);\n \tchar *url;\n \tint summary_width = transport_summary_width(stale_refs);\n \tconst char *dangling_msg = dry_run\n@@ -1158,10 +1158,9 @@ static int do_fetch(struct transport *transport,\n \t\t * don't care whether --tags was specified.\n \t\t */\n \t\tif (rs->nr) {\n-\t\t\tprune_refs(rs->items, rs->nr, ref_map, transport->url);\n+\t\t\tprune_refs(rs, ref_map, transport->url);\n \t\t} else {\n-\t\t\tprune_refs(transport->remote->fetch.items,\n-\t\t\t\t   transport->remote->fetch.nr,\n+\t\t\tprune_refs(&transport->remote->fetch,\n \t\t\t\t   ref_map,\n \t\t\t\t   transport->url);\n \t\t}\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347657","messageId":"20180514215626.164960-21-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 20/35] fetch: convert get_ref_map to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:11Z","receivedAt":"2018-05-14T21:59:18Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'get_ref_map()' to take a 'struct refspec' as a parameter\ninstead of a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c | 43 ++++++++++++++++++++-----------------------\n 1 file changed, 20 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 733feb19c..86a7f103f 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -337,7 +337,7 @@ static void find_non_local_tags(struct transport *transport,\n }\n \n static struct ref *get_ref_map(struct transport *transport,\n-\t\t\t       struct refspec_item *refspecs, int refspec_count,\n+\t\t\t       struct refspec *rs,\n \t\t\t       int tags, int *autotags)\n {\n \tint i;\n@@ -351,15 +351,16 @@ static struct ref *get_ref_map(struct transport *transport,\n \n \tconst struct ref *remote_refs;\n \n-\tfor (i = 0; i < refspec_count; i++) {\n-\t\tif (!refspecs[i].exact_sha1) {\n-\t\t\tconst char *glob = strchr(refspecs[i].src, '*');\n+\tfor (i = 0; i < rs->nr; i++) {\n+\t\tconst struct refspec_item *item = &rs->items[i];\n+\t\tif (!item->exact_sha1) {\n+\t\t\tconst char *glob = strchr(item->src, '*');\n \t\t\tif (glob)\n \t\t\t\targv_array_pushf(&ref_prefixes, \"%.*s\",\n-\t\t\t\t\t\t (int)(glob - refspecs[i].src),\n-\t\t\t\t\t\t refspecs[i].src);\n+\t\t\t\t\t\t (int)(glob - item->src),\n+\t\t\t\t\t\t item->src);\n \t\t\telse\n-\t\t\t\texpand_ref_prefix(&ref_prefixes, refspecs[i].src);\n+\t\t\t\texpand_ref_prefix(&ref_prefixes, item->src);\n \t\t}\n \t}\n \n@@ -367,13 +368,12 @@ static struct ref *get_ref_map(struct transport *transport,\n \n \targv_array_clear(&ref_prefixes);\n \n-\tif (refspec_count) {\n-\t\tstruct refspec_item *fetch_refspec;\n-\t\tint fetch_refspec_nr;\n+\tif (rs->nr) {\n+\t\tstruct refspec *fetch_refspec;\n \n-\t\tfor (i = 0; i < refspec_count; i++) {\n-\t\t\tget_fetch_map(remote_refs, &refspecs[i], &tail, 0);\n-\t\t\tif (refspecs[i].dst && refspecs[i].dst[0])\n+\t\tfor (i = 0; i < rs->nr; i++) {\n+\t\t\tget_fetch_map(remote_refs, &rs->items[i], &tail, 0);\n+\t\t\tif (rs->items[i].dst && rs->items[i].dst[0])\n \t\t\t\t*autotags = 1;\n \t\t}\n \t\t/* Merge everything on the command line (but not --tags) */\n@@ -400,16 +400,13 @@ static struct ref *get_ref_map(struct transport *transport,\n \t\t * by ref_remove_duplicates() in favor of one of these\n \t\t * opportunistic entries with FETCH_HEAD_IGNORE.\n \t\t */\n-\t\tif (refmap.nr) {\n-\t\t\tfetch_refspec = refmap.items;\n-\t\t\tfetch_refspec_nr = refmap.nr;\n-\t\t} else {\n-\t\t\tfetch_refspec = transport->remote->fetch.items;\n-\t\t\tfetch_refspec_nr = transport->remote->fetch.nr;\n-\t\t}\n+\t\tif (refmap.nr)\n+\t\t\tfetch_refspec = &refmap;\n+\t\telse\n+\t\t\tfetch_refspec = &transport->remote->fetch;\n \n-\t\tfor (i = 0; i < fetch_refspec_nr; i++)\n-\t\t\tget_fetch_map(ref_map, &fetch_refspec[i], &oref_tail, 1);\n+\t\tfor (i = 0; i < fetch_refspec->nr; i++)\n+\t\t\tget_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1);\n \t} else if (refmap.nr) {\n \t\tdie(\"--refmap option is only meaningful with command-line refspec(s).\");\n \t} else {\n@@ -1136,7 +1133,7 @@ static int do_fetch(struct transport *transport,\n \t\t\tgoto cleanup;\n \t}\n \n-\tref_map = get_ref_map(transport, rs->items, rs->nr, tags, &autotags);\n+\tref_map = get_ref_map(transport, rs, tags, &autotags);\n \tif (!update_head_ok)\n \t\tcheck_not_current_branch(ref_map);\n \n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347658","messageId":"20180514215626.164960-17-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 16/35] fetch: convert fetch_one to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:07Z","receivedAt":"2018-05-14T21:59:25Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'fetch_one()' to use 'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c | 46 +++++++++++++++++++---------------------------\n 1 file changed, 19 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 30083d4bc..769f9d2be 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1356,10 +1356,8 @@ static inline void fetch_one_setup_partial(struct remote *remote)\n \n static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok)\n {\n-\tstatic const char **refs = NULL;\n-\tstruct refspec_item *refspec;\n-\tint ref_nr = 0;\n-\tint j = 0;\n+\tstruct refspec rs = REFSPEC_INIT_FETCH;\n+\tint i;\n \tint exit_code;\n \tint maybe_prune_tags;\n \tint remote_via_config = remote_is_configured(remote, 0);\n@@ -1394,35 +1392,29 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru\n \tif (maybe_prune_tags && remote_via_config)\n \t\tadd_prune_tags_to_fetch_refspec(remote);\n \n-\tif (argc > 0 || (maybe_prune_tags && !remote_via_config)) {\n-\t\tsize_t nr_alloc = st_add3(argc, maybe_prune_tags, 1);\n-\t\trefs = xcalloc(nr_alloc, sizeof(const char *));\n-\t\tif (maybe_prune_tags) {\n-\t\t\trefs[j++] = xstrdup(\"refs/tags/*:refs/tags/*\");\n-\t\t\tref_nr++;\n-\t\t}\n-\t}\n+\tif (maybe_prune_tags && (argc || !remote_via_config))\n+\t\trefspec_append(&rs, TAG_REFSPEC);\n \n-\tif (argc > 0) {\n-\t\tint i;\n-\t\tfor (i = 0; i < argc; i++) {\n-\t\t\tif (!strcmp(argv[i], \"tag\")) {\n-\t\t\t\ti++;\n-\t\t\t\tif (i >= argc)\n-\t\t\t\t\tdie(_(\"You need to specify a tag name.\"));\n-\t\t\t\trefs[j++] = xstrfmt(\"refs/tags/%s:refs/tags/%s\",\n-\t\t\t\t\t\t    argv[i], argv[i]);\n-\t\t\t} else\n-\t\t\t\trefs[j++] = argv[i];\n-\t\t\tref_nr++;\n+\tfor (i = 0; i < argc; i++) {\n+\t\tif (!strcmp(argv[i], \"tag\")) {\n+\t\t\tchar *tag;\n+\t\t\ti++;\n+\t\t\tif (i >= argc)\n+\t\t\t\tdie(_(\"You need to specify a tag name.\"));\n+\n+\t\t\ttag = xstrfmt(\"refs/tags/%s:refs/tags/%s\",\n+\t\t\t\t      argv[i], argv[i]);\n+\t\t\trefspec_append(&rs, tag);\n+\t\t\tfree(tag);\n+\t\t} else {\n+\t\t\trefspec_append(&rs, argv[i]);\n \t\t}\n \t}\n \n \tsigchain_push_common(unlock_pack_on_signal);\n \tatexit(unlock_pack);\n-\trefspec = parse_fetch_refspec(ref_nr, refs);\n-\texit_code = do_fetch(gtransport, refspec, ref_nr);\n-\tfree_refspec(ref_nr, refspec);\n+\texit_code = do_fetch(gtransport, rs.items, rs.nr);\n+\trefspec_clear(&rs);\n \ttransport_disconnect(gtransport);\n \tgtransport = NULL;\n \treturn exit_code;\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347659","messageId":"20180514215626.164960-14-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 13/35] remote: convert push refspecs to struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:04Z","receivedAt":"2018-05-14T21:59:30Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert the set of push refspecs stored in 'struct remote' to use\n'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/push.c   | 10 +++++-----\n builtin/remote.c | 14 +++++++-------\n remote.c         | 23 +++++++++--------------\n remote.h         |  6 ++----\n 4 files changed, 23 insertions(+), 30 deletions(-)\n\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 00d81fb1d..509dc6677 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -79,11 +79,11 @@ static const char *map_refspec(const char *ref,\n \tif (count_refspec_match(ref, local_refs, &matched) != 1)\n \t\treturn ref;\n \n-\tif (remote->push) {\n+\tif (remote->push.nr) {\n \t\tstruct refspec_item query;\n \t\tmemset(&query, 0, sizeof(struct refspec_item));\n \t\tquery.src = matched->name;\n-\t\tif (!query_refspecs(remote->push, remote->push_refspec_nr, &query) &&\n+\t\tif (!query_refspecs(remote->push.items, remote->push.nr, &query) &&\n \t\t    query.dst) {\n \t\t\tstruct strbuf buf = STRBUF_INIT;\n \t\t\tstrbuf_addf(&buf, \"%s%s:%s\",\n@@ -436,9 +436,9 @@ static int do_push(const char *repo, int flags,\n \t}\n \n \tif (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {\n-\t\tif (remote->push_refspec_nr) {\n-\t\t\trefspec = remote->push_refspec;\n-\t\t\trefspec_nr = remote->push_refspec_nr;\n+\t\tif (remote->push.raw_nr) {\n+\t\t\trefspec = remote->push.raw;\n+\t\t\trefspec_nr = remote->push.raw_nr;\n \t\t} else if (!(flags & TRANSPORT_PUSH_MIRROR))\n \t\t\tsetup_default_push_refspecs(remote);\n \t}\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex d9da82dc8..fb84729d6 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -388,8 +388,8 @@ static int get_push_ref_states(const struct ref *remote_refs,\n \tlocal_refs = get_local_heads();\n \tpush_map = copy_ref_list(remote_refs);\n \n-\tmatch_push_refs(local_refs, &push_map, remote->push_refspec_nr,\n-\t\t\tremote->push_refspec, MATCH_REFS_NONE);\n+\tmatch_push_refs(local_refs, &push_map, remote->push.raw_nr,\n+\t\t\tremote->push.raw, MATCH_REFS_NONE);\n \n \tstates->push.strdup_strings = 1;\n \tfor (ref = push_map; ref; ref = ref->next) {\n@@ -435,14 +435,14 @@ static int get_push_ref_states_noquery(struct ref_states *states)\n \t\treturn 0;\n \n \tstates->push.strdup_strings = 1;\n-\tif (!remote->push_refspec_nr) {\n+\tif (!remote->push.nr) {\n \t\titem = string_list_append(&states->push, _(\"(matching)\"));\n \t\tinfo = item->util = xcalloc(1, sizeof(struct push_info));\n \t\tinfo->status = PUSH_STATUS_NOTQUERIED;\n \t\tinfo->dest = xstrdup(item->string);\n \t}\n-\tfor (i = 0; i < remote->push_refspec_nr; i++) {\n-\t\tstruct refspec_item *spec = remote->push + i;\n+\tfor (i = 0; i < remote->push.nr; i++) {\n+\t\tconst struct refspec_item *spec = &remote->push.items[i];\n \t\tif (spec->matching)\n \t\t\titem = string_list_append(&states->push, _(\"(matching)\"));\n \t\telse if (strlen(spec->src))\n@@ -586,8 +586,8 @@ static int migrate_file(struct remote *remote)\n \t\tgit_config_set_multivar(buf.buf, remote->url[i], \"^$\", 0);\n \tstrbuf_reset(&buf);\n \tstrbuf_addf(&buf, \"remote.%s.push\", remote->name);\n-\tfor (i = 0; i < remote->push_refspec_nr; i++)\n-\t\tgit_config_set_multivar(buf.buf, remote->push_refspec[i], \"^$\", 0);\n+\tfor (i = 0; i < remote->push.raw_nr; i++)\n+\t\tgit_config_set_multivar(buf.buf, remote->push.raw[i], \"^$\", 0);\n \tstrbuf_reset(&buf);\n \tstrbuf_addf(&buf, \"remote.%s.fetch\", remote->name);\n \tfor (i = 0; i < remote->fetch_refspec_nr; i++)\ndiff --git a/remote.c b/remote.c\nindex bce6e7ce4..090110c37 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -79,10 +79,7 @@ static const char *alias_url(const char *url, struct rewrites *r)\n \n static void add_push_refspec(struct remote *remote, const char *ref)\n {\n-\tALLOC_GROW(remote->push_refspec,\n-\t\t   remote->push_refspec_nr + 1,\n-\t\t   remote->push_refspec_alloc);\n-\tremote->push_refspec[remote->push_refspec_nr++] = ref;\n+\trefspec_append(&remote->push, ref);\n }\n \n static void add_fetch_refspec(struct remote *remote, const char *ref)\n@@ -175,9 +172,11 @@ static struct remote *make_remote(const char *name, int len)\n \tret = xcalloc(1, sizeof(struct remote));\n \tret->prune = -1;  /* unspecified */\n \tret->prune_tags = -1;  /* unspecified */\n+\tret->name = xstrndup(name, len);\n+\trefspec_init(&ret->push, REFSPEC_PUSH);\n+\n \tALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);\n \tremotes[remotes_nr++] = ret;\n-\tret->name = xstrndup(name, len);\n \n \thashmap_entry_init(ret, lookup_entry.hash);\n \treplaced = hashmap_put(&remotes_hash, ret);\n@@ -542,9 +541,9 @@ const char *remote_ref_for_branch(struct branch *branch, int for_push,\n \t\t\t\tpushremote_for_branch(branch, NULL);\n \t\t\tstruct remote *remote = remote_get(remote_name);\n \n-\t\t\tif (remote && remote->push_refspec_nr &&\n-\t\t\t    (dst = apply_refspecs(remote->push,\n-\t\t\t\t\t\t  remote->push_refspec_nr,\n+\t\t\tif (remote && remote->push.nr &&\n+\t\t\t    (dst = apply_refspecs(remote->push.items,\n+\t\t\t\t\t\t  remote->push.nr,\n \t\t\t\t\t\t  branch->refname))) {\n \t\t\t\tif (explicit)\n \t\t\t\t\t*explicit = 1;\n@@ -582,7 +581,6 @@ static struct remote *remote_get_1(const char *name,\n \tif (!valid_remote(ret))\n \t\treturn NULL;\n \tret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec);\n-\tret->push = parse_push_refspec(ret->push_refspec_nr, ret->push_refspec);\n \treturn ret;\n }\n \n@@ -616,9 +614,6 @@ int for_each_remote(each_remote_fn fn, void *priv)\n \t\tif (!r->fetch)\n \t\t\tr->fetch = parse_fetch_refspec(r->fetch_refspec_nr,\n \t\t\t\t\t\t       r->fetch_refspec);\n-\t\tif (!r->push)\n-\t\t\tr->push = parse_push_refspec(r->push_refspec_nr,\n-\t\t\t\t\t\t     r->push_refspec);\n \t\tresult = fn(r, priv);\n \t}\n \treturn result;\n@@ -1613,11 +1608,11 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)\n \t\t\t\t _(\"branch '%s' has no remote for pushing\"),\n \t\t\t\t branch->name);\n \n-\tif (remote->push_refspec_nr) {\n+\tif (remote->push.nr) {\n \t\tchar *dst;\n \t\tconst char *ret;\n \n-\t\tdst = apply_refspecs(remote->push, remote->push_refspec_nr,\n+\t\tdst = apply_refspecs(remote->push.items, remote->push.nr,\n \t\t\t\t     branch->refname);\n \t\tif (!dst)\n \t\t\treturn error_buf(err,\ndiff --git a/remote.h b/remote.h\nindex 3657bd43d..637fc5d0c 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -3,6 +3,7 @@\n \n #include \"parse-options.h\"\n #include \"hashmap.h\"\n+#include \"refspec.h\"\n \n enum {\n \tREMOTE_UNCONFIGURED = 0,\n@@ -27,10 +28,7 @@ struct remote {\n \tint pushurl_nr;\n \tint pushurl_alloc;\n \n-\tconst char **push_refspec;\n-\tstruct refspec_item *push;\n-\tint push_refspec_nr;\n-\tint push_refspec_alloc;\n+\tstruct refspec push;\n \n \tconst char **fetch_refspec;\n \tstruct refspec_item *fetch;\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347660","messageId":"20180514215626.164960-13-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 12/35] fast-export: convert to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:03Z","receivedAt":"2018-05-14T21:59:37Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert fast-export to use 'struct refspec' instead of using a list of\nrefspec_item's.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fast-export.c | 21 +++++++--------------\n 1 file changed, 7 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 6f105dc79..143999738 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -36,8 +36,7 @@ static int use_done_feature;\n static int no_data;\n static int full_tree;\n static struct string_list extra_refs = STRING_LIST_INIT_NODUP;\n-static struct refspec_item *refspecs;\n-static int refspecs_nr;\n+static struct refspec refspecs = REFSPEC_INIT_FETCH;\n static int anonymize;\n \n static int parse_opt_signed_tag_mode(const struct option *opt,\n@@ -830,9 +829,9 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)\n \t\tif (dwim_ref(e->name, strlen(e->name), &oid, &full_name) != 1)\n \t\t\tcontinue;\n \n-\t\tif (refspecs) {\n+\t\tif (refspecs.nr) {\n \t\t\tchar *private;\n-\t\t\tprivate = apply_refspecs(refspecs, refspecs_nr, full_name);\n+\t\t\tprivate = apply_refspecs(refspecs.items, refspecs.nr, full_name);\n \t\t\tif (private) {\n \t\t\t\tfree(full_name);\n \t\t\t\tfull_name = private;\n@@ -978,8 +977,8 @@ static void import_marks(char *input_file)\n static void handle_deletes(void)\n {\n \tint i;\n-\tfor (i = 0; i < refspecs_nr; i++) {\n-\t\tstruct refspec_item *refspec = &refspecs[i];\n+\tfor (i = 0; i < refspecs.nr; i++) {\n+\t\tstruct refspec_item *refspec = &refspecs.items[i];\n \t\tif (*refspec->src)\n \t\t\tcontinue;\n \n@@ -1040,18 +1039,12 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options (fast_export_usage, options);\n \n \tif (refspecs_list.nr) {\n-\t\tconst char **refspecs_str;\n \t\tint i;\n \n-\t\tALLOC_ARRAY(refspecs_str, refspecs_list.nr);\n \t\tfor (i = 0; i < refspecs_list.nr; i++)\n-\t\t\trefspecs_str[i] = refspecs_list.items[i].string;\n-\n-\t\trefspecs_nr = refspecs_list.nr;\n-\t\trefspecs = parse_fetch_refspec(refspecs_nr, refspecs_str);\n+\t\t\trefspec_append(&refspecs, refspecs_list.items[i].string);\n \n \t\tstring_list_clear(&refspecs_list, 1);\n-\t\tfree(refspecs_str);\n \t}\n \n \tif (use_done_feature)\n@@ -1090,7 +1083,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)\n \tif (use_done_feature)\n \t\tprintf(\"done\\n\");\n \n-\tfree_refspec(refspecs_nr, refspecs);\n+\trefspec_clear(&refspecs);\n \n \treturn 0;\n }\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347661","messageId":"20180514215626.164960-11-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 10/35] remote: convert match_push_refs to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:01Z","receivedAt":"2018-05-14T21:59:41Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'match_push_refs()' to use struct refspec.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n remote.c | 13 ++++++++-----\n 1 file changed, 8 insertions(+), 5 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 191855118..bce6e7ce4 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1312,7 +1312,7 @@ int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)\n int match_push_refs(struct ref *src, struct ref **dst,\n \t\t    int nr_refspec, const char **refspec, int flags)\n {\n-\tstruct refspec_item *rs;\n+\tstruct refspec rs = REFSPEC_INIT_PUSH;\n \tint send_all = flags & MATCH_REFS_ALL;\n \tint send_mirror = flags & MATCH_REFS_MIRROR;\n \tint send_prune = flags & MATCH_REFS_PRUNE;\n@@ -1325,8 +1325,8 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\tnr_refspec = 1;\n \t\trefspec = default_refspec;\n \t}\n-\trs = parse_push_refspec(nr_refspec, (const char **) refspec);\n-\terrs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec);\n+\trefspec_appendn(&rs, refspec, nr_refspec);\n+\terrs = match_explicit_refs(src, *dst, &dst_tail, rs.items, rs.nr);\n \n \t/* pick the remainder */\n \tfor (ref = src; ref; ref = ref->next) {\n@@ -1335,7 +1335,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\tconst struct refspec_item *pat = NULL;\n \t\tchar *dst_name;\n \n-\t\tdst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat);\n+\t\tdst_name = get_ref_match(rs.items, rs.nr, ref, send_mirror, FROM_SRC, &pat);\n \t\tif (!dst_name)\n \t\t\tcontinue;\n \n@@ -1384,7 +1384,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\t\t\t/* We're already sending something to this ref. */\n \t\t\t\tcontinue;\n \n-\t\t\tsrc_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL);\n+\t\t\tsrc_name = get_ref_match(rs.items, rs.nr, ref, send_mirror, FROM_DST, NULL);\n \t\t\tif (src_name) {\n \t\t\t\tif (!src_ref_index.nr)\n \t\t\t\t\tprepare_ref_index(&src_ref_index, src);\n@@ -1396,6 +1396,9 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\t}\n \t\tstring_list_clear(&src_ref_index, 0);\n \t}\n+\n+\trefspec_clear(&rs);\n+\n \tif (errs)\n \t\treturn -1;\n \treturn 0;\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347662","messageId":"20180514215626.164960-12-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH 11/35] clone: convert cmd_clone to use refspec_item_init","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-14T21:56:02Z","receivedAt":"2018-05-14T21:59:42Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'cmd_clone()' to use 'refspec_item_init()' instead of relying on\nthe old 'parse_fetch_refspec()' to initialize a single refspec item.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/clone.c | 10 ++++------\n 1 file changed, 4 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 854088a3a..8c5f4d8f0 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -895,8 +895,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tint err = 0, complete_refs_before_fetch = 1;\n \tint submodule_progress;\n \n-\tstruct refspec_item *refspec;\n-\tconst char *fetch_pattern;\n+\tstruct refspec_item refspec;\n \n \tfetch_if_missing = 0;\n \n@@ -1078,8 +1077,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tif (option_required_reference.nr || option_optional_reference.nr)\n \t\tsetup_reference();\n \n-\tfetch_pattern = value.buf;\n-\trefspec = parse_fetch_refspec(1, &fetch_pattern);\n+\trefspec_item_init(&refspec, value.buf, REFSPEC_FETCH);\n \n \tstrbuf_reset(&value);\n \n@@ -1139,7 +1137,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \trefs = transport_get_remote_refs(transport, NULL);\n \n \tif (refs) {\n-\t\tmapped_refs = wanted_peer_refs(refs, refspec);\n+\t\tmapped_refs = wanted_peer_refs(refs, &refspec);\n \t\t/*\n \t\t * transport_get_remote_refs() may return refs with null sha-1\n \t\t * in mapped_refs (see struct transport->get_refs_list\n@@ -1233,6 +1231,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tstrbuf_release(&value);\n \tjunk_mode = JUNK_LEAVE_ALL;\n \n-\tfree(refspec);\n+\trefspec_item_clear(&refspec);\n \treturn err;\n }\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347665","messageId":"CAGZ79kZMVLWfgWd53uV+8uaWu6_4iHVE6OJjUdtSA02mqUHCwA@mail.gmail.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"Re: [PATCH 00/35] refactoring refspecs","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-05-14T23:08:28Z","receivedAt":"2018-05-14T23:08:33Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":">  22 files changed, 514 insertions(+), 571 deletions(-)\n>  create mode 100644 refspec.c\n>  create mode 100644 refspec.h\n\nThis looks promising. I'll hope to find time to review it.\n\nStefan\n"},{"id":"347682","messageId":"xmqqvabp8hta.fsf@gitster-ct.c.googlers.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"Re: [PATCH 00/35] refactoring refspecs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-15T08:05:37Z","receivedAt":"2018-05-15T08:06:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> This series refactors how refspecs are handled through out the code base\n> by renaming the existing struct refspec to refspec_item and introducing\n> a new 'struct refspec' which is a container of refspec_items, much like\n> how a pathspec contains pathspec_items.  This simplifies many callers\n> and makes handling pathspecs a bit easier.\n\nGood.  IOW, a refspec is a set of refspec items, just like a\npathspec is a set of pathspec items.  The two level abstraction\nwould naturally let us work on individual items as well as the whole\nthing as a single set, which is quite nice.\n\n"},{"id":"347683","messageId":"xmqqr2md8hrz.fsf@gitster-ct.c.googlers.com","threadId":"48495","inReplyTo":"20180514215626.164960-2-bmwill@google.com","subject":"Re: [PATCH 01/35] refspec: move refspec parsing logic into its own file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-15T08:06:24Z","receivedAt":"2018-05-15T08:08:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> In preperation for performing a refactor on refspec related code, move\n\nPreparation?\n\n> the refspec parsing logic into its own file.\n>\n> Signed-off-by: Brandon Williams <bmwill@google.com>\n> ---\n>  Makefile                    |   1 +\n>  branch.c                    |   1 +\n>  builtin/clone.c             |   1 +\n>  builtin/fast-export.c       |   1 +\n>  builtin/fetch.c             |   1 +\n>  builtin/merge.c             |   1 +\n>  builtin/pull.c              |   1 +\n>  builtin/push.c              |   1 +\n>  builtin/remote.c            |   1 +\n>  builtin/submodule--helper.c |   1 +\n>  checkout.c                  |   1 +\n>  refspec.c                   | 168 ++++++++++++++++++++++++++++++++++++\n>  refspec.h                   |  23 +++++\n>  remote.c                    | 165 +----------------------------------\n>  remote.h                    |  20 -----\n>  transport-helper.c          |   1 +\n>  transport.c                 |   1 +\n>  17 files changed, 205 insertions(+), 184 deletions(-)\n>  create mode 100644 refspec.c\n>  create mode 100644 refspec.h\n>\n> diff --git a/Makefile b/Makefile\n> index ad880d1fc..4bca65383 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -928,6 +928,7 @@ LIB_OBJS += refs/files-backend.o\n>  LIB_OBJS += refs/iterator.o\n>  LIB_OBJS += refs/packed-backend.o\n>  LIB_OBJS += refs/ref-cache.o\n> +LIB_OBJS += refspec.o\n>  LIB_OBJS += ref-filter.o\n>  LIB_OBJS += remote.o\n>  LIB_OBJS += replace-object.o\n> diff --git a/branch.c b/branch.c\n> index 2672054f0..32ccefc6b 100644\n> --- a/branch.c\n> +++ b/branch.c\n> @@ -3,6 +3,7 @@\n>  #include \"config.h\"\n>  #include \"branch.h\"\n>  #include \"refs.h\"\n> +#include \"refspec.h\"\n>  #include \"remote.h\"\n>  #include \"commit.h\"\n>  #include \"worktree.h\"\n> diff --git a/builtin/clone.c b/builtin/clone.c\n> index 84f1473d1..6d1614ed3 100644\n> --- a/builtin/clone.c\n> +++ b/builtin/clone.c\n> @@ -14,6 +14,7 @@\n>  #include \"parse-options.h\"\n>  #include \"fetch-pack.h\"\n>  #include \"refs.h\"\n> +#include \"refspec.h\"\n>  #include \"tree.h\"\n>  #include \"tree-walk.h\"\n>  #include \"unpack-trees.h\"\n> diff --git a/builtin/fast-export.c b/builtin/fast-export.c\n> index 530df12f0..a13b7c8ef 100644\n> --- a/builtin/fast-export.c\n> +++ b/builtin/fast-export.c\n> @@ -7,6 +7,7 @@\n>  #include \"cache.h\"\n>  #include \"config.h\"\n>  #include \"refs.h\"\n> +#include \"refspec.h\"\n>  #include \"commit.h\"\n>  #include \"object.h\"\n>  #include \"tag.h\"\n> diff --git a/builtin/fetch.c b/builtin/fetch.c\n> index 7ee83ac0f..1fce68e9a 100644\n> --- a/builtin/fetch.c\n> +++ b/builtin/fetch.c\n> @@ -5,6 +5,7 @@\n>  #include \"config.h\"\n>  #include \"repository.h\"\n>  #include \"refs.h\"\n> +#include \"refspec.h\"\n>  #include \"commit.h\"\n>  #include \"builtin.h\"\n>  #include \"string-list.h\"\n> diff --git a/builtin/merge.c b/builtin/merge.c\n> index 9db5a2cf1..c362cfe90 100644\n> --- a/builtin/merge.c\n> +++ b/builtin/merge.c\n> @@ -14,6 +14,7 @@\n>  #include \"run-command.h\"\n>  #include \"diff.h\"\n>  #include \"refs.h\"\n> +#include \"refspec.h\"\n>  #include \"commit.h\"\n>  #include \"diffcore.h\"\n>  #include \"revision.h\"\n> diff --git a/builtin/pull.c b/builtin/pull.c\n> index 71aac5005..6247c956d 100644\n> --- a/builtin/pull.c\n> +++ b/builtin/pull.c\n> @@ -15,6 +15,7 @@\n>  #include \"remote.h\"\n>  #include \"dir.h\"\n>  #include \"refs.h\"\n> +#include \"refspec.h\"\n>  #include \"revision.h\"\n>  #include \"submodule.h\"\n>  #include \"submodule-config.h\"\n> diff --git a/builtin/push.c b/builtin/push.c\n> index ac3705370..fa65999b2 100644\n> --- a/builtin/push.c\n> +++ b/builtin/push.c\n> @@ -4,6 +4,7 @@\n>  #include \"cache.h\"\n>  #include \"config.h\"\n>  #include \"refs.h\"\n> +#include \"refspec.h\"\n>  #include \"run-command.h\"\n>  #include \"builtin.h\"\n>  #include \"remote.h\"\n> diff --git a/builtin/remote.c b/builtin/remote.c\n> index 8708e584e..c49513995 100644\n> --- a/builtin/remote.c\n> +++ b/builtin/remote.c\n> @@ -7,6 +7,7 @@\n>  #include \"strbuf.h\"\n>  #include \"run-command.h\"\n>  #include \"refs.h\"\n> +#include \"refspec.h\"\n>  #include \"argv-array.h\"\n>  \n>  static const char * const builtin_remote_usage[] = {\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index c2403a915..6ab032acb 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -12,6 +12,7 @@\n>  #include \"run-command.h\"\n>  #include \"remote.h\"\n>  #include \"refs.h\"\n> +#include \"refspec.h\"\n>  #include \"connect.h\"\n>  #include \"revision.h\"\n>  #include \"diffcore.h\"\n> diff --git a/checkout.c b/checkout.c\n> index ac42630f7..193ba8567 100644\n> --- a/checkout.c\n> +++ b/checkout.c\n> @@ -1,5 +1,6 @@\n>  #include \"cache.h\"\n>  #include \"remote.h\"\n> +#include \"refspec.h\"\n>  #include \"checkout.h\"\n>  \n>  struct tracking_name_data {\n> diff --git a/refspec.c b/refspec.c\n> new file mode 100644\n> index 000000000..ecb0bdff3\n> --- /dev/null\n> +++ b/refspec.c\n> @@ -0,0 +1,168 @@\n> +#include \"cache.h\"\n> +#include \"refs.h\"\n> +#include \"refspec.h\"\n> +\n> +static struct refspec s_tag_refspec = {\n> +\t0,\n> +\t1,\n> +\t0,\n> +\t0,\n> +\t\"refs/tags/*\",\n> +\t\"refs/tags/*\"\n> +};\n> +\n> +/* See TAG_REFSPEC for the string version */\n> +const struct refspec *tag_refspec = &s_tag_refspec;\n> +\n> +static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n> +{\n> +\tint i;\n> +\tstruct refspec *rs = xcalloc(nr_refspec, sizeof(*rs));\n> +\n> +\tfor (i = 0; i < nr_refspec; i++) {\n> +\t\tsize_t llen;\n> +\t\tint is_glob;\n> +\t\tconst char *lhs, *rhs;\n> +\t\tint flags;\n> +\n> +\t\tis_glob = 0;\n> +\n> +\t\tlhs = refspec[i];\n> +\t\tif (*lhs == '+') {\n> +\t\t\trs[i].force = 1;\n> +\t\t\tlhs++;\n> +\t\t}\n> +\n> +\t\trhs = strrchr(lhs, ':');\n> +\n> +\t\t/*\n> +\t\t * Before going on, special case \":\" (or \"+:\") as a refspec\n> +\t\t * for pushing matching refs.\n> +\t\t */\n> +\t\tif (!fetch && rhs == lhs && rhs[1] == '\\0') {\n> +\t\t\trs[i].matching = 1;\n> +\t\t\tcontinue;\n> +\t\t}\n> +\n> +\t\tif (rhs) {\n> +\t\t\tsize_t rlen = strlen(++rhs);\n> +\t\t\tis_glob = (1 <= rlen && strchr(rhs, '*'));\n> +\t\t\trs[i].dst = xstrndup(rhs, rlen);\n> +\t\t}\n> +\n> +\t\tllen = (rhs ? (rhs - lhs - 1) : strlen(lhs));\n> +\t\tif (1 <= llen && memchr(lhs, '*', llen)) {\n> +\t\t\tif ((rhs && !is_glob) || (!rhs && fetch))\n> +\t\t\t\tgoto invalid;\n> +\t\t\tis_glob = 1;\n> +\t\t} else if (rhs && is_glob) {\n> +\t\t\tgoto invalid;\n> +\t\t}\n> +\n> +\t\trs[i].pattern = is_glob;\n> +\t\trs[i].src = xstrndup(lhs, llen);\n> +\t\tflags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);\n> +\n> +\t\tif (fetch) {\n> +\t\t\tstruct object_id unused;\n> +\n> +\t\t\t/* LHS */\n> +\t\t\tif (!*rs[i].src)\n> +\t\t\t\t; /* empty is ok; it means \"HEAD\" */\n> +\t\t\telse if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused))\n> +\t\t\t\trs[i].exact_sha1 = 1; /* ok */\n> +\t\t\telse if (!check_refname_format(rs[i].src, flags))\n> +\t\t\t\t; /* valid looking ref is ok */\n> +\t\t\telse\n> +\t\t\t\tgoto invalid;\n> +\t\t\t/* RHS */\n> +\t\t\tif (!rs[i].dst)\n> +\t\t\t\t; /* missing is ok; it is the same as empty */\n> +\t\t\telse if (!*rs[i].dst)\n> +\t\t\t\t; /* empty is ok; it means \"do not store\" */\n> +\t\t\telse if (!check_refname_format(rs[i].dst, flags))\n> +\t\t\t\t; /* valid looking ref is ok */\n> +\t\t\telse\n> +\t\t\t\tgoto invalid;\n> +\t\t} else {\n> +\t\t\t/*\n> +\t\t\t * LHS\n> +\t\t\t * - empty is allowed; it means delete.\n> +\t\t\t * - when wildcarded, it must be a valid looking ref.\n> +\t\t\t * - otherwise, it must be an extended SHA-1, but\n> +\t\t\t *   there is no existing way to validate this.\n> +\t\t\t */\n> +\t\t\tif (!*rs[i].src)\n> +\t\t\t\t; /* empty is ok */\n> +\t\t\telse if (is_glob) {\n> +\t\t\t\tif (check_refname_format(rs[i].src, flags))\n> +\t\t\t\t\tgoto invalid;\n> +\t\t\t}\n> +\t\t\telse\n> +\t\t\t\t; /* anything goes, for now */\n> +\t\t\t/*\n> +\t\t\t * RHS\n> +\t\t\t * - missing is allowed, but LHS then must be a\n> +\t\t\t *   valid looking ref.\n> +\t\t\t * - empty is not allowed.\n> +\t\t\t * - otherwise it must be a valid looking ref.\n> +\t\t\t */\n> +\t\t\tif (!rs[i].dst) {\n> +\t\t\t\tif (check_refname_format(rs[i].src, flags))\n> +\t\t\t\t\tgoto invalid;\n> +\t\t\t} else if (!*rs[i].dst) {\n> +\t\t\t\tgoto invalid;\n> +\t\t\t} else {\n> +\t\t\t\tif (check_refname_format(rs[i].dst, flags))\n> +\t\t\t\t\tgoto invalid;\n> +\t\t\t}\n> +\t\t}\n> +\t}\n> +\treturn rs;\n> +\n> + invalid:\n> +\tif (verify) {\n> +\t\t/*\n> +\t\t * nr_refspec must be greater than zero and i must be valid\n> +\t\t * since it is only possible to reach this point from within\n> +\t\t * the for loop above.\n> +\t\t */\n> +\t\tfree_refspec(i+1, rs);\n> +\t\treturn NULL;\n> +\t}\n> +\tdie(\"Invalid refspec '%s'\", refspec[i]);\n> +}\n> +\n> +int valid_fetch_refspec(const char *fetch_refspec_str)\n> +{\n> +\tstruct refspec *refspec;\n> +\n> +\trefspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);\n> +\tfree_refspec(1, refspec);\n> +\treturn !!refspec;\n> +}\n> +\n> +struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)\n> +{\n> +\treturn parse_refspec_internal(nr_refspec, refspec, 1, 0);\n> +}\n> +\n> +struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)\n> +{\n> +\treturn parse_refspec_internal(nr_refspec, refspec, 0, 0);\n> +}\n> +\n> +void free_refspec(int nr_refspec, struct refspec *refspec)\n> +{\n> +\tint i;\n> +\n> +\tif (!refspec)\n> +\t\treturn;\n> +\n> +\tfor (i = 0; i < nr_refspec; i++) {\n> +\t\tfree(refspec[i].src);\n> +\t\tfree(refspec[i].dst);\n> +\t}\n> +\tfree(refspec);\n> +}\n> +\n> diff --git a/refspec.h b/refspec.h\n> new file mode 100644\n> index 000000000..b1db91918\n> --- /dev/null\n> +++ b/refspec.h\n> @@ -0,0 +1,23 @@\n> +#ifndef REFSPEC_H\n> +#define REFSPEC_H\n> +\n> +#define TAG_REFSPEC \"refs/tags/*:refs/tags/*\"\n> +const struct refspec *tag_refspec;\n> +\n> +struct refspec {\n> +\tunsigned force : 1;\n> +\tunsigned pattern : 1;\n> +\tunsigned matching : 1;\n> +\tunsigned exact_sha1 : 1;\n> +\n> +\tchar *src;\n> +\tchar *dst;\n> +};\n> +\n> +int valid_fetch_refspec(const char *refspec);\n> +struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);\n> +struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);\n> +\n> +void free_refspec(int nr_refspec, struct refspec *refspec);\n> +\n> +#endif /* REFSPEC_H */\n> diff --git a/remote.c b/remote.c\n> index 91eb010ca..4d67c061a 100644\n> --- a/remote.c\n> +++ b/remote.c\n> @@ -2,6 +2,7 @@\n>  #include \"config.h\"\n>  #include \"remote.h\"\n>  #include \"refs.h\"\n> +#include \"refspec.h\"\n>  #include \"commit.h\"\n>  #include \"diff.h\"\n>  #include \"revision.h\"\n> @@ -13,18 +14,6 @@\n>  \n>  enum map_direction { FROM_SRC, FROM_DST };\n>  \n> -static struct refspec s_tag_refspec = {\n> -\t0,\n> -\t1,\n> -\t0,\n> -\t0,\n> -\t\"refs/tags/*\",\n> -\t\"refs/tags/*\"\n> -};\n> -\n> -/* See TAG_REFSPEC for the string version */\n> -const struct refspec *tag_refspec = &s_tag_refspec;\n> -\n>  struct counted_string {\n>  \tsize_t len;\n>  \tconst char *s;\n> @@ -499,158 +488,6 @@ static void read_config(void)\n>  \talias_all_urls();\n>  }\n>  \n> -static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n> -{\n> -\tint i;\n> -\tstruct refspec *rs = xcalloc(nr_refspec, sizeof(*rs));\n> -\n> -\tfor (i = 0; i < nr_refspec; i++) {\n> -\t\tsize_t llen;\n> -\t\tint is_glob;\n> -\t\tconst char *lhs, *rhs;\n> -\t\tint flags;\n> -\n> -\t\tis_glob = 0;\n> -\n> -\t\tlhs = refspec[i];\n> -\t\tif (*lhs == '+') {\n> -\t\t\trs[i].force = 1;\n> -\t\t\tlhs++;\n> -\t\t}\n> -\n> -\t\trhs = strrchr(lhs, ':');\n> -\n> -\t\t/*\n> -\t\t * Before going on, special case \":\" (or \"+:\") as a refspec\n> -\t\t * for pushing matching refs.\n> -\t\t */\n> -\t\tif (!fetch && rhs == lhs && rhs[1] == '\\0') {\n> -\t\t\trs[i].matching = 1;\n> -\t\t\tcontinue;\n> -\t\t}\n> -\n> -\t\tif (rhs) {\n> -\t\t\tsize_t rlen = strlen(++rhs);\n> -\t\t\tis_glob = (1 <= rlen && strchr(rhs, '*'));\n> -\t\t\trs[i].dst = xstrndup(rhs, rlen);\n> -\t\t}\n> -\n> -\t\tllen = (rhs ? (rhs - lhs - 1) : strlen(lhs));\n> -\t\tif (1 <= llen && memchr(lhs, '*', llen)) {\n> -\t\t\tif ((rhs && !is_glob) || (!rhs && fetch))\n> -\t\t\t\tgoto invalid;\n> -\t\t\tis_glob = 1;\n> -\t\t} else if (rhs && is_glob) {\n> -\t\t\tgoto invalid;\n> -\t\t}\n> -\n> -\t\trs[i].pattern = is_glob;\n> -\t\trs[i].src = xstrndup(lhs, llen);\n> -\t\tflags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);\n> -\n> -\t\tif (fetch) {\n> -\t\t\tstruct object_id unused;\n> -\n> -\t\t\t/* LHS */\n> -\t\t\tif (!*rs[i].src)\n> -\t\t\t\t; /* empty is ok; it means \"HEAD\" */\n> -\t\t\telse if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused))\n> -\t\t\t\trs[i].exact_sha1 = 1; /* ok */\n> -\t\t\telse if (!check_refname_format(rs[i].src, flags))\n> -\t\t\t\t; /* valid looking ref is ok */\n> -\t\t\telse\n> -\t\t\t\tgoto invalid;\n> -\t\t\t/* RHS */\n> -\t\t\tif (!rs[i].dst)\n> -\t\t\t\t; /* missing is ok; it is the same as empty */\n> -\t\t\telse if (!*rs[i].dst)\n> -\t\t\t\t; /* empty is ok; it means \"do not store\" */\n> -\t\t\telse if (!check_refname_format(rs[i].dst, flags))\n> -\t\t\t\t; /* valid looking ref is ok */\n> -\t\t\telse\n> -\t\t\t\tgoto invalid;\n> -\t\t} else {\n> -\t\t\t/*\n> -\t\t\t * LHS\n> -\t\t\t * - empty is allowed; it means delete.\n> -\t\t\t * - when wildcarded, it must be a valid looking ref.\n> -\t\t\t * - otherwise, it must be an extended SHA-1, but\n> -\t\t\t *   there is no existing way to validate this.\n> -\t\t\t */\n> -\t\t\tif (!*rs[i].src)\n> -\t\t\t\t; /* empty is ok */\n> -\t\t\telse if (is_glob) {\n> -\t\t\t\tif (check_refname_format(rs[i].src, flags))\n> -\t\t\t\t\tgoto invalid;\n> -\t\t\t}\n> -\t\t\telse\n> -\t\t\t\t; /* anything goes, for now */\n> -\t\t\t/*\n> -\t\t\t * RHS\n> -\t\t\t * - missing is allowed, but LHS then must be a\n> -\t\t\t *   valid looking ref.\n> -\t\t\t * - empty is not allowed.\n> -\t\t\t * - otherwise it must be a valid looking ref.\n> -\t\t\t */\n> -\t\t\tif (!rs[i].dst) {\n> -\t\t\t\tif (check_refname_format(rs[i].src, flags))\n> -\t\t\t\t\tgoto invalid;\n> -\t\t\t} else if (!*rs[i].dst) {\n> -\t\t\t\tgoto invalid;\n> -\t\t\t} else {\n> -\t\t\t\tif (check_refname_format(rs[i].dst, flags))\n> -\t\t\t\t\tgoto invalid;\n> -\t\t\t}\n> -\t\t}\n> -\t}\n> -\treturn rs;\n> -\n> - invalid:\n> -\tif (verify) {\n> -\t\t/*\n> -\t\t * nr_refspec must be greater than zero and i must be valid\n> -\t\t * since it is only possible to reach this point from within\n> -\t\t * the for loop above.\n> -\t\t */\n> -\t\tfree_refspec(i+1, rs);\n> -\t\treturn NULL;\n> -\t}\n> -\tdie(\"Invalid refspec '%s'\", refspec[i]);\n> -}\n> -\n> -int valid_fetch_refspec(const char *fetch_refspec_str)\n> -{\n> -\tstruct refspec *refspec;\n> -\n> -\trefspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);\n> -\tfree_refspec(1, refspec);\n> -\treturn !!refspec;\n> -}\n> -\n> -struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)\n> -{\n> -\treturn parse_refspec_internal(nr_refspec, refspec, 1, 0);\n> -}\n> -\n> -struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)\n> -{\n> -\treturn parse_refspec_internal(nr_refspec, refspec, 0, 0);\n> -}\n> -\n> -void free_refspec(int nr_refspec, struct refspec *refspec)\n> -{\n> -\tint i;\n> -\n> -\tif (!refspec)\n> -\t\treturn;\n> -\n> -\tfor (i = 0; i < nr_refspec; i++) {\n> -\t\tfree(refspec[i].src);\n> -\t\tfree(refspec[i].dst);\n> -\t}\n> -\tfree(refspec);\n> -}\n> -\n>  static int valid_remote_nick(const char *name)\n>  {\n>  \tif (!name[0] || is_dot_or_dotdot(name))\n> diff --git a/remote.h b/remote.h\n> index 2b3180f94..386ced901 100644\n> --- a/remote.h\n> +++ b/remote.h\n> @@ -68,18 +68,6 @@ int for_each_remote(each_remote_fn fn, void *priv);\n>  \n>  int remote_has_url(struct remote *remote, const char *url);\n>  \n> -struct refspec {\n> -\tunsigned force : 1;\n> -\tunsigned pattern : 1;\n> -\tunsigned matching : 1;\n> -\tunsigned exact_sha1 : 1;\n> -\n> -\tchar *src;\n> -\tchar *dst;\n> -};\n> -\n> -extern const struct refspec *tag_refspec;\n> -\n>  struct ref {\n>  \tstruct ref *next;\n>  \tstruct object_id old_oid;\n> @@ -175,12 +163,6 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);\n>   */\n>  struct ref *ref_remove_duplicates(struct ref *ref_map);\n>  \n> -int valid_fetch_refspec(const char *refspec);\n> -struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);\n> -extern struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);\n> -\n> -void free_refspec(int nr_refspec, struct refspec *refspec);\n> -\n>  extern int query_refspecs(struct refspec *specs, int nr, struct refspec *query);\n>  char *apply_refspecs(struct refspec *refspecs, int nr_refspec,\n>  \t\t     const char *name);\n> @@ -313,8 +295,6 @@ extern int parseopt_push_cas_option(const struct option *, const char *arg, int\n>  extern int is_empty_cas(const struct push_cas_option *);\n>  void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);\n>  \n> -#define TAG_REFSPEC \"refs/tags/*:refs/tags/*\"\n> -\n>  void add_prune_tags_to_fetch_refspec(struct remote *remote);\n>  \n>  #endif\n> diff --git a/transport-helper.c b/transport-helper.c\n> index 11f1055b4..b99e1cce9 100644\n> --- a/transport-helper.c\n> +++ b/transport-helper.c\n> @@ -11,6 +11,7 @@\n>  #include \"sigchain.h\"\n>  #include \"argv-array.h\"\n>  #include \"refs.h\"\n> +#include \"refspec.h\"\n>  #include \"transport-internal.h\"\n>  #include \"protocol.h\"\n>  \n> diff --git a/transport.c b/transport.c\n> index 37410d8aa..2cf63d18b 100644\n> --- a/transport.c\n> +++ b/transport.c\n> @@ -11,6 +11,7 @@\n>  #include \"bundle.h\"\n>  #include \"dir.h\"\n>  #include \"refs.h\"\n> +#include \"refspec.h\"\n>  #include \"branch.h\"\n>  #include \"url.h\"\n>  #include \"submodule.h\"\n"},{"id":"347684","messageId":"87k1s52va3.fsf@evledraar.gmail.com","threadId":"48495","inReplyTo":"20180514215626.164960-36-bmwill@google.com","subject":"Re: [PATCH 35/35] submodule: convert push_unpushed_submodules to take a struct refspec","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-15T08:11:16Z","receivedAt":"2018-05-15T08:11:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, May 14 2018, Brandon Williams wrote:\n\n> Convert 'push_unpushed_submodules()' to take a 'struct refspec' as a\n> parameter instead of an array of 'const char *'.\n> [...]\n> diff --git a/submodule.h b/submodule.h\n> index e5526f6aa..aae0c9c8f 100644\n> --- a/submodule.h\n> +++ b/submodule.h\n> @@ -100,9 +100,10 @@ extern int submodule_touches_in_range(struct object_id *a,\n>  extern int find_unpushed_submodules(struct oid_array *commits,\n>  \t\t\t\t    const char *remotes_name,\n>  \t\t\t\t    struct string_list *needs_pushing);\n> +struct refspec;\n>  extern int push_unpushed_submodules(struct oid_array *commits,\n>  \t\t\t\t    const struct remote *remote,\n> -\t\t\t\t    const char **refspec, int refspec_nr,\n> +\t\t\t\t    const struct refspec *rs,\n>  \t\t\t\t    const struct string_list *push_options,\n>  \t\t\t\t    int dry_run);\n>  /*\n\nWhy do you prefer doing this to having this on top?:\n    \n    diff --git a/submodule.h b/submodule.h\n    index aae0c9c8ff..c3f206ce17 100644\n    --- a/submodule.h\n    +++ b/submodule.h\n    @@ -1,5 +1,6 @@\n     #ifndef SUBMODULE_H\n     #define SUBMODULE_H\n    +#include \"refspec.h\"\n     \n     struct repository;\n     struct diff_options;\n    @@ -100,7 +101,6 @@ extern int submodule_touches_in_range(struct object_id *a,\n     extern int find_unpushed_submodules(struct oid_array *commits,\n                                        const char *remotes_name,\n                                        struct string_list *needs_pushing);\n    -struct refspec;\n     extern int push_unpushed_submodules(struct oid_array *commits,\n                                        const struct remote *remote,\n                                        const struct refspec *rs,\n"},{"id":"347685","messageId":"xmqqlgcl8h92.fsf@gitster-ct.c.googlers.com","threadId":"48495","inReplyTo":"20180514215626.164960-4-bmwill@google.com","subject":"Re: [PATCH 03/35] refspec: rename struct refspec to struct refspec_item","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-15T08:17:45Z","receivedAt":"2018-05-15T08:17:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> In preperation for introducing an abstraction around a collection of\n> refspecs (much like how a 'struct pathspec' is a collection of 'struct\n> pathspec_item's) rename the existing 'struct refspec' to 'struct\n> refspec_item'.\n\nMakes sense.\n\n>  /* See TAG_REFSPEC for the string version */\n> -const struct refspec *tag_refspec = &s_tag_refspec;\n> +const struct refspec_item *tag_refspec = &s_tag_refspec;\n>  \n>  /*\n>   * Parses 'refspec' and populates 'rs'.  returns 1 if successful and 0 if the\n>   * refspec is invalid.\n>   */\n> -static int parse_refspec(struct refspec *rs, const char *refspec, int fetch)\n> +static int parse_refspec(struct refspec_item *rs, const char *refspec, int fetch)\n\nThe new comment given to the function in the previous step talks\nin terms of the variable name and not type, so technically it is\nstill valid after this change without updating.\n\nI however wonder if it makes more sense to go one step further and\nrename the \"const char *\" variable that is a string form of a single\nrefspec item to something other than \"refspec\".  Which would\ninvalidate the commit you gave to the function and make it necessary\nto be updated in this step.\n\nI wonder if the early part of the series becomes easier to read if\npatches 2 and 3 are swapped.  That may result in less code/comment\nchurn.\n"},{"id":"347687","messageId":"87in7p2ucb.fsf@evledraar.gmail.com","threadId":"48495","inReplyTo":"20180514215626.164960-15-bmwill@google.com","subject":"Re: [PATCH 14/35] remote: convert fetch refspecs to struct refspec","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-15T08:31:32Z","receivedAt":"2018-05-15T08:31:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, May 14 2018, Brandon Williams wrote:\n\n>  void add_prune_tags_to_fetch_refspec(struct remote *remote)\n>  {\n> -\tint nr = remote->fetch_refspec_nr;\n> -\tint bufsize = nr  + 1;\n> -\tint size = sizeof(struct refspec_item);\n> -\n> -\tremote->fetch = xrealloc(remote->fetch, size  * bufsize);\n> -\tmemcpy(&remote->fetch[nr], tag_refspec, size);\n> -\tadd_fetch_refspec(remote, xstrdup(TAG_REFSPEC));\n> +\trefspec_append(&remote->fetch, TAG_REFSPEC);\n>  }\n\nThanks for fixing the hack I needed to put in place in 97716d217c\n(\"fetch: add a --prune-tags option and fetch.pruneTags config\",\n2018-02-09).\n\nI'm not sure where it belongs in this series, but I think this makes\nsense on top of the whole thing:\n\n    diff --git a/builtin/fetch.c b/builtin/fetch.c\n    index af7064dce3..9a523249f5 100644\n    --- a/builtin/fetch.c\n    +++ b/builtin/fetch.c\n    @@ -1383,7 +1383,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru\n\n            maybe_prune_tags = prune_tags_ok && prune_tags;\n            if (maybe_prune_tags && remote_via_config)\n    -               add_prune_tags_to_fetch_refspec(remote);\n    +               refspec_append(&remote->fetch, TAG_REFSPEC);\n    +\n\n            if (maybe_prune_tags && (argc || !remote_via_config))\n                    refspec_append(&rs, TAG_REFSPEC);\n    diff --git a/remote.c b/remote.c\n    index 8e6522f4d0..946b95d18d 100644\n    --- a/remote.c\n    +++ b/remote.c\n    @@ -87,11 +87,6 @@ static void add_fetch_refspec(struct remote *remote, const char *ref)\n            refspec_append(&remote->fetch, ref);\n     }\n\n    -void add_prune_tags_to_fetch_refspec(struct remote *remote)\n    -{\n    -       refspec_append(&remote->fetch, TAG_REFSPEC);\n    -}\n    -\n     static void add_url(struct remote *remote, const char *url)\n     {\n            ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);\n    diff --git a/remote.h b/remote.h\n    index 9014f707f0..62a6566594 100644\n    --- a/remote.h\n    +++ b/remote.h\n    @@ -289,6 +289,4 @@ extern int parseopt_push_cas_option(const struct option *, const char *arg, int\n     extern int is_empty_cas(const struct push_cas_option *);\n     void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);\n\n    -void add_prune_tags_to_fetch_refspec(struct remote *remote);\n    -\n     #endif\n\nI.e. the whole reason we have this function is because of my above\ncommit where I had to very carefully hack around the fact that we didn't\nhave something which could ALLOW_GROW() the structure after it had been\ncreated.\n\nSo I added the add_prune_tags_to_fetch_refspec() function to very\ncarefully do *only* that so others wouldn't be tempted to use this hack\nmore generally.\n\nBut now we have a nice API for it, so we can just throw away the\nwrapper, and use the same API everywhere. You already did the other half\nof that in your e69b54f53a (\"fetch: convert fetch_one to use struct\nrefspec\", 2018-05-11).\n"},{"id":"347688","messageId":"87h8n92tzb.fsf@evledraar.gmail.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"Re: [PATCH 00/35] refactoring refspecs","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-15T08:39:20Z","receivedAt":"2018-05-15T08:39:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, May 14 2018, Brandon Williams wrote:\n\n> When working on protocol v2 I noticed that working with refspecs was a\n> little difficult because of the various api's that existed.  Some\n> functions expected an array of \"const char *\" while others expected an\n> array of \"struct refspec\".  In all cases a length parameter needed to be\n> passed as a parameter as well.  This makes working with refspecs a\n> little challenging because of the different expectations different parts\n> of the code base have.\n>\n> This series refactors how refspecs are handled through out the code base\n> by renaming the existing struct refspec to refspec_item and introducing\n> a new 'struct refspec' which is a container of refspec_items, much like\n> how a pathspec contains pathspec_items.  This simplifies many callers\n> and makes handling pathspecs a bit easier.\n\nThis looks really good to me. The API you're replacing is one of the\nworst I've had a chance to encounter in git.git (as noted in my\nhttps://public-inbox.org/git/87in7p2ucb.fsf@evledraar.gmail.com/ but\nmaybe I haven't looked widely enough), and now it's really nice.\n\n> I have some follow on work which I'll build on top of this series, but\n> since this was already getting a bit lengthy at 35 patches I'll save\n> that for another time.\n\nIn addition to my other suggestions for stuff to put on top, which I see\nnow you may have just had in your local tree but didn't submit, I think\nthis makes sense:\n\n    diff --git a/remote.c b/remote.c\n    index 946b95d18d..cb97e662e8 100644\n    --- a/remote.c\n    +++ b/remote.c\n    @@ -77,16 +77,6 @@ static const char *alias_url(const char *url, struct rewrites *r)\n     \treturn xstrfmt(\"%s%s\", r->rewrite[longest_i]->base, url + longest->len);\n     }\n\n    -static void add_push_refspec(struct remote *remote, const char *ref)\n    -{\n    -\trefspec_append(&remote->push, ref);\n    -}\n    -\n    -static void add_fetch_refspec(struct remote *remote, const char *ref)\n    -{\n    -\trefspec_append(&remote->fetch, ref);\n    -}\n    -\n     static void add_url(struct remote *remote, const char *url)\n     {\n     \tALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);\n    @@ -261,9 +251,9 @@ static void read_remotes_file(struct remote *remote)\n     \t\tif (skip_prefix(buf.buf, \"URL:\", &v))\n     \t\t\tadd_url_alias(remote, xstrdup(skip_spaces(v)));\n     \t\telse if (skip_prefix(buf.buf, \"Push:\", &v))\n    -\t\t\tadd_push_refspec(remote, xstrdup(skip_spaces(v)));\n    +\t\t\trefspec_append(&remote->push, xstrdup(skip_spaces(v)));\n     \t\telse if (skip_prefix(buf.buf, \"Pull:\", &v))\n    -\t\t\tadd_fetch_refspec(remote, xstrdup(skip_spaces(v)));\n    +\t\t\trefspec_append(&remote->fetch, xstrdup(skip_spaces(v)));\n     \t}\n     \tstrbuf_release(&buf);\n     \tfclose(f);\n    @@ -302,14 +292,14 @@ static void read_branches_file(struct remote *remote)\n     \t\tfrag = \"master\";\n\n     \tadd_url_alias(remote, strbuf_detach(&buf, NULL));\n    -\tadd_fetch_refspec(remote, xstrfmt(\"refs/heads/%s:refs/heads/%s\",\n    -\t\t\t\t\t  frag, remote->name));\n    +\trefspec_append(&remote->fetch, xstrfmt(\"refs/heads/%s:refs/heads/%s\",\n    +\t\t\t\t\t       frag, remote->name));\n\n     \t/*\n     \t * Cogito compatible push: push current HEAD to remote #branch\n     \t * (master if missing)\n     \t */\n    -\tadd_push_refspec(remote, xstrfmt(\"HEAD:refs/heads/%s\", frag));\n    +\trefspec_append(&remote->push, xstrfmt(\"HEAD:refs/heads/%s\", frag));\n     \tremote->fetch_tags = 1; /* always auto-follow */\n     }\n\n    @@ -395,12 +385,12 @@ static int handle_config(const char *key, const char *value, void *cb)\n     \t\tconst char *v;\n     \t\tif (git_config_string(&v, key, value))\n     \t\t\treturn -1;\n    -\t\tadd_push_refspec(remote, v);\n    +\t\trefspec_append(&remote->push, v);\n     \t} else if (!strcmp(subkey, \"fetch\")) {\n     \t\tconst char *v;\n     \t\tif (git_config_string(&v, key, value))\n     \t\t\treturn -1;\n    -\t\tadd_fetch_refspec(remote, v);\n    +\t\trefspec_append(&remote->fetch, v);\n     \t} else if (!strcmp(subkey, \"receivepack\")) {\n     \t\tconst char *v;\n     \t\tif (git_config_string(&v, key, value))\n\nI.e. the reason we have add_{push,fetch}_refspec() in the first place is\nbecause without your series it's tricky to add new ones, but now it's\ntrivial, so let's not leave behind wrapper static functions whose sole\npurpose is to just call another exported API as-is.\n\nI've pushed all the patches I quoted inline in this review at\navar-bwill/refspec in github.com/avar/git, consider them all signed-off,\nand depending on whether you agree/disagree etc. please squash\nthem/adapt them/drop them however you see fit.\n"},{"id":"347690","messageId":"xmqqefid8djg.fsf@gitster-ct.c.googlers.com","threadId":"48495","inReplyTo":"20180514215626.164960-5-bmwill@google.com","subject":"Re: [PATCH 04/35] refspec: introduce struct refspec","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-15T09:37:55Z","receivedAt":"2018-05-15T09:38:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> diff --git a/refspec.h b/refspec.h\n> index 173cea882..f6fb251f3 100644\n> --- a/refspec.h\n> +++ b/refspec.h\n> @@ -20,4 +20,29 @@ struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec);\n>  \n>  void free_refspec(int nr_refspec, struct refspec_item *refspec);\n>  \n> +#define REFSPEC_FETCH 1\n> +#define REFSPEC_PUSH 0\n\nThe reversed order of these two definitions looks somewhat unusual.\nI guess the reason why you made FETCH true and PUSH false is\nprobably because quite a lot of places in the existing code we do\n\n\tif (fetch)\n\t\tdo the fetch thing;\n \telse\n\t\tdo the push thing;\n\ni.e. \"fetch\" variable is used as \"is this a fetch: yes/no?\"\nboolean, and a caller that mysteriously passes \"1\" (or \"0\")\nis harder to read than necessary.  Being able to pass REFSPEC_PUSH\ninstead of \"0\" would certainly make the caller easier to read.  But\nas long as the variable is understood as \"is_fetch? Yes/no\", the\ncaller can pass Yes or No and be still descriptive enough.\n\nI think the way to make such a code more readable would not be to\nrewrite the above to\n\n\tif (fetch_or_push)\n\t\tdo the fetch thing;\n \telse\n\t\tdo the push thing;\n\nRather it would be \n\n\tif (fetch_or_push == REFSPEC_FETCH)\n\t\tdo the fetch thing;\n \telse\n\t\tdo the push thing;\n\nAnd once you have gone that far, the actual \"enum\" value assignment\nno longer makes difference.\n\n> +#define REFSPEC_INIT_FETCH { .fetch = REFSPEC_FETCH }\n> +#define REFSPEC_INIT_PUSH { .fetch = REFSPEC_PUSH }\n> +\n> +struct refspec {\n> +\tstruct refspec_item *items;\n> +\tint alloc;\n> +\tint nr;\n> +\n> +\tconst char **raw;\n> +\tint raw_alloc;\n> +\tint raw_nr;\n> +\n> +\tint fetch;\n> +};\n> +\n> +void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch);\n> +void refspec_item_clear(struct refspec_item *item);\n> +void refspec_init(struct refspec *rs, int fetch);\n> +void refspec_append(struct refspec *rs, const char *refspec);\n> +void refspec_appendn(struct refspec *rs, const char **refspecs, int nr);\n> +void refspec_clear(struct refspec *rs);\n> +\n>  #endif /* REFSPEC_H */\n"},{"id":"347691","messageId":"xmqqa7t18dcw.fsf@gitster-ct.c.googlers.com","threadId":"48495","inReplyTo":"20180514215626.164960-6-bmwill@google.com","subject":"Re: [PATCH 05/35] refspec: convert valid_fetch_refspec to use parse_refspec","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-15T09:41:51Z","receivedAt":"2018-05-15T09:41:57Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> Convert 'valid_fetch_refspec()' to use the new 'parse_refspec()'\n> function to only parse a single refspec an eliminate an allocation.\n\ns/an/and/, perhaps?\n\n> -int valid_fetch_refspec(const char *fetch_refspec_str)\n> -{\n> -\tstruct refspec_item *refspec;\n> -\n> -\trefspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);\n> -\tfree_refspec(1, refspec);\n> -\treturn !!refspec;\n> -}\n> -\n>  struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec)\n>  {\n>  \treturn parse_refspec_internal(nr_refspec, refspec, 1, 0);\n> @@ -242,3 +233,11 @@ void refspec_clear(struct refspec *rs)\n>  \n>  \trs->fetch = 0;\n>  }\n> +\n> +int valid_fetch_refspec(const char *fetch_refspec_str)\n> +{\n> +\tstruct refspec_item refspec;\n> +\tint ret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n> +\trefspec_item_clear(&refspec);\n> +\treturn ret;\n> +}\n\nMakes quite a lot of sense.  The function name may eventually want\nto become parse_refspec_item(), though?\n\n\n"},{"id":"347698","messageId":"20180515165106.GA72551@google.com","threadId":"48495","inReplyTo":"xmqqr2md8hrz.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 01/35] refspec: move refspec parsing logic into its own file","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-15T16:51:06Z","receivedAt":"2018-05-15T16:51:13Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 05/15, Junio C Hamano wrote:\n> Brandon Williams <bmwill@google.com> writes:\n> \n> > In preperation for performing a refactor on refspec related code, move\n> \n> Preparation?\n\nOops, I'll fix that.\n\n\n-- \nBrandon Williams\n"},{"id":"347699","messageId":"CAGZ79kaOt4oonsEMn47Rp_xL4_tGy6k78kFZFfxUOg_4sD_xtg@mail.gmail.com","threadId":"48495","inReplyTo":"87k1s52va3.fsf@evledraar.gmail.com","subject":"Re: [PATCH 35/35] submodule: convert push_unpushed_submodules to take a struct refspec","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-05-15T16:52:05Z","receivedAt":"2018-05-15T16:52:09Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Tue, May 15, 2018 at 1:11 AM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> On Mon, May 14 2018, Brandon Williams wrote:\n>\n>> Convert 'push_unpushed_submodules()' to take a 'struct refspec' as a\n>> parameter instead of an array of 'const char *'.\n>> [...]\n>> diff --git a/submodule.h b/submodule.h\n>> index e5526f6aa..aae0c9c8f 100644\n>> --- a/submodule.h\n>> +++ b/submodule.h\n>> @@ -100,9 +100,10 @@ extern int submodule_touches_in_range(struct object_id *a,\n>>  extern int find_unpushed_submodules(struct oid_array *commits,\n>>                                   const char *remotes_name,\n>>                                   struct string_list *needs_pushing);\n>> +struct refspec;\n>>  extern int push_unpushed_submodules(struct oid_array *commits,\n>>                                   const struct remote *remote,\n>> -                                 const char **refspec, int refspec_nr,\n>> +                                 const struct refspec *rs,\n>>                                   const struct string_list *push_options,\n>>                                   int dry_run);\n>>  /*\n>\n> Why do you prefer doing this to having this on top?:\n\nThe fewer includes in header files the better, as then the headers\nthemselves don't have dependencies. (Otherwise we'd end up\nmultiplying cache.h ;)\n\nIn the source files we have to include all needed headers, but for\nthe headers, it is better if we can just get away with declaring the\nexistence of a struct.\n\nThis way we reduce compile time, so I'd am not keen on your patch\non top.\n\nThis is discussed a lot on stackoverflow, e.g.:\nhttps://softwareengineering.stackexchange.com/questions/195806/forward-declaration-vs-include\nhttps://stackoverflow.com/a/15828094\n\nStefan\n"},{"id":"347700","messageId":"20180515165959.GB72551@google.com","threadId":"48495","inReplyTo":"87k1s52va3.fsf@evledraar.gmail.com","subject":"Re: [PATCH 35/35] submodule: convert push_unpushed_submodules to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-15T16:59:59Z","receivedAt":"2018-05-15T17:00:06Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 05/15, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Mon, May 14 2018, Brandon Williams wrote:\n> \n> > Convert 'push_unpushed_submodules()' to take a 'struct refspec' as a\n> > parameter instead of an array of 'const char *'.\n> > [...]\n> > diff --git a/submodule.h b/submodule.h\n> > index e5526f6aa..aae0c9c8f 100644\n> > --- a/submodule.h\n> > +++ b/submodule.h\n> > @@ -100,9 +100,10 @@ extern int submodule_touches_in_range(struct object_id *a,\n> >  extern int find_unpushed_submodules(struct oid_array *commits,\n> >  \t\t\t\t    const char *remotes_name,\n> >  \t\t\t\t    struct string_list *needs_pushing);\n> > +struct refspec;\n> >  extern int push_unpushed_submodules(struct oid_array *commits,\n> >  \t\t\t\t    const struct remote *remote,\n> > -\t\t\t\t    const char **refspec, int refspec_nr,\n> > +\t\t\t\t    const struct refspec *rs,\n> >  \t\t\t\t    const struct string_list *push_options,\n> >  \t\t\t\t    int dry_run);\n> >  /*\n> \n> Why do you prefer doing this to having this on top?:\n>     \n>     diff --git a/submodule.h b/submodule.h\n>     index aae0c9c8ff..c3f206ce17 100644\n>     --- a/submodule.h\n>     +++ b/submodule.h\n>     @@ -1,5 +1,6 @@\n>      #ifndef SUBMODULE_H\n>      #define SUBMODULE_H\n>     +#include \"refspec.h\"\n>      \n>      struct repository;\n>      struct diff_options;\n>     @@ -100,7 +101,6 @@ extern int submodule_touches_in_range(struct object_id *a,\n>      extern int find_unpushed_submodules(struct oid_array *commits,\n>                                         const char *remotes_name,\n>                                         struct string_list *needs_pushing);\n>     -struct refspec;\n>      extern int push_unpushed_submodules(struct oid_array *commits,\n>                                         const struct remote *remote,\n>                                         const struct refspec *rs,\n\nBasically for the reason that stefan pointed out, though in practice I\ndon't know how much that would actually impact compile times given we\nalready are including cache.h and a bunch of others everywhere.\n\n-- \nBrandon Williams\n"},{"id":"347702","messageId":"20180515175747.GD72551@google.com","threadId":"48495","inReplyTo":"87in7p2ucb.fsf@evledraar.gmail.com","subject":"Re: [PATCH 14/35] remote: convert fetch refspecs to struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-15T17:57:47Z","receivedAt":"2018-05-15T17:57:53Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 05/15, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Mon, May 14 2018, Brandon Williams wrote:\n> \n> >  void add_prune_tags_to_fetch_refspec(struct remote *remote)\n> >  {\n> > -\tint nr = remote->fetch_refspec_nr;\n> > -\tint bufsize = nr  + 1;\n> > -\tint size = sizeof(struct refspec_item);\n> > -\n> > -\tremote->fetch = xrealloc(remote->fetch, size  * bufsize);\n> > -\tmemcpy(&remote->fetch[nr], tag_refspec, size);\n> > -\tadd_fetch_refspec(remote, xstrdup(TAG_REFSPEC));\n> > +\trefspec_append(&remote->fetch, TAG_REFSPEC);\n> >  }\n> \n> Thanks for fixing the hack I needed to put in place in 97716d217c\n> (\"fetch: add a --prune-tags option and fetch.pruneTags config\",\n> 2018-02-09).\n> \n> I'm not sure where it belongs in this series, but I think this makes\n> sense on top of the whole thing:\n\nThis actually would work well immediately after this patch, so I'll add\nit here :)\n\nThanks!\n\n> \n>     diff --git a/builtin/fetch.c b/builtin/fetch.c\n>     index af7064dce3..9a523249f5 100644\n>     --- a/builtin/fetch.c\n>     +++ b/builtin/fetch.c\n>     @@ -1383,7 +1383,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru\n> \n>             maybe_prune_tags = prune_tags_ok && prune_tags;\n>             if (maybe_prune_tags && remote_via_config)\n>     -               add_prune_tags_to_fetch_refspec(remote);\n>     +               refspec_append(&remote->fetch, TAG_REFSPEC);\n>     +\n> \n>             if (maybe_prune_tags && (argc || !remote_via_config))\n>                     refspec_append(&rs, TAG_REFSPEC);\n>     diff --git a/remote.c b/remote.c\n>     index 8e6522f4d0..946b95d18d 100644\n>     --- a/remote.c\n>     +++ b/remote.c\n>     @@ -87,11 +87,6 @@ static void add_fetch_refspec(struct remote *remote, const char *ref)\n>             refspec_append(&remote->fetch, ref);\n>      }\n> \n>     -void add_prune_tags_to_fetch_refspec(struct remote *remote)\n>     -{\n>     -       refspec_append(&remote->fetch, TAG_REFSPEC);\n>     -}\n>     -\n>      static void add_url(struct remote *remote, const char *url)\n>      {\n>             ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);\n>     diff --git a/remote.h b/remote.h\n>     index 9014f707f0..62a6566594 100644\n>     --- a/remote.h\n>     +++ b/remote.h\n>     @@ -289,6 +289,4 @@ extern int parseopt_push_cas_option(const struct option *, const char *arg, int\n>      extern int is_empty_cas(const struct push_cas_option *);\n>      void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);\n> \n>     -void add_prune_tags_to_fetch_refspec(struct remote *remote);\n>     -\n>      #endif\n> \n> I.e. the whole reason we have this function is because of my above\n> commit where I had to very carefully hack around the fact that we didn't\n> have something which could ALLOW_GROW() the structure after it had been\n> created.\n> \n> So I added the add_prune_tags_to_fetch_refspec() function to very\n> carefully do *only* that so others wouldn't be tempted to use this hack\n> more generally.\n> \n> But now we have a nice API for it, so we can just throw away the\n> wrapper, and use the same API everywhere. You already did the other half\n> of that in your e69b54f53a (\"fetch: convert fetch_one to use struct\n> refspec\", 2018-05-11).\n\n-- \nBrandon Williams\n"},{"id":"347703","messageId":"20180515180127.GE72551@google.com","threadId":"48495","inReplyTo":"87h8n92tzb.fsf@evledraar.gmail.com","subject":"Re: [PATCH 00/35] refactoring refspecs","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-15T18:01:27Z","receivedAt":"2018-05-15T18:01:34Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 05/15, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Mon, May 14 2018, Brandon Williams wrote:\n> \n> > When working on protocol v2 I noticed that working with refspecs was a\n> > little difficult because of the various api's that existed.  Some\n> > functions expected an array of \"const char *\" while others expected an\n> > array of \"struct refspec\".  In all cases a length parameter needed to be\n> > passed as a parameter as well.  This makes working with refspecs a\n> > little challenging because of the different expectations different parts\n> > of the code base have.\n> >\n> > This series refactors how refspecs are handled through out the code base\n> > by renaming the existing struct refspec to refspec_item and introducing\n> > a new 'struct refspec' which is a container of refspec_items, much like\n> > how a pathspec contains pathspec_items.  This simplifies many callers\n> > and makes handling pathspecs a bit easier.\n> \n> This looks really good to me. The API you're replacing is one of the\n> worst I've had a chance to encounter in git.git (as noted in my\n> https://public-inbox.org/git/87in7p2ucb.fsf@evledraar.gmail.com/ but\n> maybe I haven't looked widely enough), and now it's really nice.\n\nThanks! Yeah its one of the rougher edges I've worked with and I'm glad\nI finally got around to fixing it.\n\n> \n> > I have some follow on work which I'll build on top of this series, but\n> > since this was already getting a bit lengthy at 35 patches I'll save\n> > that for another time.\n> \n> In addition to my other suggestions for stuff to put on top, which I see\n> now you may have just had in your local tree but didn't submit, I think\n> this makes sense:\n\nYes these changes make sense, though I'll need to tweak them to avoid\nsome memory leaks.  I'll probably go ahead and squash it into the two\npatches which effect those two functions.\n\nThanks for catching this.\n\n> \n>     diff --git a/remote.c b/remote.c\n>     index 946b95d18d..cb97e662e8 100644\n>     --- a/remote.c\n>     +++ b/remote.c\n>     @@ -77,16 +77,6 @@ static const char *alias_url(const char *url, struct rewrites *r)\n>      \treturn xstrfmt(\"%s%s\", r->rewrite[longest_i]->base, url + longest->len);\n>      }\n> \n>     -static void add_push_refspec(struct remote *remote, const char *ref)\n>     -{\n>     -\trefspec_append(&remote->push, ref);\n>     -}\n>     -\n>     -static void add_fetch_refspec(struct remote *remote, const char *ref)\n>     -{\n>     -\trefspec_append(&remote->fetch, ref);\n>     -}\n>     -\n>      static void add_url(struct remote *remote, const char *url)\n>      {\n>      \tALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);\n>     @@ -261,9 +251,9 @@ static void read_remotes_file(struct remote *remote)\n>      \t\tif (skip_prefix(buf.buf, \"URL:\", &v))\n>      \t\t\tadd_url_alias(remote, xstrdup(skip_spaces(v)));\n>      \t\telse if (skip_prefix(buf.buf, \"Push:\", &v))\n>     -\t\t\tadd_push_refspec(remote, xstrdup(skip_spaces(v)));\n>     +\t\t\trefspec_append(&remote->push, xstrdup(skip_spaces(v)));\n>      \t\telse if (skip_prefix(buf.buf, \"Pull:\", &v))\n>     -\t\t\tadd_fetch_refspec(remote, xstrdup(skip_spaces(v)));\n>     +\t\t\trefspec_append(&remote->fetch, xstrdup(skip_spaces(v)));\n>      \t}\n>      \tstrbuf_release(&buf);\n>      \tfclose(f);\n>     @@ -302,14 +292,14 @@ static void read_branches_file(struct remote *remote)\n>      \t\tfrag = \"master\";\n> \n>      \tadd_url_alias(remote, strbuf_detach(&buf, NULL));\n>     -\tadd_fetch_refspec(remote, xstrfmt(\"refs/heads/%s:refs/heads/%s\",\n>     -\t\t\t\t\t  frag, remote->name));\n>     +\trefspec_append(&remote->fetch, xstrfmt(\"refs/heads/%s:refs/heads/%s\",\n>     +\t\t\t\t\t       frag, remote->name));\n> \n>      \t/*\n>      \t * Cogito compatible push: push current HEAD to remote #branch\n>      \t * (master if missing)\n>      \t */\n>     -\tadd_push_refspec(remote, xstrfmt(\"HEAD:refs/heads/%s\", frag));\n>     +\trefspec_append(&remote->push, xstrfmt(\"HEAD:refs/heads/%s\", frag));\n>      \tremote->fetch_tags = 1; /* always auto-follow */\n>      }\n> \n>     @@ -395,12 +385,12 @@ static int handle_config(const char *key, const char *value, void *cb)\n>      \t\tconst char *v;\n>      \t\tif (git_config_string(&v, key, value))\n>      \t\t\treturn -1;\n>     -\t\tadd_push_refspec(remote, v);\n>     +\t\trefspec_append(&remote->push, v);\n>      \t} else if (!strcmp(subkey, \"fetch\")) {\n>      \t\tconst char *v;\n>      \t\tif (git_config_string(&v, key, value))\n>      \t\t\treturn -1;\n>     -\t\tadd_fetch_refspec(remote, v);\n>     +\t\trefspec_append(&remote->fetch, v);\n>      \t} else if (!strcmp(subkey, \"receivepack\")) {\n>      \t\tconst char *v;\n>      \t\tif (git_config_string(&v, key, value))\n> \n> I.e. the reason we have add_{push,fetch}_refspec() in the first place is\n> because without your series it's tricky to add new ones, but now it's\n> trivial, so let's not leave behind wrapper static functions whose sole\n> purpose is to just call another exported API as-is.\n> \n> I've pushed all the patches I quoted inline in this review at\n> avar-bwill/refspec in github.com/avar/git, consider them all signed-off,\n> and depending on whether you agree/disagree etc. please squash\n> them/adapt them/drop them however you see fit.\n\n-- \nBrandon Williams\n"},{"id":"347705","messageId":"20180515181950.GF72551@google.com","threadId":"48495","inReplyTo":"xmqqlgcl8h92.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 03/35] refspec: rename struct refspec to struct refspec_item","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-15T18:19:50Z","receivedAt":"2018-05-15T18:19:56Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 05/15, Junio C Hamano wrote:\n> Brandon Williams <bmwill@google.com> writes:\n> \n> > In preperation for introducing an abstraction around a collection of\n> > refspecs (much like how a 'struct pathspec' is a collection of 'struct\n> > pathspec_item's) rename the existing 'struct refspec' to 'struct\n> > refspec_item'.\n> \n> Makes sense.\n> \n> >  /* See TAG_REFSPEC for the string version */\n> > -const struct refspec *tag_refspec = &s_tag_refspec;\n> > +const struct refspec_item *tag_refspec = &s_tag_refspec;\n> >  \n> >  /*\n> >   * Parses 'refspec' and populates 'rs'.  returns 1 if successful and 0 if the\n> >   * refspec is invalid.\n> >   */\n> > -static int parse_refspec(struct refspec *rs, const char *refspec, int fetch)\n> > +static int parse_refspec(struct refspec_item *rs, const char *refspec, int fetch)\n> \n> The new comment given to the function in the previous step talks\n> in terms of the variable name and not type, so technically it is\n> still valid after this change without updating.\n> \n> I however wonder if it makes more sense to go one step further and\n> rename the \"const char *\" variable that is a string form of a single\n> refspec item to something other than \"refspec\".  Which would\n> invalidate the commit you gave to the function and make it necessary\n> to be updated in this step.\n> \n> I wonder if the early part of the series becomes easier to read if\n> patches 2 and 3 are swapped.  That may result in less code/comment\n> churn.\n\nI'll go ahead and switch these two patches and fixup the comment and\nfunction name.\n\n-- \nBrandon Williams\n"},{"id":"347706","messageId":"20180515183702.GG72551@google.com","threadId":"48495","inReplyTo":"xmqqefid8djg.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 04/35] refspec: introduce struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-15T18:37:02Z","receivedAt":"2018-05-15T18:37:08Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 05/15, Junio C Hamano wrote:\n> Brandon Williams <bmwill@google.com> writes:\n> \n> > diff --git a/refspec.h b/refspec.h\n> > index 173cea882..f6fb251f3 100644\n> > --- a/refspec.h\n> > +++ b/refspec.h\n> > @@ -20,4 +20,29 @@ struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec);\n> >  \n> >  void free_refspec(int nr_refspec, struct refspec_item *refspec);\n> >  \n> > +#define REFSPEC_FETCH 1\n> > +#define REFSPEC_PUSH 0\n> \n> The reversed order of these two definitions looks somewhat unusual.\n> I guess the reason why you made FETCH true and PUSH false is\n> probably because quite a lot of places in the existing code we do\n\nYeah I really just made the choice based on what the existing logic did\n(parse_refspec takes in a parameter 'fetch' which is 1 if we are parsing\nthe refspec for a fetch and 0 for push).  So it wouldn't be too\ndifficult to go and make this change here since all future callers use\nthe #defines themselves, because it is significantly easier to read\n'REFSPEC_PUSH' than to read a 0 like you point out below :)\n\n> \n> \tif (fetch)\n> \t\tdo the fetch thing;\n>  \telse\n> \t\tdo the push thing;\n> \n> i.e. \"fetch\" variable is used as \"is this a fetch: yes/no?\"\n> boolean, and a caller that mysteriously passes \"1\" (or \"0\")\n> is harder to read than necessary.  Being able to pass REFSPEC_PUSH\n> instead of \"0\" would certainly make the caller easier to read.  But\n> as long as the variable is understood as \"is_fetch? Yes/no\", the\n> caller can pass Yes or No and be still descriptive enough.\n> \n> I think the way to make such a code more readable would not be to\n> rewrite the above to\n> \n> \tif (fetch_or_push)\n> \t\tdo the fetch thing;\n>  \telse\n> \t\tdo the push thing;\n> \n> Rather it would be \n> \n> \tif (fetch_or_push == REFSPEC_FETCH)\n> \t\tdo the fetch thing;\n>  \telse\n> \t\tdo the push thing;\n> \n> And once you have gone that far, the actual \"enum\" value assignment\n> no longer makes difference.\n> \n> > +#define REFSPEC_INIT_FETCH { .fetch = REFSPEC_FETCH }\n> > +#define REFSPEC_INIT_PUSH { .fetch = REFSPEC_PUSH }\n> > +\n> > +struct refspec {\n> > +\tstruct refspec_item *items;\n> > +\tint alloc;\n> > +\tint nr;\n> > +\n> > +\tconst char **raw;\n> > +\tint raw_alloc;\n> > +\tint raw_nr;\n> > +\n> > +\tint fetch;\n> > +};\n> > +\n> > +void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch);\n> > +void refspec_item_clear(struct refspec_item *item);\n> > +void refspec_init(struct refspec *rs, int fetch);\n> > +void refspec_append(struct refspec *rs, const char *refspec);\n> > +void refspec_appendn(struct refspec *rs, const char **refspecs, int nr);\n> > +void refspec_clear(struct refspec *rs);\n> > +\n> >  #endif /* REFSPEC_H */\n\n-- \nBrandon Williams\n"},{"id":"347744","messageId":"xmqqy3gk77qg.fsf@gitster-ct.c.googlers.com","threadId":"48495","inReplyTo":"20180515165106.GA72551@google.com","subject":"Re: [PATCH 01/35] refspec: move refspec parsing logic into its own file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-16T00:40:55Z","receivedAt":"2018-05-16T00:41:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> On 05/15, Junio C Hamano wrote:\n>> Brandon Williams <bmwill@google.com> writes:\n>> \n>> > In preperation for performing a refactor on refspec related code, move\n>> \n>> Preparation?\n>\n> Oops, I'll fix that.\n\nThanks.  And sorry for forcing you to page thru 600+ lines only to\nfind that I commented on only that line.  I'll try harder to make\nsure I trim quotes from my responses.\n\n"},{"id":"347848","messageId":"20180516225823.235426-1-bmwill@google.com","threadId":"48495","inReplyTo":"20180514215626.164960-1-bmwill@google.com","subject":"[PATCH v2 00/36] refactoring refspecs","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:57:47Z","receivedAt":"2018-05-16T22:58:42Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Changes in v2:\n * added missing extern keyword in the first patch\n * reordered patch 2 and 3 and updated some comments to be clearer.\n * fixed some memory leaks\n * squashed in some changes recommended by Aevar \n\nBrandon Williams (36):\n  refspec: move refspec parsing logic into its own file\n  refspec: rename struct refspec to struct refspec_item\n  refspec: factor out parsing a single refspec\n  refspec: introduce struct refspec\n  refspec: convert valid_fetch_refspec to use parse_refspec\n  submodule--helper: convert push_check to use struct refspec\n  pull: convert get_tracking_branch to use refspec_item_init\n  transport: convert transport_push to use struct refspec\n  remote: convert check_push_refs to use struct refspec\n  remote: convert match_push_refs to use struct refspec\n  clone: convert cmd_clone to use refspec_item_init\n  fast-export: convert to use struct refspec\n  remote: convert push refspecs to struct refspec\n  remote: convert fetch refspecs to struct refspec\n  remote: remove add_prune_tags_to_fetch_refspec\n  transport-helper: convert to use struct refspec\n  fetch: convert fetch_one to use struct refspec\n  fetch: convert refmap to use struct refspec\n  refspec: remove the deprecated functions\n  fetch: convert do_fetch to take a struct refspec\n  fetch: convert get_ref_map to take a struct refspec\n  fetch: convert prune_refs to take a struct refspec\n  remote: convert get_stale_heads to take a struct refspec\n  remote: convert apply_refspecs to take a struct refspec\n  remote: convert query_refspecs to take a struct refspec\n  remote: convert get_ref_match to take a struct refspec\n  remote: convert match_explicit_refs to take a struct refspec\n  push: check for errors earlier\n  push: convert to use struct refspec\n  transport: convert transport_push to take a struct refspec\n  send-pack: store refspecs in a struct refspec\n  transport: remove transport_verify_remote_names\n  http-push: store refspecs in a struct refspec\n  remote: convert match_push_refs to take a struct refspec\n  remote: convert check_push_refs to take a struct refspec\n  submodule: convert push_unpushed_submodules to take a struct refspec\n\n Makefile                    |   1 +\n branch.c                    |   7 +-\n builtin/clone.c             |  13 +-\n builtin/fast-export.c       |  22 +--\n builtin/fetch.c             | 134 ++++++--------\n builtin/merge.c             |   1 +\n builtin/pull.c              |   9 +-\n builtin/push.c              |  80 ++++----\n builtin/remote.c            |  37 ++--\n builtin/send-pack.c         |  24 +--\n builtin/submodule--helper.c |  14 +-\n checkout.c                  |   5 +-\n http-push.c                 |  18 +-\n refspec.c                   | 194 ++++++++++++++++++++\n refspec.h                   |  44 +++++\n remote.c                    | 353 ++++++++----------------------------\n remote.h                    |  50 ++---\n submodule.c                 |  19 +-\n submodule.h                 |   3 +-\n transport-helper.c          |  39 ++--\n transport.c                 |  51 ++----\n transport.h                 |   4 +-\n 22 files changed, 527 insertions(+), 595 deletions(-)\n create mode 100644 refspec.c\n create mode 100644 refspec.h\n\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347849","messageId":"20180516225823.235426-2-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 01/36] refspec: move refspec parsing logic into its own file","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:57:48Z","receivedAt":"2018-05-16T22:58:46Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"In preparation for performing a refactor on refspec related code, move\nthe refspec parsing logic into its own file.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n Makefile                    |   1 +\n branch.c                    |   1 +\n builtin/clone.c             |   1 +\n builtin/fast-export.c       |   1 +\n builtin/fetch.c             |   1 +\n builtin/merge.c             |   1 +\n builtin/pull.c              |   1 +\n builtin/push.c              |   1 +\n builtin/remote.c            |   1 +\n builtin/submodule--helper.c |   1 +\n checkout.c                  |   1 +\n refspec.c                   | 168 ++++++++++++++++++++++++++++++++++++\n refspec.h                   |  23 +++++\n remote.c                    | 165 +----------------------------------\n remote.h                    |  20 -----\n transport-helper.c          |   1 +\n transport.c                 |   1 +\n 17 files changed, 205 insertions(+), 184 deletions(-)\n create mode 100644 refspec.c\n create mode 100644 refspec.h\n\ndiff --git a/Makefile b/Makefile\nindex ad880d1fc..4bca65383 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -928,6 +928,7 @@ LIB_OBJS += refs/files-backend.o\n LIB_OBJS += refs/iterator.o\n LIB_OBJS += refs/packed-backend.o\n LIB_OBJS += refs/ref-cache.o\n+LIB_OBJS += refspec.o\n LIB_OBJS += ref-filter.o\n LIB_OBJS += remote.o\n LIB_OBJS += replace-object.o\ndiff --git a/branch.c b/branch.c\nindex 2672054f0..32ccefc6b 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -3,6 +3,7 @@\n #include \"config.h\"\n #include \"branch.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"remote.h\"\n #include \"commit.h\"\n #include \"worktree.h\"\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 84f1473d1..6d1614ed3 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -14,6 +14,7 @@\n #include \"parse-options.h\"\n #include \"fetch-pack.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"tree.h\"\n #include \"tree-walk.h\"\n #include \"unpack-trees.h\"\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 530df12f0..a13b7c8ef 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -7,6 +7,7 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"commit.h\"\n #include \"object.h\"\n #include \"tag.h\"\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 7ee83ac0f..1fce68e9a 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -5,6 +5,7 @@\n #include \"config.h\"\n #include \"repository.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"commit.h\"\n #include \"builtin.h\"\n #include \"string-list.h\"\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex 9db5a2cf1..c362cfe90 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -14,6 +14,7 @@\n #include \"run-command.h\"\n #include \"diff.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"commit.h\"\n #include \"diffcore.h\"\n #include \"revision.h\"\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 71aac5005..6247c956d 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -15,6 +15,7 @@\n #include \"remote.h\"\n #include \"dir.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"revision.h\"\n #include \"submodule.h\"\n #include \"submodule-config.h\"\ndiff --git a/builtin/push.c b/builtin/push.c\nindex ac3705370..fa65999b2 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -4,6 +4,7 @@\n #include \"cache.h\"\n #include \"config.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"run-command.h\"\n #include \"builtin.h\"\n #include \"remote.h\"\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 8708e584e..c49513995 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -7,6 +7,7 @@\n #include \"strbuf.h\"\n #include \"run-command.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"argv-array.h\"\n \n static const char * const builtin_remote_usage[] = {\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex c2403a915..6ab032acb 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -12,6 +12,7 @@\n #include \"run-command.h\"\n #include \"remote.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"connect.h\"\n #include \"revision.h\"\n #include \"diffcore.h\"\ndiff --git a/checkout.c b/checkout.c\nindex ac42630f7..193ba8567 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -1,5 +1,6 @@\n #include \"cache.h\"\n #include \"remote.h\"\n+#include \"refspec.h\"\n #include \"checkout.h\"\n \n struct tracking_name_data {\ndiff --git a/refspec.c b/refspec.c\nnew file mode 100644\nindex 000000000..ecb0bdff3\n--- /dev/null\n+++ b/refspec.c\n@@ -0,0 +1,168 @@\n+#include \"cache.h\"\n+#include \"refs.h\"\n+#include \"refspec.h\"\n+\n+static struct refspec s_tag_refspec = {\n+\t0,\n+\t1,\n+\t0,\n+\t0,\n+\t\"refs/tags/*\",\n+\t\"refs/tags/*\"\n+};\n+\n+/* See TAG_REFSPEC for the string version */\n+const struct refspec *tag_refspec = &s_tag_refspec;\n+\n+static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n+{\n+\tint i;\n+\tstruct refspec *rs = xcalloc(nr_refspec, sizeof(*rs));\n+\n+\tfor (i = 0; i < nr_refspec; i++) {\n+\t\tsize_t llen;\n+\t\tint is_glob;\n+\t\tconst char *lhs, *rhs;\n+\t\tint flags;\n+\n+\t\tis_glob = 0;\n+\n+\t\tlhs = refspec[i];\n+\t\tif (*lhs == '+') {\n+\t\t\trs[i].force = 1;\n+\t\t\tlhs++;\n+\t\t}\n+\n+\t\trhs = strrchr(lhs, ':');\n+\n+\t\t/*\n+\t\t * Before going on, special case \":\" (or \"+:\") as a refspec\n+\t\t * for pushing matching refs.\n+\t\t */\n+\t\tif (!fetch && rhs == lhs && rhs[1] == '\\0') {\n+\t\t\trs[i].matching = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (rhs) {\n+\t\t\tsize_t rlen = strlen(++rhs);\n+\t\t\tis_glob = (1 <= rlen && strchr(rhs, '*'));\n+\t\t\trs[i].dst = xstrndup(rhs, rlen);\n+\t\t}\n+\n+\t\tllen = (rhs ? (rhs - lhs - 1) : strlen(lhs));\n+\t\tif (1 <= llen && memchr(lhs, '*', llen)) {\n+\t\t\tif ((rhs && !is_glob) || (!rhs && fetch))\n+\t\t\t\tgoto invalid;\n+\t\t\tis_glob = 1;\n+\t\t} else if (rhs && is_glob) {\n+\t\t\tgoto invalid;\n+\t\t}\n+\n+\t\trs[i].pattern = is_glob;\n+\t\trs[i].src = xstrndup(lhs, llen);\n+\t\tflags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);\n+\n+\t\tif (fetch) {\n+\t\t\tstruct object_id unused;\n+\n+\t\t\t/* LHS */\n+\t\t\tif (!*rs[i].src)\n+\t\t\t\t; /* empty is ok; it means \"HEAD\" */\n+\t\t\telse if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused))\n+\t\t\t\trs[i].exact_sha1 = 1; /* ok */\n+\t\t\telse if (!check_refname_format(rs[i].src, flags))\n+\t\t\t\t; /* valid looking ref is ok */\n+\t\t\telse\n+\t\t\t\tgoto invalid;\n+\t\t\t/* RHS */\n+\t\t\tif (!rs[i].dst)\n+\t\t\t\t; /* missing is ok; it is the same as empty */\n+\t\t\telse if (!*rs[i].dst)\n+\t\t\t\t; /* empty is ok; it means \"do not store\" */\n+\t\t\telse if (!check_refname_format(rs[i].dst, flags))\n+\t\t\t\t; /* valid looking ref is ok */\n+\t\t\telse\n+\t\t\t\tgoto invalid;\n+\t\t} else {\n+\t\t\t/*\n+\t\t\t * LHS\n+\t\t\t * - empty is allowed; it means delete.\n+\t\t\t * - when wildcarded, it must be a valid looking ref.\n+\t\t\t * - otherwise, it must be an extended SHA-1, but\n+\t\t\t *   there is no existing way to validate this.\n+\t\t\t */\n+\t\t\tif (!*rs[i].src)\n+\t\t\t\t; /* empty is ok */\n+\t\t\telse if (is_glob) {\n+\t\t\t\tif (check_refname_format(rs[i].src, flags))\n+\t\t\t\t\tgoto invalid;\n+\t\t\t}\n+\t\t\telse\n+\t\t\t\t; /* anything goes, for now */\n+\t\t\t/*\n+\t\t\t * RHS\n+\t\t\t * - missing is allowed, but LHS then must be a\n+\t\t\t *   valid looking ref.\n+\t\t\t * - empty is not allowed.\n+\t\t\t * - otherwise it must be a valid looking ref.\n+\t\t\t */\n+\t\t\tif (!rs[i].dst) {\n+\t\t\t\tif (check_refname_format(rs[i].src, flags))\n+\t\t\t\t\tgoto invalid;\n+\t\t\t} else if (!*rs[i].dst) {\n+\t\t\t\tgoto invalid;\n+\t\t\t} else {\n+\t\t\t\tif (check_refname_format(rs[i].dst, flags))\n+\t\t\t\t\tgoto invalid;\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn rs;\n+\n+ invalid:\n+\tif (verify) {\n+\t\t/*\n+\t\t * nr_refspec must be greater than zero and i must be valid\n+\t\t * since it is only possible to reach this point from within\n+\t\t * the for loop above.\n+\t\t */\n+\t\tfree_refspec(i+1, rs);\n+\t\treturn NULL;\n+\t}\n+\tdie(\"Invalid refspec '%s'\", refspec[i]);\n+}\n+\n+int valid_fetch_refspec(const char *fetch_refspec_str)\n+{\n+\tstruct refspec *refspec;\n+\n+\trefspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);\n+\tfree_refspec(1, refspec);\n+\treturn !!refspec;\n+}\n+\n+struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)\n+{\n+\treturn parse_refspec_internal(nr_refspec, refspec, 1, 0);\n+}\n+\n+struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)\n+{\n+\treturn parse_refspec_internal(nr_refspec, refspec, 0, 0);\n+}\n+\n+void free_refspec(int nr_refspec, struct refspec *refspec)\n+{\n+\tint i;\n+\n+\tif (!refspec)\n+\t\treturn;\n+\n+\tfor (i = 0; i < nr_refspec; i++) {\n+\t\tfree(refspec[i].src);\n+\t\tfree(refspec[i].dst);\n+\t}\n+\tfree(refspec);\n+}\n+\ndiff --git a/refspec.h b/refspec.h\nnew file mode 100644\nindex 000000000..62625c23a\n--- /dev/null\n+++ b/refspec.h\n@@ -0,0 +1,23 @@\n+#ifndef REFSPEC_H\n+#define REFSPEC_H\n+\n+#define TAG_REFSPEC \"refs/tags/*:refs/tags/*\"\n+extern const struct refspec *tag_refspec;\n+\n+struct refspec {\n+\tunsigned force : 1;\n+\tunsigned pattern : 1;\n+\tunsigned matching : 1;\n+\tunsigned exact_sha1 : 1;\n+\n+\tchar *src;\n+\tchar *dst;\n+};\n+\n+int valid_fetch_refspec(const char *refspec);\n+struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);\n+struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);\n+\n+void free_refspec(int nr_refspec, struct refspec *refspec);\n+\n+#endif /* REFSPEC_H */\ndiff --git a/remote.c b/remote.c\nindex 91eb010ca..4d67c061a 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -2,6 +2,7 @@\n #include \"config.h\"\n #include \"remote.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"commit.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n@@ -13,18 +14,6 @@\n \n enum map_direction { FROM_SRC, FROM_DST };\n \n-static struct refspec s_tag_refspec = {\n-\t0,\n-\t1,\n-\t0,\n-\t0,\n-\t\"refs/tags/*\",\n-\t\"refs/tags/*\"\n-};\n-\n-/* See TAG_REFSPEC for the string version */\n-const struct refspec *tag_refspec = &s_tag_refspec;\n-\n struct counted_string {\n \tsize_t len;\n \tconst char *s;\n@@ -499,158 +488,6 @@ static void read_config(void)\n \talias_all_urls();\n }\n \n-static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n-{\n-\tint i;\n-\tstruct refspec *rs = xcalloc(nr_refspec, sizeof(*rs));\n-\n-\tfor (i = 0; i < nr_refspec; i++) {\n-\t\tsize_t llen;\n-\t\tint is_glob;\n-\t\tconst char *lhs, *rhs;\n-\t\tint flags;\n-\n-\t\tis_glob = 0;\n-\n-\t\tlhs = refspec[i];\n-\t\tif (*lhs == '+') {\n-\t\t\trs[i].force = 1;\n-\t\t\tlhs++;\n-\t\t}\n-\n-\t\trhs = strrchr(lhs, ':');\n-\n-\t\t/*\n-\t\t * Before going on, special case \":\" (or \"+:\") as a refspec\n-\t\t * for pushing matching refs.\n-\t\t */\n-\t\tif (!fetch && rhs == lhs && rhs[1] == '\\0') {\n-\t\t\trs[i].matching = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\tif (rhs) {\n-\t\t\tsize_t rlen = strlen(++rhs);\n-\t\t\tis_glob = (1 <= rlen && strchr(rhs, '*'));\n-\t\t\trs[i].dst = xstrndup(rhs, rlen);\n-\t\t}\n-\n-\t\tllen = (rhs ? (rhs - lhs - 1) : strlen(lhs));\n-\t\tif (1 <= llen && memchr(lhs, '*', llen)) {\n-\t\t\tif ((rhs && !is_glob) || (!rhs && fetch))\n-\t\t\t\tgoto invalid;\n-\t\t\tis_glob = 1;\n-\t\t} else if (rhs && is_glob) {\n-\t\t\tgoto invalid;\n-\t\t}\n-\n-\t\trs[i].pattern = is_glob;\n-\t\trs[i].src = xstrndup(lhs, llen);\n-\t\tflags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);\n-\n-\t\tif (fetch) {\n-\t\t\tstruct object_id unused;\n-\n-\t\t\t/* LHS */\n-\t\t\tif (!*rs[i].src)\n-\t\t\t\t; /* empty is ok; it means \"HEAD\" */\n-\t\t\telse if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused))\n-\t\t\t\trs[i].exact_sha1 = 1; /* ok */\n-\t\t\telse if (!check_refname_format(rs[i].src, flags))\n-\t\t\t\t; /* valid looking ref is ok */\n-\t\t\telse\n-\t\t\t\tgoto invalid;\n-\t\t\t/* RHS */\n-\t\t\tif (!rs[i].dst)\n-\t\t\t\t; /* missing is ok; it is the same as empty */\n-\t\t\telse if (!*rs[i].dst)\n-\t\t\t\t; /* empty is ok; it means \"do not store\" */\n-\t\t\telse if (!check_refname_format(rs[i].dst, flags))\n-\t\t\t\t; /* valid looking ref is ok */\n-\t\t\telse\n-\t\t\t\tgoto invalid;\n-\t\t} else {\n-\t\t\t/*\n-\t\t\t * LHS\n-\t\t\t * - empty is allowed; it means delete.\n-\t\t\t * - when wildcarded, it must be a valid looking ref.\n-\t\t\t * - otherwise, it must be an extended SHA-1, but\n-\t\t\t *   there is no existing way to validate this.\n-\t\t\t */\n-\t\t\tif (!*rs[i].src)\n-\t\t\t\t; /* empty is ok */\n-\t\t\telse if (is_glob) {\n-\t\t\t\tif (check_refname_format(rs[i].src, flags))\n-\t\t\t\t\tgoto invalid;\n-\t\t\t}\n-\t\t\telse\n-\t\t\t\t; /* anything goes, for now */\n-\t\t\t/*\n-\t\t\t * RHS\n-\t\t\t * - missing is allowed, but LHS then must be a\n-\t\t\t *   valid looking ref.\n-\t\t\t * - empty is not allowed.\n-\t\t\t * - otherwise it must be a valid looking ref.\n-\t\t\t */\n-\t\t\tif (!rs[i].dst) {\n-\t\t\t\tif (check_refname_format(rs[i].src, flags))\n-\t\t\t\t\tgoto invalid;\n-\t\t\t} else if (!*rs[i].dst) {\n-\t\t\t\tgoto invalid;\n-\t\t\t} else {\n-\t\t\t\tif (check_refname_format(rs[i].dst, flags))\n-\t\t\t\t\tgoto invalid;\n-\t\t\t}\n-\t\t}\n-\t}\n-\treturn rs;\n-\n- invalid:\n-\tif (verify) {\n-\t\t/*\n-\t\t * nr_refspec must be greater than zero and i must be valid\n-\t\t * since it is only possible to reach this point from within\n-\t\t * the for loop above.\n-\t\t */\n-\t\tfree_refspec(i+1, rs);\n-\t\treturn NULL;\n-\t}\n-\tdie(\"Invalid refspec '%s'\", refspec[i]);\n-}\n-\n-int valid_fetch_refspec(const char *fetch_refspec_str)\n-{\n-\tstruct refspec *refspec;\n-\n-\trefspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);\n-\tfree_refspec(1, refspec);\n-\treturn !!refspec;\n-}\n-\n-struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)\n-{\n-\treturn parse_refspec_internal(nr_refspec, refspec, 1, 0);\n-}\n-\n-struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)\n-{\n-\treturn parse_refspec_internal(nr_refspec, refspec, 0, 0);\n-}\n-\n-void free_refspec(int nr_refspec, struct refspec *refspec)\n-{\n-\tint i;\n-\n-\tif (!refspec)\n-\t\treturn;\n-\n-\tfor (i = 0; i < nr_refspec; i++) {\n-\t\tfree(refspec[i].src);\n-\t\tfree(refspec[i].dst);\n-\t}\n-\tfree(refspec);\n-}\n-\n static int valid_remote_nick(const char *name)\n {\n \tif (!name[0] || is_dot_or_dotdot(name))\ndiff --git a/remote.h b/remote.h\nindex 2b3180f94..386ced901 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -68,18 +68,6 @@ int for_each_remote(each_remote_fn fn, void *priv);\n \n int remote_has_url(struct remote *remote, const char *url);\n \n-struct refspec {\n-\tunsigned force : 1;\n-\tunsigned pattern : 1;\n-\tunsigned matching : 1;\n-\tunsigned exact_sha1 : 1;\n-\n-\tchar *src;\n-\tchar *dst;\n-};\n-\n-extern const struct refspec *tag_refspec;\n-\n struct ref {\n \tstruct ref *next;\n \tstruct object_id old_oid;\n@@ -175,12 +163,6 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);\n  */\n struct ref *ref_remove_duplicates(struct ref *ref_map);\n \n-int valid_fetch_refspec(const char *refspec);\n-struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);\n-extern struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);\n-\n-void free_refspec(int nr_refspec, struct refspec *refspec);\n-\n extern int query_refspecs(struct refspec *specs, int nr, struct refspec *query);\n char *apply_refspecs(struct refspec *refspecs, int nr_refspec,\n \t\t     const char *name);\n@@ -313,8 +295,6 @@ extern int parseopt_push_cas_option(const struct option *, const char *arg, int\n extern int is_empty_cas(const struct push_cas_option *);\n void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);\n \n-#define TAG_REFSPEC \"refs/tags/*:refs/tags/*\"\n-\n void add_prune_tags_to_fetch_refspec(struct remote *remote);\n \n #endif\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 11f1055b4..b99e1cce9 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -11,6 +11,7 @@\n #include \"sigchain.h\"\n #include \"argv-array.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"transport-internal.h\"\n #include \"protocol.h\"\n \ndiff --git a/transport.c b/transport.c\nindex 37410d8aa..2cf63d18b 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -11,6 +11,7 @@\n #include \"bundle.h\"\n #include \"dir.h\"\n #include \"refs.h\"\n+#include \"refspec.h\"\n #include \"branch.h\"\n #include \"url.h\"\n #include \"submodule.h\"\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347850","messageId":"20180516225823.235426-3-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 02/36] refspec: rename struct refspec to struct refspec_item","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:57:49Z","receivedAt":"2018-05-16T22:58:47Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"In preparation for introducing an abstraction around a collection of\nrefspecs (much like how a 'struct pathspec' is a collection of 'struct\npathspec_item's) rename the existing 'struct refspec' to 'struct\nrefspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n branch.c                    |  6 ++---\n builtin/clone.c             |  4 +--\n builtin/fast-export.c       |  4 +--\n builtin/fetch.c             | 12 ++++-----\n builtin/pull.c              |  2 +-\n builtin/push.c              |  4 +--\n builtin/remote.c            |  8 +++---\n builtin/submodule--helper.c |  4 +--\n checkout.c                  |  4 +--\n refspec.c                   | 17 ++++++-------\n refspec.h                   | 10 ++++----\n remote.c                    | 50 ++++++++++++++++++-------------------\n remote.h                    | 16 ++++++------\n transport-helper.c          |  2 +-\n transport.c                 |  4 +--\n 15 files changed, 73 insertions(+), 74 deletions(-)\n\ndiff --git a/branch.c b/branch.c\nindex 32ccefc6b..f967c98f6 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -9,7 +9,7 @@\n #include \"worktree.h\"\n \n struct tracking {\n-\tstruct refspec spec;\n+\tstruct refspec_item spec;\n \tchar *src;\n \tconst char *remote;\n \tint matches;\n@@ -219,8 +219,8 @@ int validate_new_branchname(const char *name, struct strbuf *ref, int force)\n static int check_tracking_branch(struct remote *remote, void *cb_data)\n {\n \tchar *tracking_branch = cb_data;\n-\tstruct refspec query;\n-\tmemset(&query, 0, sizeof(struct refspec));\n+\tstruct refspec_item query;\n+\tmemset(&query, 0, sizeof(struct refspec_item));\n \tquery.dst = tracking_branch;\n \treturn !remote_find_tracking(remote, &query);\n }\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 6d1614ed3..854088a3a 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -547,7 +547,7 @@ static struct ref *find_remote_branch(const struct ref *refs, const char *branch\n }\n \n static struct ref *wanted_peer_refs(const struct ref *refs,\n-\t\tstruct refspec *refspec)\n+\t\tstruct refspec_item *refspec)\n {\n \tstruct ref *head = copy_ref(find_ref_by_name(refs, \"HEAD\"));\n \tstruct ref *local_refs = head;\n@@ -895,7 +895,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tint err = 0, complete_refs_before_fetch = 1;\n \tint submodule_progress;\n \n-\tstruct refspec *refspec;\n+\tstruct refspec_item *refspec;\n \tconst char *fetch_pattern;\n \n \tfetch_if_missing = 0;\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex a13b7c8ef..6f105dc79 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -36,7 +36,7 @@ static int use_done_feature;\n static int no_data;\n static int full_tree;\n static struct string_list extra_refs = STRING_LIST_INIT_NODUP;\n-static struct refspec *refspecs;\n+static struct refspec_item *refspecs;\n static int refspecs_nr;\n static int anonymize;\n \n@@ -979,7 +979,7 @@ static void handle_deletes(void)\n {\n \tint i;\n \tfor (i = 0; i < refspecs_nr; i++) {\n-\t\tstruct refspec *refspec = &refspecs[i];\n+\t\tstruct refspec_item *refspec = &refspecs[i];\n \t\tif (*refspec->src)\n \t\t\tcontinue;\n \ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 1fce68e9a..745020a10 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -203,7 +203,7 @@ static void add_merge_config(struct ref **head,\n \n \tfor (i = 0; i < branch->merge_nr; i++) {\n \t\tstruct ref *rm, **old_tail = *tail;\n-\t\tstruct refspec refspec;\n+\t\tstruct refspec_item refspec;\n \n \t\tfor (rm = *head; rm; rm = rm->next) {\n \t\t\tif (branch_merge_matches(branch, i, rm->name)) {\n@@ -340,7 +340,7 @@ static void find_non_local_tags(struct transport *transport,\n }\n \n static struct ref *get_ref_map(struct transport *transport,\n-\t\t\t       struct refspec *refspecs, int refspec_count,\n+\t\t\t       struct refspec_item *refspecs, int refspec_count,\n \t\t\t       int tags, int *autotags)\n {\n \tint i;\n@@ -371,7 +371,7 @@ static struct ref *get_ref_map(struct transport *transport,\n \targv_array_clear(&ref_prefixes);\n \n \tif (refspec_count) {\n-\t\tstruct refspec *fetch_refspec;\n+\t\tstruct refspec_item *fetch_refspec;\n \t\tint fetch_refspec_nr;\n \n \t\tfor (i = 0; i < refspec_count; i++) {\n@@ -965,7 +965,7 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)\n \treturn ret;\n }\n \n-static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,\n+static int prune_refs(struct refspec_item *refs, int ref_count, struct ref *ref_map,\n \t\tconst char *raw_url)\n {\n \tint url_len, i, result = 0;\n@@ -1115,7 +1115,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)\n }\n \n static int do_fetch(struct transport *transport,\n-\t\t    struct refspec *refs, int ref_count)\n+\t\t    struct refspec_item *refs, int ref_count)\n {\n \tstruct string_list existing_refs = STRING_LIST_INIT_DUP;\n \tstruct ref *ref_map;\n@@ -1357,7 +1357,7 @@ static inline void fetch_one_setup_partial(struct remote *remote)\n static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok)\n {\n \tstatic const char **refs = NULL;\n-\tstruct refspec *refspec;\n+\tstruct refspec_item *refspec;\n \tint ref_nr = 0;\n \tint j = 0;\n \tint exit_code;\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 6247c956d..5a79deae5 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -676,7 +676,7 @@ static const char *get_upstream_branch(const char *remote)\n  */\n static const char *get_tracking_branch(const char *remote, const char *refspec)\n {\n-\tstruct refspec *spec;\n+\tstruct refspec_item *spec;\n \tconst char *spec_src;\n \tconst char *merge_branch;\n \ndiff --git a/builtin/push.c b/builtin/push.c\nindex fa65999b2..00d81fb1d 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -80,8 +80,8 @@ static const char *map_refspec(const char *ref,\n \t\treturn ref;\n \n \tif (remote->push) {\n-\t\tstruct refspec query;\n-\t\tmemset(&query, 0, sizeof(struct refspec));\n+\t\tstruct refspec_item query;\n+\t\tmemset(&query, 0, sizeof(struct refspec_item));\n \t\tquery.src = matched->name;\n \t\tif (!query_refspecs(remote->push, remote->push_refspec_nr, &query) &&\n \t\t    query.dst) {\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex c49513995..d9da82dc8 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -442,7 +442,7 @@ static int get_push_ref_states_noquery(struct ref_states *states)\n \t\tinfo->dest = xstrdup(item->string);\n \t}\n \tfor (i = 0; i < remote->push_refspec_nr; i++) {\n-\t\tstruct refspec *spec = remote->push + i;\n+\t\tstruct refspec_item *spec = remote->push + i;\n \t\tif (spec->matching)\n \t\t\titem = string_list_append(&states->push, _(\"(matching)\"));\n \t\telse if (strlen(spec->src))\n@@ -462,7 +462,7 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat\n {\n \tstruct ref *ref, *matches;\n \tstruct ref *fetch_map = NULL, **fetch_map_tail = &fetch_map;\n-\tstruct refspec refspec;\n+\tstruct refspec_item refspec;\n \n \trefspec.force = 0;\n \trefspec.pattern = 1;\n@@ -515,7 +515,7 @@ static int add_branch_for_removal(const char *refname,\n \tconst struct object_id *oid, int flags, void *cb_data)\n {\n \tstruct branches_for_remote *branches = cb_data;\n-\tstruct refspec refspec;\n+\tstruct refspec_item refspec;\n \tstruct known_remote *kr;\n \n \tmemset(&refspec, 0, sizeof(refspec));\n@@ -834,7 +834,7 @@ static int append_ref_to_tracked_list(const char *refname,\n \tconst struct object_id *oid, int flags, void *cb_data)\n {\n \tstruct ref_states *states = cb_data;\n-\tstruct refspec refspec;\n+\tstruct refspec_item refspec;\n \n \tif (flags & REF_ISSYMREF)\n \t\treturn 0;\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 6ab032acb..c0c4db007 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1746,11 +1746,11 @@ static int push_check(int argc, const char **argv, const char *prefix)\n \tif (argc > 2) {\n \t\tint i, refspec_nr = argc - 2;\n \t\tstruct ref *local_refs = get_local_heads();\n-\t\tstruct refspec *refspec = parse_push_refspec(refspec_nr,\n+\t\tstruct refspec_item *refspec = parse_push_refspec(refspec_nr,\n \t\t\t\t\t\t\t     argv + 2);\n \n \t\tfor (i = 0; i < refspec_nr; i++) {\n-\t\t\tstruct refspec *rs = refspec + i;\n+\t\t\tstruct refspec_item *rs = refspec + i;\n \n \t\t\tif (rs->pattern || rs->matching)\n \t\t\t\tcontinue;\ndiff --git a/checkout.c b/checkout.c\nindex 193ba8567..bdefc888b 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -13,8 +13,8 @@ struct tracking_name_data {\n static int check_tracking_name(struct remote *remote, void *cb_data)\n {\n \tstruct tracking_name_data *cb = cb_data;\n-\tstruct refspec query;\n-\tmemset(&query, 0, sizeof(struct refspec));\n+\tstruct refspec_item query;\n+\tmemset(&query, 0, sizeof(struct refspec_item));\n \tquery.src = cb->src_ref;\n \tif (remote_find_tracking(remote, &query) ||\n \t    get_oid(query.dst, cb->dst_oid)) {\ndiff --git a/refspec.c b/refspec.c\nindex ecb0bdff3..22188f010 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -2,7 +2,7 @@\n #include \"refs.h\"\n #include \"refspec.h\"\n \n-static struct refspec s_tag_refspec = {\n+static struct refspec_item s_tag_refspec = {\n \t0,\n \t1,\n \t0,\n@@ -12,12 +12,12 @@ static struct refspec s_tag_refspec = {\n };\n \n /* See TAG_REFSPEC for the string version */\n-const struct refspec *tag_refspec = &s_tag_refspec;\n+const struct refspec_item *tag_refspec = &s_tag_refspec;\n \n-static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n+static struct refspec_item *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n {\n \tint i;\n-\tstruct refspec *rs = xcalloc(nr_refspec, sizeof(*rs));\n+\tstruct refspec_item *rs = xcalloc(nr_refspec, sizeof(*rs));\n \n \tfor (i = 0; i < nr_refspec; i++) {\n \t\tsize_t llen;\n@@ -135,24 +135,24 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp\n \n int valid_fetch_refspec(const char *fetch_refspec_str)\n {\n-\tstruct refspec *refspec;\n+\tstruct refspec_item *refspec;\n \n \trefspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);\n \tfree_refspec(1, refspec);\n \treturn !!refspec;\n }\n \n-struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)\n+struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec)\n {\n \treturn parse_refspec_internal(nr_refspec, refspec, 1, 0);\n }\n \n-struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)\n+struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec)\n {\n \treturn parse_refspec_internal(nr_refspec, refspec, 0, 0);\n }\n \n-void free_refspec(int nr_refspec, struct refspec *refspec)\n+void free_refspec(int nr_refspec, struct refspec_item *refspec)\n {\n \tint i;\n \n@@ -165,4 +165,3 @@ void free_refspec(int nr_refspec, struct refspec *refspec)\n \t}\n \tfree(refspec);\n }\n-\ndiff --git a/refspec.h b/refspec.h\nindex 62625c23a..fc9c1af77 100644\n--- a/refspec.h\n+++ b/refspec.h\n@@ -2,9 +2,9 @@\n #define REFSPEC_H\n \n #define TAG_REFSPEC \"refs/tags/*:refs/tags/*\"\n-extern const struct refspec *tag_refspec;\n+extern const struct refspec_item *tag_refspec;\n \n-struct refspec {\n+struct refspec_item {\n \tunsigned force : 1;\n \tunsigned pattern : 1;\n \tunsigned matching : 1;\n@@ -15,9 +15,9 @@ struct refspec {\n };\n \n int valid_fetch_refspec(const char *refspec);\n-struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);\n-struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);\n+struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec);\n+struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec);\n \n-void free_refspec(int nr_refspec, struct refspec *refspec);\n+void free_refspec(int nr_refspec, struct refspec_item *refspec);\n \n #endif /* REFSPEC_H */\ndiff --git a/remote.c b/remote.c\nindex 4d67c061a..89820c476 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -97,7 +97,7 @@ void add_prune_tags_to_fetch_refspec(struct remote *remote)\n {\n \tint nr = remote->fetch_refspec_nr;\n \tint bufsize = nr  + 1;\n-\tint size = sizeof(struct refspec);\n+\tint size = sizeof(struct refspec_item);\n \n \tremote->fetch = xrealloc(remote->fetch, size  * bufsize);\n \tmemcpy(&remote->fetch[nr], tag_refspec, size);\n@@ -724,7 +724,7 @@ static int match_name_with_pattern(const char *key, const char *name,\n \treturn ret;\n }\n \n-static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct refspec *query, struct string_list *results)\n+static void query_refspecs_multiple(struct refspec_item *refs, int ref_count, struct refspec_item *query, struct string_list *results)\n {\n \tint i;\n \tint find_src = !query->src;\n@@ -733,7 +733,7 @@ static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct\n \t\terror(\"query_refspecs_multiple: need either src or dst\");\n \n \tfor (i = 0; i < ref_count; i++) {\n-\t\tstruct refspec *refspec = &refs[i];\n+\t\tstruct refspec_item *refspec = &refs[i];\n \t\tconst char *key = find_src ? refspec->dst : refspec->src;\n \t\tconst char *value = find_src ? refspec->src : refspec->dst;\n \t\tconst char *needle = find_src ? query->dst : query->src;\n@@ -750,7 +750,7 @@ static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct\n \t}\n }\n \n-int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)\n+int query_refspecs(struct refspec_item *refs, int ref_count, struct refspec_item *query)\n {\n \tint i;\n \tint find_src = !query->src;\n@@ -761,7 +761,7 @@ int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)\n \t\treturn error(\"query_refspecs: need either src or dst\");\n \n \tfor (i = 0; i < ref_count; i++) {\n-\t\tstruct refspec *refspec = &refs[i];\n+\t\tstruct refspec_item *refspec = &refs[i];\n \t\tconst char *key = find_src ? refspec->dst : refspec->src;\n \t\tconst char *value = find_src ? refspec->src : refspec->dst;\n \n@@ -781,12 +781,12 @@ int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)\n \treturn -1;\n }\n \n-char *apply_refspecs(struct refspec *refspecs, int nr_refspec,\n+char *apply_refspecs(struct refspec_item *refspecs, int nr_refspec,\n \t\t     const char *name)\n {\n-\tstruct refspec query;\n+\tstruct refspec_item query;\n \n-\tmemset(&query, 0, sizeof(struct refspec));\n+\tmemset(&query, 0, sizeof(struct refspec_item));\n \tquery.src = (char *)name;\n \n \tif (query_refspecs(refspecs, nr_refspec, &query))\n@@ -795,7 +795,7 @@ char *apply_refspecs(struct refspec *refspecs, int nr_refspec,\n \treturn query.dst;\n }\n \n-int remote_find_tracking(struct remote *remote, struct refspec *refspec)\n+int remote_find_tracking(struct remote *remote, struct refspec_item *refspec)\n {\n \treturn query_refspecs(remote->fetch, remote->fetch_refspec_nr, refspec);\n }\n@@ -1004,7 +1004,7 @@ static char *guess_ref(const char *name, struct ref *peer)\n }\n \n static int match_explicit_lhs(struct ref *src,\n-\t\t\t      struct refspec *rs,\n+\t\t\t      struct refspec_item *rs,\n \t\t\t      struct ref **match,\n \t\t\t      int *allocated_match)\n {\n@@ -1030,7 +1030,7 @@ static int match_explicit_lhs(struct ref *src,\n \n static int match_explicit(struct ref *src, struct ref *dst,\n \t\t\t  struct ref ***dst_tail,\n-\t\t\t  struct refspec *rs)\n+\t\t\t  struct refspec_item *rs)\n {\n \tstruct ref *matched_src, *matched_dst;\n \tint allocated_src;\n@@ -1099,7 +1099,7 @@ static int match_explicit(struct ref *src, struct ref *dst,\n }\n \n static int match_explicit_refs(struct ref *src, struct ref *dst,\n-\t\t\t       struct ref ***dst_tail, struct refspec *rs,\n+\t\t\t       struct ref ***dst_tail, struct refspec_item *rs,\n \t\t\t       int rs_nr)\n {\n \tint i, errs;\n@@ -1108,10 +1108,10 @@ static int match_explicit_refs(struct ref *src, struct ref *dst,\n \treturn errs;\n }\n \n-static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref *ref,\n-\t\tint send_mirror, int direction, const struct refspec **ret_pat)\n+static char *get_ref_match(const struct refspec_item *rs, int rs_nr, const struct ref *ref,\n+\t\tint send_mirror, int direction, const struct refspec_item **ret_pat)\n {\n-\tconst struct refspec *pat;\n+\tconst struct refspec_item *pat;\n \tchar *name;\n \tint i;\n \tint matching_refs = -1;\n@@ -1282,12 +1282,12 @@ static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)\n  */\n int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)\n {\n-\tstruct refspec *refspec = parse_push_refspec(nr_refspec, refspec_names);\n+\tstruct refspec_item *refspec = parse_push_refspec(nr_refspec, refspec_names);\n \tint ret = 0;\n \tint i;\n \n \tfor (i = 0; i < nr_refspec; i++) {\n-\t\tstruct refspec *rs = refspec + i;\n+\t\tstruct refspec_item *rs = refspec + i;\n \n \t\tif (rs->pattern || rs->matching)\n \t\t\tcontinue;\n@@ -1310,7 +1310,7 @@ int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)\n int match_push_refs(struct ref *src, struct ref **dst,\n \t\t    int nr_refspec, const char **refspec, int flags)\n {\n-\tstruct refspec *rs;\n+\tstruct refspec_item *rs;\n \tint send_all = flags & MATCH_REFS_ALL;\n \tint send_mirror = flags & MATCH_REFS_MIRROR;\n \tint send_prune = flags & MATCH_REFS_PRUNE;\n@@ -1330,7 +1330,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \tfor (ref = src; ref; ref = ref->next) {\n \t\tstruct string_list_item *dst_item;\n \t\tstruct ref *dst_peer;\n-\t\tconst struct refspec *pat = NULL;\n+\t\tconst struct refspec_item *pat = NULL;\n \t\tchar *dst_name;\n \n \t\tdst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat);\n@@ -1686,7 +1686,7 @@ static int ignore_symref_update(const char *refname)\n  * local symbolic ref.\n  */\n static struct ref *get_expanded_map(const struct ref *remote_refs,\n-\t\t\t\t    const struct refspec *refspec)\n+\t\t\t\t    const struct refspec_item *refspec)\n {\n \tconst struct ref *ref;\n \tstruct ref *ret = NULL;\n@@ -1751,7 +1751,7 @@ static struct ref *get_local_ref(const char *name)\n }\n \n int get_fetch_map(const struct ref *remote_refs,\n-\t\t  const struct refspec *refspec,\n+\t\t  const struct refspec_item *refspec,\n \t\t  struct ref ***tail,\n \t\t  int missing_ok)\n {\n@@ -2089,7 +2089,7 @@ struct ref *guess_remote_head(const struct ref *head,\n struct stale_heads_info {\n \tstruct string_list *ref_names;\n \tstruct ref **stale_refs_tail;\n-\tstruct refspec *refs;\n+\tstruct refspec_item *refs;\n \tint ref_count;\n };\n \n@@ -2098,9 +2098,9 @@ static int get_stale_heads_cb(const char *refname, const struct object_id *oid,\n {\n \tstruct stale_heads_info *info = cb_data;\n \tstruct string_list matches = STRING_LIST_INIT_DUP;\n-\tstruct refspec query;\n+\tstruct refspec_item query;\n \tint i, stale = 1;\n-\tmemset(&query, 0, sizeof(struct refspec));\n+\tmemset(&query, 0, sizeof(struct refspec_item));\n \tquery.dst = (char *)refname;\n \n \tquery_refspecs_multiple(info->refs, info->ref_count, &query, &matches);\n@@ -2131,7 +2131,7 @@ static int get_stale_heads_cb(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n-struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map)\n+struct ref *get_stale_heads(struct refspec_item *refs, int ref_count, struct ref *fetch_map)\n {\n \tstruct ref *ref, *stale_refs = NULL;\n \tstruct string_list ref_names = STRING_LIST_INIT_NODUP;\ndiff --git a/remote.h b/remote.h\nindex 386ced901..3657bd43d 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -28,12 +28,12 @@ struct remote {\n \tint pushurl_alloc;\n \n \tconst char **push_refspec;\n-\tstruct refspec *push;\n+\tstruct refspec_item *push;\n \tint push_refspec_nr;\n \tint push_refspec_alloc;\n \n \tconst char **fetch_refspec;\n-\tstruct refspec *fetch;\n+\tstruct refspec_item *fetch;\n \tint fetch_refspec_nr;\n \tint fetch_refspec_alloc;\n \n@@ -163,8 +163,8 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);\n  */\n struct ref *ref_remove_duplicates(struct ref *ref_map);\n \n-extern int query_refspecs(struct refspec *specs, int nr, struct refspec *query);\n-char *apply_refspecs(struct refspec *refspecs, int nr_refspec,\n+extern int query_refspecs(struct refspec_item *specs, int nr, struct refspec_item *query);\n+char *apply_refspecs(struct refspec_item *refspecs, int nr_refspec,\n \t\t     const char *name);\n \n int check_push_refs(struct ref *src, int nr_refspec, const char **refspec);\n@@ -185,7 +185,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\n  * missing_ok is usually false, but when we are adding branch.$name.merge\n  * it is Ok if the branch is not at the remote anymore.\n  */\n-int get_fetch_map(const struct ref *remote_refs, const struct refspec *refspec,\n+int get_fetch_map(const struct ref *remote_refs, const struct refspec_item *refspec,\n \t\t  struct ref ***tail, int missing_ok);\n \n struct ref *get_remote_ref(const struct ref *remote_refs, const char *name);\n@@ -193,7 +193,7 @@ struct ref *get_remote_ref(const struct ref *remote_refs, const char *name);\n /*\n  * For the given remote, reads the refspec's src and sets the other fields.\n  */\n-int remote_find_tracking(struct remote *remote, struct refspec *refspec);\n+int remote_find_tracking(struct remote *remote, struct refspec_item *refspec);\n \n struct branch {\n \tconst char *name;\n@@ -203,7 +203,7 @@ struct branch {\n \tconst char *pushremote_name;\n \n \tconst char **merge_name;\n-\tstruct refspec **merge;\n+\tstruct refspec_item **merge;\n \tint merge_nr;\n \tint merge_alloc;\n \n@@ -272,7 +272,7 @@ struct ref *guess_remote_head(const struct ref *head,\n \t\t\t      int all);\n \n /* Return refs which no longer exist on remote */\n-struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map);\n+struct ref *get_stale_heads(struct refspec_item *refs, int ref_count, struct ref *fetch_map);\n \n /*\n  * Compare-and-swap\ndiff --git a/transport-helper.c b/transport-helper.c\nindex b99e1cce9..b156a37e7 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -36,7 +36,7 @@ struct helper_data {\n \tchar *export_marks;\n \tchar *import_marks;\n \t/* These go from remote name (as in \"list\") to private name */\n-\tstruct refspec *refspecs;\n+\tstruct refspec_item *refspecs;\n \tint refspec_nr;\n \t/* Transport options for fetch-pack/send-pack (should one of\n \t * those be invoked).\ndiff --git a/transport.c b/transport.c\nindex 2cf63d18b..3ad4d37dc 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -390,7 +390,7 @@ int transport_refs_pushed(struct ref *ref)\n \n void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose)\n {\n-\tstruct refspec rs;\n+\tstruct refspec_item rs;\n \n \tif (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE)\n \t\treturn;\n@@ -1111,7 +1111,7 @@ int transport_push(struct transport *transport,\n \t\tint porcelain = flags & TRANSPORT_PUSH_PORCELAIN;\n \t\tint pretend = flags & TRANSPORT_PUSH_DRY_RUN;\n \t\tint push_ret, ret, err;\n-\t\tstruct refspec *tmp_rs;\n+\t\tstruct refspec_item *tmp_rs;\n \t\tstruct argv_array ref_prefixes = ARGV_ARRAY_INIT;\n \t\tint i;\n \n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347851","messageId":"20180516225823.235426-4-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 03/36] refspec: factor out parsing a single refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:57:50Z","receivedAt":"2018-05-16T22:58:51Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Factor out the logic which parses a single refspec into its own\nfunction.  This makes it easier to reuse this logic in a future patch.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n refspec.c | 195 +++++++++++++++++++++++++++++-------------------------\n 1 file changed, 104 insertions(+), 91 deletions(-)\n\ndiff --git a/refspec.c b/refspec.c\nindex 22188f010..8bf4ebbd3 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -14,110 +14,123 @@ static struct refspec_item s_tag_refspec = {\n /* See TAG_REFSPEC for the string version */\n const struct refspec_item *tag_refspec = &s_tag_refspec;\n \n-static struct refspec_item *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n+/*\n+ * Parses the provided refspec 'refspec' and populates the refspec_item 'item'.\n+ * Returns 1 if successful and 0 if the refspec is invalid.\n+ */\n+static int parse_refspec(struct refspec_item *item, const char *refspec, int fetch)\n {\n-\tint i;\n-\tstruct refspec_item *rs = xcalloc(nr_refspec, sizeof(*rs));\n+\tsize_t llen;\n+\tint is_glob;\n+\tconst char *lhs, *rhs;\n+\tint flags;\n \n-\tfor (i = 0; i < nr_refspec; i++) {\n-\t\tsize_t llen;\n-\t\tint is_glob;\n-\t\tconst char *lhs, *rhs;\n-\t\tint flags;\n+\tis_glob = 0;\n \n-\t\tis_glob = 0;\n+\tlhs = refspec;\n+\tif (*lhs == '+') {\n+\t\titem->force = 1;\n+\t\tlhs++;\n+\t}\n \n-\t\tlhs = refspec[i];\n-\t\tif (*lhs == '+') {\n-\t\t\trs[i].force = 1;\n-\t\t\tlhs++;\n-\t\t}\n+\trhs = strrchr(lhs, ':');\n \n-\t\trhs = strrchr(lhs, ':');\n+\t/*\n+\t * Before going on, special case \":\" (or \"+:\") as a refspec\n+\t * for pushing matching refs.\n+\t */\n+\tif (!fetch && rhs == lhs && rhs[1] == '\\0') {\n+\t\titem->matching = 1;\n+\t\treturn 1;\n+\t}\n \n+\tif (rhs) {\n+\t\tsize_t rlen = strlen(++rhs);\n+\t\tis_glob = (1 <= rlen && strchr(rhs, '*'));\n+\t\titem->dst = xstrndup(rhs, rlen);\n+\t}\n+\n+\tllen = (rhs ? (rhs - lhs - 1) : strlen(lhs));\n+\tif (1 <= llen && memchr(lhs, '*', llen)) {\n+\t\tif ((rhs && !is_glob) || (!rhs && fetch))\n+\t\t\treturn 0;\n+\t\tis_glob = 1;\n+\t} else if (rhs && is_glob) {\n+\t\treturn 0;\n+\t}\n+\n+\titem->pattern = is_glob;\n+\titem->src = xstrndup(lhs, llen);\n+\tflags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);\n+\n+\tif (fetch) {\n+\t\tstruct object_id unused;\n+\n+\t\t/* LHS */\n+\t\tif (!*item->src)\n+\t\t\t; /* empty is ok; it means \"HEAD\" */\n+\t\telse if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(item->src, &unused))\n+\t\t\titem->exact_sha1 = 1; /* ok */\n+\t\telse if (!check_refname_format(item->src, flags))\n+\t\t\t; /* valid looking ref is ok */\n+\t\telse\n+\t\t\treturn 0;\n+\t\t/* RHS */\n+\t\tif (!item->dst)\n+\t\t\t; /* missing is ok; it is the same as empty */\n+\t\telse if (!*item->dst)\n+\t\t\t; /* empty is ok; it means \"do not store\" */\n+\t\telse if (!check_refname_format(item->dst, flags))\n+\t\t\t; /* valid looking ref is ok */\n+\t\telse\n+\t\t\treturn 0;\n+\t} else {\n \t\t/*\n-\t\t * Before going on, special case \":\" (or \"+:\") as a refspec\n-\t\t * for pushing matching refs.\n+\t\t * LHS\n+\t\t * - empty is allowed; it means delete.\n+\t\t * - when wildcarded, it must be a valid looking ref.\n+\t\t * - otherwise, it must be an extended SHA-1, but\n+\t\t *   there is no existing way to validate this.\n \t\t */\n-\t\tif (!fetch && rhs == lhs && rhs[1] == '\\0') {\n-\t\t\trs[i].matching = 1;\n-\t\t\tcontinue;\n+\t\tif (!*item->src)\n+\t\t\t; /* empty is ok */\n+\t\telse if (is_glob) {\n+\t\t\tif (check_refname_format(item->src, flags))\n+\t\t\t\treturn 0;\n \t\t}\n-\n-\t\tif (rhs) {\n-\t\t\tsize_t rlen = strlen(++rhs);\n-\t\t\tis_glob = (1 <= rlen && strchr(rhs, '*'));\n-\t\t\trs[i].dst = xstrndup(rhs, rlen);\n+\t\telse\n+\t\t\t; /* anything goes, for now */\n+\t\t/*\n+\t\t * RHS\n+\t\t * - missing is allowed, but LHS then must be a\n+\t\t *   valid looking ref.\n+\t\t * - empty is not allowed.\n+\t\t * - otherwise it must be a valid looking ref.\n+\t\t */\n+\t\tif (!item->dst) {\n+\t\t\tif (check_refname_format(item->src, flags))\n+\t\t\t\treturn 0;\n+\t\t} else if (!*item->dst) {\n+\t\t\treturn 0;\n+\t\t} else {\n+\t\t\tif (check_refname_format(item->dst, flags))\n+\t\t\t\treturn 0;\n \t\t}\n+\t}\n \n-\t\tllen = (rhs ? (rhs - lhs - 1) : strlen(lhs));\n-\t\tif (1 <= llen && memchr(lhs, '*', llen)) {\n-\t\t\tif ((rhs && !is_glob) || (!rhs && fetch))\n-\t\t\t\tgoto invalid;\n-\t\t\tis_glob = 1;\n-\t\t} else if (rhs && is_glob) {\n-\t\t\tgoto invalid;\n-\t\t}\n+\treturn 1;\n+}\n \n-\t\trs[i].pattern = is_glob;\n-\t\trs[i].src = xstrndup(lhs, llen);\n-\t\tflags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);\n-\n-\t\tif (fetch) {\n-\t\t\tstruct object_id unused;\n-\n-\t\t\t/* LHS */\n-\t\t\tif (!*rs[i].src)\n-\t\t\t\t; /* empty is ok; it means \"HEAD\" */\n-\t\t\telse if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused))\n-\t\t\t\trs[i].exact_sha1 = 1; /* ok */\n-\t\t\telse if (!check_refname_format(rs[i].src, flags))\n-\t\t\t\t; /* valid looking ref is ok */\n-\t\t\telse\n-\t\t\t\tgoto invalid;\n-\t\t\t/* RHS */\n-\t\t\tif (!rs[i].dst)\n-\t\t\t\t; /* missing is ok; it is the same as empty */\n-\t\t\telse if (!*rs[i].dst)\n-\t\t\t\t; /* empty is ok; it means \"do not store\" */\n-\t\t\telse if (!check_refname_format(rs[i].dst, flags))\n-\t\t\t\t; /* valid looking ref is ok */\n-\t\t\telse\n-\t\t\t\tgoto invalid;\n-\t\t} else {\n-\t\t\t/*\n-\t\t\t * LHS\n-\t\t\t * - empty is allowed; it means delete.\n-\t\t\t * - when wildcarded, it must be a valid looking ref.\n-\t\t\t * - otherwise, it must be an extended SHA-1, but\n-\t\t\t *   there is no existing way to validate this.\n-\t\t\t */\n-\t\t\tif (!*rs[i].src)\n-\t\t\t\t; /* empty is ok */\n-\t\t\telse if (is_glob) {\n-\t\t\t\tif (check_refname_format(rs[i].src, flags))\n-\t\t\t\t\tgoto invalid;\n-\t\t\t}\n-\t\t\telse\n-\t\t\t\t; /* anything goes, for now */\n-\t\t\t/*\n-\t\t\t * RHS\n-\t\t\t * - missing is allowed, but LHS then must be a\n-\t\t\t *   valid looking ref.\n-\t\t\t * - empty is not allowed.\n-\t\t\t * - otherwise it must be a valid looking ref.\n-\t\t\t */\n-\t\t\tif (!rs[i].dst) {\n-\t\t\t\tif (check_refname_format(rs[i].src, flags))\n-\t\t\t\t\tgoto invalid;\n-\t\t\t} else if (!*rs[i].dst) {\n-\t\t\t\tgoto invalid;\n-\t\t\t} else {\n-\t\t\t\tif (check_refname_format(rs[i].dst, flags))\n-\t\t\t\t\tgoto invalid;\n-\t\t\t}\n-\t\t}\n+static struct refspec_item *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n+{\n+\tint i;\n+\tstruct refspec_item *rs = xcalloc(nr_refspec, sizeof(*rs));\n+\n+\tfor (i = 0; i < nr_refspec; i++) {\n+\t\tif (!parse_refspec(&rs[i], refspec[i], fetch))\n+\t\t\tgoto invalid;\n \t}\n+\n \treturn rs;\n \n  invalid:\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347852","messageId":"20180516225823.235426-6-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 05/36] refspec: convert valid_fetch_refspec to use parse_refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:57:52Z","receivedAt":"2018-05-16T22:58:55Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'valid_fetch_refspec()' to use the new 'parse_refspec()'\nfunction to only parse a single refspec and eliminate an allocation.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n refspec.c | 17 ++++++++---------\n refspec.h |  3 ++-\n 2 files changed, 10 insertions(+), 10 deletions(-)\n\ndiff --git a/refspec.c b/refspec.c\nindex af9d0d4b3..ab37b5ba1 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -146,15 +146,6 @@ static struct refspec_item *parse_refspec_internal(int nr_refspec, const char **\n \tdie(\"Invalid refspec '%s'\", refspec[i]);\n }\n \n-int valid_fetch_refspec(const char *fetch_refspec_str)\n-{\n-\tstruct refspec_item *refspec;\n-\n-\trefspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);\n-\tfree_refspec(1, refspec);\n-\treturn !!refspec;\n-}\n-\n struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec)\n {\n \treturn parse_refspec_internal(nr_refspec, refspec, 1, 0);\n@@ -242,3 +233,11 @@ void refspec_clear(struct refspec *rs)\n \n \trs->fetch = 0;\n }\n+\n+int valid_fetch_refspec(const char *fetch_refspec_str)\n+{\n+\tstruct refspec_item refspec;\n+\tint ret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n+\trefspec_item_clear(&refspec);\n+\treturn ret;\n+}\ndiff --git a/refspec.h b/refspec.h\nindex da3135825..1063c64cc 100644\n--- a/refspec.h\n+++ b/refspec.h\n@@ -14,7 +14,6 @@ struct refspec_item {\n \tchar *dst;\n };\n \n-int valid_fetch_refspec(const char *refspec);\n struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec);\n struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec);\n \n@@ -45,4 +44,6 @@ void refspec_append(struct refspec *rs, const char *refspec);\n void refspec_appendn(struct refspec *rs, const char **refspecs, int nr);\n void refspec_clear(struct refspec *rs);\n \n+int valid_fetch_refspec(const char *refspec);\n+\n #endif /* REFSPEC_H */\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347853","messageId":"20180516225823.235426-5-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 04/36] refspec: introduce struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:57:51Z","receivedAt":"2018-05-16T22:58:56Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Introduce 'struct refspec', an abstraction around a collection of\n'struct refspec_item's much like how 'struct pathspec' holds a\ncollection of 'struct pathspec_item's.\n\nA refspec struct also contains an array of the original refspec strings\nwhich will be used to facilitate the migration to using this new\nabstraction throughout the code base.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n refspec.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\n refspec.h | 25 ++++++++++++++++++++++\n 2 files changed, 89 insertions(+)\n\ndiff --git a/refspec.c b/refspec.c\nindex 8bf4ebbd3..af9d0d4b3 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -178,3 +178,67 @@ void free_refspec(int nr_refspec, struct refspec_item *refspec)\n \t}\n \tfree(refspec);\n }\n+\n+void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)\n+{\n+\tmemset(item, 0, sizeof(*item));\n+\n+\tif (!parse_refspec(item, refspec, fetch))\n+\t\tdie(\"Invalid refspec '%s'\", refspec);\n+}\n+\n+void refspec_item_clear(struct refspec_item *item)\n+{\n+\tFREE_AND_NULL(item->src);\n+\tFREE_AND_NULL(item->dst);\n+\titem->force = 0;\n+\titem->pattern = 0;\n+\titem->matching = 0;\n+\titem->exact_sha1 = 0;\n+}\n+\n+void refspec_init(struct refspec *rs, int fetch)\n+{\n+\tmemset(rs, 0, sizeof(*rs));\n+\trs->fetch = fetch;\n+}\n+\n+void refspec_append(struct refspec *rs, const char *refspec)\n+{\n+\tstruct refspec_item item;\n+\n+\trefspec_item_init(&item, refspec, rs->fetch);\n+\n+\tALLOC_GROW(rs->items, rs->nr + 1, rs->alloc);\n+\trs->items[rs->nr++] = item;\n+\n+\tALLOC_GROW(rs->raw, rs->raw_nr + 1, rs->raw_alloc);\n+\trs->raw[rs->raw_nr++] = xstrdup(refspec);\n+}\n+\n+void refspec_appendn(struct refspec *rs, const char **refspecs, int nr)\n+{\n+\tint i;\n+\tfor (i = 0; i < nr; i++)\n+\t\trefspec_append(rs, refspecs[i]);\n+}\n+\n+void refspec_clear(struct refspec *rs)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < rs->nr; i++)\n+\t\trefspec_item_clear(&rs->items[i]);\n+\n+\tFREE_AND_NULL(rs->items);\n+\trs->alloc = 0;\n+\trs->nr = 0;\n+\n+\tfor (i = 0; i < rs->raw_nr; i++)\n+\t\tfree((char *)rs->raw[i]);\n+\tFREE_AND_NULL(rs->raw);\n+\trs->raw_alloc = 0;\n+\trs->raw_nr = 0;\n+\n+\trs->fetch = 0;\n+}\ndiff --git a/refspec.h b/refspec.h\nindex fc9c1af77..da3135825 100644\n--- a/refspec.h\n+++ b/refspec.h\n@@ -20,4 +20,29 @@ struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec);\n \n void free_refspec(int nr_refspec, struct refspec_item *refspec);\n \n+#define REFSPEC_FETCH 1\n+#define REFSPEC_PUSH 0\n+\n+#define REFSPEC_INIT_FETCH { .fetch = REFSPEC_FETCH }\n+#define REFSPEC_INIT_PUSH { .fetch = REFSPEC_PUSH }\n+\n+struct refspec {\n+\tstruct refspec_item *items;\n+\tint alloc;\n+\tint nr;\n+\n+\tconst char **raw;\n+\tint raw_alloc;\n+\tint raw_nr;\n+\n+\tint fetch;\n+};\n+\n+void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch);\n+void refspec_item_clear(struct refspec_item *item);\n+void refspec_init(struct refspec *rs, int fetch);\n+void refspec_append(struct refspec *rs, const char *refspec);\n+void refspec_appendn(struct refspec *rs, const char **refspecs, int nr);\n+void refspec_clear(struct refspec *rs);\n+\n #endif /* REFSPEC_H */\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347854","messageId":"20180516225823.235426-7-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 06/36] submodule--helper: convert push_check to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:57:53Z","receivedAt":"2018-05-16T22:58:59Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'push_check()' to use 'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/submodule--helper.c | 13 +++++++------\n 1 file changed, 7 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex c0c4db007..88a149a2c 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1744,13 +1744,14 @@ static int push_check(int argc, const char **argv, const char *prefix)\n \n \t/* Check the refspec */\n \tif (argc > 2) {\n-\t\tint i, refspec_nr = argc - 2;\n+\t\tint i;\n \t\tstruct ref *local_refs = get_local_heads();\n-\t\tstruct refspec_item *refspec = parse_push_refspec(refspec_nr,\n-\t\t\t\t\t\t\t     argv + 2);\n+\t\tstruct refspec refspec = REFSPEC_INIT_PUSH;\n \n-\t\tfor (i = 0; i < refspec_nr; i++) {\n-\t\t\tstruct refspec_item *rs = refspec + i;\n+\t\trefspec_appendn(&refspec, argv + 2, argc - 2);\n+\n+\t\tfor (i = 0; i < refspec.nr; i++) {\n+\t\t\tconst struct refspec_item *rs = &refspec.items[i];\n \n \t\t\tif (rs->pattern || rs->matching)\n \t\t\t\tcontinue;\n@@ -1777,7 +1778,7 @@ static int push_check(int argc, const char **argv, const char *prefix)\n \t\t\t\t    rs->src);\n \t\t\t}\n \t\t}\n-\t\tfree_refspec(refspec_nr, refspec);\n+\t\trefspec_clear(&refspec);\n \t}\n \tfree(head);\n \n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347855","messageId":"20180516225823.235426-8-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 07/36] pull: convert get_tracking_branch to use refspec_item_init","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:57:54Z","receivedAt":"2018-05-16T22:59:02Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'get_tracking_branch()' to use 'refspec_item_init()' instead of\nthe old 'parse_fetch_refspec()' function.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/pull.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 5a79deae5..09575fd23 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -676,12 +676,12 @@ static const char *get_upstream_branch(const char *remote)\n  */\n static const char *get_tracking_branch(const char *remote, const char *refspec)\n {\n-\tstruct refspec_item *spec;\n+\tstruct refspec_item spec;\n \tconst char *spec_src;\n \tconst char *merge_branch;\n \n-\tspec = parse_fetch_refspec(1, &refspec);\n-\tspec_src = spec->src;\n+\trefspec_item_init(&spec, refspec, REFSPEC_FETCH);\n+\tspec_src = spec.src;\n \tif (!*spec_src || !strcmp(spec_src, \"HEAD\"))\n \t\tspec_src = \"HEAD\";\n \telse if (skip_prefix(spec_src, \"heads/\", &spec_src))\n@@ -701,7 +701,7 @@ static const char *get_tracking_branch(const char *remote, const char *refspec)\n \t} else\n \t\tmerge_branch = NULL;\n \n-\tfree_refspec(1, spec);\n+\trefspec_item_clear(&spec);\n \treturn merge_branch;\n }\n \n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347856","messageId":"20180516225823.235426-11-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 10/36] remote: convert match_push_refs to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:57:57Z","receivedAt":"2018-05-16T22:59:06Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'match_push_refs()' to use struct refspec.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n remote.c | 13 ++++++++-----\n 1 file changed, 8 insertions(+), 5 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 191855118..bce6e7ce4 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1312,7 +1312,7 @@ int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)\n int match_push_refs(struct ref *src, struct ref **dst,\n \t\t    int nr_refspec, const char **refspec, int flags)\n {\n-\tstruct refspec_item *rs;\n+\tstruct refspec rs = REFSPEC_INIT_PUSH;\n \tint send_all = flags & MATCH_REFS_ALL;\n \tint send_mirror = flags & MATCH_REFS_MIRROR;\n \tint send_prune = flags & MATCH_REFS_PRUNE;\n@@ -1325,8 +1325,8 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\tnr_refspec = 1;\n \t\trefspec = default_refspec;\n \t}\n-\trs = parse_push_refspec(nr_refspec, (const char **) refspec);\n-\terrs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec);\n+\trefspec_appendn(&rs, refspec, nr_refspec);\n+\terrs = match_explicit_refs(src, *dst, &dst_tail, rs.items, rs.nr);\n \n \t/* pick the remainder */\n \tfor (ref = src; ref; ref = ref->next) {\n@@ -1335,7 +1335,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\tconst struct refspec_item *pat = NULL;\n \t\tchar *dst_name;\n \n-\t\tdst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat);\n+\t\tdst_name = get_ref_match(rs.items, rs.nr, ref, send_mirror, FROM_SRC, &pat);\n \t\tif (!dst_name)\n \t\t\tcontinue;\n \n@@ -1384,7 +1384,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\t\t\t/* We're already sending something to this ref. */\n \t\t\t\tcontinue;\n \n-\t\t\tsrc_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL);\n+\t\t\tsrc_name = get_ref_match(rs.items, rs.nr, ref, send_mirror, FROM_DST, NULL);\n \t\t\tif (src_name) {\n \t\t\t\tif (!src_ref_index.nr)\n \t\t\t\t\tprepare_ref_index(&src_ref_index, src);\n@@ -1396,6 +1396,9 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\t}\n \t\tstring_list_clear(&src_ref_index, 0);\n \t}\n+\n+\trefspec_clear(&rs);\n+\n \tif (errs)\n \t\treturn -1;\n \treturn 0;\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347857","messageId":"20180516225823.235426-12-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 11/36] clone: convert cmd_clone to use refspec_item_init","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:57:58Z","receivedAt":"2018-05-16T22:59:08Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'cmd_clone()' to use 'refspec_item_init()' instead of relying on\nthe old 'parse_fetch_refspec()' to initialize a single refspec item.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/clone.c | 10 ++++------\n 1 file changed, 4 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 854088a3a..8c5f4d8f0 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -895,8 +895,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tint err = 0, complete_refs_before_fetch = 1;\n \tint submodule_progress;\n \n-\tstruct refspec_item *refspec;\n-\tconst char *fetch_pattern;\n+\tstruct refspec_item refspec;\n \n \tfetch_if_missing = 0;\n \n@@ -1078,8 +1077,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tif (option_required_reference.nr || option_optional_reference.nr)\n \t\tsetup_reference();\n \n-\tfetch_pattern = value.buf;\n-\trefspec = parse_fetch_refspec(1, &fetch_pattern);\n+\trefspec_item_init(&refspec, value.buf, REFSPEC_FETCH);\n \n \tstrbuf_reset(&value);\n \n@@ -1139,7 +1137,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \trefs = transport_get_remote_refs(transport, NULL);\n \n \tif (refs) {\n-\t\tmapped_refs = wanted_peer_refs(refs, refspec);\n+\t\tmapped_refs = wanted_peer_refs(refs, &refspec);\n \t\t/*\n \t\t * transport_get_remote_refs() may return refs with null sha-1\n \t\t * in mapped_refs (see struct transport->get_refs_list\n@@ -1233,6 +1231,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tstrbuf_release(&value);\n \tjunk_mode = JUNK_LEAVE_ALL;\n \n-\tfree(refspec);\n+\trefspec_item_clear(&refspec);\n \treturn err;\n }\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347858","messageId":"20180516225823.235426-13-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 12/36] fast-export: convert to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:57:59Z","receivedAt":"2018-05-16T22:59:12Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert fast-export to use 'struct refspec' instead of using a list of\nrefspec_item's.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fast-export.c | 21 +++++++--------------\n 1 file changed, 7 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 6f105dc79..143999738 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -36,8 +36,7 @@ static int use_done_feature;\n static int no_data;\n static int full_tree;\n static struct string_list extra_refs = STRING_LIST_INIT_NODUP;\n-static struct refspec_item *refspecs;\n-static int refspecs_nr;\n+static struct refspec refspecs = REFSPEC_INIT_FETCH;\n static int anonymize;\n \n static int parse_opt_signed_tag_mode(const struct option *opt,\n@@ -830,9 +829,9 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)\n \t\tif (dwim_ref(e->name, strlen(e->name), &oid, &full_name) != 1)\n \t\t\tcontinue;\n \n-\t\tif (refspecs) {\n+\t\tif (refspecs.nr) {\n \t\t\tchar *private;\n-\t\t\tprivate = apply_refspecs(refspecs, refspecs_nr, full_name);\n+\t\t\tprivate = apply_refspecs(refspecs.items, refspecs.nr, full_name);\n \t\t\tif (private) {\n \t\t\t\tfree(full_name);\n \t\t\t\tfull_name = private;\n@@ -978,8 +977,8 @@ static void import_marks(char *input_file)\n static void handle_deletes(void)\n {\n \tint i;\n-\tfor (i = 0; i < refspecs_nr; i++) {\n-\t\tstruct refspec_item *refspec = &refspecs[i];\n+\tfor (i = 0; i < refspecs.nr; i++) {\n+\t\tstruct refspec_item *refspec = &refspecs.items[i];\n \t\tif (*refspec->src)\n \t\t\tcontinue;\n \n@@ -1040,18 +1039,12 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)\n \t\tusage_with_options (fast_export_usage, options);\n \n \tif (refspecs_list.nr) {\n-\t\tconst char **refspecs_str;\n \t\tint i;\n \n-\t\tALLOC_ARRAY(refspecs_str, refspecs_list.nr);\n \t\tfor (i = 0; i < refspecs_list.nr; i++)\n-\t\t\trefspecs_str[i] = refspecs_list.items[i].string;\n-\n-\t\trefspecs_nr = refspecs_list.nr;\n-\t\trefspecs = parse_fetch_refspec(refspecs_nr, refspecs_str);\n+\t\t\trefspec_append(&refspecs, refspecs_list.items[i].string);\n \n \t\tstring_list_clear(&refspecs_list, 1);\n-\t\tfree(refspecs_str);\n \t}\n \n \tif (use_done_feature)\n@@ -1090,7 +1083,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)\n \tif (use_done_feature)\n \t\tprintf(\"done\\n\");\n \n-\tfree_refspec(refspecs_nr, refspecs);\n+\trefspec_clear(&refspecs);\n \n \treturn 0;\n }\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347859","messageId":"20180516225823.235426-10-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 09/36] remote: convert check_push_refs to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:57:56Z","receivedAt":"2018-05-16T22:59:15Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'check_push_refs()' to use 'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n remote.c | 10 ++++++----\n 1 file changed, 6 insertions(+), 4 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 89820c476..191855118 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1282,12 +1282,14 @@ static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)\n  */\n int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)\n {\n-\tstruct refspec_item *refspec = parse_push_refspec(nr_refspec, refspec_names);\n+\tstruct refspec refspec = REFSPEC_INIT_PUSH;\n \tint ret = 0;\n \tint i;\n \n-\tfor (i = 0; i < nr_refspec; i++) {\n-\t\tstruct refspec_item *rs = refspec + i;\n+\trefspec_appendn(&refspec, refspec_names, nr_refspec);\n+\n+\tfor (i = 0; i < refspec.nr; i++) {\n+\t\tstruct refspec_item *rs = &refspec.items[i];\n \n \t\tif (rs->pattern || rs->matching)\n \t\t\tcontinue;\n@@ -1295,7 +1297,7 @@ int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)\n \t\tret |= match_explicit_lhs(src, rs, NULL, NULL);\n \t}\n \n-\tfree_refspec(nr_refspec, refspec);\n+\trefspec_clear(&refspec);\n \treturn ret;\n }\n \n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347860","messageId":"20180516225823.235426-15-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 14/36] remote: convert fetch refspecs to struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:01Z","receivedAt":"2018-05-16T22:59:17Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert the set of fetch refspecs stored in 'struct remote' to use\n'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c  | 20 ++++++++++----------\n builtin/remote.c | 18 +++++++++---------\n remote.c         | 38 ++++++++++++--------------------------\n remote.h         |  5 +----\n 4 files changed, 32 insertions(+), 49 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 745020a10..30083d4bc 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -407,8 +407,8 @@ static struct ref *get_ref_map(struct transport *transport,\n \t\t\tfetch_refspec = parse_fetch_refspec(refmap_nr, refmap_array);\n \t\t\tfetch_refspec_nr = refmap_nr;\n \t\t} else {\n-\t\t\tfetch_refspec = transport->remote->fetch;\n-\t\t\tfetch_refspec_nr = transport->remote->fetch_refspec_nr;\n+\t\t\tfetch_refspec = transport->remote->fetch.items;\n+\t\t\tfetch_refspec_nr = transport->remote->fetch.nr;\n \t\t}\n \n \t\tfor (i = 0; i < fetch_refspec_nr; i++)\n@@ -421,16 +421,16 @@ static struct ref *get_ref_map(struct transport *transport,\n \t\tstruct branch *branch = branch_get(NULL);\n \t\tint has_merge = branch_has_merge_config(branch);\n \t\tif (remote &&\n-\t\t    (remote->fetch_refspec_nr ||\n+\t\t    (remote->fetch.nr ||\n \t\t     /* Note: has_merge implies non-NULL branch->remote_name */\n \t\t     (has_merge && !strcmp(branch->remote_name, remote->name)))) {\n-\t\t\tfor (i = 0; i < remote->fetch_refspec_nr; i++) {\n-\t\t\t\tget_fetch_map(remote_refs, &remote->fetch[i], &tail, 0);\n-\t\t\t\tif (remote->fetch[i].dst &&\n-\t\t\t\t    remote->fetch[i].dst[0])\n+\t\t\tfor (i = 0; i < remote->fetch.nr; i++) {\n+\t\t\t\tget_fetch_map(remote_refs, &remote->fetch.items[i], &tail, 0);\n+\t\t\t\tif (remote->fetch.items[i].dst &&\n+\t\t\t\t    remote->fetch.items[i].dst[0])\n \t\t\t\t\t*autotags = 1;\n \t\t\t\tif (!i && !has_merge && ref_map &&\n-\t\t\t\t    !remote->fetch[0].pattern)\n+\t\t\t\t    !remote->fetch.items[0].pattern)\n \t\t\t\t\tref_map->fetch_head_status = FETCH_HEAD_MERGE;\n \t\t\t}\n \t\t\t/*\n@@ -1166,8 +1166,8 @@ static int do_fetch(struct transport *transport,\n \t\tif (ref_count) {\n \t\t\tprune_refs(refs, ref_count, ref_map, transport->url);\n \t\t} else {\n-\t\t\tprune_refs(transport->remote->fetch,\n-\t\t\t\t   transport->remote->fetch_refspec_nr,\n+\t\t\tprune_refs(transport->remote->fetch.items,\n+\t\t\t\t   transport->remote->fetch.nr,\n \t\t\t\t   ref_map,\n \t\t\t\t   transport->url);\n \t\t}\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex fb84729d6..94dfcb78b 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -333,10 +333,10 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat\n \tstruct ref *ref, *stale_refs;\n \tint i;\n \n-\tfor (i = 0; i < states->remote->fetch_refspec_nr; i++)\n-\t\tif (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1))\n+\tfor (i = 0; i < states->remote->fetch.nr; i++)\n+\t\tif (get_fetch_map(remote_refs, &states->remote->fetch.items[i], &tail, 1))\n \t\t\tdie(_(\"Could not get fetch map for refspec %s\"),\n-\t\t\t\tstates->remote->fetch_refspec[i]);\n+\t\t\t\tstates->remote->fetch.raw[i]);\n \n \tstates->new_refs.strdup_strings = 1;\n \tstates->tracked.strdup_strings = 1;\n@@ -347,8 +347,8 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat\n \t\telse\n \t\t\tstring_list_append(&states->tracked, abbrev_branch(ref->name));\n \t}\n-\tstale_refs = get_stale_heads(states->remote->fetch,\n-\t\t\t\t     states->remote->fetch_refspec_nr, fetch_map);\n+\tstale_refs = get_stale_heads(states->remote->fetch.items,\n+\t\t\t\t     states->remote->fetch.nr, fetch_map);\n \tfor (ref = stale_refs; ref; ref = ref->next) {\n \t\tstruct string_list_item *item =\n \t\t\tstring_list_append(&states->stale, abbrev_branch(ref->name));\n@@ -590,8 +590,8 @@ static int migrate_file(struct remote *remote)\n \t\tgit_config_set_multivar(buf.buf, remote->push.raw[i], \"^$\", 0);\n \tstrbuf_reset(&buf);\n \tstrbuf_addf(&buf, \"remote.%s.fetch\", remote->name);\n-\tfor (i = 0; i < remote->fetch_refspec_nr; i++)\n-\t\tgit_config_set_multivar(buf.buf, remote->fetch_refspec[i], \"^$\", 0);\n+\tfor (i = 0; i < remote->fetch.raw_nr; i++)\n+\t\tgit_config_set_multivar(buf.buf, remote->fetch.raw[i], \"^$\", 0);\n \tif (remote->origin == REMOTE_REMOTES)\n \t\tunlink_or_warn(git_path(\"remotes/%s\", remote->name));\n \telse if (remote->origin == REMOTE_BRANCHES)\n@@ -646,11 +646,11 @@ static int mv(int argc, const char **argv)\n \tstrbuf_addf(&buf, \"remote.%s.fetch\", rename.new_name);\n \tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n \tstrbuf_addf(&old_remote_context, \":refs/remotes/%s/\", rename.old_name);\n-\tfor (i = 0; i < oldremote->fetch_refspec_nr; i++) {\n+\tfor (i = 0; i < oldremote->fetch.raw_nr; i++) {\n \t\tchar *ptr;\n \n \t\tstrbuf_reset(&buf2);\n-\t\tstrbuf_addstr(&buf2, oldremote->fetch_refspec[i]);\n+\t\tstrbuf_addstr(&buf2, oldremote->fetch.raw[i]);\n \t\tptr = strstr(buf2.buf, old_remote_context.buf);\n \t\tif (ptr) {\n \t\t\trefspec_updated = 1;\ndiff --git a/remote.c b/remote.c\nindex 1b7258f77..26842ce37 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -77,23 +77,9 @@ static const char *alias_url(const char *url, struct rewrites *r)\n \treturn xstrfmt(\"%s%s\", r->rewrite[longest_i]->base, url + longest->len);\n }\n \n-static void add_fetch_refspec(struct remote *remote, const char *ref)\n-{\n-\tALLOC_GROW(remote->fetch_refspec,\n-\t\t   remote->fetch_refspec_nr + 1,\n-\t\t   remote->fetch_refspec_alloc);\n-\tremote->fetch_refspec[remote->fetch_refspec_nr++] = ref;\n-}\n-\n void add_prune_tags_to_fetch_refspec(struct remote *remote)\n {\n-\tint nr = remote->fetch_refspec_nr;\n-\tint bufsize = nr  + 1;\n-\tint size = sizeof(struct refspec_item);\n-\n-\tremote->fetch = xrealloc(remote->fetch, size  * bufsize);\n-\tmemcpy(&remote->fetch[nr], tag_refspec, size);\n-\tadd_fetch_refspec(remote, xstrdup(TAG_REFSPEC));\n+\trefspec_append(&remote->fetch, TAG_REFSPEC);\n }\n \n static void add_url(struct remote *remote, const char *url)\n@@ -169,6 +155,7 @@ static struct remote *make_remote(const char *name, int len)\n \tret->prune_tags = -1;  /* unspecified */\n \tret->name = xstrndup(name, 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@@ -271,7 +258,7 @@ static void read_remotes_file(struct remote *remote)\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-\t\t\tadd_fetch_refspec(remote, xstrdup(skip_spaces(v)));\n+\t\t\trefspec_append(&remote->fetch, skip_spaces(v));\n \t}\n \tstrbuf_release(&buf);\n \tfclose(f);\n@@ -310,13 +297,15 @@ static void read_branches_file(struct remote *remote)\n \t\tfrag = \"master\";\n \n \tadd_url_alias(remote, strbuf_detach(&buf, NULL));\n-\tadd_fetch_refspec(remote, xstrfmt(\"refs/heads/%s:refs/heads/%s\",\n-\t\t\t\t\t  frag, remote->name));\n+\tstrbuf_addf(&buf, \"refs/heads/%s:refs/heads/%s\",\n+\t\t    frag, remote->name);\n+\trefspec_append(&remote->fetch, buf.buf);\n \n \t/*\n \t * Cogito compatible push: push current HEAD to remote #branch\n \t * (master if missing)\n \t */\n+\tstrbuf_reset(&buf);\n \tstrbuf_addf(&buf, \"HEAD:refs/heads/%s\", frag);\n \trefspec_append(&remote->push, buf.buf);\n \tremote->fetch_tags = 1; /* always auto-follow */\n@@ -411,7 +400,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\tconst char *v;\n \t\tif (git_config_string(&v, key, value))\n \t\t\treturn -1;\n-\t\tadd_fetch_refspec(remote, v);\n+\t\trefspec_append(&remote->fetch, v);\n+\t\tfree((char *)v);\n \t} else if (!strcmp(subkey, \"receivepack\")) {\n \t\tconst char *v;\n \t\tif (git_config_string(&v, key, value))\n@@ -578,7 +568,6 @@ static struct remote *remote_get_1(const char *name,\n \t\tadd_url_alias(ret, name);\n \tif (!valid_remote(ret))\n \t\treturn NULL;\n-\tret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec);\n \treturn ret;\n }\n \n@@ -609,9 +598,6 @@ int for_each_remote(each_remote_fn fn, void *priv)\n \t\tstruct remote *r = remotes[i];\n \t\tif (!r)\n \t\t\tcontinue;\n-\t\tif (!r->fetch)\n-\t\t\tr->fetch = parse_fetch_refspec(r->fetch_refspec_nr,\n-\t\t\t\t\t\t       r->fetch_refspec);\n \t\tresult = fn(r, priv);\n \t}\n \treturn result;\n@@ -790,7 +776,7 @@ char *apply_refspecs(struct refspec_item *refspecs, int nr_refspec,\n \n int remote_find_tracking(struct remote *remote, struct refspec_item *refspec)\n {\n-\treturn query_refspecs(remote->fetch, remote->fetch_refspec_nr, refspec);\n+\treturn query_refspecs(remote->fetch.items, remote->fetch.nr, refspec);\n }\n \n static struct ref *alloc_ref_with_prefix(const char *prefix, size_t prefixlen,\n@@ -1588,7 +1574,7 @@ static const char *tracking_for_push_dest(struct remote *remote,\n {\n \tchar *ret;\n \n-\tret = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);\n+\tret = apply_refspecs(remote->fetch.items, remote->fetch.nr, refname);\n \tif (!ret)\n \t\treturn error_buf(err,\n \t\t\t\t _(\"push destination '%s' on remote '%s' has no local tracking branch\"),\n@@ -2222,7 +2208,7 @@ static int remote_tracking(struct remote *remote, const char *refname,\n {\n \tchar *dst;\n \n-\tdst = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);\n+\tdst = apply_refspecs(remote->fetch.items, remote->fetch.nr, refname);\n \tif (!dst)\n \t\treturn -1; /* no tracking ref for refname at remote */\n \tif (read_ref(dst, oid))\ndiff --git a/remote.h b/remote.h\nindex 637fc5d0c..e7d00fe2a 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -30,10 +30,7 @@ struct remote {\n \n \tstruct refspec push;\n \n-\tconst char **fetch_refspec;\n-\tstruct refspec_item *fetch;\n-\tint fetch_refspec_nr;\n-\tint fetch_refspec_alloc;\n+\tstruct refspec fetch;\n \n \t/*\n \t * -1 to never fetch tags\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347861","messageId":"20180516225823.235426-14-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 13/36] remote: convert push refspecs to struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:00Z","receivedAt":"2018-05-16T22:59:19Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert the set of push refspecs stored in 'struct remote' to use\n'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/push.c   | 10 +++++-----\n builtin/remote.c | 14 +++++++-------\n remote.c         | 35 ++++++++++++++---------------------\n remote.h         |  6 ++----\n 4 files changed, 28 insertions(+), 37 deletions(-)\n\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 00d81fb1d..509dc6677 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -79,11 +79,11 @@ static const char *map_refspec(const char *ref,\n \tif (count_refspec_match(ref, local_refs, &matched) != 1)\n \t\treturn ref;\n \n-\tif (remote->push) {\n+\tif (remote->push.nr) {\n \t\tstruct refspec_item query;\n \t\tmemset(&query, 0, sizeof(struct refspec_item));\n \t\tquery.src = matched->name;\n-\t\tif (!query_refspecs(remote->push, remote->push_refspec_nr, &query) &&\n+\t\tif (!query_refspecs(remote->push.items, remote->push.nr, &query) &&\n \t\t    query.dst) {\n \t\t\tstruct strbuf buf = STRBUF_INIT;\n \t\t\tstrbuf_addf(&buf, \"%s%s:%s\",\n@@ -436,9 +436,9 @@ static int do_push(const char *repo, int flags,\n \t}\n \n \tif (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {\n-\t\tif (remote->push_refspec_nr) {\n-\t\t\trefspec = remote->push_refspec;\n-\t\t\trefspec_nr = remote->push_refspec_nr;\n+\t\tif (remote->push.raw_nr) {\n+\t\t\trefspec = remote->push.raw;\n+\t\t\trefspec_nr = remote->push.raw_nr;\n \t\t} else if (!(flags & TRANSPORT_PUSH_MIRROR))\n \t\t\tsetup_default_push_refspecs(remote);\n \t}\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex d9da82dc8..fb84729d6 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -388,8 +388,8 @@ static int get_push_ref_states(const struct ref *remote_refs,\n \tlocal_refs = get_local_heads();\n \tpush_map = copy_ref_list(remote_refs);\n \n-\tmatch_push_refs(local_refs, &push_map, remote->push_refspec_nr,\n-\t\t\tremote->push_refspec, MATCH_REFS_NONE);\n+\tmatch_push_refs(local_refs, &push_map, remote->push.raw_nr,\n+\t\t\tremote->push.raw, MATCH_REFS_NONE);\n \n \tstates->push.strdup_strings = 1;\n \tfor (ref = push_map; ref; ref = ref->next) {\n@@ -435,14 +435,14 @@ static int get_push_ref_states_noquery(struct ref_states *states)\n \t\treturn 0;\n \n \tstates->push.strdup_strings = 1;\n-\tif (!remote->push_refspec_nr) {\n+\tif (!remote->push.nr) {\n \t\titem = string_list_append(&states->push, _(\"(matching)\"));\n \t\tinfo = item->util = xcalloc(1, sizeof(struct push_info));\n \t\tinfo->status = PUSH_STATUS_NOTQUERIED;\n \t\tinfo->dest = xstrdup(item->string);\n \t}\n-\tfor (i = 0; i < remote->push_refspec_nr; i++) {\n-\t\tstruct refspec_item *spec = remote->push + i;\n+\tfor (i = 0; i < remote->push.nr; i++) {\n+\t\tconst struct refspec_item *spec = &remote->push.items[i];\n \t\tif (spec->matching)\n \t\t\titem = string_list_append(&states->push, _(\"(matching)\"));\n \t\telse if (strlen(spec->src))\n@@ -586,8 +586,8 @@ static int migrate_file(struct remote *remote)\n \t\tgit_config_set_multivar(buf.buf, remote->url[i], \"^$\", 0);\n \tstrbuf_reset(&buf);\n \tstrbuf_addf(&buf, \"remote.%s.push\", remote->name);\n-\tfor (i = 0; i < remote->push_refspec_nr; i++)\n-\t\tgit_config_set_multivar(buf.buf, remote->push_refspec[i], \"^$\", 0);\n+\tfor (i = 0; i < remote->push.raw_nr; i++)\n+\t\tgit_config_set_multivar(buf.buf, remote->push.raw[i], \"^$\", 0);\n \tstrbuf_reset(&buf);\n \tstrbuf_addf(&buf, \"remote.%s.fetch\", remote->name);\n \tfor (i = 0; i < remote->fetch_refspec_nr; i++)\ndiff --git a/remote.c b/remote.c\nindex bce6e7ce4..1b7258f77 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -77,14 +77,6 @@ static const char *alias_url(const char *url, struct rewrites *r)\n \treturn xstrfmt(\"%s%s\", r->rewrite[longest_i]->base, url + longest->len);\n }\n \n-static void add_push_refspec(struct remote *remote, const char *ref)\n-{\n-\tALLOC_GROW(remote->push_refspec,\n-\t\t   remote->push_refspec_nr + 1,\n-\t\t   remote->push_refspec_alloc);\n-\tremote->push_refspec[remote->push_refspec_nr++] = ref;\n-}\n-\n static void add_fetch_refspec(struct remote *remote, const char *ref)\n {\n \tALLOC_GROW(remote->fetch_refspec,\n@@ -175,9 +167,11 @@ static struct remote *make_remote(const char *name, int len)\n \tret = xcalloc(1, sizeof(struct remote));\n \tret->prune = -1;  /* unspecified */\n \tret->prune_tags = -1;  /* unspecified */\n+\tret->name = xstrndup(name, len);\n+\trefspec_init(&ret->push, REFSPEC_PUSH);\n+\n \tALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);\n \tremotes[remotes_nr++] = ret;\n-\tret->name = xstrndup(name, len);\n \n \thashmap_entry_init(ret, lookup_entry.hash);\n \treplaced = hashmap_put(&remotes_hash, ret);\n@@ -275,7 +269,7 @@ static void read_remotes_file(struct remote *remote)\n \t\tif (skip_prefix(buf.buf, \"URL:\", &v))\n \t\t\tadd_url_alias(remote, xstrdup(skip_spaces(v)));\n \t\telse if (skip_prefix(buf.buf, \"Push:\", &v))\n-\t\t\tadd_push_refspec(remote, xstrdup(skip_spaces(v)));\n+\t\t\trefspec_append(&remote->push, skip_spaces(v));\n \t\telse if (skip_prefix(buf.buf, \"Pull:\", &v))\n \t\t\tadd_fetch_refspec(remote, xstrdup(skip_spaces(v)));\n \t}\n@@ -323,8 +317,10 @@ static void read_branches_file(struct remote *remote)\n \t * Cogito compatible push: push current HEAD to remote #branch\n \t * (master if missing)\n \t */\n-\tadd_push_refspec(remote, xstrfmt(\"HEAD:refs/heads/%s\", frag));\n+\tstrbuf_addf(&buf, \"HEAD:refs/heads/%s\", frag);\n+\trefspec_append(&remote->push, buf.buf);\n \tremote->fetch_tags = 1; /* always auto-follow */\n+\tstrbuf_release(&buf);\n }\n \n static int handle_config(const char *key, const char *value, void *cb)\n@@ -409,7 +405,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\tconst char *v;\n \t\tif (git_config_string(&v, key, value))\n \t\t\treturn -1;\n-\t\tadd_push_refspec(remote, v);\n+\t\trefspec_append(&remote->push, v);\n+\t\tfree((char *)v);\n \t} else if (!strcmp(subkey, \"fetch\")) {\n \t\tconst char *v;\n \t\tif (git_config_string(&v, key, value))\n@@ -542,9 +539,9 @@ const char *remote_ref_for_branch(struct branch *branch, int for_push,\n \t\t\t\tpushremote_for_branch(branch, NULL);\n \t\t\tstruct remote *remote = remote_get(remote_name);\n \n-\t\t\tif (remote && remote->push_refspec_nr &&\n-\t\t\t    (dst = apply_refspecs(remote->push,\n-\t\t\t\t\t\t  remote->push_refspec_nr,\n+\t\t\tif (remote && remote->push.nr &&\n+\t\t\t    (dst = apply_refspecs(remote->push.items,\n+\t\t\t\t\t\t  remote->push.nr,\n \t\t\t\t\t\t  branch->refname))) {\n \t\t\t\tif (explicit)\n \t\t\t\t\t*explicit = 1;\n@@ -582,7 +579,6 @@ static struct remote *remote_get_1(const char *name,\n \tif (!valid_remote(ret))\n \t\treturn NULL;\n \tret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec);\n-\tret->push = parse_push_refspec(ret->push_refspec_nr, ret->push_refspec);\n \treturn ret;\n }\n \n@@ -616,9 +612,6 @@ int for_each_remote(each_remote_fn fn, void *priv)\n \t\tif (!r->fetch)\n \t\t\tr->fetch = parse_fetch_refspec(r->fetch_refspec_nr,\n \t\t\t\t\t\t       r->fetch_refspec);\n-\t\tif (!r->push)\n-\t\t\tr->push = parse_push_refspec(r->push_refspec_nr,\n-\t\t\t\t\t\t     r->push_refspec);\n \t\tresult = fn(r, priv);\n \t}\n \treturn result;\n@@ -1613,11 +1606,11 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)\n \t\t\t\t _(\"branch '%s' has no remote for pushing\"),\n \t\t\t\t branch->name);\n \n-\tif (remote->push_refspec_nr) {\n+\tif (remote->push.nr) {\n \t\tchar *dst;\n \t\tconst char *ret;\n \n-\t\tdst = apply_refspecs(remote->push, remote->push_refspec_nr,\n+\t\tdst = apply_refspecs(remote->push.items, remote->push.nr,\n \t\t\t\t     branch->refname);\n \t\tif (!dst)\n \t\t\treturn error_buf(err,\ndiff --git a/remote.h b/remote.h\nindex 3657bd43d..637fc5d0c 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -3,6 +3,7 @@\n \n #include \"parse-options.h\"\n #include \"hashmap.h\"\n+#include \"refspec.h\"\n \n enum {\n \tREMOTE_UNCONFIGURED = 0,\n@@ -27,10 +28,7 @@ struct remote {\n \tint pushurl_nr;\n \tint pushurl_alloc;\n \n-\tconst char **push_refspec;\n-\tstruct refspec_item *push;\n-\tint push_refspec_nr;\n-\tint push_refspec_alloc;\n+\tstruct refspec push;\n \n \tconst char **fetch_refspec;\n \tstruct refspec_item *fetch;\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347862","messageId":"20180516225823.235426-17-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 16/36] transport-helper: convert to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:03Z","receivedAt":"2018-05-16T22:59:22Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert the refspecs in transport-helper.c to be stored in a\n'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n transport-helper.c | 38 ++++++++++++--------------------------\n 1 file changed, 12 insertions(+), 26 deletions(-)\n\ndiff --git a/transport-helper.c b/transport-helper.c\nindex b156a37e7..33f51ebfc 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -36,8 +36,7 @@ struct helper_data {\n \tchar *export_marks;\n \tchar *import_marks;\n \t/* These go from remote name (as in \"list\") to private name */\n-\tstruct refspec_item *refspecs;\n-\tint refspec_nr;\n+\tstruct refspec rs;\n \t/* Transport options for fetch-pack/send-pack (should one of\n \t * those be invoked).\n \t */\n@@ -107,9 +106,6 @@ static struct child_process *get_helper(struct transport *transport)\n \tstruct helper_data *data = transport->data;\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct child_process *helper;\n-\tconst char **refspecs = NULL;\n-\tint refspec_nr = 0;\n-\tint refspec_alloc = 0;\n \tint duped;\n \tint code;\n \n@@ -139,6 +135,7 @@ static struct child_process *get_helper(struct transport *transport)\n \n \tdata->helper = helper;\n \tdata->no_disconnect_req = 0;\n+\trefspec_init(&data->rs, REFSPEC_FETCH);\n \n \t/*\n \t * Open the output as FILE* so strbuf_getline_*() family of\n@@ -184,11 +181,8 @@ static struct child_process *get_helper(struct transport *transport)\n \t\t\tdata->export = 1;\n \t\telse if (!strcmp(capname, \"check-connectivity\"))\n \t\t\tdata->check_connectivity = 1;\n-\t\telse if (!data->refspecs && skip_prefix(capname, \"refspec \", &arg)) {\n-\t\t\tALLOC_GROW(refspecs,\n-\t\t\t\t   refspec_nr + 1,\n-\t\t\t\t   refspec_alloc);\n-\t\t\trefspecs[refspec_nr++] = xstrdup(arg);\n+\t\telse if (skip_prefix(capname, \"refspec \", &arg)) {\n+\t\t\trefspec_append(&data->rs, arg);\n \t\t} else if (!strcmp(capname, \"connect\")) {\n \t\t\tdata->connect = 1;\n \t\t} else if (!strcmp(capname, \"stateless-connect\")) {\n@@ -207,14 +201,7 @@ static struct child_process *get_helper(struct transport *transport)\n \t\t\t    capname);\n \t\t}\n \t}\n-\tif (refspecs) {\n-\t\tint i;\n-\t\tdata->refspec_nr = refspec_nr;\n-\t\tdata->refspecs = parse_fetch_refspec(refspec_nr, refspecs);\n-\t\tfor (i = 0; i < refspec_nr; i++)\n-\t\t\tfree((char *)refspecs[i]);\n-\t\tfree(refspecs);\n-\t} else if (data->import || data->bidi_import || data->export) {\n+\tif (!data->rs.nr && (data->import || data->bidi_import || data->export)) {\n \t\twarning(\"This remote helper should implement refspec capability.\");\n \t}\n \tstrbuf_release(&buf);\n@@ -378,8 +365,7 @@ static int release_helper(struct transport *transport)\n {\n \tint res = 0;\n \tstruct helper_data *data = transport->data;\n-\tfree_refspec(data->refspec_nr, data->refspecs);\n-\tdata->refspecs = NULL;\n+\trefspec_clear(&data->rs);\n \tres = disconnect_helper(transport);\n \tfree(transport->data);\n \treturn res;\n@@ -536,8 +522,8 @@ static int fetch_with_import(struct transport *transport,\n \t\tif (posn->status & REF_STATUS_UPTODATE)\n \t\t\tcontinue;\n \t\tname = posn->symref ? posn->symref : posn->name;\n-\t\tif (data->refspecs)\n-\t\t\tprivate = apply_refspecs(data->refspecs, data->refspec_nr, name);\n+\t\tif (data->rs.nr)\n+\t\t\tprivate = apply_refspecs(data->rs.items, data->rs.nr, name);\n \t\telse\n \t\t\tprivate = xstrdup(name);\n \t\tif (private) {\n@@ -815,11 +801,11 @@ static int push_update_refs_status(struct helper_data *data,\n \t\tif (push_update_ref_status(&buf, &ref, remote_refs))\n \t\t\tcontinue;\n \n-\t\tif (flags & TRANSPORT_PUSH_DRY_RUN || !data->refspecs || data->no_private_update)\n+\t\tif (flags & TRANSPORT_PUSH_DRY_RUN || !data->rs.nr || data->no_private_update)\n \t\t\tcontinue;\n \n \t\t/* propagate back the update to the remote namespace */\n-\t\tprivate = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);\n+\t\tprivate = apply_refspecs(data->rs.items, data->rs.nr, ref->name);\n \t\tif (!private)\n \t\t\tcontinue;\n \t\tupdate_ref(\"update by helper\", private, &ref->new_oid, NULL,\n@@ -939,7 +925,7 @@ static int push_refs_with_export(struct transport *transport,\n \tstruct string_list revlist_args = STRING_LIST_INIT_DUP;\n \tstruct strbuf buf = STRBUF_INIT;\n \n-\tif (!data->refspecs)\n+\tif (!data->rs.nr)\n \t\tdie(\"remote-helper doesn't support push; refspec needed\");\n \n \tset_common_push_options(transport, data->name, flags);\n@@ -956,7 +942,7 @@ static int push_refs_with_export(struct transport *transport,\n \t\tchar *private;\n \t\tstruct object_id oid;\n \n-\t\tprivate = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);\n+\t\tprivate = apply_refspecs(data->rs.items, data->rs.nr, ref->name);\n \t\tif (private && !get_oid(private, &oid)) {\n \t\t\tstrbuf_addf(&buf, \"^%s\", private);\n \t\t\tstring_list_append_nodup(&revlist_args,\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347863","messageId":"20180516225823.235426-19-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 18/36] fetch: convert refmap to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:05Z","receivedAt":"2018-05-16T22:59:26Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert the refmap in builtin/fetch.c to be stored in a\n'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c | 17 +++++++----------\n 1 file changed, 7 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 18704c6cd..6b909cd5b 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -60,8 +60,7 @@ static const char *submodule_prefix = \"\";\n static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;\n static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;\n static int shown_url = 0;\n-static int refmap_alloc, refmap_nr;\n-static const char **refmap_array;\n+static struct refspec refmap = REFSPEC_INIT_FETCH;\n static struct list_objects_filter_options filter_options;\n \n static int git_fetch_config(const char *k, const char *v, void *cb)\n@@ -108,14 +107,12 @@ static int gitmodules_fetch_config(const char *var, const char *value, void *cb)\n \n static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)\n {\n-\tALLOC_GROW(refmap_array, refmap_nr + 1, refmap_alloc);\n-\n \t/*\n \t * \"git fetch --refmap='' origin foo\"\n \t * can be used to tell the command not to store anywhere\n \t */\n-\tif (*arg)\n-\t\trefmap_array[refmap_nr++] = arg;\n+\trefspec_append(&refmap, arg);\n+\n \treturn 0;\n }\n \n@@ -403,9 +400,9 @@ static struct ref *get_ref_map(struct transport *transport,\n \t\t * by ref_remove_duplicates() in favor of one of these\n \t\t * opportunistic entries with FETCH_HEAD_IGNORE.\n \t\t */\n-\t\tif (refmap_array) {\n-\t\t\tfetch_refspec = parse_fetch_refspec(refmap_nr, refmap_array);\n-\t\t\tfetch_refspec_nr = refmap_nr;\n+\t\tif (refmap.nr) {\n+\t\t\tfetch_refspec = refmap.items;\n+\t\t\tfetch_refspec_nr = refmap.nr;\n \t\t} else {\n \t\t\tfetch_refspec = transport->remote->fetch.items;\n \t\t\tfetch_refspec_nr = transport->remote->fetch.nr;\n@@ -413,7 +410,7 @@ static struct ref *get_ref_map(struct transport *transport,\n \n \t\tfor (i = 0; i < fetch_refspec_nr; i++)\n \t\t\tget_fetch_map(ref_map, &fetch_refspec[i], &oref_tail, 1);\n-\t} else if (refmap_array) {\n+\t} else if (refmap.nr) {\n \t\tdie(\"--refmap option is only meaningful with command-line refspec(s).\");\n \t} else {\n \t\t/* Use the defaults */\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347864","messageId":"20180516225823.235426-20-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 19/36] refspec: remove the deprecated functions","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:06Z","receivedAt":"2018-05-16T22:59:28Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Now that there are no callers of 'parse_push_refspec()',\n'parse_fetch_refspec()', and 'free_refspec()', remove these\nfunctions.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n refspec.c | 49 -------------------------------------------------\n refspec.h |  5 -----\n 2 files changed, 54 deletions(-)\n\ndiff --git a/refspec.c b/refspec.c\nindex ab37b5ba1..97e76e8b1 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -121,55 +121,6 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet\n \treturn 1;\n }\n \n-static struct refspec_item *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)\n-{\n-\tint i;\n-\tstruct refspec_item *rs = xcalloc(nr_refspec, sizeof(*rs));\n-\n-\tfor (i = 0; i < nr_refspec; i++) {\n-\t\tif (!parse_refspec(&rs[i], refspec[i], fetch))\n-\t\t\tgoto invalid;\n-\t}\n-\n-\treturn rs;\n-\n- invalid:\n-\tif (verify) {\n-\t\t/*\n-\t\t * nr_refspec must be greater than zero and i must be valid\n-\t\t * since it is only possible to reach this point from within\n-\t\t * the for loop above.\n-\t\t */\n-\t\tfree_refspec(i+1, rs);\n-\t\treturn NULL;\n-\t}\n-\tdie(\"Invalid refspec '%s'\", refspec[i]);\n-}\n-\n-struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec)\n-{\n-\treturn parse_refspec_internal(nr_refspec, refspec, 1, 0);\n-}\n-\n-struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec)\n-{\n-\treturn parse_refspec_internal(nr_refspec, refspec, 0, 0);\n-}\n-\n-void free_refspec(int nr_refspec, struct refspec_item *refspec)\n-{\n-\tint i;\n-\n-\tif (!refspec)\n-\t\treturn;\n-\n-\tfor (i = 0; i < nr_refspec; i++) {\n-\t\tfree(refspec[i].src);\n-\t\tfree(refspec[i].dst);\n-\t}\n-\tfree(refspec);\n-}\n-\n void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)\n {\n \tmemset(item, 0, sizeof(*item));\ndiff --git a/refspec.h b/refspec.h\nindex 1063c64cc..7e1ff94ac 100644\n--- a/refspec.h\n+++ b/refspec.h\n@@ -14,11 +14,6 @@ struct refspec_item {\n \tchar *dst;\n };\n \n-struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec);\n-struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec);\n-\n-void free_refspec(int nr_refspec, struct refspec_item *refspec);\n-\n #define REFSPEC_FETCH 1\n #define REFSPEC_PUSH 0\n \n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347865","messageId":"20180516225823.235426-21-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 20/36] fetch: convert do_fetch to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:07Z","receivedAt":"2018-05-16T22:59:32Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'do_fetch()' to take a 'struct refspec' as a parameter instead\nof a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c | 10 +++++-----\n 1 file changed, 5 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 6b909cd5b..ec54b1dfe 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1112,7 +1112,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)\n }\n \n static int do_fetch(struct transport *transport,\n-\t\t    struct refspec_item *refs, int ref_count)\n+\t\t    struct refspec *rs)\n {\n \tstruct string_list existing_refs = STRING_LIST_INIT_DUP;\n \tstruct ref *ref_map;\n@@ -1136,7 +1136,7 @@ static int do_fetch(struct transport *transport,\n \t\t\tgoto cleanup;\n \t}\n \n-\tref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);\n+\tref_map = get_ref_map(transport, rs->items, rs->nr, tags, &autotags);\n \tif (!update_head_ok)\n \t\tcheck_not_current_branch(ref_map);\n \n@@ -1160,8 +1160,8 @@ static int do_fetch(struct transport *transport,\n \t\t * explicitly (via command line or configuration); we\n \t\t * don't care whether --tags was specified.\n \t\t */\n-\t\tif (ref_count) {\n-\t\t\tprune_refs(refs, ref_count, ref_map, transport->url);\n+\t\tif (rs->nr) {\n+\t\t\tprune_refs(rs->items, rs->nr, ref_map, transport->url);\n \t\t} else {\n \t\t\tprune_refs(transport->remote->fetch.items,\n \t\t\t\t   transport->remote->fetch.nr,\n@@ -1410,7 +1410,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru\n \n \tsigchain_push_common(unlock_pack_on_signal);\n \tatexit(unlock_pack);\n-\texit_code = do_fetch(gtransport, rs.items, rs.nr);\n+\texit_code = do_fetch(gtransport, &rs);\n \trefspec_clear(&rs);\n \ttransport_disconnect(gtransport);\n \tgtransport = NULL;\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347866","messageId":"20180516225823.235426-22-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 21/36] fetch: convert get_ref_map to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:08Z","receivedAt":"2018-05-16T22:59:36Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'get_ref_map()' to take a 'struct refspec' as a parameter\ninstead of a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c | 43 ++++++++++++++++++++-----------------------\n 1 file changed, 20 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex ec54b1dfe..836eb7545 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -337,7 +337,7 @@ static void find_non_local_tags(struct transport *transport,\n }\n \n static struct ref *get_ref_map(struct transport *transport,\n-\t\t\t       struct refspec_item *refspecs, int refspec_count,\n+\t\t\t       struct refspec *rs,\n \t\t\t       int tags, int *autotags)\n {\n \tint i;\n@@ -351,15 +351,16 @@ static struct ref *get_ref_map(struct transport *transport,\n \n \tconst struct ref *remote_refs;\n \n-\tfor (i = 0; i < refspec_count; i++) {\n-\t\tif (!refspecs[i].exact_sha1) {\n-\t\t\tconst char *glob = strchr(refspecs[i].src, '*');\n+\tfor (i = 0; i < rs->nr; i++) {\n+\t\tconst struct refspec_item *item = &rs->items[i];\n+\t\tif (!item->exact_sha1) {\n+\t\t\tconst char *glob = strchr(item->src, '*');\n \t\t\tif (glob)\n \t\t\t\targv_array_pushf(&ref_prefixes, \"%.*s\",\n-\t\t\t\t\t\t (int)(glob - refspecs[i].src),\n-\t\t\t\t\t\t refspecs[i].src);\n+\t\t\t\t\t\t (int)(glob - item->src),\n+\t\t\t\t\t\t item->src);\n \t\t\telse\n-\t\t\t\texpand_ref_prefix(&ref_prefixes, refspecs[i].src);\n+\t\t\t\texpand_ref_prefix(&ref_prefixes, item->src);\n \t\t}\n \t}\n \n@@ -367,13 +368,12 @@ static struct ref *get_ref_map(struct transport *transport,\n \n \targv_array_clear(&ref_prefixes);\n \n-\tif (refspec_count) {\n-\t\tstruct refspec_item *fetch_refspec;\n-\t\tint fetch_refspec_nr;\n+\tif (rs->nr) {\n+\t\tstruct refspec *fetch_refspec;\n \n-\t\tfor (i = 0; i < refspec_count; i++) {\n-\t\t\tget_fetch_map(remote_refs, &refspecs[i], &tail, 0);\n-\t\t\tif (refspecs[i].dst && refspecs[i].dst[0])\n+\t\tfor (i = 0; i < rs->nr; i++) {\n+\t\t\tget_fetch_map(remote_refs, &rs->items[i], &tail, 0);\n+\t\t\tif (rs->items[i].dst && rs->items[i].dst[0])\n \t\t\t\t*autotags = 1;\n \t\t}\n \t\t/* Merge everything on the command line (but not --tags) */\n@@ -400,16 +400,13 @@ static struct ref *get_ref_map(struct transport *transport,\n \t\t * by ref_remove_duplicates() in favor of one of these\n \t\t * opportunistic entries with FETCH_HEAD_IGNORE.\n \t\t */\n-\t\tif (refmap.nr) {\n-\t\t\tfetch_refspec = refmap.items;\n-\t\t\tfetch_refspec_nr = refmap.nr;\n-\t\t} else {\n-\t\t\tfetch_refspec = transport->remote->fetch.items;\n-\t\t\tfetch_refspec_nr = transport->remote->fetch.nr;\n-\t\t}\n+\t\tif (refmap.nr)\n+\t\t\tfetch_refspec = &refmap;\n+\t\telse\n+\t\t\tfetch_refspec = &transport->remote->fetch;\n \n-\t\tfor (i = 0; i < fetch_refspec_nr; i++)\n-\t\t\tget_fetch_map(ref_map, &fetch_refspec[i], &oref_tail, 1);\n+\t\tfor (i = 0; i < fetch_refspec->nr; i++)\n+\t\t\tget_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1);\n \t} else if (refmap.nr) {\n \t\tdie(\"--refmap option is only meaningful with command-line refspec(s).\");\n \t} else {\n@@ -1136,7 +1133,7 @@ static int do_fetch(struct transport *transport,\n \t\t\tgoto cleanup;\n \t}\n \n-\tref_map = get_ref_map(transport, rs->items, rs->nr, tags, &autotags);\n+\tref_map = get_ref_map(transport, rs, tags, &autotags);\n \tif (!update_head_ok)\n \t\tcheck_not_current_branch(ref_map);\n \n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347867","messageId":"20180516225823.235426-16-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 15/36] remote: remove add_prune_tags_to_fetch_refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:02Z","receivedAt":"2018-05-16T22:59:37Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Remove 'add_prune_tags_to_fetch_refspec()' function and instead have the\nonly caller directly add the tag refspec using 'refspec_append()'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c | 2 +-\n remote.c        | 5 -----\n remote.h        | 2 --\n 3 files changed, 1 insertion(+), 8 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 30083d4bc..7a1637d35 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1392,7 +1392,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru\n \n \tmaybe_prune_tags = prune_tags_ok && prune_tags;\n \tif (maybe_prune_tags && remote_via_config)\n-\t\tadd_prune_tags_to_fetch_refspec(remote);\n+\t\trefspec_append(&remote->fetch, TAG_REFSPEC);\n \n \tif (argc > 0 || (maybe_prune_tags && !remote_via_config)) {\n \t\tsize_t nr_alloc = st_add3(argc, maybe_prune_tags, 1);\ndiff --git a/remote.c b/remote.c\nindex 26842ce37..4a9bddf0d 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -77,11 +77,6 @@ static const char *alias_url(const char *url, struct rewrites *r)\n \treturn xstrfmt(\"%s%s\", r->rewrite[longest_i]->base, url + longest->len);\n }\n \n-void add_prune_tags_to_fetch_refspec(struct remote *remote)\n-{\n-\trefspec_append(&remote->fetch, TAG_REFSPEC);\n-}\n-\n static void add_url(struct remote *remote, const char *url)\n {\n \tALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);\ndiff --git a/remote.h b/remote.h\nindex e7d00fe2a..4ffbc0082 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -290,6 +290,4 @@ extern int parseopt_push_cas_option(const struct option *, const char *arg, int\n extern int is_empty_cas(const struct push_cas_option *);\n void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);\n \n-void add_prune_tags_to_fetch_refspec(struct remote *remote);\n-\n #endif\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347868","messageId":"20180516225823.235426-25-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 24/36] remote: convert apply_refspecs to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:11Z","receivedAt":"2018-05-16T22:59:41Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'apply_refspecs()' to take a 'struct refspec' as a parameter instead\nof a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fast-export.c |  2 +-\n remote.c              | 15 ++++++---------\n remote.h              |  3 +--\n transport-helper.c    |  6 +++---\n 4 files changed, 11 insertions(+), 15 deletions(-)\n\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 143999738..41fe49e4d 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -831,7 +831,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)\n \n \t\tif (refspecs.nr) {\n \t\t\tchar *private;\n-\t\t\tprivate = apply_refspecs(refspecs.items, refspecs.nr, full_name);\n+\t\t\tprivate = apply_refspecs(&refspecs, full_name);\n \t\t\tif (private) {\n \t\t\t\tfree(full_name);\n \t\t\t\tfull_name = private;\ndiff --git a/remote.c b/remote.c\nindex 358442e4b..89415a263 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -525,8 +525,7 @@ const char *remote_ref_for_branch(struct branch *branch, int for_push,\n \t\t\tstruct remote *remote = remote_get(remote_name);\n \n \t\t\tif (remote && remote->push.nr &&\n-\t\t\t    (dst = apply_refspecs(remote->push.items,\n-\t\t\t\t\t\t  remote->push.nr,\n+\t\t\t    (dst = apply_refspecs(&remote->push,\n \t\t\t\t\t\t  branch->refname))) {\n \t\t\t\tif (explicit)\n \t\t\t\t\t*explicit = 1;\n@@ -757,15 +756,14 @@ int query_refspecs(struct refspec_item *refs, int ref_count, struct refspec_item\n \treturn -1;\n }\n \n-char *apply_refspecs(struct refspec_item *refspecs, int nr_refspec,\n-\t\t     const char *name)\n+char *apply_refspecs(struct refspec *rs, const char *name)\n {\n \tstruct refspec_item query;\n \n \tmemset(&query, 0, sizeof(struct refspec_item));\n \tquery.src = (char *)name;\n \n-\tif (query_refspecs(refspecs, nr_refspec, &query))\n+\tif (query_refspecs(rs->items, rs->nr, &query))\n \t\treturn NULL;\n \n \treturn query.dst;\n@@ -1571,7 +1569,7 @@ static const char *tracking_for_push_dest(struct remote *remote,\n {\n \tchar *ret;\n \n-\tret = apply_refspecs(remote->fetch.items, remote->fetch.nr, refname);\n+\tret = apply_refspecs(&remote->fetch, refname);\n \tif (!ret)\n \t\treturn error_buf(err,\n \t\t\t\t _(\"push destination '%s' on remote '%s' has no local tracking branch\"),\n@@ -1593,8 +1591,7 @@ static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)\n \t\tchar *dst;\n \t\tconst char *ret;\n \n-\t\tdst = apply_refspecs(remote->push.items, remote->push.nr,\n-\t\t\t\t     branch->refname);\n+\t\tdst = apply_refspecs(&remote->push, branch->refname);\n \t\tif (!dst)\n \t\t\treturn error_buf(err,\n \t\t\t\t\t _(\"push refspecs for '%s' do not include '%s'\"),\n@@ -2203,7 +2200,7 @@ static int remote_tracking(struct remote *remote, const char *refname,\n {\n \tchar *dst;\n \n-\tdst = apply_refspecs(remote->fetch.items, remote->fetch.nr, refname);\n+\tdst = apply_refspecs(&remote->fetch, refname);\n \tif (!dst)\n \t\treturn -1; /* no tracking ref for refname at remote */\n \tif (read_ref(dst, oid))\ndiff --git a/remote.h b/remote.h\nindex 1a45542cd..0b1fcc051 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -159,8 +159,7 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);\n struct ref *ref_remove_duplicates(struct ref *ref_map);\n \n extern int query_refspecs(struct refspec_item *specs, int nr, struct refspec_item *query);\n-char *apply_refspecs(struct refspec_item *refspecs, int nr_refspec,\n-\t\t     const char *name);\n+char *apply_refspecs(struct refspec *rs, const char *name);\n \n int check_push_refs(struct ref *src, int nr_refspec, const char **refspec);\n int match_push_refs(struct ref *src, struct ref **dst,\ndiff --git a/transport-helper.c b/transport-helper.c\nindex 33f51ebfc..1f8ff7e94 100644\n--- a/transport-helper.c\n+++ b/transport-helper.c\n@@ -523,7 +523,7 @@ static int fetch_with_import(struct transport *transport,\n \t\t\tcontinue;\n \t\tname = posn->symref ? posn->symref : posn->name;\n \t\tif (data->rs.nr)\n-\t\t\tprivate = apply_refspecs(data->rs.items, data->rs.nr, name);\n+\t\t\tprivate = apply_refspecs(&data->rs, name);\n \t\telse\n \t\t\tprivate = xstrdup(name);\n \t\tif (private) {\n@@ -805,7 +805,7 @@ static int push_update_refs_status(struct helper_data *data,\n \t\t\tcontinue;\n \n \t\t/* propagate back the update to the remote namespace */\n-\t\tprivate = apply_refspecs(data->rs.items, data->rs.nr, ref->name);\n+\t\tprivate = apply_refspecs(&data->rs, ref->name);\n \t\tif (!private)\n \t\t\tcontinue;\n \t\tupdate_ref(\"update by helper\", private, &ref->new_oid, NULL,\n@@ -942,7 +942,7 @@ static int push_refs_with_export(struct transport *transport,\n \t\tchar *private;\n \t\tstruct object_id oid;\n \n-\t\tprivate = apply_refspecs(data->rs.items, data->rs.nr, ref->name);\n+\t\tprivate = apply_refspecs(&data->rs, ref->name);\n \t\tif (private && !get_oid(private, &oid)) {\n \t\t\tstrbuf_addf(&buf, \"^%s\", private);\n \t\t\tstring_list_append_nodup(&revlist_args,\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347869","messageId":"20180516225823.235426-27-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 26/36] remote: convert get_ref_match to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:13Z","receivedAt":"2018-05-16T22:59:45Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'get_ref_match()' to take a 'struct refspec' as a parameter\ninstead of a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n remote.c | 26 ++++++++++++++------------\n 1 file changed, 14 insertions(+), 12 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex dd68e6b22..9eb79ea19 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1082,27 +1082,29 @@ static int match_explicit_refs(struct ref *src, struct ref *dst,\n \treturn errs;\n }\n \n-static char *get_ref_match(const struct refspec_item *rs, int rs_nr, const struct ref *ref,\n-\t\tint send_mirror, int direction, const struct refspec_item **ret_pat)\n+static char *get_ref_match(const struct refspec *rs, const struct ref *ref,\n+\t\t\t   int send_mirror, int direction,\n+\t\t\t   const struct refspec_item **ret_pat)\n {\n \tconst struct refspec_item *pat;\n \tchar *name;\n \tint i;\n \tint matching_refs = -1;\n-\tfor (i = 0; i < rs_nr; i++) {\n-\t\tif (rs[i].matching &&\n-\t\t    (matching_refs == -1 || rs[i].force)) {\n+\tfor (i = 0; i < rs->nr; i++) {\n+\t\tconst struct refspec_item *item = &rs->items[i];\n+\t\tif (item->matching &&\n+\t\t    (matching_refs == -1 || item->force)) {\n \t\t\tmatching_refs = i;\n \t\t\tcontinue;\n \t\t}\n \n-\t\tif (rs[i].pattern) {\n-\t\t\tconst char *dst_side = rs[i].dst ? rs[i].dst : rs[i].src;\n+\t\tif (item->pattern) {\n+\t\t\tconst char *dst_side = item->dst ? item->dst : item->src;\n \t\t\tint match;\n \t\t\tif (direction == FROM_SRC)\n-\t\t\t\tmatch = match_name_with_pattern(rs[i].src, ref->name, dst_side, &name);\n+\t\t\t\tmatch = match_name_with_pattern(item->src, ref->name, dst_side, &name);\n \t\t\telse\n-\t\t\t\tmatch = match_name_with_pattern(dst_side, ref->name, rs[i].src, &name);\n+\t\t\t\tmatch = match_name_with_pattern(dst_side, ref->name, item->src, &name);\n \t\t\tif (match) {\n \t\t\t\tmatching_refs = i;\n \t\t\t\tbreak;\n@@ -1112,7 +1114,7 @@ static char *get_ref_match(const struct refspec_item *rs, int rs_nr, const struc\n \tif (matching_refs == -1)\n \t\treturn NULL;\n \n-\tpat = rs + matching_refs;\n+\tpat = &rs->items[matching_refs];\n \tif (pat->matching) {\n \t\t/*\n \t\t * \"matching refs\"; traditionally we pushed everything\n@@ -1309,7 +1311,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\tconst struct refspec_item *pat = NULL;\n \t\tchar *dst_name;\n \n-\t\tdst_name = get_ref_match(rs.items, rs.nr, ref, send_mirror, FROM_SRC, &pat);\n+\t\tdst_name = get_ref_match(&rs, ref, send_mirror, FROM_SRC, &pat);\n \t\tif (!dst_name)\n \t\t\tcontinue;\n \n@@ -1358,7 +1360,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\t\t\t/* We're already sending something to this ref. */\n \t\t\t\tcontinue;\n \n-\t\t\tsrc_name = get_ref_match(rs.items, rs.nr, ref, send_mirror, FROM_DST, NULL);\n+\t\t\tsrc_name = get_ref_match(&rs, ref, send_mirror, FROM_DST, NULL);\n \t\t\tif (src_name) {\n \t\t\t\tif (!src_ref_index.nr)\n \t\t\t\t\tprepare_ref_index(&src_ref_index, src);\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347870","messageId":"20180516225823.235426-28-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 27/36] remote: convert match_explicit_refs to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:14Z","receivedAt":"2018-05-16T22:59:47Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'match_explicit_refs()' to take a 'struct refspec' as a\nparameter instead of a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n remote.c | 9 ++++-----\n 1 file changed, 4 insertions(+), 5 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 9eb79ea19..84dda3fd0 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1073,12 +1073,11 @@ static int match_explicit(struct ref *src, struct ref *dst,\n }\n \n static int match_explicit_refs(struct ref *src, struct ref *dst,\n-\t\t\t       struct ref ***dst_tail, struct refspec_item *rs,\n-\t\t\t       int rs_nr)\n+\t\t\t       struct ref ***dst_tail, struct refspec *rs)\n {\n \tint i, errs;\n-\tfor (i = errs = 0; i < rs_nr; i++)\n-\t\terrs += match_explicit(src, dst, dst_tail, &rs[i]);\n+\tfor (i = errs = 0; i < rs->nr; i++)\n+\t\terrs += match_explicit(src, dst, dst_tail, &rs->items[i]);\n \treturn errs;\n }\n \n@@ -1302,7 +1301,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\trefspec = default_refspec;\n \t}\n \trefspec_appendn(&rs, refspec, nr_refspec);\n-\terrs = match_explicit_refs(src, *dst, &dst_tail, rs.items, rs.nr);\n+\terrs = match_explicit_refs(src, *dst, &dst_tail, &rs);\n \n \t/* pick the remainder */\n \tfor (ref = src; ref; ref = ref->next) {\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347871","messageId":"20180516225823.235426-18-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 17/36] fetch: convert fetch_one to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:04Z","receivedAt":"2018-05-16T22:59:50Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'fetch_one()' to use 'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c | 46 +++++++++++++++++++---------------------------\n 1 file changed, 19 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 7a1637d35..18704c6cd 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -1356,10 +1356,8 @@ static inline void fetch_one_setup_partial(struct remote *remote)\n \n static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok)\n {\n-\tstatic const char **refs = NULL;\n-\tstruct refspec_item *refspec;\n-\tint ref_nr = 0;\n-\tint j = 0;\n+\tstruct refspec rs = REFSPEC_INIT_FETCH;\n+\tint i;\n \tint exit_code;\n \tint maybe_prune_tags;\n \tint remote_via_config = remote_is_configured(remote, 0);\n@@ -1394,35 +1392,29 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru\n \tif (maybe_prune_tags && remote_via_config)\n \t\trefspec_append(&remote->fetch, TAG_REFSPEC);\n \n-\tif (argc > 0 || (maybe_prune_tags && !remote_via_config)) {\n-\t\tsize_t nr_alloc = st_add3(argc, maybe_prune_tags, 1);\n-\t\trefs = xcalloc(nr_alloc, sizeof(const char *));\n-\t\tif (maybe_prune_tags) {\n-\t\t\trefs[j++] = xstrdup(\"refs/tags/*:refs/tags/*\");\n-\t\t\tref_nr++;\n-\t\t}\n-\t}\n+\tif (maybe_prune_tags && (argc || !remote_via_config))\n+\t\trefspec_append(&rs, TAG_REFSPEC);\n \n-\tif (argc > 0) {\n-\t\tint i;\n-\t\tfor (i = 0; i < argc; i++) {\n-\t\t\tif (!strcmp(argv[i], \"tag\")) {\n-\t\t\t\ti++;\n-\t\t\t\tif (i >= argc)\n-\t\t\t\t\tdie(_(\"You need to specify a tag name.\"));\n-\t\t\t\trefs[j++] = xstrfmt(\"refs/tags/%s:refs/tags/%s\",\n-\t\t\t\t\t\t    argv[i], argv[i]);\n-\t\t\t} else\n-\t\t\t\trefs[j++] = argv[i];\n-\t\t\tref_nr++;\n+\tfor (i = 0; i < argc; i++) {\n+\t\tif (!strcmp(argv[i], \"tag\")) {\n+\t\t\tchar *tag;\n+\t\t\ti++;\n+\t\t\tif (i >= argc)\n+\t\t\t\tdie(_(\"You need to specify a tag name.\"));\n+\n+\t\t\ttag = xstrfmt(\"refs/tags/%s:refs/tags/%s\",\n+\t\t\t\t      argv[i], argv[i]);\n+\t\t\trefspec_append(&rs, tag);\n+\t\t\tfree(tag);\n+\t\t} else {\n+\t\t\trefspec_append(&rs, argv[i]);\n \t\t}\n \t}\n \n \tsigchain_push_common(unlock_pack_on_signal);\n \tatexit(unlock_pack);\n-\trefspec = parse_fetch_refspec(ref_nr, refs);\n-\texit_code = do_fetch(gtransport, refspec, ref_nr);\n-\tfree_refspec(ref_nr, refspec);\n+\texit_code = do_fetch(gtransport, rs.items, rs.nr);\n+\trefspec_clear(&rs);\n \ttransport_disconnect(gtransport);\n \tgtransport = NULL;\n \treturn exit_code;\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347872","messageId":"20180516225823.235426-26-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 25/36] remote: convert query_refspecs to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:12Z","receivedAt":"2018-05-16T22:59:52Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'query_refspecs()' to take a 'struct refspec' as a parameter instead\nof a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/push.c |  3 +--\n remote.c       | 10 +++++-----\n remote.h       |  2 +-\n 3 files changed, 7 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 509dc6677..6b7e45890 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -83,8 +83,7 @@ static const char *map_refspec(const char *ref,\n \t\tstruct refspec_item query;\n \t\tmemset(&query, 0, sizeof(struct refspec_item));\n \t\tquery.src = matched->name;\n-\t\tif (!query_refspecs(remote->push.items, remote->push.nr, &query) &&\n-\t\t    query.dst) {\n+\t\tif (!query_refspecs(&remote->push, &query) && query.dst) {\n \t\t\tstruct strbuf buf = STRBUF_INIT;\n \t\t\tstrbuf_addf(&buf, \"%s%s:%s\",\n \t\t\t\t    query.force ? \"+\" : \"\",\ndiff --git a/remote.c b/remote.c\nindex 89415a263..dd68e6b22 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -725,7 +725,7 @@ static void query_refspecs_multiple(struct refspec *rs,\n \t}\n }\n \n-int query_refspecs(struct refspec_item *refs, int ref_count, struct refspec_item *query)\n+int query_refspecs(struct refspec *rs, struct refspec_item *query)\n {\n \tint i;\n \tint find_src = !query->src;\n@@ -735,8 +735,8 @@ int query_refspecs(struct refspec_item *refs, int ref_count, struct refspec_item\n \tif (find_src && !query->dst)\n \t\treturn error(\"query_refspecs: need either src or dst\");\n \n-\tfor (i = 0; i < ref_count; i++) {\n-\t\tstruct refspec_item *refspec = &refs[i];\n+\tfor (i = 0; i < rs->nr; i++) {\n+\t\tstruct refspec_item *refspec = &rs->items[i];\n \t\tconst char *key = find_src ? refspec->dst : refspec->src;\n \t\tconst char *value = find_src ? refspec->src : refspec->dst;\n \n@@ -763,7 +763,7 @@ char *apply_refspecs(struct refspec *rs, const char *name)\n \tmemset(&query, 0, sizeof(struct refspec_item));\n \tquery.src = (char *)name;\n \n-\tif (query_refspecs(rs->items, rs->nr, &query))\n+\tif (query_refspecs(rs, &query))\n \t\treturn NULL;\n \n \treturn query.dst;\n@@ -771,7 +771,7 @@ char *apply_refspecs(struct refspec *rs, const char *name)\n \n int remote_find_tracking(struct remote *remote, struct refspec_item *refspec)\n {\n-\treturn query_refspecs(remote->fetch.items, remote->fetch.nr, refspec);\n+\treturn query_refspecs(&remote->fetch, refspec);\n }\n \n static struct ref *alloc_ref_with_prefix(const char *prefix, size_t prefixlen,\ndiff --git a/remote.h b/remote.h\nindex 0b1fcc051..9050ff75a 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -158,7 +158,7 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);\n  */\n struct ref *ref_remove_duplicates(struct ref *ref_map);\n \n-extern int query_refspecs(struct refspec_item *specs, int nr, struct refspec_item *query);\n+int query_refspecs(struct refspec *rs, struct refspec_item *query);\n char *apply_refspecs(struct refspec *rs, const char *name);\n \n int check_push_refs(struct ref *src, int nr_refspec, const char **refspec);\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347873","messageId":"20180516225823.235426-29-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 28/36] push: check for errors earlier","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:15Z","receivedAt":"2018-05-16T22:59:54Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Move the error checking for using the \"--mirror\", \"--all\", and \"--tags\"\noptions earlier and explicitly check for the presence of the flags\ninstead of checking for a side-effect of the flag.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/push.c | 31 ++++++++++++++-----------------\n 1 file changed, 14 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 6b7e45890..df5df6c0d 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -417,23 +417,6 @@ static int do_push(const char *repo, int flags,\n \tif (push_options->nr)\n \t\tflags |= TRANSPORT_PUSH_OPTIONS;\n \n-\tif ((flags & TRANSPORT_PUSH_ALL) && refspec) {\n-\t\tif (!strcmp(*refspec, \"refs/tags/*\"))\n-\t\t\treturn error(_(\"--all and --tags are incompatible\"));\n-\t\treturn error(_(\"--all can't be combined with refspecs\"));\n-\t}\n-\n-\tif ((flags & TRANSPORT_PUSH_MIRROR) && refspec) {\n-\t\tif (!strcmp(*refspec, \"refs/tags/*\"))\n-\t\t\treturn error(_(\"--mirror and --tags are incompatible\"));\n-\t\treturn error(_(\"--mirror can't be combined with refspecs\"));\n-\t}\n-\n-\tif ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==\n-\t\t\t\t(TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {\n-\t\treturn error(_(\"--all and --mirror are incompatible\"));\n-\t}\n-\n \tif (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {\n \t\tif (remote->push.raw_nr) {\n \t\t\trefspec = remote->push.raw;\n@@ -625,6 +608,20 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tdie(_(\"--delete is incompatible with --all, --mirror and --tags\"));\n \tif (deleterefs && argc < 2)\n \t\tdie(_(\"--delete doesn't make sense without any refs\"));\n+\tif (flags & TRANSPORT_PUSH_ALL) {\n+\t\tif (tags)\n+\t\t\tdie(_(\"--all and --tags are incompatible\"));\n+\t\tif (argc >= 2)\n+\t\t\tdie(_(\"--all can't be combined with refspecs\"));\n+\t}\n+\tif (flags & TRANSPORT_PUSH_MIRROR) {\n+\t\tif (tags)\n+\t\t\tdie(_(\"--mirror and --tags are incompatible\"));\n+\t\tif (argc >= 2)\n+\t\t\tdie(_(\"--mirror can't be combined with refspecs\"));\n+\t}\n+\tif ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR))\n+\t\tdie(_(\"--all and --mirror are incompatible\"));\n \n \tif (recurse_submodules == RECURSE_SUBMODULES_CHECK)\n \t\tflags |= TRANSPORT_RECURSE_SUBMODULES_CHECK;\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347874","messageId":"20180516225823.235426-33-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 32/36] transport: remove transport_verify_remote_names","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:19Z","receivedAt":"2018-05-16T23:00:02Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Remove 'transprot_verify_remote_names()' because all callers have\nmigrated to using 'struct refspec' which performs the same checks in\n'parse_refspec()'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/send-pack.c |  2 --\n transport.c         | 24 ------------------------\n transport.h         |  2 --\n 3 files changed, 28 deletions(-)\n\ndiff --git a/builtin/send-pack.c b/builtin/send-pack.c\nindex ef512616f..7c34bf467 100644\n--- a/builtin/send-pack.c\n+++ b/builtin/send-pack.c\n@@ -265,8 +265,6 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \t\tBUG(\"unknown protocol version\");\n \t}\n \n-\ttransport_verify_remote_names(rs.raw_nr, rs.raw);\n-\n \tlocal_refs = get_local_heads();\n \n \tflags = MATCH_REFS_NONE;\ndiff --git a/transport.c b/transport.c\nindex a89f17744..fe96c0b80 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -619,29 +619,6 @@ void transport_print_push_status(const char *dest, struct ref *refs,\n \tfree(head);\n }\n \n-void transport_verify_remote_names(int nr_heads, const char **heads)\n-{\n-\tint i;\n-\n-\tfor (i = 0; i < nr_heads; i++) {\n-\t\tconst char *local = heads[i];\n-\t\tconst char *remote = strrchr(heads[i], ':');\n-\n-\t\tif (*local == '+')\n-\t\t\tlocal++;\n-\n-\t\t/* A matching refspec is okay.  */\n-\t\tif (remote == local && remote[1] == '\\0')\n-\t\t\tcontinue;\n-\n-\t\tremote = remote ? (remote + 1) : local;\n-\t\tif (check_refname_format(remote,\n-\t\t\t\tREFNAME_ALLOW_ONELEVEL|REFNAME_REFSPEC_PATTERN))\n-\t\t\tdie(\"remote part of refspec is not a valid name in %s\",\n-\t\t\t\theads[i]);\n-\t}\n-}\n-\n static int git_transport_push(struct transport *transport, struct ref *remote_refs, int flags)\n {\n \tstruct git_transport_data *data = transport->data;\n@@ -1097,7 +1074,6 @@ int transport_push(struct transport *transport,\n \t\t   unsigned int *reject_reasons)\n {\n \t*reject_reasons = 0;\n-\ttransport_verify_remote_names(rs->raw_nr, rs->raw);\n \n \tif (transport_color_config() < 0)\n \t\treturn -1;\ndiff --git a/transport.h b/transport.h\nindex e2c809af4..bac085ce0 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -227,8 +227,6 @@ int transport_helper_init(struct transport *transport, const char *name);\n int bidirectional_transfer_loop(int input, int output);\n \n /* common methods used by transport.c and builtin/send-pack.c */\n-void transport_verify_remote_names(int nr_heads, const char **heads);\n-\n void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose);\n \n int transport_refs_pushed(struct ref *ref);\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347875","messageId":"20180516225823.235426-35-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 34/36] remote: convert match_push_refs to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:21Z","receivedAt":"2018-05-16T23:00:03Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'match_push_refs()' to take a 'struct refspec' as a parameter\ninstead of an array of 'const char *'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/remote.c    |  3 +--\n builtin/send-pack.c |  2 +-\n http-push.c         |  3 +--\n remote.c            | 21 ++++++++-------------\n remote.h            |  2 +-\n transport.c         |  4 +---\n 6 files changed, 13 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex b8e66589f..b84175cc6 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -387,8 +387,7 @@ static int get_push_ref_states(const struct ref *remote_refs,\n \tlocal_refs = get_local_heads();\n \tpush_map = copy_ref_list(remote_refs);\n \n-\tmatch_push_refs(local_refs, &push_map, remote->push.raw_nr,\n-\t\t\tremote->push.raw, MATCH_REFS_NONE);\n+\tmatch_push_refs(local_refs, &push_map, &remote->push, MATCH_REFS_NONE);\n \n \tstates->push.strdup_strings = 1;\n \tfor (ref = push_map; ref; ref = ref->next) {\ndiff --git a/builtin/send-pack.c b/builtin/send-pack.c\nindex 7c34bf467..4923b1058 100644\n--- a/builtin/send-pack.c\n+++ b/builtin/send-pack.c\n@@ -275,7 +275,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \t\tflags |= MATCH_REFS_MIRROR;\n \n \t/* match them up */\n-\tif (match_push_refs(local_refs, &remote_refs, rs.raw_nr, rs.raw, flags))\n+\tif (match_push_refs(local_refs, &remote_refs, &rs, flags))\n \t\treturn -1;\n \n \tif (!is_empty_cas(&cas))\ndiff --git a/http-push.c b/http-push.c\nindex a724ef03f..ea5af6227 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1823,8 +1823,7 @@ int cmd_main(int argc, const char **argv)\n \t}\n \n \t/* match them up */\n-\tif (match_push_refs(local_refs, &remote_refs,\n-\t\t\t    rs.raw_nr, rs.raw, push_all)) {\n+\tif (match_push_refs(local_refs, &remote_refs, &rs, push_all)) {\n \t\trc = -1;\n \t\tgoto cleanup;\n \t}\ndiff --git a/remote.c b/remote.c\nindex 84dda3fd0..0046d4e28 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1285,23 +1285,20 @@ int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)\n  * dst (e.g. pushing to a new branch, done in match_explicit_refs).\n  */\n int match_push_refs(struct ref *src, struct ref **dst,\n-\t\t    int nr_refspec, const char **refspec, int flags)\n+\t\t    struct refspec *rs, int flags)\n {\n-\tstruct refspec rs = REFSPEC_INIT_PUSH;\n \tint send_all = flags & MATCH_REFS_ALL;\n \tint send_mirror = flags & MATCH_REFS_MIRROR;\n \tint send_prune = flags & MATCH_REFS_PRUNE;\n \tint errs;\n-\tstatic const char *default_refspec[] = { \":\", NULL };\n \tstruct ref *ref, **dst_tail = tail_ref(dst);\n \tstruct string_list dst_ref_index = STRING_LIST_INIT_NODUP;\n \n-\tif (!nr_refspec) {\n-\t\tnr_refspec = 1;\n-\t\trefspec = default_refspec;\n-\t}\n-\trefspec_appendn(&rs, refspec, nr_refspec);\n-\terrs = match_explicit_refs(src, *dst, &dst_tail, &rs);\n+\t/* If no refspec is provided, use the default \":\" */\n+\tif (!rs->nr)\n+\t\trefspec_append(rs, \":\");\n+\n+\terrs = match_explicit_refs(src, *dst, &dst_tail, rs);\n \n \t/* pick the remainder */\n \tfor (ref = src; ref; ref = ref->next) {\n@@ -1310,7 +1307,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\tconst struct refspec_item *pat = NULL;\n \t\tchar *dst_name;\n \n-\t\tdst_name = get_ref_match(&rs, ref, send_mirror, FROM_SRC, &pat);\n+\t\tdst_name = get_ref_match(rs, ref, send_mirror, FROM_SRC, &pat);\n \t\tif (!dst_name)\n \t\t\tcontinue;\n \n@@ -1359,7 +1356,7 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\t\t\t/* We're already sending something to this ref. */\n \t\t\t\tcontinue;\n \n-\t\t\tsrc_name = get_ref_match(&rs, ref, send_mirror, FROM_DST, NULL);\n+\t\t\tsrc_name = get_ref_match(rs, ref, send_mirror, FROM_DST, NULL);\n \t\t\tif (src_name) {\n \t\t\t\tif (!src_ref_index.nr)\n \t\t\t\t\tprepare_ref_index(&src_ref_index, src);\n@@ -1372,8 +1369,6 @@ int match_push_refs(struct ref *src, struct ref **dst,\n \t\tstring_list_clear(&src_ref_index, 0);\n \t}\n \n-\trefspec_clear(&rs);\n-\n \tif (errs)\n \t\treturn -1;\n \treturn 0;\ndiff --git a/remote.h b/remote.h\nindex 9050ff75a..74c557457 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -163,7 +163,7 @@ char *apply_refspecs(struct refspec *rs, const char *name);\n \n int check_push_refs(struct ref *src, int nr_refspec, const char **refspec);\n int match_push_refs(struct ref *src, struct ref **dst,\n-\t\t    int nr_refspec, const char **refspec, int all);\n+\t\t    struct refspec *rs, int flags);\n void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\n \tint force_update);\n \ndiff --git a/transport.c b/transport.c\nindex fe96c0b80..24a97d9e8 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1127,10 +1127,8 @@ int transport_push(struct transport *transport,\n \t\tif (flags & TRANSPORT_PUSH_FOLLOW_TAGS)\n \t\t\tmatch_flags |= MATCH_REFS_FOLLOW_TAGS;\n \n-\t\tif (match_push_refs(local_refs, &remote_refs,\n-\t\t\t\t    rs->raw_nr, rs->raw, match_flags)) {\n+\t\tif (match_push_refs(local_refs, &remote_refs, rs, match_flags))\n \t\t\treturn -1;\n-\t\t}\n \n \t\tif (transport->smart_options &&\n \t\t    transport->smart_options->cas &&\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347876","messageId":"20180516225823.235426-36-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 35/36] remote: convert check_push_refs to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:22Z","receivedAt":"2018-05-16T23:00:06Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'check_push_refs()' to take a 'struct refspec' as a parameter\ninstead of an array of 'const char *'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n remote.c    | 14 +++++---------\n remote.h    |  2 +-\n transport.c |  2 +-\n 3 files changed, 7 insertions(+), 11 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex 0046d4e28..0d1a3d07f 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1255,24 +1255,20 @@ static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)\n  * but we can catch some errors early before even talking to the\n  * remote side.\n  */\n-int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)\n+int check_push_refs(struct ref *src, struct refspec *rs)\n {\n-\tstruct refspec refspec = REFSPEC_INIT_PUSH;\n \tint ret = 0;\n \tint i;\n \n-\trefspec_appendn(&refspec, refspec_names, nr_refspec);\n-\n-\tfor (i = 0; i < refspec.nr; i++) {\n-\t\tstruct refspec_item *rs = &refspec.items[i];\n+\tfor (i = 0; i < rs->nr; i++) {\n+\t\tstruct refspec_item *item = &rs->items[i];\n \n-\t\tif (rs->pattern || rs->matching)\n+\t\tif (item->pattern || item->matching)\n \t\t\tcontinue;\n \n-\t\tret |= match_explicit_lhs(src, rs, NULL, NULL);\n+\t\tret |= match_explicit_lhs(src, item, NULL, NULL);\n \t}\n \n-\trefspec_clear(&refspec);\n \treturn ret;\n }\n \ndiff --git a/remote.h b/remote.h\nindex 74c557457..62a656659 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -161,7 +161,7 @@ struct ref *ref_remove_duplicates(struct ref *ref_map);\n int query_refspecs(struct refspec *rs, struct refspec_item *query);\n char *apply_refspecs(struct refspec *rs, const char *name);\n \n-int check_push_refs(struct ref *src, int nr_refspec, const char **refspec);\n+int check_push_refs(struct ref *src, struct refspec *rs);\n int match_push_refs(struct ref *src, struct ref **dst,\n \t\t    struct refspec *rs, int flags);\n void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,\ndiff --git a/transport.c b/transport.c\nindex 24a97d9e8..e32bc320c 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1090,7 +1090,7 @@ int transport_push(struct transport *transport,\n \t\tstruct argv_array ref_prefixes = ARGV_ARRAY_INIT;\n \t\tint i;\n \n-\t\tif (check_push_refs(local_refs, rs->raw_nr, rs->raw) < 0)\n+\t\tif (check_push_refs(local_refs, rs) < 0)\n \t\t\treturn -1;\n \n \t\tfor (i = 0; i < rs->nr; i++) {\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347877","messageId":"20180516225823.235426-34-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 33/36] http-push: store refspecs in a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:20Z","receivedAt":"2018-05-16T23:00:11Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert http-push.c to store refspecs in a 'struct refspec' instead of\nin an array of 'const char *'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n http-push.c | 17 ++++++++---------\n 1 file changed, 8 insertions(+), 9 deletions(-)\n\ndiff --git a/http-push.c b/http-push.c\nindex f308ce019..a724ef03f 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1692,8 +1692,7 @@ int cmd_main(int argc, const char **argv)\n {\n \tstruct transfer_request *request;\n \tstruct transfer_request *next_request;\n-\tint nr_refspec = 0;\n-\tconst char **refspec = NULL;\n+\tstruct refspec rs = REFSPEC_INIT_PUSH;\n \tstruct remote_lock *ref_lock = NULL;\n \tstruct remote_lock *info_ref_lock = NULL;\n \tstruct rev_info revs;\n@@ -1756,8 +1755,7 @@ int cmd_main(int argc, const char **argv)\n \t\t\t}\n \t\t\tcontinue;\n \t\t}\n-\t\trefspec = argv;\n-\t\tnr_refspec = argc - i;\n+\t\trefspec_appendn(&rs, argv, argc - i);\n \t\tbreak;\n \t}\n \n@@ -1768,7 +1766,7 @@ int cmd_main(int argc, const char **argv)\n \tif (!repo->url)\n \t\tusage(http_push_usage);\n \n-\tif (delete_branch && nr_refspec != 1)\n+\tif (delete_branch && rs.nr != 1)\n \t\tdie(\"You must specify only one branch name when deleting a remote branch\");\n \n \tsetup_git_directory();\n@@ -1814,18 +1812,19 @@ int cmd_main(int argc, const char **argv)\n \n \t/* Remove a remote branch if -d or -D was specified */\n \tif (delete_branch) {\n-\t\tif (delete_remote_branch(refspec[0], force_delete) == -1) {\n+\t\tconst char *branch = rs.items[i].src;\n+\t\tif (delete_remote_branch(branch, force_delete) == -1) {\n \t\t\tfprintf(stderr, \"Unable to delete remote branch %s\\n\",\n-\t\t\t\trefspec[0]);\n+\t\t\t\tbranch);\n \t\t\tif (helper_status)\n-\t\t\t\tprintf(\"error %s cannot remove\\n\", refspec[0]);\n+\t\t\t\tprintf(\"error %s cannot remove\\n\", branch);\n \t\t}\n \t\tgoto cleanup;\n \t}\n \n \t/* match them up */\n \tif (match_push_refs(local_refs, &remote_refs,\n-\t\t\t    nr_refspec, (const char **) refspec, push_all)) {\n+\t\t\t    rs.raw_nr, rs.raw, push_all)) {\n \t\trc = -1;\n \t\tgoto cleanup;\n \t}\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347878","messageId":"20180516225823.235426-37-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 36/36] submodule: convert push_unpushed_submodules to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:23Z","receivedAt":"2018-05-16T23:00:13Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'push_unpushed_submodules()' to take a 'struct refspec' as a\nparameter instead of an array of 'const char *'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n submodule.c | 19 +++++++++----------\n submodule.h |  3 ++-\n transport.c |  2 +-\n 3 files changed, 12 insertions(+), 12 deletions(-)\n\ndiff --git a/submodule.c b/submodule.c\nindex 74d35b257..cdeadd80e 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -968,7 +968,7 @@ int find_unpushed_submodules(struct oid_array *commits,\n \n static int push_submodule(const char *path,\n \t\t\t  const struct remote *remote,\n-\t\t\t  const char **refspec, int refspec_nr,\n+\t\t\t  const struct refspec *rs,\n \t\t\t  const struct string_list *push_options,\n \t\t\t  int dry_run)\n {\n@@ -991,8 +991,8 @@ static int push_submodule(const char *path,\n \t\tif (remote->origin != REMOTE_UNCONFIGURED) {\n \t\t\tint i;\n \t\t\targv_array_push(&cp.args, remote->name);\n-\t\t\tfor (i = 0; i < refspec_nr; i++)\n-\t\t\t\targv_array_push(&cp.args, refspec[i]);\n+\t\t\tfor (i = 0; i < rs->raw_nr; i++)\n+\t\t\t\targv_array_push(&cp.args, rs->raw[i]);\n \t\t}\n \n \t\tprepare_submodule_repo_env(&cp.env_array);\n@@ -1013,7 +1013,7 @@ static int push_submodule(const char *path,\n  */\n static void submodule_push_check(const char *path, const char *head,\n \t\t\t\t const struct remote *remote,\n-\t\t\t\t const char **refspec, int refspec_nr)\n+\t\t\t\t const struct refspec *rs)\n {\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tint i;\n@@ -1023,8 +1023,8 @@ static void submodule_push_check(const char *path, const char *head,\n \targv_array_push(&cp.args, head);\n \targv_array_push(&cp.args, remote->name);\n \n-\tfor (i = 0; i < refspec_nr; i++)\n-\t\targv_array_push(&cp.args, refspec[i]);\n+\tfor (i = 0; i < rs->raw_nr; i++)\n+\t\targv_array_push(&cp.args, rs->raw[i]);\n \n \tprepare_submodule_repo_env(&cp.env_array);\n \tcp.git_cmd = 1;\n@@ -1043,7 +1043,7 @@ static void submodule_push_check(const char *path, const char *head,\n \n int push_unpushed_submodules(struct oid_array *commits,\n \t\t\t     const struct remote *remote,\n-\t\t\t     const char **refspec, int refspec_nr,\n+\t\t\t     const struct refspec *rs,\n \t\t\t     const struct string_list *push_options,\n \t\t\t     int dry_run)\n {\n@@ -1069,8 +1069,7 @@ int push_unpushed_submodules(struct oid_array *commits,\n \n \t\tfor (i = 0; i < needs_pushing.nr; i++)\n \t\t\tsubmodule_push_check(needs_pushing.items[i].string,\n-\t\t\t\t\t     head, remote,\n-\t\t\t\t\t     refspec, refspec_nr);\n+\t\t\t\t\t     head, remote, rs);\n \t\tfree(head);\n \t}\n \n@@ -1078,7 +1077,7 @@ int push_unpushed_submodules(struct oid_array *commits,\n \tfor (i = 0; i < needs_pushing.nr; i++) {\n \t\tconst char *path = needs_pushing.items[i].string;\n \t\tfprintf(stderr, \"Pushing submodule '%s'\\n\", path);\n-\t\tif (!push_submodule(path, remote, refspec, refspec_nr,\n+\t\tif (!push_submodule(path, remote, rs,\n \t\t\t\t    push_options, dry_run)) {\n \t\t\tfprintf(stderr, \"Unable to push submodule '%s'\\n\", path);\n \t\t\tret = 0;\ndiff --git a/submodule.h b/submodule.h\nindex e5526f6aa..aae0c9c8f 100644\n--- a/submodule.h\n+++ b/submodule.h\n@@ -100,9 +100,10 @@ extern int submodule_touches_in_range(struct object_id *a,\n extern int find_unpushed_submodules(struct oid_array *commits,\n \t\t\t\t    const char *remotes_name,\n \t\t\t\t    struct string_list *needs_pushing);\n+struct refspec;\n extern int push_unpushed_submodules(struct oid_array *commits,\n \t\t\t\t    const struct remote *remote,\n-\t\t\t\t    const char **refspec, int refspec_nr,\n+\t\t\t\t    const struct refspec *rs,\n \t\t\t\t    const struct string_list *push_options,\n \t\t\t\t    int dry_run);\n /*\ndiff --git a/transport.c b/transport.c\nindex e32bc320c..7e0b9abba 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1157,7 +1157,7 @@ int transport_push(struct transport *transport,\n \n \t\t\tif (!push_unpushed_submodules(&commits,\n \t\t\t\t\t\t      transport->remote,\n-\t\t\t\t\t\t      rs->raw, rs->raw_nr,\n+\t\t\t\t\t\t      rs,\n \t\t\t\t\t\t      transport->push_options,\n \t\t\t\t\t\t      pretend)) {\n \t\t\t\toid_array_clear(&commits);\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347879","messageId":"20180516225823.235426-31-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 30/36] transport: convert transport_push to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:17Z","receivedAt":"2018-05-16T23:00:21Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'transport_push()' to take a 'struct refspec' as a\nparameter instead of an array of strings which represent\nrefspecs.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/push.c |  3 +--\n transport.c    | 17 +++++++----------\n transport.h    |  2 +-\n 3 files changed, 9 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/push.c b/builtin/push.c\nindex ef42979d1..9cd8e8cd5 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -355,8 +355,7 @@ static int push_with_options(struct transport *transport, struct refspec *rs,\n \n \tif (verbosity > 0)\n \t\tfprintf(stderr, _(\"Pushing to %s\\n\"), transport->url);\n-\terr = transport_push(transport, rs->raw_nr, rs->raw, flags,\n-\t\t\t     &reject_reasons);\n+\terr = transport_push(transport, rs, flags, &reject_reasons);\n \tif (err != 0) {\n \t\tfprintf(stderr, \"%s\", push_get_color(PUSH_COLOR_ERROR));\n \t\terror(_(\"failed to push some refs to '%s'\"), transport->url);\ndiff --git a/transport.c b/transport.c\nindex 181db4d4d..a89f17744 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1093,11 +1093,11 @@ static int run_pre_push_hook(struct transport *transport,\n }\n \n int transport_push(struct transport *transport,\n-\t\t   int refspec_nr, const char **refspec, int flags,\n+\t\t   struct refspec *rs, int flags,\n \t\t   unsigned int *reject_reasons)\n {\n \t*reject_reasons = 0;\n-\ttransport_verify_remote_names(refspec_nr, refspec);\n+\ttransport_verify_remote_names(rs->raw_nr, rs->raw);\n \n \tif (transport_color_config() < 0)\n \t\treturn -1;\n@@ -1111,16 +1111,14 @@ int transport_push(struct transport *transport,\n \t\tint porcelain = flags & TRANSPORT_PUSH_PORCELAIN;\n \t\tint pretend = flags & TRANSPORT_PUSH_DRY_RUN;\n \t\tint push_ret, ret, err;\n-\t\tstruct refspec tmp_rs = REFSPEC_INIT_PUSH;\n \t\tstruct argv_array ref_prefixes = ARGV_ARRAY_INIT;\n \t\tint i;\n \n-\t\tif (check_push_refs(local_refs, refspec_nr, refspec) < 0)\n+\t\tif (check_push_refs(local_refs, rs->raw_nr, rs->raw) < 0)\n \t\t\treturn -1;\n \n-\t\trefspec_appendn(&tmp_rs, refspec, refspec_nr);\n-\t\tfor (i = 0; i < tmp_rs.nr; i++) {\n-\t\t\tconst struct refspec_item *item = &tmp_rs.items[i];\n+\t\tfor (i = 0; i < rs->nr; i++) {\n+\t\t\tconst struct refspec_item *item = &rs->items[i];\n \t\t\tconst char *prefix = NULL;\n \n \t\t\tif (item->dst)\n@@ -1143,7 +1141,6 @@ int transport_push(struct transport *transport,\n \t\t\t\t\t\t\t       &ref_prefixes);\n \n \t\targv_array_clear(&ref_prefixes);\n-\t\trefspec_clear(&tmp_rs);\n \n \t\tif (flags & TRANSPORT_PUSH_ALL)\n \t\t\tmatch_flags |= MATCH_REFS_ALL;\n@@ -1155,7 +1152,7 @@ int transport_push(struct transport *transport,\n \t\t\tmatch_flags |= MATCH_REFS_FOLLOW_TAGS;\n \n \t\tif (match_push_refs(local_refs, &remote_refs,\n-\t\t\t\t    refspec_nr, refspec, match_flags)) {\n+\t\t\t\t    rs->raw_nr, rs->raw, match_flags)) {\n \t\t\treturn -1;\n \t\t}\n \n@@ -1186,7 +1183,7 @@ int transport_push(struct transport *transport,\n \n \t\t\tif (!push_unpushed_submodules(&commits,\n \t\t\t\t\t\t      transport->remote,\n-\t\t\t\t\t\t      refspec, refspec_nr,\n+\t\t\t\t\t\t      rs->raw, rs->raw_nr,\n \t\t\t\t\t\t      transport->push_options,\n \t\t\t\t\t\t      pretend)) {\n \t\t\t\toid_array_clear(&commits);\ndiff --git a/transport.h b/transport.h\nindex e783cfa07..e2c809af4 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -197,7 +197,7 @@ void transport_set_verbosity(struct transport *transport, int verbosity,\n #define REJECT_NEEDS_FORCE     0x10\n \n int transport_push(struct transport *connection,\n-\t\t   int refspec_nr, const char **refspec, int flags,\n+\t\t   struct refspec *rs, int flags,\n \t\t   unsigned int * reject_reasons);\n \n /*\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347880","messageId":"20180516225823.235426-32-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 31/36] send-pack: store refspecs in a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:18Z","receivedAt":"2018-05-16T23:00:31Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert send-pack.c to store refspecs in a 'struct refspec' instead of\nas an array of 'const char *'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/send-pack.c | 24 +++++++-----------------\n 1 file changed, 7 insertions(+), 17 deletions(-)\n\ndiff --git a/builtin/send-pack.c b/builtin/send-pack.c\nindex b5427f75e..ef512616f 100644\n--- a/builtin/send-pack.c\n+++ b/builtin/send-pack.c\n@@ -126,8 +126,7 @@ static int send_pack_config(const char *k, const char *v, void *cb)\n \n int cmd_send_pack(int argc, const char **argv, const char *prefix)\n {\n-\tint i, nr_refspecs = 0;\n-\tconst char **refspecs = NULL;\n+\tstruct refspec rs = REFSPEC_INIT_PUSH;\n \tconst char *remote_name = NULL;\n \tstruct remote *remote = NULL;\n \tconst char *dest = NULL;\n@@ -189,8 +188,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options, send_pack_usage, 0);\n \tif (argc > 0) {\n \t\tdest = argv[0];\n-\t\trefspecs = (const char **)(argv + 1);\n-\t\tnr_refspecs = argc - 1;\n+\t\trefspec_appendn(&rs, argv + 1, argc - 1);\n \t}\n \n \tif (!dest)\n@@ -209,31 +207,23 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \targs.push_options = push_options.nr ? &push_options : NULL;\n \n \tif (from_stdin) {\n-\t\tstruct argv_array all_refspecs = ARGV_ARRAY_INIT;\n-\n-\t\tfor (i = 0; i < nr_refspecs; i++)\n-\t\t\targv_array_push(&all_refspecs, refspecs[i]);\n-\n \t\tif (args.stateless_rpc) {\n \t\t\tconst char *buf;\n \t\t\twhile ((buf = packet_read_line(0, NULL)))\n-\t\t\t\targv_array_push(&all_refspecs, buf);\n+\t\t\t\trefspec_append(&rs, buf);\n \t\t} else {\n \t\t\tstruct strbuf line = STRBUF_INIT;\n \t\t\twhile (strbuf_getline(&line, stdin) != EOF)\n-\t\t\t\targv_array_push(&all_refspecs, line.buf);\n+\t\t\t\trefspec_append(&rs, line.buf);\n \t\t\tstrbuf_release(&line);\n \t\t}\n-\n-\t\trefspecs = all_refspecs.argv;\n-\t\tnr_refspecs = all_refspecs.argc;\n \t}\n \n \t/*\n \t * --all and --mirror are incompatible; neither makes sense\n \t * with any refspecs.\n \t */\n-\tif ((nr_refspecs > 0 && (send_all || args.send_mirror)) ||\n+\tif ((rs.nr > 0 && (send_all || args.send_mirror)) ||\n \t    (send_all && args.send_mirror))\n \t\tusage_with_options(send_pack_usage, options);\n \n@@ -275,7 +265,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \t\tBUG(\"unknown protocol version\");\n \t}\n \n-\ttransport_verify_remote_names(nr_refspecs, refspecs);\n+\ttransport_verify_remote_names(rs.raw_nr, rs.raw);\n \n \tlocal_refs = get_local_heads();\n \n@@ -287,7 +277,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)\n \t\tflags |= MATCH_REFS_MIRROR;\n \n \t/* match them up */\n-\tif (match_push_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags))\n+\tif (match_push_refs(local_refs, &remote_refs, rs.raw_nr, rs.raw, flags))\n \t\treturn -1;\n \n \tif (!is_empty_cas(&cas))\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347881","messageId":"20180516225823.235426-30-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 29/36] push: convert to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:16Z","receivedAt":"2018-05-16T23:00:37Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert the refspecs in builtin/push.c to be stored in a 'struct\nrefspec' instead of being stored in a list of 'struct refspec_item's.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/push.c | 38 +++++++++++++++-----------------------\n 1 file changed, 15 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/push.c b/builtin/push.c\nindex df5df6c0d..ef42979d1 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -57,19 +57,10 @@ static enum transport_family family;\n \n static struct push_cas_option cas;\n \n-static const char **refspec;\n-static int refspec_nr;\n-static int refspec_alloc;\n+static struct refspec rs = REFSPEC_INIT_PUSH;\n \n static struct string_list push_options_config = STRING_LIST_INIT_DUP;\n \n-static void add_refspec(const char *ref)\n-{\n-\trefspec_nr++;\n-\tALLOC_GROW(refspec, refspec_nr, refspec_alloc);\n-\trefspec[refspec_nr-1] = ref;\n-}\n-\n static const char *map_refspec(const char *ref,\n \t\t\t       struct remote *remote, struct ref *local_refs)\n {\n@@ -138,7 +129,7 @@ static void set_refspecs(const char **refs, int nr, const char *repo)\n \t\t\t}\n \t\t\tref = map_refspec(ref, remote, local_refs);\n \t\t}\n-\t\tadd_refspec(ref);\n+\t\trefspec_append(&rs, ref);\n \t}\n }\n \n@@ -226,7 +217,7 @@ static void setup_push_upstream(struct remote *remote, struct branch *branch,\n \t}\n \n \tstrbuf_addf(&refspec, \"%s:%s\", branch->refname, branch->merge[0]->src);\n-\tadd_refspec(refspec.buf);\n+\trefspec_append(&rs, refspec.buf);\n }\n \n static void setup_push_current(struct remote *remote, struct branch *branch)\n@@ -236,7 +227,7 @@ static void setup_push_current(struct remote *remote, struct branch *branch)\n \tif (!branch)\n \t\tdie(_(message_detached_head_die), remote->name);\n \tstrbuf_addf(&refspec, \"%s:%s\", branch->refname, branch->refname);\n-\tadd_refspec(refspec.buf);\n+\trefspec_append(&rs, refspec.buf);\n }\n \n static int is_workflow_triangular(struct remote *remote)\n@@ -253,7 +244,7 @@ static void setup_default_push_refspecs(struct remote *remote)\n \tswitch (push_default) {\n \tdefault:\n \tcase PUSH_DEFAULT_MATCHING:\n-\t\tadd_refspec(\":\");\n+\t\trefspec_append(&rs, \":\");\n \t\tbreak;\n \n \tcase PUSH_DEFAULT_UNSPECIFIED:\n@@ -341,7 +332,8 @@ static void advise_ref_needs_force(void)\n \tadvise(_(message_advice_ref_needs_force));\n }\n \n-static int push_with_options(struct transport *transport, int flags)\n+static int push_with_options(struct transport *transport, struct refspec *rs,\n+\t\t\t     int flags)\n {\n \tint err;\n \tunsigned int reject_reasons;\n@@ -363,7 +355,7 @@ static int push_with_options(struct transport *transport, int flags)\n \n \tif (verbosity > 0)\n \t\tfprintf(stderr, _(\"Pushing to %s\\n\"), transport->url);\n-\terr = transport_push(transport, refspec_nr, refspec, flags,\n+\terr = transport_push(transport, rs->raw_nr, rs->raw, flags,\n \t\t\t     &reject_reasons);\n \tif (err != 0) {\n \t\tfprintf(stderr, \"%s\", push_get_color(PUSH_COLOR_ERROR));\n@@ -397,6 +389,7 @@ static int do_push(const char *repo, int flags,\n \tstruct remote *remote = pushremote_get(repo);\n \tconst char **url;\n \tint url_nr;\n+\tstruct refspec *push_refspec = &rs;\n \n \tif (!remote) {\n \t\tif (repo)\n@@ -417,10 +410,9 @@ static int do_push(const char *repo, int flags,\n \tif (push_options->nr)\n \t\tflags |= TRANSPORT_PUSH_OPTIONS;\n \n-\tif (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {\n-\t\tif (remote->push.raw_nr) {\n-\t\t\trefspec = remote->push.raw;\n-\t\t\trefspec_nr = remote->push.raw_nr;\n+\tif (!push_refspec->nr && !(flags & TRANSPORT_PUSH_ALL)) {\n+\t\tif (remote->push.nr) {\n+\t\t\tpush_refspec = &remote->push;\n \t\t} else if (!(flags & TRANSPORT_PUSH_MIRROR))\n \t\t\tsetup_default_push_refspecs(remote);\n \t}\n@@ -432,7 +424,7 @@ static int do_push(const char *repo, int flags,\n \t\t\t\ttransport_get(remote, url[i]);\n \t\t\tif (flags & TRANSPORT_PUSH_OPTIONS)\n \t\t\t\ttransport->push_options = push_options;\n-\t\t\tif (push_with_options(transport, flags))\n+\t\t\tif (push_with_options(transport, push_refspec, flags))\n \t\t\t\terrs++;\n \t\t}\n \t} else {\n@@ -440,7 +432,7 @@ static int do_push(const char *repo, int flags,\n \t\t\ttransport_get(remote, NULL);\n \t\tif (flags & TRANSPORT_PUSH_OPTIONS)\n \t\t\ttransport->push_options = push_options;\n-\t\tif (push_with_options(transport, flags))\n+\t\tif (push_with_options(transport, push_refspec, flags))\n \t\t\terrs++;\n \t}\n \treturn !!errs;\n@@ -631,7 +623,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tflags |= TRANSPORT_RECURSE_SUBMODULES_ONLY;\n \n \tif (tags)\n-\t\tadd_refspec(\"refs/tags/*\");\n+\t\trefspec_append(&rs, \"refs/tags/*\");\n \n \tif (argc > 0) {\n \t\trepo = argv[0];\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347882","messageId":"20180516225823.235426-23-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 22/36] fetch: convert prune_refs to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:09Z","receivedAt":"2018-05-16T23:00:41Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'prune_refs()' to take a 'struct refspec' as a parameter instead\nof a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c | 11 +++++------\n 1 file changed, 5 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 836eb7545..5e46df70c 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -959,11 +959,11 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)\n \treturn ret;\n }\n \n-static int prune_refs(struct refspec_item *refs, int ref_count, struct ref *ref_map,\n-\t\tconst char *raw_url)\n+static int prune_refs(struct refspec *rs, struct ref *ref_map,\n+\t\t      const char *raw_url)\n {\n \tint url_len, i, result = 0;\n-\tstruct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);\n+\tstruct ref *ref, *stale_refs = get_stale_heads(rs->items, rs->nr, ref_map);\n \tchar *url;\n \tint summary_width = transport_summary_width(stale_refs);\n \tconst char *dangling_msg = dry_run\n@@ -1158,10 +1158,9 @@ static int do_fetch(struct transport *transport,\n \t\t * don't care whether --tags was specified.\n \t\t */\n \t\tif (rs->nr) {\n-\t\t\tprune_refs(rs->items, rs->nr, ref_map, transport->url);\n+\t\t\tprune_refs(rs, ref_map, transport->url);\n \t\t} else {\n-\t\t\tprune_refs(transport->remote->fetch.items,\n-\t\t\t\t   transport->remote->fetch.nr,\n+\t\t\tprune_refs(&transport->remote->fetch,\n \t\t\t\t   ref_map,\n \t\t\t\t   transport->url);\n \t\t}\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347883","messageId":"20180516225823.235426-24-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 23/36] remote: convert get_stale_heads to take a struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:58:10Z","receivedAt":"2018-05-16T23:00:43Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert 'get_stale_heads()' to take a 'struct refspec' as a parameter instead\nof a list of 'struct refspec_item'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c  |  2 +-\n builtin/remote.c |  3 +--\n remote.c         | 18 +++++++++---------\n remote.h         |  2 +-\n 4 files changed, 12 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 5e46df70c..3fad1f0db 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -963,7 +963,7 @@ static int prune_refs(struct refspec *rs, struct ref *ref_map,\n \t\t      const char *raw_url)\n {\n \tint url_len, i, result = 0;\n-\tstruct ref *ref, *stale_refs = get_stale_heads(rs->items, rs->nr, ref_map);\n+\tstruct ref *ref, *stale_refs = get_stale_heads(rs, ref_map);\n \tchar *url;\n \tint summary_width = transport_summary_width(stale_refs);\n \tconst char *dangling_msg = dry_run\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 94dfcb78b..b8e66589f 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -347,8 +347,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat\n \t\telse\n \t\t\tstring_list_append(&states->tracked, abbrev_branch(ref->name));\n \t}\n-\tstale_refs = get_stale_heads(states->remote->fetch.items,\n-\t\t\t\t     states->remote->fetch.nr, fetch_map);\n+\tstale_refs = get_stale_heads(&states->remote->fetch, fetch_map);\n \tfor (ref = stale_refs; ref; ref = ref->next) {\n \t\tstruct string_list_item *item =\n \t\t\tstring_list_append(&states->stale, abbrev_branch(ref->name));\ndiff --git a/remote.c b/remote.c\nindex 4a9bddf0d..358442e4b 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -698,7 +698,9 @@ static int match_name_with_pattern(const char *key, const char *name,\n \treturn ret;\n }\n \n-static void query_refspecs_multiple(struct refspec_item *refs, int ref_count, struct refspec_item *query, struct string_list *results)\n+static void query_refspecs_multiple(struct refspec *rs,\n+\t\t\t\t    struct refspec_item *query,\n+\t\t\t\t    struct string_list *results)\n {\n \tint i;\n \tint find_src = !query->src;\n@@ -706,8 +708,8 @@ static void query_refspecs_multiple(struct refspec_item *refs, int ref_count, st\n \tif (find_src && !query->dst)\n \t\terror(\"query_refspecs_multiple: need either src or dst\");\n \n-\tfor (i = 0; i < ref_count; i++) {\n-\t\tstruct refspec_item *refspec = &refs[i];\n+\tfor (i = 0; i < rs->nr; i++) {\n+\t\tstruct refspec_item *refspec = &rs->items[i];\n \t\tconst char *key = find_src ? refspec->dst : refspec->src;\n \t\tconst char *value = find_src ? refspec->src : refspec->dst;\n \t\tconst char *needle = find_src ? query->dst : query->src;\n@@ -2068,8 +2070,7 @@ struct ref *guess_remote_head(const struct ref *head,\n struct stale_heads_info {\n \tstruct string_list *ref_names;\n \tstruct ref **stale_refs_tail;\n-\tstruct refspec_item *refs;\n-\tint ref_count;\n+\tstruct refspec *rs;\n };\n \n static int get_stale_heads_cb(const char *refname, const struct object_id *oid,\n@@ -2082,7 +2083,7 @@ static int get_stale_heads_cb(const char *refname, const struct object_id *oid,\n \tmemset(&query, 0, sizeof(struct refspec_item));\n \tquery.dst = (char *)refname;\n \n-\tquery_refspecs_multiple(info->refs, info->ref_count, &query, &matches);\n+\tquery_refspecs_multiple(info->rs, &query, &matches);\n \tif (matches.nr == 0)\n \t\tgoto clean_exit; /* No matches */\n \n@@ -2110,7 +2111,7 @@ static int get_stale_heads_cb(const char *refname, const struct object_id *oid,\n \treturn 0;\n }\n \n-struct ref *get_stale_heads(struct refspec_item *refs, int ref_count, struct ref *fetch_map)\n+struct ref *get_stale_heads(struct refspec *rs, struct ref *fetch_map)\n {\n \tstruct ref *ref, *stale_refs = NULL;\n \tstruct string_list ref_names = STRING_LIST_INIT_NODUP;\n@@ -2118,8 +2119,7 @@ struct ref *get_stale_heads(struct refspec_item *refs, int ref_count, struct ref\n \n \tinfo.ref_names = &ref_names;\n \tinfo.stale_refs_tail = &stale_refs;\n-\tinfo.refs = refs;\n-\tinfo.ref_count = ref_count;\n+\tinfo.rs = rs;\n \tfor (ref = fetch_map; ref; ref = ref->next)\n \t\tstring_list_append(&ref_names, ref->name);\n \tstring_list_sort(&ref_names);\ndiff --git a/remote.h b/remote.h\nindex 4ffbc0082..1a45542cd 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -267,7 +267,7 @@ struct ref *guess_remote_head(const struct ref *head,\n \t\t\t      int all);\n \n /* Return refs which no longer exist on remote */\n-struct ref *get_stale_heads(struct refspec_item *refs, int ref_count, struct ref *fetch_map);\n+struct ref *get_stale_heads(struct refspec *rs, struct ref *fetch_map);\n \n /*\n  * Compare-and-swap\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347884","messageId":"20180516225823.235426-9-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH v2 08/36] transport: convert transport_push to use struct refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T22:57:55Z","receivedAt":"2018-05-16T23:00:50Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Convert the logic in 'transport_push()' which calculates a list of\nref-prefixes to use 'struct refspec'.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n transport.c | 17 +++++++++--------\n 1 file changed, 9 insertions(+), 8 deletions(-)\n\ndiff --git a/transport.c b/transport.c\nindex 3ad4d37dc..181db4d4d 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1111,21 +1111,22 @@ int transport_push(struct transport *transport,\n \t\tint porcelain = flags & TRANSPORT_PUSH_PORCELAIN;\n \t\tint pretend = flags & TRANSPORT_PUSH_DRY_RUN;\n \t\tint push_ret, ret, err;\n-\t\tstruct refspec_item *tmp_rs;\n+\t\tstruct refspec tmp_rs = REFSPEC_INIT_PUSH;\n \t\tstruct argv_array ref_prefixes = ARGV_ARRAY_INIT;\n \t\tint i;\n \n \t\tif (check_push_refs(local_refs, refspec_nr, refspec) < 0)\n \t\t\treturn -1;\n \n-\t\ttmp_rs = parse_push_refspec(refspec_nr, refspec);\n-\t\tfor (i = 0; i < refspec_nr; i++) {\n+\t\trefspec_appendn(&tmp_rs, refspec, refspec_nr);\n+\t\tfor (i = 0; i < tmp_rs.nr; i++) {\n+\t\t\tconst struct refspec_item *item = &tmp_rs.items[i];\n \t\t\tconst char *prefix = NULL;\n \n-\t\t\tif (tmp_rs[i].dst)\n-\t\t\t\tprefix = tmp_rs[i].dst;\n-\t\t\telse if (tmp_rs[i].src && !tmp_rs[i].exact_sha1)\n-\t\t\t\tprefix = tmp_rs[i].src;\n+\t\t\tif (item->dst)\n+\t\t\t\tprefix = item->dst;\n+\t\t\telse if (item->src && !item->exact_sha1)\n+\t\t\t\tprefix = item->src;\n \n \t\t\tif (prefix) {\n \t\t\t\tconst char *glob = strchr(prefix, '*');\n@@ -1142,7 +1143,7 @@ int transport_push(struct transport *transport,\n \t\t\t\t\t\t\t       &ref_prefixes);\n \n \t\targv_array_clear(&ref_prefixes);\n-\t\tfree_refspec(refspec_nr, tmp_rs);\n+\t\trefspec_clear(&tmp_rs);\n \n \t\tif (flags & TRANSPORT_PUSH_ALL)\n \t\t\tmatch_flags |= MATCH_REFS_ALL;\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347888","messageId":"20180516234822.182663-1-bmwill@google.com","threadId":"48495","inReplyTo":"20180516225823.235426-1-bmwill@google.com","subject":"[PATCH 0/2] generating ref-prefixes for configured refspecs","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T23:48:20Z","receivedAt":"2018-05-16T23:48:29Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Here's my short follow on series to the refspec refactoring.\n\nWhen v2 was introduced ref-prefixes were only generated for user\nprovided refspecs (given via the command line).  This means that you can\nonly benefit from server-side ref filtering if you explicitly provide a\nrefspec, so this short series extends this to generate the ref-prefixes\neven for the refspecs which are configured in 'remote.<name>.fetch'.\n\nThis series is based on the v2 of the refspec refactoring series.\n\nBrandon Williams (2):\n  refspec: consolidate ref-prefix generation logic\n  fetch: generate ref-prefixes when using a configured refspec\n\n builtin/fetch.c        | 19 ++++++++-----------\n refspec.c              | 29 +++++++++++++++++++++++++++++\n refspec.h              |  4 ++++\n t/t5702-protocol-v2.sh | 14 ++++++++++++++\n transport.c            | 21 +--------------------\n 5 files changed, 56 insertions(+), 31 deletions(-)\n\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347889","messageId":"20180516234822.182663-2-bmwill@google.com","threadId":"48495","inReplyTo":"20180516234822.182663-1-bmwill@google.com","subject":"[PATCH 1/2] refspec: consolidate ref-prefix generation logic","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T23:48:21Z","receivedAt":"2018-05-16T23:48:32Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"When using protocol v2 a client constructs a list of ref-prefixes which\nare sent across the wire so that the server can do server-side filtering\nof the ref-advertisement.  The logic that does this exists for both\nfetch and push (even though no push support for v2 currently exists yet)\nand is roughly the same so lets consolidate this logic and make it\ngeneral enough that it can be used for both the push and fetch cases.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c | 13 +------------\n refspec.c       | 29 +++++++++++++++++++++++++++++\n refspec.h       |  4 ++++\n transport.c     | 21 +--------------------\n 4 files changed, 35 insertions(+), 32 deletions(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 3fad1f0db..80bb14370 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -351,18 +351,7 @@ static struct ref *get_ref_map(struct transport *transport,\n \n \tconst struct ref *remote_refs;\n \n-\tfor (i = 0; i < rs->nr; i++) {\n-\t\tconst struct refspec_item *item = &rs->items[i];\n-\t\tif (!item->exact_sha1) {\n-\t\t\tconst char *glob = strchr(item->src, '*');\n-\t\t\tif (glob)\n-\t\t\t\targv_array_pushf(&ref_prefixes, \"%.*s\",\n-\t\t\t\t\t\t (int)(glob - item->src),\n-\t\t\t\t\t\t item->src);\n-\t\t\telse\n-\t\t\t\texpand_ref_prefix(&ref_prefixes, item->src);\n-\t\t}\n-\t}\n+\trefspec_ref_prefixes(rs, &ref_prefixes);\n \n \tremote_refs = transport_get_remote_refs(transport, &ref_prefixes);\n \ndiff --git a/refspec.c b/refspec.c\nindex 97e76e8b1..c59a4ccf1 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -1,4 +1,5 @@\n #include \"cache.h\"\n+#include \"argv-array.h\"\n #include \"refs.h\"\n #include \"refspec.h\"\n \n@@ -192,3 +193,31 @@ int valid_fetch_refspec(const char *fetch_refspec_str)\n \trefspec_item_clear(&refspec);\n \treturn ret;\n }\n+\n+void refspec_ref_prefixes(const struct refspec *rs,\n+\t\t\t  struct argv_array *ref_prefixes)\n+{\n+\tint i;\n+\tfor (i = 0; i < rs->nr; i++) {\n+\t\tconst struct refspec_item *item = &rs->items[i];\n+\t\tconst char *prefix = NULL;\n+\n+\t\tif (rs->fetch == REFSPEC_FETCH)\n+\t\t\tprefix = item->src;\n+\t\telse if (item->dst)\n+\t\t\tprefix = item->dst;\n+\t\telse if (item->src && !item->exact_sha1)\n+\t\t\tprefix = item->src;\n+\n+\t\tif (prefix) {\n+\t\t\tif (item->pattern) {\n+\t\t\t\tconst char *glob = strchr(prefix, '*');\n+\t\t\t\targv_array_pushf(ref_prefixes, \"%.*s\",\n+\t\t\t\t\t\t (int)(glob - prefix),\n+\t\t\t\t\t\t prefix);\n+\t\t\t} else {\n+\t\t\t\texpand_ref_prefix(ref_prefixes, prefix);\n+\t\t\t}\n+\t\t}\n+\t}\n+}\ndiff --git a/refspec.h b/refspec.h\nindex 7e1ff94ac..01b700e09 100644\n--- a/refspec.h\n+++ b/refspec.h\n@@ -41,4 +41,8 @@ void refspec_clear(struct refspec *rs);\n \n int valid_fetch_refspec(const char *refspec);\n \n+struct argv_array;\n+void refspec_ref_prefixes(const struct refspec *rs,\n+\t\t\t  struct argv_array *ref_prefixes);\n+\n #endif /* REFSPEC_H */\ndiff --git a/transport.c b/transport.c\nindex 7e0b9abba..cbf0044c3 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1088,30 +1088,11 @@ int transport_push(struct transport *transport,\n \t\tint pretend = flags & TRANSPORT_PUSH_DRY_RUN;\n \t\tint push_ret, ret, err;\n \t\tstruct argv_array ref_prefixes = ARGV_ARRAY_INIT;\n-\t\tint i;\n \n \t\tif (check_push_refs(local_refs, rs) < 0)\n \t\t\treturn -1;\n \n-\t\tfor (i = 0; i < rs->nr; i++) {\n-\t\t\tconst struct refspec_item *item = &rs->items[i];\n-\t\t\tconst char *prefix = NULL;\n-\n-\t\t\tif (item->dst)\n-\t\t\t\tprefix = item->dst;\n-\t\t\telse if (item->src && !item->exact_sha1)\n-\t\t\t\tprefix = item->src;\n-\n-\t\t\tif (prefix) {\n-\t\t\t\tconst char *glob = strchr(prefix, '*');\n-\t\t\t\tif (glob)\n-\t\t\t\t\targv_array_pushf(&ref_prefixes, \"%.*s\",\n-\t\t\t\t\t\t\t (int)(glob - prefix),\n-\t\t\t\t\t\t\t prefix);\n-\t\t\t\telse\n-\t\t\t\t\texpand_ref_prefix(&ref_prefixes, prefix);\n-\t\t\t}\n-\t\t}\n+\t\trefspec_ref_prefixes(rs, &ref_prefixes);\n \n \t\tremote_refs = transport->vtable->get_refs_list(transport, 1,\n \t\t\t\t\t\t\t       &ref_prefixes);\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347890","messageId":"20180516234822.182663-3-bmwill@google.com","threadId":"48495","inReplyTo":"20180516234822.182663-1-bmwill@google.com","subject":"[PATCH 2/2] fetch: generate ref-prefixes when using a configured refspec","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-16T23:48:22Z","receivedAt":"2018-05-16T23:48:36Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Teach fetch to generate ref-prefixes, to be used for server-side\nfiltering of the ref-advertisement, based on the configured fetch\nrefspec ('remote.<name>.fetch') when no user provided refspec exists.\n\nSigned-off-by: Brandon Williams <bmwill@google.com>\n---\n builtin/fetch.c        | 10 +++++++++-\n t/t5702-protocol-v2.sh | 14 ++++++++++++++\n 2 files changed, 23 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 80bb14370..7cc7a52de 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -351,7 +351,15 @@ static struct ref *get_ref_map(struct transport *transport,\n \n \tconst struct ref *remote_refs;\n \n-\trefspec_ref_prefixes(rs, &ref_prefixes);\n+\tif (rs->nr)\n+\t\trefspec_ref_prefixes(rs, &ref_prefixes);\n+\telse if (transport->remote && transport->remote->fetch.nr)\n+\t\trefspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes);\n+\n+\tif (ref_prefixes.argc &&\n+\t    (tags == TAGS_SET || (tags == TAGS_DEFAULT && !rs->nr))) {\n+\t\targv_array_push(&ref_prefixes, \"refs/tags/\");\n+\t}\n \n \tremote_refs = transport_get_remote_refs(transport, &ref_prefixes);\n \ndiff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh\nindex 56f7c3c32..b6c72ab51 100755\n--- a/t/t5702-protocol-v2.sh\n+++ b/t/t5702-protocol-v2.sh\n@@ -201,6 +201,20 @@ test_expect_success 'ref advertisment is filtered during fetch using protocol v2\n \t! grep \"refs/tags/three\" log\n '\n \n+test_expect_success 'default refspec is used to filter ref when fetchcing' '\n+\ttest_when_finished \"rm -f log\" &&\n+\n+\tGIT_TRACE_PACKET=\"$(pwd)/log\" git -C file_child -c protocol.version=2 \\\n+\t\tfetch origin &&\n+\n+\tgit -C file_child log -1 --format=%s three >actual &&\n+\tgit -C file_parent log -1 --format=%s three >expect &&\n+\ttest_cmp expect actual &&\n+\n+\tgrep \"ref-prefix refs/heads/\" log &&\n+\tgrep \"ref-prefix refs/tags/\" log\n+'\n+\n # Test protocol v2 with 'http://' transport\n #\n . \"$TEST_DIRECTORY\"/lib-httpd.sh\n-- \n2.17.0.441.gb46fe60e1d-goog\n\n"},{"id":"347932","messageId":"xmqqr2ma55pi.fsf@gitster-ct.c.googlers.com","threadId":"48495","inReplyTo":"20180516234822.182663-1-bmwill@google.com","subject":"Re: [PATCH 0/2] generating ref-prefixes for configured refspecs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-17T21:32:09Z","receivedAt":"2018-05-17T21:32:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Brandon Williams <bmwill@google.com> writes:\n\n> Here's my short follow on series to the refspec refactoring.\n>\n> When v2 was introduced ref-prefixes were only generated for user\n> provided refspecs (given via the command line).  This means that you can\n> only benefit from server-side ref filtering if you explicitly provide a\n> refspec, so this short series extends this to generate the ref-prefixes\n> even for the refspecs which are configured in 'remote.<name>.fetch'.\n>\n> This series is based on the v2 of the refspec refactoring series.\n\nMakes sense.\n\n>\n> Brandon Williams (2):\n>   refspec: consolidate ref-prefix generation logic\n>   fetch: generate ref-prefixes when using a configured refspec\n>\n>  builtin/fetch.c        | 19 ++++++++-----------\n>  refspec.c              | 29 +++++++++++++++++++++++++++++\n>  refspec.h              |  4 ++++\n>  t/t5702-protocol-v2.sh | 14 ++++++++++++++\n>  transport.c            | 21 +--------------------\n>  5 files changed, 56 insertions(+), 31 deletions(-)\n"},{"id":"348858","messageId":"20180531004309.GA36515@aiede.svl.corp.google.com","threadId":"48495","inReplyTo":"20180516234822.182663-2-bmwill@google.com","subject":"Re: [PATCH 1/2] refspec: consolidate ref-prefix generation logic","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2018-05-31T00:43:09Z","receivedAt":"2018-05-31T00:43:19Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\nBrandon Williams wrote:\n\n> When using protocol v2 a client constructs a list of ref-prefixes which\n> are sent across the wire so that the server can do server-side filtering\n> of the ref-advertisement.  The logic that does this exists for both\n> fetch and push (even though no push support for v2 currently exists yet)\n> and is roughly the same so lets consolidate this logic and make it\n> general enough that it can be used for both the push and fetch cases.\n>\n> Signed-off-by: Brandon Williams <bmwill@google.com>\n> ---\n>  builtin/fetch.c | 13 +------------\n>  refspec.c       | 29 +++++++++++++++++++++++++++++\n>  refspec.h       |  4 ++++\n>  transport.c     | 21 +--------------------\n>  4 files changed, 35 insertions(+), 32 deletions(-)\n\nI assume this is meant to be a refactoring with no functional change.\nAlas, it's causing fetch-by-SHA-1 to fail for me:\n\n $ rm -fr /tmp/r\n $ git init /tmp/r\n Initialized empty Git repository in /tmp/r/.git/\n $ bin-wrappers/git -C /tmp/r -c protocol.version=2 fetch \\\n\thttps://kernel.googlesource.com/pub/scm/git/git \\\n\t6373cb598e1a4e0340583ad75d5abba01ff79774\n fatal: no matching remote head\n\nThe parent commit succeeds.\n\nKnown issue?\n\nThanks,\nJonathan\n"},{"id":"348859","messageId":"20180531010739.GB36515@aiede.svl.corp.google.com","threadId":"48495","inReplyTo":"20180531004309.GA36515@aiede.svl.corp.google.com","subject":"Re: [PATCH 1/2] refspec: consolidate ref-prefix generation logic","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2018-05-31T01:07:39Z","receivedAt":"2018-05-31T01:07:46Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Jonathan Nieder wrote:\n> Brandon Williams wrote:\n\n>> When using protocol v2 a client constructs a list of ref-prefixes which\n>> are sent across the wire so that the server can do server-side filtering\n>> of the ref-advertisement.  The logic that does this exists for both\n>> fetch and push (even though no push support for v2 currently exists yet)\n>> and is roughly the same so lets consolidate this logic and make it\n>> general enough that it can be used for both the push and fetch cases.\n>>\n>> Signed-off-by: Brandon Williams <bmwill@google.com>\n>> ---\n>>  builtin/fetch.c | 13 +------------\n>>  refspec.c       | 29 +++++++++++++++++++++++++++++\n>>  refspec.h       |  4 ++++\n>>  transport.c     | 21 +--------------------\n>>  4 files changed, 35 insertions(+), 32 deletions(-)\n>\n> I assume this is meant to be a refactoring with no functional change.\n> Alas, it's causing fetch-by-SHA-1 to fail for me:\n[...]\n>  $ bin-wrappers/git -C /tmp/r -c protocol.version=2 fetch \\\n> \thttps://kernel.googlesource.com/pub/scm/git/git \\\n> \t6373cb598e1a4e0340583ad75d5abba01ff79774\n>  fatal: no matching remote head\n\nBacktrace:\n\n #0  0x0000555555671e09 in fetch_pack (args=0x7fffffffda80, fd=0x555555a6a9b0, conn=0x555555a65780, ref=0x0, \n     dest=0x555555a64900 \"https://kernel.googlesource.com/pub/scm/git/git\", sought=0x555555a69a80, nr_sought=1, shallow=0x555555a6a9d8, \n     pack_lockfile=0x555555a631c8, version=protocol_v2) at fetch-pack.c:1562\n #1  0x00005555557290b9 in fetch_refs_via_pack (transport=0x555555a63190, nr_heads=1, to_fetch=0x555555a69a80) at transport.c:326\n #2  0x000055555572b712 in transport_fetch_refs (transport=0x555555a63190, refs=0x555555a65520) at transport.c:1247\n #3  0x000055555559a2a6 in fetch_refs (transport=0x555555a63190, ref_map=0x555555a65520) at builtin/fetch.c:942\n #4  0x000055555559ac3e in do_fetch (transport=0x555555a63190, rs=0x7fffffffdc30) at builtin/fetch.c:1157\n #5  0x000055555559b535 in fetch_one (remote=0x555555a63870, argc=1, argv=0x7fffffffdff8, prune_tags_ok=0) at builtin/fetch.c:1398\n #6  0x000055555559b9a3 in cmd_fetch (argc=1, argv=0x7fffffffdff8, prefix=0x0) at builtin/fetch.c:1487\n #7  0x000055555556764b in run_builtin (p=0x555555a0fa30 <commands+816>, argc=3, argv=0x7fffffffdff0) at git.c:350\n #8  0x000055555556795f in handle_builtin (argc=3, argv=0x7fffffffdff0) at git.c:565\n #9  0x0000555555567b07 in run_argv (argcp=0x7fffffffde8c, argv=0x7fffffffde80) at git.c:617\n #10 0x0000555555567cbc in cmd_main (argc=3, argv=0x7fffffffdff0) at git.c:694\n #11 0x000055555560774c in main (argc=8, argv=0x7fffffffdfc8) at common-main.c:45\n\nBoth refs_tmp and transport->remote_refs are NULL.\n\nWhen refspec_ref_prefixes gets called, it produces the usual\nargv_array with 6373cb598e1a4e0340583ad75d5abba01ff79774,\nrefs/6373cb598e1a4e0340583ad75d5abba01ff79774, etc.\n\nIn the old code, this doesn't happen because we trigger the\nitem->exact_sha1 test.\n\nWould something like the following make sense?  It passes tests and\nappears to avoid trouble but I haven't looked any closer than that.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n\n--- i/refspec.c\n+++ w/refspec.c\n@@ -202,6 +202,8 @@ void refspec_ref_prefixes(const struct refspec *rs,\n \t\tconst struct refspec_item *item = &rs->items[i];\n \t\tconst char *prefix = NULL;\n \n+\t\tif (item->exact_sha1)\n+\t\t\tcontinue;\n \t\tif (rs->fetch == REFSPEC_FETCH)\n \t\t\tprefix = item->src;\n \t\telse if (item->dst)\n"},{"id":"348882","messageId":"20180531072339.GA43435@aiede.svl.corp.google.com","threadId":"48495","inReplyTo":"20180516234822.182663-2-bmwill@google.com","subject":"[PATCH] fetch: do not pass ref-prefixes for fetch by exact SHA1","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2018-05-31T07:23:39Z","receivedAt":"2018-05-31T07:24:14Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"When v2.18.0-rc0~10^2~1 (refspec: consolidate ref-prefix generation\nlogic, 2018-05-16) factored out the ref-prefix generation code for\nreuse, it left out the 'if (!item->exact_sha1)' test in the original\nref-prefix generation code. As a result, fetches by SHA-1 generate\nref-prefixes as though the SHA-1 being fetched were an abbreviated ref\nname:\n\n $ GIT_TRACE_PACKET=1 bin-wrappers/git -c protocol.version=2 \\\n\tfetch origin 12039e008f9a4e3394f3f94f8ea897785cb09448\n[...]\n packet:        fetch> ref-prefix 12039e008f9a4e3394f3f94f8ea897785cb09448\n packet:        fetch> ref-prefix refs/12039e008f9a4e3394f3f94f8ea897785cb09448\n packet:        fetch> ref-prefix refs/tags/12039e008f9a4e3394f3f94f8ea897785cb09448\n packet:        fetch> ref-prefix refs/heads/12039e008f9a4e3394f3f94f8ea897785cb09448\n packet:        fetch> ref-prefix refs/remotes/12039e008f9a4e3394f3f94f8ea897785cb09448\n packet:        fetch> ref-prefix refs/remotes/12039e008f9a4e3394f3f94f8ea897785cb09448/HEAD\n packet:        fetch> 0000\n\nIf there is another ref name on the command line or the object being\nfetched is already available locally, then that's mostly harmless.\nBut otherwise, we error out with\n\n fatal: no matching remote head\n\nsince the server did not send any refs we are interested in.  Filter\nout the exact_sha1 refspecs to avoid this.\n\nThis patch adds a test to check this behavior that notices another\nbehavior difference between protocol v0 and v2 in the process.  Add a\nNEEDSWORK comment to clear it up.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\nHere's the change described in\nhttps://public-inbox.org/git/20180531010739.GB36515@aiede.svl.corp.google.com/\nas a proper patch.\n\nThoughts of all kinds welcome, as always.\n\n refspec.c             |  2 ++\n refspec.h             |  4 ++++\n t/t5516-fetch-push.sh | 19 +++++++++++++++++++\n 3 files changed, 25 insertions(+)\n\ndiff --git a/refspec.c b/refspec.c\nindex c59a4ccf1e..ada7854f7a 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -202,6 +202,8 @@ void refspec_ref_prefixes(const struct refspec *rs,\n \t\tconst struct refspec_item *item = &rs->items[i];\n \t\tconst char *prefix = NULL;\n \n+\t\tif (item->exact_sha1)\n+\t\t\tcontinue;\n \t\tif (rs->fetch == REFSPEC_FETCH)\n \t\t\tprefix = item->src;\n \t\telse if (item->dst)\ndiff --git a/refspec.h b/refspec.h\nindex 01b700e094..3a9363887c 100644\n--- a/refspec.h\n+++ b/refspec.h\n@@ -42,6 +42,10 @@ void refspec_clear(struct refspec *rs);\n int valid_fetch_refspec(const char *refspec);\n \n struct argv_array;\n+/*\n+ * Determine what <prefix> values to pass to the peer in ref-prefix lines\n+ * (see Documentation/technical/protocol-v2.txt).\n+ */\n void refspec_ref_prefixes(const struct refspec *rs,\n \t\t\t  struct argv_array *ref_prefixes);\n \ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex f4d28288f0..a5077d8b7c 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1121,6 +1121,25 @@ test_expect_success 'fetch exact SHA1' '\n \t)\n '\n \n+test_expect_success 'fetch exact SHA1 in protocol v2' '\n+\tmk_test testrepo heads/master hidden/one &&\n+\tgit push testrepo master:refs/hidden/one &&\n+\tgit -C testrepo config transfer.hiderefs refs/hidden &&\n+\tcheck_push_result testrepo $the_commit hidden/one &&\n+\n+\tmk_child testrepo child &&\n+\tgit -C child config protocol.version 2 &&\n+\n+\t# make sure $the_commit does not exist here\n+\tgit -C child repack -a -d &&\n+\tgit -C child prune &&\n+\ttest_must_fail git -C child cat-file -t $the_commit &&\n+\n+\t# fetching the hidden object succeeds by default\n+\t# NEEDSWORK: should this match the v0 behavior instead?\n+\tgit -C child fetch -v ../testrepo $the_commit:refs/heads/copy\n+'\n+\n for configallowtipsha1inwant in true false\n do\n \ttest_expect_success \"shallow fetch reachable SHA1 (but not a ref), allowtipsha1inwant=$configallowtipsha1inwant\" '\n-- \n2.17.1.1185.g55be947832\n\n"},{"id":"348899","messageId":"CAKoko1peCNXq-2txoMfQ07FgFDOOcb8b9gMq1c1h9B8Le4kGww@mail.gmail.com","threadId":"48495","inReplyTo":"20180531072339.GA43435@aiede.svl.corp.google.com","subject":"Re: [PATCH] fetch: do not pass ref-prefixes for fetch by exact SHA1","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-05-31T15:44:37Z","receivedAt":"2018-05-31T15:44:43Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"Thanks for finding this, I don't know how I missed moving that bit\nover when factoring it out.  Well I guess I sort of rewrote it and\ncombined two pieces of logic so that's how.  Anyway, this looks right\nand thanks for adding the test.\n\nOn Thu, May 31, 2018 at 12:23 AM, Jonathan Nieder <jrnieder@gmail.com> wrote:\n> When v2.18.0-rc0~10^2~1 (refspec: consolidate ref-prefix generation\n> logic, 2018-05-16) factored out the ref-prefix generation code for\n> reuse, it left out the 'if (!item->exact_sha1)' test in the original\n> ref-prefix generation code. As a result, fetches by SHA-1 generate\n> ref-prefixes as though the SHA-1 being fetched were an abbreviated ref\n> name:\n>\n>  $ GIT_TRACE_PACKET=1 bin-wrappers/git -c protocol.version=2 \\\n>         fetch origin 12039e008f9a4e3394f3f94f8ea897785cb09448\n> [...]\n>  packet:        fetch> ref-prefix 12039e008f9a4e3394f3f94f8ea897785cb09448\n>  packet:        fetch> ref-prefix refs/12039e008f9a4e3394f3f94f8ea897785cb09448\n>  packet:        fetch> ref-prefix refs/tags/12039e008f9a4e3394f3f94f8ea897785cb09448\n>  packet:        fetch> ref-prefix refs/heads/12039e008f9a4e3394f3f94f8ea897785cb09448\n>  packet:        fetch> ref-prefix refs/remotes/12039e008f9a4e3394f3f94f8ea897785cb09448\n>  packet:        fetch> ref-prefix refs/remotes/12039e008f9a4e3394f3f94f8ea897785cb09448/HEAD\n>  packet:        fetch> 0000\n>\n> If there is another ref name on the command line or the object being\n> fetched is already available locally, then that's mostly harmless.\n> But otherwise, we error out with\n>\n>  fatal: no matching remote head\n>\n> since the server did not send any refs we are interested in.  Filter\n> out the exact_sha1 refspecs to avoid this.\n>\n> This patch adds a test to check this behavior that notices another\n> behavior difference between protocol v0 and v2 in the process.  Add a\n> NEEDSWORK comment to clear it up.\n>\n> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>\n> ---\n> Here's the change described in\n> https://public-inbox.org/git/20180531010739.GB36515@aiede.svl.corp.google.com/\n> as a proper patch.\n>\n> Thoughts of all kinds welcome, as always.\n>\n>  refspec.c             |  2 ++\n>  refspec.h             |  4 ++++\n>  t/t5516-fetch-push.sh | 19 +++++++++++++++++++\n>  3 files changed, 25 insertions(+)\n>\n> diff --git a/refspec.c b/refspec.c\n> index c59a4ccf1e..ada7854f7a 100644\n> --- a/refspec.c\n> +++ b/refspec.c\n> @@ -202,6 +202,8 @@ void refspec_ref_prefixes(const struct refspec *rs,\n>                 const struct refspec_item *item = &rs->items[i];\n>                 const char *prefix = NULL;\n>\n> +               if (item->exact_sha1)\n> +                       continue;\n>                 if (rs->fetch == REFSPEC_FETCH)\n>                         prefix = item->src;\n>                 else if (item->dst)\n> diff --git a/refspec.h b/refspec.h\n> index 01b700e094..3a9363887c 100644\n> --- a/refspec.h\n> +++ b/refspec.h\n> @@ -42,6 +42,10 @@ void refspec_clear(struct refspec *rs);\n>  int valid_fetch_refspec(const char *refspec);\n>\n>  struct argv_array;\n> +/*\n> + * Determine what <prefix> values to pass to the peer in ref-prefix lines\n> + * (see Documentation/technical/protocol-v2.txt).\n> + */\n>  void refspec_ref_prefixes(const struct refspec *rs,\n>                           struct argv_array *ref_prefixes);\n>\n> diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\n> index f4d28288f0..a5077d8b7c 100755\n> --- a/t/t5516-fetch-push.sh\n> +++ b/t/t5516-fetch-push.sh\n> @@ -1121,6 +1121,25 @@ test_expect_success 'fetch exact SHA1' '\n>         )\n>  '\n>\n> +test_expect_success 'fetch exact SHA1 in protocol v2' '\n> +       mk_test testrepo heads/master hidden/one &&\n> +       git push testrepo master:refs/hidden/one &&\n> +       git -C testrepo config transfer.hiderefs refs/hidden &&\n> +       check_push_result testrepo $the_commit hidden/one &&\n> +\n> +       mk_child testrepo child &&\n> +       git -C child config protocol.version 2 &&\n> +\n> +       # make sure $the_commit does not exist here\n> +       git -C child repack -a -d &&\n> +       git -C child prune &&\n> +       test_must_fail git -C child cat-file -t $the_commit &&\n> +\n> +       # fetching the hidden object succeeds by default\n> +       # NEEDSWORK: should this match the v0 behavior instead?\n> +       git -C child fetch -v ../testrepo $the_commit:refs/heads/copy\n> +'\n> +\n>  for configallowtipsha1inwant in true false\n>  do\n>         test_expect_success \"shallow fetch reachable SHA1 (but not a ref), allowtipsha1inwant=$configallowtipsha1inwant\" '\n> --\n> 2.17.1.1185.g55be947832\n>\n"},{"id":"348960","messageId":"xmqqtvqn9rwa.fsf@gitster-ct.c.googlers.com","threadId":"48495","inReplyTo":"20180531072339.GA43435@aiede.svl.corp.google.com","subject":"Re: [PATCH] fetch: do not pass ref-prefixes for fetch by exact SHA1","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-06-01T02:12:21Z","receivedAt":"2018-06-01T02:12:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Nieder <jrnieder@gmail.com> writes:\n\n> When v2.18.0-rc0~10^2~1 (refspec: consolidate ref-prefix generation\n> logic, 2018-05-16) factored out the ref-prefix generation code for\n> reuse, it left out the 'if (!item->exact_sha1)' test in the original\n> ref-prefix generation code. As a result, fetches by SHA-1 generate\n> ref-prefixes as though the SHA-1 being fetched were an abbreviated ref\n> name:\n>\n>  $ GIT_TRACE_PACKET=1 bin-wrappers/git -c protocol.version=2 \\\n> \tfetch origin 12039e008f9a4e3394f3f94f8ea897785cb09448\n> [...]\n>  packet:        fetch> ref-prefix 12039e008f9a4e3394f3f94f8ea897785cb09448\n>  packet:        fetch> ref-prefix refs/12039e008f9a4e3394f3f94f8ea897785cb09448\n>  packet:        fetch> ref-prefix refs/tags/12039e008f9a4e3394f3f94f8ea897785cb09448\n>  packet:        fetch> ref-prefix refs/heads/12039e008f9a4e3394f3f94f8ea897785cb09448\n>  packet:        fetch> ref-prefix refs/remotes/12039e008f9a4e3394f3f94f8ea897785cb09448\n>  packet:        fetch> ref-prefix refs/remotes/12039e008f9a4e3394f3f94f8ea897785cb09448/HEAD\n>  packet:        fetch> 0000\n>\n> If there is another ref name on the command line or the object being\n> fetched is already available locally, then that's mostly harmless.\n> But otherwise, we error out with\n>\n>  fatal: no matching remote head\n>\n> since the server did not send any refs we are interested in.  Filter\n> out the exact_sha1 refspecs to avoid this.\n>\n> This patch adds a test to check this behavior that notices another\n> behavior difference between protocol v0 and v2 in the process.  Add a\n> NEEDSWORK comment to clear it up.\n\nThanks.\n\nI wonder if there is a more effective way to smoke out other bugs\nremaining in proto v2.  When the fetch-by-SHA1 feature was added\noriginally, we certainly would have added a test or two to make sure\nit won't break.  The root cause of this breakage is that we lack the\nability to easily exercise proto v2 on these existing tests that\nwere written back in the proto v0 days.  It there were such a way\n(like, a common set of tests that are run with all supported\nprotos), we would have caught the breakge even before the topic hit\n'next'.\n\n\n"},{"id":"348966","messageId":"20180601024924.GA111965@aiede.svl.corp.google.com","threadId":"48495","inReplyTo":"xmqqtvqn9rwa.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] fetch: do not pass ref-prefixes for fetch by exact SHA1","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2018-06-01T02:49:24Z","receivedAt":"2018-06-01T02:49:51Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Junio C Hamano wrote:\n> Jonathan Nieder <jrnieder@gmail.com> writes:\n\n>> This patch adds a test to check this behavior that notices another\n>> behavior difference between protocol v0 and v2 in the process.  Add a\n>> NEEDSWORK comment to clear it up.\n>\n> Thanks.\n>\n> I wonder if there is a more effective way to smoke out other bugs\n> remaining in proto v2.  When the fetch-by-SHA1 feature was added\n> originally, we certainly would have added a test or two to make sure\n> it won't break.  The root cause of this breakage is that we lack the\n> ability to easily exercise proto v2 on these existing tests that\n> were written back in the proto v0 days.  It there were such a way\n> (like, a common set of tests that are run with all supported\n> protos), we would have caught the breakge even before the topic hit\n> 'next'.\n\nI had a similar thought.\n\nI am not sure I agree about the root cause, but root causes are\ngenerally slippery to define.  Because this bug had significant\ninternal impact, we came up with a few next steps:\n\n- shore up protocol v2 test coverage, as you described\n\n- arrange for long refactoring series we submit to be divided up for\n  the team to review, to avoid reviewer fatigue.  Hopefully this will\n  make us a better example for other submitters of long series.  We're\n  open to cooperating with others --- maybe we can set up a volunteer\n  reviewer brigade to get a more diverse set of eyes on each series\n  --- though organizing that is harder.\n\n- improve telemetry for our internal deployment, to get earlier notice\n  when Git is producing more errors.  I suspect other installations\n  may want something like this too --- e.g. I think this is one of the\n  benefits of what Jeff Hostetler is starting to build with json-writer.\n\n- help internal users triage errors from Git (like those decision\n  trees parents have to help decide when to bring a child to the\n  doctor), so that we get earlier notice and can roll back and report\n  upstream more quickly when they've run into a Git bug\n\nOr in other words, please expect more in this area soon, and feel free\nto pester me if the test coverage doesn't arrive. :)\n\nThanks,\nJonathan\n"},{"id":"349178","messageId":"CAN0heSr6dPa+--Mut0+3Zgy-mWF0d2762vKU=d08CKhR8BnKmA@mail.gmail.com","threadId":"48495","inReplyTo":"20180516225823.235426-6-bmwill@google.com","subject":"Re: [PATCH v2 05/36] refspec: convert valid_fetch_refspec to use parse_refspec","fromName":"Martin Ågren","fromEmail":"martin.agren@gmail.com","sentAt":"2018-06-03T17:13:14Z","receivedAt":"2018-06-03T17:13:18Z","isPatch":true,"sender":{"key":"martin.agren@gmail.com","avatar":null},"body":"Hi Brandon,\n\nOn 17 May 2018 at 00:57, Brandon Williams <bmwill@google.com> wrote:\n> Convert 'valid_fetch_refspec()' to use the new 'parse_refspec()'\n> function to only parse a single refspec and eliminate an allocation.\n>\n> Signed-off-by: Brandon Williams <bmwill@google.com>\n> ---\n>  refspec.c | 17 ++++++++---------\n>  refspec.h |  3 ++-\n>  2 files changed, 10 insertions(+), 10 deletions(-)\n>\n> diff --git a/refspec.c b/refspec.c\n> index af9d0d4b3..ab37b5ba1 100644\n> --- a/refspec.c\n> +++ b/refspec.c\n> @@ -146,15 +146,6 @@ static struct refspec_item *parse_refspec_internal(int nr_refspec, const char **\n>         die(\"Invalid refspec '%s'\", refspec[i]);\n>  }\n>\n> -int valid_fetch_refspec(const char *fetch_refspec_str)\n> -{\n> -       struct refspec_item *refspec;\n> -\n> -       refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);\n> -       free_refspec(1, refspec);\n> -       return !!refspec;\n> -}\n> -\n>  struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec)\n>  {\n>         return parse_refspec_internal(nr_refspec, refspec, 1, 0);\n> @@ -242,3 +233,11 @@ void refspec_clear(struct refspec *rs)\n>\n>         rs->fetch = 0;\n>  }\n> +\n> +int valid_fetch_refspec(const char *fetch_refspec_str)\n> +{\n> +       struct refspec_item refspec;\n> +       int ret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n> +       refspec_item_clear(&refspec);\n> +       return ret;\n> +}\n\nMy compiler warned about this function. The `dst` and `src` pointers\nwill equal some random data on the stack, then they may or may not be\nassigned to, then we will call `free()` on them.\n\nAt least I *think* that we \"may or may not\" assign to them. I don't have\nmuch or any time to really dig into this right now unfortunately.\n\nI suppose this could use a REFSPEC_ITEM_INIT, or a memset inside\n`parse_refspec()`, but I am very unfamiliar with this code.\n\nMartin\n"},{"id":"349250","messageId":"20180604144305.29909-1-martin.agren@gmail.com","threadId":"48495","inReplyTo":"CAN0heSr6dPa+--Mut0+3Zgy-mWF0d2762vKU=d08CKhR8BnKmA@mail.gmail.com","subject":"[PATCH] refspec: initalize `refspec_item` in `valid_fetch_refspec()`","fromName":"Martin Ågren","fromEmail":"martin.agren@gmail.com","sentAt":"2018-06-04T14:43:05Z","receivedAt":"2018-06-04T14:44:23Z","isPatch":true,"sender":{"key":"martin.agren@gmail.com","avatar":null},"body":"We allocate a `struct refspec_item` on the stack without initializing\nit. In particular, its `dst` and `src` members will contain some random\ndata from the stack. When we later call `refspec_item_clear()`, it will\ncall `free()` on those pointers. So if the call to `parse_refspec()` did\nnot assign to them, we will be freeing some random \"pointers\". This is\nundefined behavior.\n\nTo the best of my understanding, this cannot currently be triggered by\nuser-provided data. And for what it's worth, the test-suite does not\ntrigger this with SANITIZE=address. It can be provoked by calling\n`valid_fetch_refspec(\":*\")`.\n\nZero the struct, as is done in other users of `struct refspec_item`.\n\nSigned-off-by: Martin Ågren <martin.agren@gmail.com>\n---\nI found some time to look into this. It does not seem to be a\nuser-visible bug, so not particularly critical.\n\n refspec.c | 5 ++++-\n 1 file changed, 4 insertions(+), 1 deletion(-)\n\ndiff --git a/refspec.c b/refspec.c\nindex ada7854f7a..7dd7e361e5 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -189,7 +189,10 @@ void refspec_clear(struct refspec *rs)\n int valid_fetch_refspec(const char *fetch_refspec_str)\n {\n \tstruct refspec_item refspec;\n-\tint ret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n+\tint ret;\n+\n+\tmemset(&refspec, 0, sizeof(refspec));\n+\tret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n \trefspec_item_clear(&refspec);\n \treturn ret;\n }\n-- \n2.18.0.rc0.43.gb85e7bcbff\n\n"},{"id":"349290","messageId":"20180604173620.GB91449@google.com","threadId":"48495","inReplyTo":"20180604144305.29909-1-martin.agren@gmail.com","subject":"Re: [PATCH] refspec: initalize `refspec_item` in `valid_fetch_refspec()`","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-06-04T17:36:20Z","receivedAt":"2018-06-04T17:36:25Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 06/04, Martin Ågren wrote:\n> We allocate a `struct refspec_item` on the stack without initializing\n> it. In particular, its `dst` and `src` members will contain some random\n> data from the stack. When we later call `refspec_item_clear()`, it will\n> call `free()` on those pointers. So if the call to `parse_refspec()` did\n> not assign to them, we will be freeing some random \"pointers\". This is\n> undefined behavior.\n> \n> To the best of my understanding, this cannot currently be triggered by\n> user-provided data. And for what it's worth, the test-suite does not\n> trigger this with SANITIZE=address. It can be provoked by calling\n> `valid_fetch_refspec(\":*\")`.\n> \n> Zero the struct, as is done in other users of `struct refspec_item`.\n> \n> Signed-off-by: Martin Ågren <martin.agren@gmail.com>\n> ---\n> I found some time to look into this. It does not seem to be a\n> user-visible bug, so not particularly critical.\n\nThanks for fixing this.  I don't think I noticed this because at some\npoint in developing this series I had a memset call in parse_refspec.\nI don't remember why I ended up removing it, but maybe it would have\nbeen better to leave it in there.\n\n> \n>  refspec.c | 5 ++++-\n>  1 file changed, 4 insertions(+), 1 deletion(-)\n> \n> diff --git a/refspec.c b/refspec.c\n> index ada7854f7a..7dd7e361e5 100644\n> --- a/refspec.c\n> +++ b/refspec.c\n> @@ -189,7 +189,10 @@ void refspec_clear(struct refspec *rs)\n>  int valid_fetch_refspec(const char *fetch_refspec_str)\n>  {\n>  \tstruct refspec_item refspec;\n> -\tint ret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n> +\tint ret;\n> +\n> +\tmemset(&refspec, 0, sizeof(refspec));\n> +\tret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n>  \trefspec_item_clear(&refspec);\n>  \treturn ret;\n>  }\n> -- \n> 2.18.0.rc0.43.gb85e7bcbff\n> \n\n-- \nBrandon Williams\n"},{"id":"349300","messageId":"87tvqiz06t.fsf@evledraar.gmail.com","threadId":"48495","inReplyTo":"20180604144305.29909-1-martin.agren@gmail.com","subject":"Re: [PATCH] refspec: initalize `refspec_item` in `valid_fetch_refspec()`","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-04T21:55:22Z","receivedAt":"2018-06-04T21:55:28Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Jun 04 2018, Martin Ågren wrote:\n\n> We allocate a `struct refspec_item` on the stack without initializing\n> it. In particular, its `dst` and `src` members will contain some random\n> data from the stack. When we later call `refspec_item_clear()`, it will\n> call `free()` on those pointers. So if the call to `parse_refspec()` did\n> not assign to them, we will be freeing some random \"pointers\". This is\n> undefined behavior.\n>\n> To the best of my understanding, this cannot currently be triggered by\n> user-provided data. And for what it's worth, the test-suite does not\n> trigger this with SANITIZE=address. It can be provoked by calling\n> `valid_fetch_refspec(\":*\")`.\n>\n> Zero the struct, as is done in other users of `struct refspec_item`.\n>\n> Signed-off-by: Martin Ågren <martin.agren@gmail.com>\n> ---\n> I found some time to look into this. It does not seem to be a\n> user-visible bug, so not particularly critical.\n>\n>  refspec.c | 5 ++++-\n>  1 file changed, 4 insertions(+), 1 deletion(-)\n>\n> diff --git a/refspec.c b/refspec.c\n> index ada7854f7a..7dd7e361e5 100644\n> --- a/refspec.c\n> +++ b/refspec.c\n> @@ -189,7 +189,10 @@ void refspec_clear(struct refspec *rs)\n>  int valid_fetch_refspec(const char *fetch_refspec_str)\n>  {\n>  \tstruct refspec_item refspec;\n> -\tint ret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n> +\tint ret;\n> +\n> +\tmemset(&refspec, 0, sizeof(refspec));\n> +\tret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n>  \trefspec_item_clear(&refspec);\n>  \treturn ret;\n>  }\n\nI think this makes more sense instead of this fix:\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 99e73dae85..74a804f2e8 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -1077,7 +1077,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tif (option_required_reference.nr || option_optional_reference.nr)\n \t\tsetup_reference();\n\n-\trefspec_item_init(&refspec, value.buf, REFSPEC_FETCH);\n+\trefspec_item_init_or_die(&refspec, value.buf, REFSPEC_FETCH);\n\n \tstrbuf_reset(&value);\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 1f2ecf3a88..bb64631d98 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -684,7 +684,7 @@ static const char *get_tracking_branch(const char *remote, const char *refspec)\n \tconst char *spec_src;\n \tconst char *merge_branch;\n\n-\trefspec_item_init(&spec, refspec, REFSPEC_FETCH);\n+\trefspec_item_init_or_die(&spec, refspec, REFSPEC_FETCH);\n \tspec_src = spec.src;\n \tif (!*spec_src || !strcmp(spec_src, \"HEAD\"))\n \t\tspec_src = \"HEAD\";\ndiff --git a/refspec.c b/refspec.c\nindex 78edc48ae8..8806df0fd2 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -124,11 +124,16 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet\n \treturn 1;\n }\n\n-void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)\n+int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)\n {\n \tmemset(item, 0, sizeof(*item));\n+\tint ret = parse_refspec(item, refspec, fetch);\n+\treturn ret;\n+}\n\n-\tif (!parse_refspec(item, refspec, fetch))\n+void refspec_item_init_or_die(struct refspec_item *item, const char *refspec, int fetch)\n+{\n+\tif (!refspec_item_init(item, refspec, fetch))\n \t\tdie(\"Invalid refspec '%s'\", refspec);\n }\n\n@@ -152,7 +157,7 @@ void refspec_append(struct refspec *rs, const char *refspec)\n {\n \tstruct refspec_item item;\n\n-\trefspec_item_init(&item, refspec, rs->fetch);\n+\trefspec_item_init_or_die(&item, refspec, rs->fetch);\n\n \tALLOC_GROW(rs->items, rs->nr + 1, rs->alloc);\n \trs->items[rs->nr++] = item;\n@@ -191,7 +196,7 @@ void refspec_clear(struct refspec *rs)\n int valid_fetch_refspec(const char *fetch_refspec_str)\n {\n \tstruct refspec_item refspec;\n-\tint ret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n+\tint ret = refspec_item_init(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n \trefspec_item_clear(&refspec);\n \treturn ret;\n }\ndiff --git a/refspec.h b/refspec.h\nindex 3a9363887c..ed5d997f7f 100644\n--- a/refspec.h\n+++ b/refspec.h\n@@ -32,7 +32,8 @@ struct refspec {\n \tint fetch;\n };\n\n-void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch);\n+int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch);\n+void refspec_item_init_or_die(struct refspec_item *item, const char *refspec, int fetch);\n void refspec_item_clear(struct refspec_item *item);\n void refspec_init(struct refspec *rs, int fetch);\n void refspec_append(struct refspec *rs, const char *refspec);\n\nI.e. let's fix the bug, but with this admittedly more verbose fix we're\nleft with exactly two memset() in refspec.c, one for each type of struct\nthat's initialized by the API.\n\nThe reason this is difficult now is because the current API conflates\nthe init function with an init_or_die, which is what most callers want,\nso let's just split those concerns up. Then we're left with one init\nfunction that does the memset.\n"},{"id":"349316","messageId":"CAN0heSprfMRf97J0zeJ4+e7vdM-hk5Z3M1mX2hniBVrRByM4KQ@mail.gmail.com","threadId":"48495","inReplyTo":"87tvqiz06t.fsf@evledraar.gmail.com","subject":"Re: [PATCH] refspec: initalize `refspec_item` in `valid_fetch_refspec()`","fromName":"Martin Ågren","fromEmail":"martin.agren@gmail.com","sentAt":"2018-06-05T05:10:59Z","receivedAt":"2018-06-05T05:11:03Z","isPatch":true,"sender":{"key":"martin.agren@gmail.com","avatar":null},"body":"On 4 June 2018 at 23:55, Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n\n> I think this makes more sense instead of this fix:\n[...]\n> -void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)\n> +int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)\n>  {\n>         memset(item, 0, sizeof(*item));\n> +       int ret = parse_refspec(item, refspec, fetch);\n> +       return ret;\n> +}\n\nNit: Declaration after statement. Intriguingly, you do use a `ret`\nvariable, so I suspect you sort of thought about it but left the final\ncleaning up for later. ;-)\n\n> -void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch);\n> +int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch);\n> +void refspec_item_init_or_die(struct refspec_item *item, const char *refspec, int fetch);\n>  void refspec_item_clear(struct refspec_item *item);\n>  void refspec_init(struct refspec *rs, int fetch);\n>  void refspec_append(struct refspec *rs, const char *refspec);\n>\n> I.e. let's fix the bug, but with this admittedly more verbose fix we're\n> left with exactly two memset() in refspec.c, one for each type of struct\n> that's initialized by the API.\n>\n> The reason this is difficult now is because the current API conflates\n> the init function with an init_or_die, which is what most callers want,\n> so let's just split those concerns up. Then we're left with one init\n> function that does the memset.\n\nI didn't test this, but it looks sane in general IMHO, and should fix\nthe issue I saw. Should we be worried that someone might already depend\non `refspec_item_init()` to die, and we'll silently break that\nassumption?\n\nMartin\n"},{"id":"349360","messageId":"20180605162939.GA158365@google.com","threadId":"48495","inReplyTo":"87tvqiz06t.fsf@evledraar.gmail.com","subject":"Re: [PATCH] refspec: initalize `refspec_item` in `valid_fetch_refspec()`","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-06-05T16:29:39Z","receivedAt":"2018-06-05T16:29:45Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 06/04, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Mon, Jun 04 2018, Martin Ågren wrote:\n> \n> > We allocate a `struct refspec_item` on the stack without initializing\n> > it. In particular, its `dst` and `src` members will contain some random\n> > data from the stack. When we later call `refspec_item_clear()`, it will\n> > call `free()` on those pointers. So if the call to `parse_refspec()` did\n> > not assign to them, we will be freeing some random \"pointers\". This is\n> > undefined behavior.\n> >\n> > To the best of my understanding, this cannot currently be triggered by\n> > user-provided data. And for what it's worth, the test-suite does not\n> > trigger this with SANITIZE=address. It can be provoked by calling\n> > `valid_fetch_refspec(\":*\")`.\n> >\n> > Zero the struct, as is done in other users of `struct refspec_item`.\n> >\n> > Signed-off-by: Martin Ågren <martin.agren@gmail.com>\n> > ---\n> > I found some time to look into this. It does not seem to be a\n> > user-visible bug, so not particularly critical.\n> >\n> >  refspec.c | 5 ++++-\n> >  1 file changed, 4 insertions(+), 1 deletion(-)\n> >\n> > diff --git a/refspec.c b/refspec.c\n> > index ada7854f7a..7dd7e361e5 100644\n> > --- a/refspec.c\n> > +++ b/refspec.c\n> > @@ -189,7 +189,10 @@ void refspec_clear(struct refspec *rs)\n> >  int valid_fetch_refspec(const char *fetch_refspec_str)\n> >  {\n> >  \tstruct refspec_item refspec;\n> > -\tint ret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n> > +\tint ret;\n> > +\n> > +\tmemset(&refspec, 0, sizeof(refspec));\n> > +\tret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n> >  \trefspec_item_clear(&refspec);\n> >  \treturn ret;\n> >  }\n> \n> I think this makes more sense instead of this fix:\n\nI like this diff.  The only nit I have is the same as what Martin\npointed out.  At least this way all memory will be initialized by the\ntime a call to parse_refspec is made.\n\n> \n> diff --git a/builtin/clone.c b/builtin/clone.c\n> index 99e73dae85..74a804f2e8 100644\n> --- a/builtin/clone.c\n> +++ b/builtin/clone.c\n> @@ -1077,7 +1077,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \tif (option_required_reference.nr || option_optional_reference.nr)\n>  \t\tsetup_reference();\n> \n> -\trefspec_item_init(&refspec, value.buf, REFSPEC_FETCH);\n> +\trefspec_item_init_or_die(&refspec, value.buf, REFSPEC_FETCH);\n> \n>  \tstrbuf_reset(&value);\n> \n> diff --git a/builtin/pull.c b/builtin/pull.c\n> index 1f2ecf3a88..bb64631d98 100644\n> --- a/builtin/pull.c\n> +++ b/builtin/pull.c\n> @@ -684,7 +684,7 @@ static const char *get_tracking_branch(const char *remote, const char *refspec)\n>  \tconst char *spec_src;\n>  \tconst char *merge_branch;\n> \n> -\trefspec_item_init(&spec, refspec, REFSPEC_FETCH);\n> +\trefspec_item_init_or_die(&spec, refspec, REFSPEC_FETCH);\n>  \tspec_src = spec.src;\n>  \tif (!*spec_src || !strcmp(spec_src, \"HEAD\"))\n>  \t\tspec_src = \"HEAD\";\n> diff --git a/refspec.c b/refspec.c\n> index 78edc48ae8..8806df0fd2 100644\n> --- a/refspec.c\n> +++ b/refspec.c\n> @@ -124,11 +124,16 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet\n>  \treturn 1;\n>  }\n> \n> -void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)\n> +int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)\n>  {\n>  \tmemset(item, 0, sizeof(*item));\n> +\tint ret = parse_refspec(item, refspec, fetch);\n> +\treturn ret;\n> +}\n> \n> -\tif (!parse_refspec(item, refspec, fetch))\n> +void refspec_item_init_or_die(struct refspec_item *item, const char *refspec, int fetch)\n> +{\n> +\tif (!refspec_item_init(item, refspec, fetch))\n>  \t\tdie(\"Invalid refspec '%s'\", refspec);\n>  }\n> \n> @@ -152,7 +157,7 @@ void refspec_append(struct refspec *rs, const char *refspec)\n>  {\n>  \tstruct refspec_item item;\n> \n> -\trefspec_item_init(&item, refspec, rs->fetch);\n> +\trefspec_item_init_or_die(&item, refspec, rs->fetch);\n> \n>  \tALLOC_GROW(rs->items, rs->nr + 1, rs->alloc);\n>  \trs->items[rs->nr++] = item;\n> @@ -191,7 +196,7 @@ void refspec_clear(struct refspec *rs)\n>  int valid_fetch_refspec(const char *fetch_refspec_str)\n>  {\n>  \tstruct refspec_item refspec;\n> -\tint ret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n> +\tint ret = refspec_item_init(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n>  \trefspec_item_clear(&refspec);\n>  \treturn ret;\n>  }\n> diff --git a/refspec.h b/refspec.h\n> index 3a9363887c..ed5d997f7f 100644\n> --- a/refspec.h\n> +++ b/refspec.h\n> @@ -32,7 +32,8 @@ struct refspec {\n>  \tint fetch;\n>  };\n> \n> -void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch);\n> +int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch);\n> +void refspec_item_init_or_die(struct refspec_item *item, const char *refspec, int fetch);\n>  void refspec_item_clear(struct refspec_item *item);\n>  void refspec_init(struct refspec *rs, int fetch);\n>  void refspec_append(struct refspec *rs, const char *refspec);\n> \n> I.e. let's fix the bug, but with this admittedly more verbose fix we're\n> left with exactly two memset() in refspec.c, one for each type of struct\n> that's initialized by the API.\n> \n> The reason this is difficult now is because the current API conflates\n> the init function with an init_or_die, which is what most callers want,\n> so let's just split those concerns up. Then we're left with one init\n> function that does the memset.\n\n-- \nBrandon Williams\n"},{"id":"349382","messageId":"20180605195440.8505-1-avarab@gmail.com","threadId":"48495","inReplyTo":"20180605162939.GA158365@google.com","subject":"[PATCH 0/3] refspec: refactor & fix free() behavior","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-05T19:54:37Z","receivedAt":"2018-06-05T19:54:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since Martin & Brandon both liked this direction I've fixed it\nup.\n\nMartin: I didn't want to be the author of the actual fix for the bug\nyou found, so I rewrote your commit in 3/3. The diff is different, and\nI slightly modified the 3rd paragraph of the commit message & added my\nsign-off, but otherwise it's the same.\n\nMartin Ågren (1):\n  refspec: initalize `refspec_item` in `valid_fetch_refspec()`\n\nÆvar Arnfjörð Bjarmason (2):\n  refspec: s/refspec_item_init/&_or_die/g\n  refspec: add back a refspec_item_init() function\n\n builtin/clone.c |  2 +-\n builtin/pull.c  |  2 +-\n refspec.c       | 13 +++++++++----\n refspec.h       |  5 ++++-\n 4 files changed, 15 insertions(+), 7 deletions(-)\n\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349383","messageId":"20180605195440.8505-3-avarab@gmail.com","threadId":"48495","inReplyTo":"20180605195440.8505-1-avarab@gmail.com","subject":"[PATCH 2/3] refspec: add back a refspec_item_init() function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-05T19:54:39Z","receivedAt":"2018-06-05T19:54:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Re-add the non-fatal version of refspec_item_init_or_die() renamed\naway in an earlier change to get a more minimal diff. This should be\nused by callers that have their own error handling.\n\nThis new function could be marked \"static\" since nothing outside of\nrefspec.c uses it, but expecting future use of it, let's make it\navailable to other users.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n refspec.c | 10 +++++++---\n refspec.h |  2 ++\n 2 files changed, 9 insertions(+), 3 deletions(-)\n\ndiff --git a/refspec.c b/refspec.c\nindex 0fd392e96b..a35493e35e 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -124,12 +124,16 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet\n \treturn 1;\n }\n \n-void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,\n-\t\t\t      int fetch)\n+int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)\n {\n \tmemset(item, 0, sizeof(*item));\n+\treturn parse_refspec(item, refspec, fetch);\n+}\n \n-\tif (!parse_refspec(item, refspec, fetch))\n+void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,\n+\t\t\t      int fetch)\n+{\n+\tif (!refspec_item_init(item, refspec, fetch))\n \t\tdie(\"Invalid refspec '%s'\", refspec);\n }\n \ndiff --git a/refspec.h b/refspec.h\nindex 4caaf1f8e3..9b6e64a824 100644\n--- a/refspec.h\n+++ b/refspec.h\n@@ -32,6 +32,8 @@ struct refspec {\n \tint fetch;\n };\n \n+int refspec_item_init(struct refspec_item *item, const char *refspec,\n+\t\t      int fetch);\n void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,\n \t\t\t      int fetch);\n void refspec_item_clear(struct refspec_item *item);\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349384","messageId":"20180605195440.8505-2-avarab@gmail.com","threadId":"48495","inReplyTo":"20180605195440.8505-1-avarab@gmail.com","subject":"[PATCH 1/3] refspec: s/refspec_item_init/&_or_die/g","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-05T19:54:38Z","receivedAt":"2018-06-05T19:54:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rename the refspec_item_init() function introduced in\n6d4c057859 (\"refspec: introduce struct refspec\", 2018-05-16) to\nrefspec_item_init_or_die().\n\nThis follows the convention of other *_or_die() functions, and is done\nin preparation for making it a wrapper for a non-fatal variant.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/clone.c | 2 +-\n builtin/pull.c  | 2 +-\n refspec.c       | 5 +++--\n refspec.h       | 3 ++-\n 4 files changed, 7 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex 99e73dae85..74a804f2e8 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -1077,7 +1077,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tif (option_required_reference.nr || option_optional_reference.nr)\n \t\tsetup_reference();\n \n-\trefspec_item_init(&refspec, value.buf, REFSPEC_FETCH);\n+\trefspec_item_init_or_die(&refspec, value.buf, REFSPEC_FETCH);\n \n \tstrbuf_reset(&value);\n \ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 1f2ecf3a88..bb64631d98 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -684,7 +684,7 @@ static const char *get_tracking_branch(const char *remote, const char *refspec)\n \tconst char *spec_src;\n \tconst char *merge_branch;\n \n-\trefspec_item_init(&spec, refspec, REFSPEC_FETCH);\n+\trefspec_item_init_or_die(&spec, refspec, REFSPEC_FETCH);\n \tspec_src = spec.src;\n \tif (!*spec_src || !strcmp(spec_src, \"HEAD\"))\n \t\tspec_src = \"HEAD\";\ndiff --git a/refspec.c b/refspec.c\nindex 78edc48ae8..0fd392e96b 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -124,7 +124,8 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet\n \treturn 1;\n }\n \n-void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)\n+void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,\n+\t\t\t      int fetch)\n {\n \tmemset(item, 0, sizeof(*item));\n \n@@ -152,7 +153,7 @@ void refspec_append(struct refspec *rs, const char *refspec)\n {\n \tstruct refspec_item item;\n \n-\trefspec_item_init(&item, refspec, rs->fetch);\n+\trefspec_item_init_or_die(&item, refspec, rs->fetch);\n \n \tALLOC_GROW(rs->items, rs->nr + 1, rs->alloc);\n \trs->items[rs->nr++] = item;\ndiff --git a/refspec.h b/refspec.h\nindex 3a9363887c..4caaf1f8e3 100644\n--- a/refspec.h\n+++ b/refspec.h\n@@ -32,7 +32,8 @@ struct refspec {\n \tint fetch;\n };\n \n-void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch);\n+void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,\n+\t\t\t      int fetch);\n void refspec_item_clear(struct refspec_item *item);\n void refspec_init(struct refspec *rs, int fetch);\n void refspec_append(struct refspec *rs, const char *refspec);\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349385","messageId":"20180605195440.8505-4-avarab@gmail.com","threadId":"48495","inReplyTo":"20180605195440.8505-1-avarab@gmail.com","subject":"[PATCH 3/3] refspec: initalize `refspec_item` in `valid_fetch_refspec()`","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-05T19:54:40Z","receivedAt":"2018-06-05T19:55:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"From: Martin Ågren <martin.agren@gmail.com>\n\nWe allocate a `struct refspec_item` on the stack without initializing\nit. In particular, its `dst` and `src` members will contain some random\ndata from the stack. When we later call `refspec_item_clear()`, it will\ncall `free()` on those pointers. So if the call to `parse_refspec()` did\nnot assign to them, we will be freeing some random \"pointers\". This is\nundefined behavior.\n\nTo the best of my understanding, this cannot currently be triggered by\nuser-provided data. And for what it's worth, the test-suite does not\ntrigger this with SANITIZE=address. It can be provoked by calling\n`valid_fetch_refspec(\":*\")`.\n\nZero the struct, as is done in other users of `struct refspec_item` by\nusing the refspec_item_init() initialization function.\n\nSigned-off-by: Martin Ågren <martin.agren@gmail.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n refspec.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/refspec.c b/refspec.c\nindex a35493e35e..e8010dce0c 100644\n--- a/refspec.c\n+++ b/refspec.c\n@@ -196,7 +196,7 @@ void refspec_clear(struct refspec *rs)\n int valid_fetch_refspec(const char *fetch_refspec_str)\n {\n \tstruct refspec_item refspec;\n-\tint ret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n+\tint ret = refspec_item_init(&refspec, fetch_refspec_str, REFSPEC_FETCH);\n \trefspec_item_clear(&refspec);\n \treturn ret;\n }\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349386","messageId":"20180605195816.GC158365@google.com","threadId":"48495","inReplyTo":"20180605195440.8505-1-avarab@gmail.com","subject":"Re: [PATCH 0/3] refspec: refactor & fix free() behavior","fromName":"Brandon Williams","fromEmail":"bmwill@google.com","sentAt":"2018-06-05T19:58:16Z","receivedAt":"2018-06-05T19:58:21Z","isPatch":true,"sender":{"key":"bwilliams.eng@gmail.com","avatar":null},"body":"On 06/05, Ævar Arnfjörð Bjarmason wrote:\n> Since Martin & Brandon both liked this direction I've fixed it\n> up.\n> \n> Martin: I didn't want to be the author of the actual fix for the bug\n> you found, so I rewrote your commit in 3/3. The diff is different, and\n> I slightly modified the 3rd paragraph of the commit message & added my\n> sign-off, but otherwise it's the same.\n\nThanks for writing up a proper patch series for this fix.  I liked\nbreaking up your diff into two different patches to make it clear that\nall callers of refpsec_item_init relying on dieing.\n\n> \n> Martin Ågren (1):\n>   refspec: initalize `refspec_item` in `valid_fetch_refspec()`\n> \n> Ævar Arnfjörð Bjarmason (2):\n>   refspec: s/refspec_item_init/&_or_die/g\n>   refspec: add back a refspec_item_init() function\n> \n>  builtin/clone.c |  2 +-\n>  builtin/pull.c  |  2 +-\n>  refspec.c       | 13 +++++++++----\n>  refspec.h       |  5 ++++-\n>  4 files changed, 15 insertions(+), 7 deletions(-)\n> \n> -- \n> 2.17.0.290.gded63e768a\n> \n\n-- \nBrandon Williams\n"},{"id":"349387","messageId":"CAN0heSqsxzTmGSaLLOn0FiWhjt9kBuRAtfxua7iVGoXeLxobww@mail.gmail.com","threadId":"48495","inReplyTo":"20180605195816.GC158365@google.com","subject":"Re: [PATCH 0/3] refspec: refactor & fix free() behavior","fromName":"Martin Ågren","fromEmail":"martin.agren@gmail.com","sentAt":"2018-06-05T20:20:11Z","receivedAt":"2018-06-05T20:20:15Z","isPatch":true,"sender":{"key":"martin.agren@gmail.com","avatar":null},"body":"On 5 June 2018 at 21:58, Brandon Williams <bmwill@google.com> wrote:\n> On 06/05, Ævar Arnfjörð Bjarmason wrote:\n>> Since Martin & Brandon both liked this direction I've fixed it\n>> up.\n>>\n>> Martin: I didn't want to be the author of the actual fix for the bug\n>> you found, so I rewrote your commit in 3/3. The diff is different, and\n>> I slightly modified the 3rd paragraph of the commit message & added my\n>> sign-off, but otherwise it's the same.\n>\n> Thanks for writing up a proper patch series for this fix.  I liked\n> breaking up your diff into two different patches to make it clear that\n> all callers of refpsec_item_init relying on dieing.\n\nMe too.\n\n>> Martin Ågren (1):\n>>   refspec: initalize `refspec_item` in `valid_fetch_refspec()`\n\nI was a bit surprised at first that this wasn't a \"while at it\" in the\nsecond patch, but on second thought, it does make sense to keep this\nseparate. Thanks for picking this up and polishing it.\n\nJust noticed: s/initalize/initialize/. That would be my fault...\n\nMartin\n"}]}