/src/git/builtin/ls-remote.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "builtin.h" |
2 | | #include "gettext.h" |
3 | | #include "hex.h" |
4 | | #include "transport.h" |
5 | | #include "pkt-line.h" |
6 | | #include "ref-filter.h" |
7 | | #include "remote.h" |
8 | | #include "parse-options.h" |
9 | | #include "wildmatch.h" |
10 | | |
11 | | static const char * const ls_remote_usage[] = { |
12 | | N_("git ls-remote [--branches] [--tags] [--refs] [--upload-pack=<exec>]\n" |
13 | | " [-q | --quiet] [--exit-code] [--get-url] [--sort=<key>]\n" |
14 | | " [--symref] [<repository> [<patterns>...]]"), |
15 | | NULL |
16 | | }; |
17 | | |
18 | | /* |
19 | | * Is there one among the list of patterns that match the tail part |
20 | | * of the path? |
21 | | */ |
22 | | static int tail_match(const struct strvec *pattern, const char *path) |
23 | 0 | { |
24 | 0 | char *pathbuf; |
25 | |
|
26 | 0 | if (!pattern->nr) |
27 | 0 | return 1; /* no restriction */ |
28 | | |
29 | 0 | pathbuf = xstrfmt("/%s", path); |
30 | 0 | for (size_t i = 0; i < pattern->nr; i++) { |
31 | 0 | if (!wildmatch(pattern->v[i], pathbuf, 0)) { |
32 | 0 | free(pathbuf); |
33 | 0 | return 1; |
34 | 0 | } |
35 | 0 | } |
36 | 0 | free(pathbuf); |
37 | 0 | return 0; |
38 | 0 | } |
39 | | |
40 | | int cmd_ls_remote(int argc, const char **argv, const char *prefix) |
41 | 0 | { |
42 | 0 | const char *dest = NULL; |
43 | 0 | unsigned flags = 0; |
44 | 0 | int get_url = 0; |
45 | 0 | int quiet = 0; |
46 | 0 | int status = 0; |
47 | 0 | int show_symref_target = 0; |
48 | 0 | const char *uploadpack = NULL; |
49 | 0 | struct strvec pattern = STRVEC_INIT; |
50 | 0 | struct transport_ls_refs_options transport_options = |
51 | 0 | TRANSPORT_LS_REFS_OPTIONS_INIT; |
52 | 0 | int i; |
53 | 0 | struct string_list server_options = STRING_LIST_INIT_DUP; |
54 | |
|
55 | 0 | struct remote *remote; |
56 | 0 | struct transport *transport; |
57 | 0 | const struct ref *ref; |
58 | 0 | struct ref_array ref_array; |
59 | 0 | struct ref_sorting *sorting; |
60 | 0 | struct string_list sorting_options = STRING_LIST_INIT_DUP; |
61 | |
|
62 | 0 | struct option options[] = { |
63 | 0 | OPT__QUIET(&quiet, N_("do not print remote URL")), |
64 | 0 | OPT_STRING(0, "upload-pack", &uploadpack, N_("exec"), |
65 | 0 | N_("path of git-upload-pack on the remote host")), |
66 | 0 | { OPTION_STRING, 0, "exec", &uploadpack, N_("exec"), |
67 | 0 | N_("path of git-upload-pack on the remote host"), |
68 | 0 | PARSE_OPT_HIDDEN }, |
69 | 0 | OPT_BIT('t', "tags", &flags, N_("limit to tags"), REF_TAGS), |
70 | 0 | OPT_BIT('b', "branches", &flags, N_("limit to branches"), REF_BRANCHES), |
71 | 0 | OPT_BIT_F('h', "heads", &flags, |
72 | 0 | N_("deprecated synonym for --branches"), REF_BRANCHES, |
73 | 0 | PARSE_OPT_HIDDEN), |
74 | 0 | OPT_BIT(0, "refs", &flags, N_("do not show peeled tags"), REF_NORMAL), |
75 | 0 | OPT_BOOL(0, "get-url", &get_url, |
76 | 0 | N_("take url.<base>.insteadOf into account")), |
77 | 0 | OPT_REF_SORT(&sorting_options), |
78 | 0 | OPT_SET_INT_F(0, "exit-code", &status, |
79 | 0 | N_("exit with exit code 2 if no matching refs are found"), |
80 | 0 | 2, PARSE_OPT_NOCOMPLETE), |
81 | 0 | OPT_BOOL(0, "symref", &show_symref_target, |
82 | 0 | N_("show underlying ref in addition to the object pointed by it")), |
83 | 0 | OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")), |
84 | 0 | OPT_END() |
85 | 0 | }; |
86 | |
|
87 | 0 | memset(&ref_array, 0, sizeof(ref_array)); |
88 | |
|
89 | 0 | argc = parse_options(argc, argv, prefix, options, ls_remote_usage, |
90 | 0 | PARSE_OPT_STOP_AT_NON_OPTION); |
91 | 0 | dest = argv[0]; |
92 | | |
93 | | /* |
94 | | * TODO: This is buggy, but required for transport helpers. When a |
95 | | * transport helper advertises a "refspec", then we'd add that to a |
96 | | * list of refspecs via `refspec_append()`, which transitively depends |
97 | | * on `the_hash_algo`. Thus, when the hash algorithm isn't properly set |
98 | | * up, this would lead to a segfault. |
99 | | * |
100 | | * We really should fix this in the transport helper logic such that we |
101 | | * lazily parse refspec capabilities _after_ we have learned about the |
102 | | * remote's object format. Otherwise, we may end up misparsing refspecs |
103 | | * depending on what object hash the remote uses. |
104 | | */ |
105 | 0 | if (!the_repository->hash_algo) |
106 | 0 | repo_set_hash_algo(the_repository, GIT_HASH_SHA1); |
107 | |
|
108 | 0 | packet_trace_identity("ls-remote"); |
109 | |
|
110 | 0 | for (int i = 1; i < argc; i++) |
111 | 0 | strvec_pushf(&pattern, "*/%s", argv[i]); |
112 | |
|
113 | 0 | if (flags & REF_TAGS) |
114 | 0 | strvec_push(&transport_options.ref_prefixes, "refs/tags/"); |
115 | 0 | if (flags & REF_BRANCHES) |
116 | 0 | strvec_push(&transport_options.ref_prefixes, "refs/heads/"); |
117 | |
|
118 | 0 | remote = remote_get(dest); |
119 | 0 | if (!remote) { |
120 | 0 | if (dest) |
121 | 0 | die("bad repository '%s'", dest); |
122 | 0 | die("No remote configured to list refs from."); |
123 | 0 | } |
124 | | |
125 | 0 | if (get_url) { |
126 | 0 | printf("%s\n", remote->url.v[0]); |
127 | 0 | return 0; |
128 | 0 | } |
129 | | |
130 | 0 | transport = transport_get(remote, NULL); |
131 | 0 | if (uploadpack) |
132 | 0 | transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack); |
133 | 0 | if (server_options.nr) |
134 | 0 | transport->server_options = &server_options; |
135 | |
|
136 | 0 | ref = transport_get_remote_refs(transport, &transport_options); |
137 | 0 | if (ref) { |
138 | 0 | int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport)); |
139 | 0 | repo_set_hash_algo(the_repository, hash_algo); |
140 | 0 | } |
141 | |
|
142 | 0 | if (!dest && !quiet) |
143 | 0 | fprintf(stderr, "From %s\n", remote->url.v[0]); |
144 | 0 | for ( ; ref; ref = ref->next) { |
145 | 0 | struct ref_array_item *item; |
146 | 0 | if (!check_ref_type(ref, flags)) |
147 | 0 | continue; |
148 | 0 | if (!tail_match(&pattern, ref->name)) |
149 | 0 | continue; |
150 | 0 | item = ref_array_push(&ref_array, ref->name, &ref->old_oid); |
151 | 0 | item->symref = xstrdup_or_null(ref->symref); |
152 | 0 | } |
153 | |
|
154 | 0 | sorting = ref_sorting_options(&sorting_options); |
155 | 0 | ref_array_sort(sorting, &ref_array); |
156 | |
|
157 | 0 | for (i = 0; i < ref_array.nr; i++) { |
158 | 0 | const struct ref_array_item *ref = ref_array.items[i]; |
159 | 0 | if (show_symref_target && ref->symref) |
160 | 0 | printf("ref: %s\t%s\n", ref->symref, ref->refname); |
161 | 0 | printf("%s\t%s\n", oid_to_hex(&ref->objectname), ref->refname); |
162 | 0 | status = 0; /* we found something */ |
163 | 0 | } |
164 | |
|
165 | 0 | ref_sorting_release(sorting); |
166 | 0 | ref_array_clear(&ref_array); |
167 | 0 | if (transport_disconnect(transport)) |
168 | 0 | status = 1; |
169 | 0 | transport_ls_refs_options_release(&transport_options); |
170 | |
|
171 | 0 | strvec_clear(&pattern); |
172 | 0 | return status; |
173 | 0 | } |