Git fork
at reftables-rust 345 lines 9.5 kB view raw
1#define USE_THE_REPOSITORY_VARIABLE 2#include "builtin.h" 3#include "config.h" 4#include "environment.h" 5#include "gettext.h" 6#include "hex.h" 7#include "refs/refs-internal.h" 8#include "object-name.h" 9#include "odb.h" 10#include "object.h" 11#include "string-list.h" 12#include "parse-options.h" 13 14static const char * const show_ref_usage[] = { 15 N_("git show-ref [--head] [-d | --dereference]\n" 16 " [-s | --hash[=<n>]] [--abbrev[=<n>]] [--branches] [--tags]\n" 17 " [--] [<pattern>...]"), 18 N_("git show-ref --verify [-q | --quiet] [-d | --dereference]\n" 19 " [-s | --hash[=<n>]] [--abbrev[=<n>]]\n" 20 " [--] [<ref>...]"), 21 N_("git show-ref --exclude-existing[=<pattern>]"), 22 N_("git show-ref --exists <ref>"), 23 NULL 24}; 25 26struct show_one_options { 27 int quiet; 28 int hash_only; 29 int abbrev; 30 int deref_tags; 31}; 32 33static void show_one(const struct show_one_options *opts, 34 const char *refname, const struct object_id *oid) 35{ 36 const char *hex; 37 struct object_id peeled; 38 39 if (!odb_has_object(the_repository->objects, oid, 40 HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) 41 die("git show-ref: bad ref %s (%s)", refname, 42 oid_to_hex(oid)); 43 44 if (opts->quiet) 45 return; 46 47 hex = repo_find_unique_abbrev(the_repository, oid, opts->abbrev); 48 if (opts->hash_only) 49 printf("%s\n", hex); 50 else 51 printf("%s %s\n", hex, refname); 52 53 if (!opts->deref_tags) 54 return; 55 56 if (!peel_iterated_oid(the_repository, oid, &peeled)) { 57 hex = repo_find_unique_abbrev(the_repository, &peeled, opts->abbrev); 58 printf("%s %s^{}\n", hex, refname); 59 } 60} 61 62struct show_ref_data { 63 const struct show_one_options *show_one_opts; 64 const char **patterns; 65 int found_match; 66 int show_head; 67}; 68 69static int show_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid, 70 int flag UNUSED, void *cbdata) 71{ 72 struct show_ref_data *data = cbdata; 73 74 if (data->show_head && !strcmp(refname, "HEAD")) 75 goto match; 76 77 if (data->patterns) { 78 int reflen = strlen(refname); 79 const char **p = data->patterns, *m; 80 while ((m = *p++) != NULL) { 81 int len = strlen(m); 82 if (len > reflen) 83 continue; 84 if (memcmp(m, refname + reflen - len, len)) 85 continue; 86 if (len == reflen) 87 goto match; 88 if (refname[reflen - len - 1] == '/') 89 goto match; 90 } 91 return 0; 92 } 93 94match: 95 data->found_match++; 96 97 show_one(data->show_one_opts, refname, oid); 98 99 return 0; 100} 101 102static int add_existing(const char *refname, 103 const char *referent UNUSED, 104 const struct object_id *oid UNUSED, 105 int flag UNUSED, void *cbdata) 106{ 107 struct string_list *list = (struct string_list *)cbdata; 108 string_list_insert(list, refname); 109 return 0; 110} 111 112struct exclude_existing_options { 113 /* 114 * We need an explicit `enabled` field because it is perfectly valid 115 * for `pattern` to be `NULL` even if `--exclude-existing` was given. 116 */ 117 int enabled; 118 const char *pattern; 119}; 120 121/* 122 * read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input, 123 * and 124 * (1) strip "^{}" at the end of line if any; 125 * (2) ignore if match is provided and does not head-match refname; 126 * (3) warn if refname is not a well-formed refname and skip; 127 * (4) ignore if refname is a ref that exists in the local repository; 128 * (5) otherwise output the line. 129 */ 130static int cmd_show_ref__exclude_existing(const struct exclude_existing_options *opts) 131{ 132 struct string_list existing_refs = STRING_LIST_INIT_DUP; 133 char buf[1024]; 134 int patternlen = opts->pattern ? strlen(opts->pattern) : 0; 135 136 refs_for_each_ref(get_main_ref_store(the_repository), add_existing, 137 &existing_refs); 138 while (fgets(buf, sizeof(buf), stdin)) { 139 char *ref; 140 int len = strlen(buf); 141 142 if (len > 0 && buf[len - 1] == '\n') 143 buf[--len] = '\0'; 144 if (3 <= len && !strcmp(buf + len - 3, "^{}")) { 145 len -= 3; 146 buf[len] = '\0'; 147 } 148 for (ref = buf + len; buf < ref; ref--) 149 if (isspace(ref[-1])) 150 break; 151 if (opts->pattern) { 152 int reflen = buf + len - ref; 153 if (reflen < patternlen) 154 continue; 155 if (strncmp(ref, opts->pattern, patternlen)) 156 continue; 157 } 158 if (check_refname_format(ref, 0)) { 159 warning("ref '%s' ignored", ref); 160 continue; 161 } 162 if (!string_list_has_string(&existing_refs, ref)) { 163 printf("%s\n", buf); 164 } 165 } 166 167 string_list_clear(&existing_refs, 0); 168 return 0; 169} 170 171static int cmd_show_ref__verify(const struct show_one_options *show_one_opts, 172 const char **refs) 173{ 174 if (!refs || !*refs) 175 die("--verify requires a reference"); 176 177 while (*refs) { 178 struct object_id oid; 179 180 if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) && 181 !refs_read_ref(get_main_ref_store(the_repository), *refs, &oid)) { 182 show_one(show_one_opts, *refs, &oid); 183 } 184 else if (!show_one_opts->quiet) 185 die("'%s' - not a valid ref", *refs); 186 else 187 return 1; 188 refs++; 189 } 190 191 return 0; 192} 193 194struct patterns_options { 195 int show_head; 196 int branches_only; 197 int tags_only; 198}; 199 200static int cmd_show_ref__patterns(const struct patterns_options *opts, 201 const struct show_one_options *show_one_opts, 202 const char **patterns) 203{ 204 struct show_ref_data show_ref_data = { 205 .show_one_opts = show_one_opts, 206 .show_head = opts->show_head, 207 }; 208 209 if (patterns && *patterns) 210 show_ref_data.patterns = patterns; 211 212 if (opts->show_head) 213 refs_head_ref(get_main_ref_store(the_repository), show_ref, 214 &show_ref_data); 215 if (opts->branches_only || opts->tags_only) { 216 if (opts->branches_only) 217 refs_for_each_fullref_in(get_main_ref_store(the_repository), 218 "refs/heads/", NULL, 219 show_ref, &show_ref_data); 220 if (opts->tags_only) 221 refs_for_each_fullref_in(get_main_ref_store(the_repository), 222 "refs/tags/", NULL, show_ref, 223 &show_ref_data); 224 } else { 225 refs_for_each_ref(get_main_ref_store(the_repository), 226 show_ref, &show_ref_data); 227 } 228 if (!show_ref_data.found_match) 229 return 1; 230 231 return 0; 232} 233 234static int cmd_show_ref__exists(const char **refs) 235{ 236 struct strbuf unused_referent = STRBUF_INIT; 237 struct object_id unused_oid; 238 unsigned int unused_type; 239 int failure_errno = 0; 240 const char *ref; 241 int ret = 0; 242 243 if (!refs || !*refs) 244 die("--exists requires a reference"); 245 ref = *refs++; 246 if (*refs) 247 die("--exists requires exactly one reference"); 248 249 if (refs_read_raw_ref(get_main_ref_store(the_repository), ref, 250 &unused_oid, &unused_referent, &unused_type, 251 &failure_errno)) { 252 if (failure_errno == ENOENT || failure_errno == EISDIR) { 253 error(_("reference does not exist")); 254 ret = 2; 255 } else { 256 errno = failure_errno; 257 error_errno(_("failed to look up reference")); 258 ret = 1; 259 } 260 261 goto out; 262 } 263 264out: 265 strbuf_release(&unused_referent); 266 return ret; 267} 268 269static int hash_callback(const struct option *opt, const char *arg, int unset) 270{ 271 struct show_one_options *opts = opt->value; 272 struct option abbrev_opt = *opt; 273 274 opts->hash_only = 1; 275 /* Use full length SHA1 if no argument */ 276 if (!arg) 277 return 0; 278 279 abbrev_opt.value = &opts->abbrev; 280 return parse_opt_abbrev_cb(&abbrev_opt, arg, unset); 281} 282 283static int exclude_existing_callback(const struct option *opt, const char *arg, 284 int unset) 285{ 286 struct exclude_existing_options *opts = opt->value; 287 BUG_ON_OPT_NEG(unset); 288 opts->enabled = 1; 289 opts->pattern = arg; 290 return 0; 291} 292 293int cmd_show_ref(int argc, 294const char **argv, 295const char *prefix, 296struct repository *repo UNUSED) 297{ 298 struct exclude_existing_options exclude_existing_opts = {0}; 299 struct patterns_options patterns_opts = {0}; 300 struct show_one_options show_one_opts = {0}; 301 int verify = 0, exists = 0; 302 const struct option show_ref_options[] = { 303 OPT_BOOL(0, "tags", &patterns_opts.tags_only, N_("only show tags (can be combined with --branches)")), 304 OPT_BOOL(0, "branches", &patterns_opts.branches_only, N_("only show branches (can be combined with --tags)")), 305 OPT_HIDDEN_BOOL(0, "heads", &patterns_opts.branches_only, 306 N_("deprecated synonym for --branches")), 307 OPT_BOOL(0, "exists", &exists, N_("check for reference existence without resolving")), 308 OPT_BOOL(0, "verify", &verify, N_("stricter reference checking, " 309 "requires exact ref path")), 310 OPT_HIDDEN_BOOL('h', NULL, &patterns_opts.show_head, 311 N_("show the HEAD reference, even if it would be filtered out")), 312 OPT_BOOL(0, "head", &patterns_opts.show_head, 313 N_("show the HEAD reference, even if it would be filtered out")), 314 OPT_BOOL('d', "dereference", &show_one_opts.deref_tags, 315 N_("dereference tags into object IDs")), 316 OPT_CALLBACK_F('s', "hash", &show_one_opts, N_("n"), 317 N_("only show SHA1 hash using <n> digits"), 318 PARSE_OPT_OPTARG, &hash_callback), 319 OPT__ABBREV(&show_one_opts.abbrev), 320 OPT__QUIET(&show_one_opts.quiet, 321 N_("do not print results to stdout (useful with --verify)")), 322 OPT_CALLBACK_F(0, "exclude-existing", &exclude_existing_opts, 323 N_("pattern"), N_("show refs from stdin that aren't in local repository"), 324 PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback), 325 OPT_END() 326 }; 327 328 repo_config(the_repository, git_default_config, NULL); 329 330 argc = parse_options(argc, argv, prefix, show_ref_options, 331 show_ref_usage, 0); 332 333 die_for_incompatible_opt3(exclude_existing_opts.enabled, "--exclude-existing", 334 verify, "--verify", 335 exists, "--exists"); 336 337 if (exclude_existing_opts.enabled) 338 return cmd_show_ref__exclude_existing(&exclude_existing_opts); 339 else if (verify) 340 return cmd_show_ref__verify(&show_one_opts, argv); 341 else if (exists) 342 return cmd_show_ref__exists(argv); 343 else 344 return cmd_show_ref__patterns(&patterns_opts, &show_one_opts, argv); 345}