builtin / show-ref.con commit fast-import: Improve robustness when D->F changes provided in wrong order (253fb5f)
   1#include "builtin.h"
   2#include "cache.h"
   3#include "refs.h"
   4#include "object.h"
   5#include "tag.h"
   6#include "string-list.h"
   7#include "parse-options.h"
   8
   9static const char * const show_ref_usage[] = {
  10        "git show-ref [-q|--quiet] [--verify] [--head] [-d|--dereference] [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [pattern*] ",
  11        "git show-ref --exclude-existing[=pattern] < ref-list",
  12        NULL
  13};
  14
  15static int deref_tags, show_head, tags_only, heads_only, found_match, verify,
  16           quiet, hash_only, abbrev, exclude_arg;
  17static const char **pattern;
  18static const char *exclude_existing_arg;
  19
  20static void show_one(const char *refname, const unsigned char *sha1)
  21{
  22        const char *hex = find_unique_abbrev(sha1, abbrev);
  23        if (hash_only)
  24                printf("%s\n", hex);
  25        else
  26                printf("%s %s\n", hex, refname);
  27}
  28
  29static int show_ref(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
  30{
  31        struct object *obj;
  32        const char *hex;
  33        unsigned char peeled[20];
  34
  35        if (tags_only || heads_only) {
  36                int match;
  37
  38                match = heads_only && !prefixcmp(refname, "refs/heads/");
  39                match |= tags_only && !prefixcmp(refname, "refs/tags/");
  40                if (!match)
  41                        return 0;
  42        }
  43        if (pattern) {
  44                int reflen = strlen(refname);
  45                const char **p = pattern, *m;
  46                while ((m = *p++) != NULL) {
  47                        int len = strlen(m);
  48                        if (len > reflen)
  49                                continue;
  50                        if (memcmp(m, refname + reflen - len, len))
  51                                continue;
  52                        if (len == reflen)
  53                                goto match;
  54                        /* "--verify" requires an exact match */
  55                        if (verify)
  56                                continue;
  57                        if (refname[reflen - len - 1] == '/')
  58                                goto match;
  59                }
  60                return 0;
  61        }
  62
  63match:
  64        found_match++;
  65
  66        /* This changes the semantics slightly that even under quiet we
  67         * detect and return error if the repository is corrupt and
  68         * ref points at a nonexistent object.
  69         */
  70        if (!has_sha1_file(sha1))
  71                die("git show-ref: bad ref %s (%s)", refname,
  72                    sha1_to_hex(sha1));
  73
  74        if (quiet)
  75                return 0;
  76
  77        show_one(refname, sha1);
  78
  79        if (!deref_tags)
  80                return 0;
  81
  82        if ((flag & REF_ISPACKED) && !peel_ref(refname, peeled)) {
  83                if (!is_null_sha1(peeled)) {
  84                        hex = find_unique_abbrev(peeled, abbrev);
  85                        printf("%s %s^{}\n", hex, refname);
  86                }
  87        }
  88        else {
  89                obj = parse_object(sha1);
  90                if (!obj)
  91                        die("git show-ref: bad ref %s (%s)", refname,
  92                            sha1_to_hex(sha1));
  93                if (obj->type == OBJ_TAG) {
  94                        obj = deref_tag(obj, refname, 0);
  95                        if (!obj)
  96                                die("git show-ref: bad tag at ref %s (%s)", refname,
  97                                    sha1_to_hex(sha1));
  98                        hex = find_unique_abbrev(obj->sha1, abbrev);
  99                        printf("%s %s^{}\n", hex, refname);
 100                }
 101        }
 102        return 0;
 103}
 104
 105static int add_existing(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
 106{
 107        struct string_list *list = (struct string_list *)cbdata;
 108        string_list_insert(refname, list);
 109        return 0;
 110}
 111
 112/*
 113 * read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input,
 114 * and
 115 * (1) strip "^{}" at the end of line if any;
 116 * (2) ignore if match is provided and does not head-match refname;
 117 * (3) warn if refname is not a well-formed refname and skip;
 118 * (4) ignore if refname is a ref that exists in the local repository;
 119 * (5) otherwise output the line.
 120 */
 121static int exclude_existing(const char *match)
 122{
 123        static struct string_list existing_refs = { NULL, 0, 0, 0 };
 124        char buf[1024];
 125        int matchlen = match ? strlen(match) : 0;
 126
 127        for_each_ref(add_existing, &existing_refs);
 128        while (fgets(buf, sizeof(buf), stdin)) {
 129                char *ref;
 130                int len = strlen(buf);
 131
 132                if (len > 0 && buf[len - 1] == '\n')
 133                        buf[--len] = '\0';
 134                if (3 <= len && !strcmp(buf + len - 3, "^{}")) {
 135                        len -= 3;
 136                        buf[len] = '\0';
 137                }
 138                for (ref = buf + len; buf < ref; ref--)
 139                        if (isspace(ref[-1]))
 140                                break;
 141                if (match) {
 142                        int reflen = buf + len - ref;
 143                        if (reflen < matchlen)
 144                                continue;
 145                        if (strncmp(ref, match, matchlen))
 146                                continue;
 147                }
 148                if (check_ref_format(ref)) {
 149                        warning("ref '%s' ignored", ref);
 150                        continue;
 151                }
 152                if (!string_list_has_string(&existing_refs, ref)) {
 153                        printf("%s\n", buf);
 154                }
 155        }
 156        return 0;
 157}
 158
 159static int hash_callback(const struct option *opt, const char *arg, int unset)
 160{
 161        hash_only = 1;
 162        /* Use full length SHA1 if no argument */
 163        if (!arg)
 164                return 0;
 165        return parse_opt_abbrev_cb(opt, arg, unset);
 166}
 167
 168static int exclude_existing_callback(const struct option *opt, const char *arg,
 169                                     int unset)
 170{
 171        exclude_arg = 1;
 172        *(const char **)opt->value = arg;
 173        return 0;
 174}
 175
 176static int help_callback(const struct option *opt, const char *arg, int unset)
 177{
 178        return -1;
 179}
 180
 181static const struct option show_ref_options[] = {
 182        OPT_BOOLEAN(0, "tags", &tags_only, "only show tags (can be combined with heads)"),
 183        OPT_BOOLEAN(0, "heads", &heads_only, "only show heads (can be combined with tags)"),
 184        OPT_BOOLEAN(0, "verify", &verify, "stricter reference checking, "
 185                    "requires exact ref path"),
 186        { OPTION_BOOLEAN, 'h', NULL, &show_head, NULL,
 187          "show the HEAD reference",
 188          PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
 189        OPT_BOOLEAN(0, "head", &show_head, "show the HEAD reference"),
 190        OPT_BOOLEAN('d', "dereference", &deref_tags,
 191                    "dereference tags into object IDs"),
 192        { OPTION_CALLBACK, 's', "hash", &abbrev, "n",
 193          "only show SHA1 hash using <n> digits",
 194          PARSE_OPT_OPTARG, &hash_callback },
 195        OPT__ABBREV(&abbrev),
 196        OPT__QUIET(&quiet),
 197        { OPTION_CALLBACK, 0, "exclude-existing", &exclude_existing_arg,
 198          "pattern", "show refs from stdin that aren't in local repository",
 199          PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback },
 200        { OPTION_CALLBACK, 0, "help-all", NULL, NULL, "show usage",
 201          PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
 202        OPT_END()
 203};
 204
 205int cmd_show_ref(int argc, const char **argv, const char *prefix)
 206{
 207        if (argc == 2 && !strcmp(argv[1], "-h"))
 208                usage_with_options(show_ref_usage, show_ref_options);
 209
 210        argc = parse_options(argc, argv, prefix, show_ref_options,
 211                             show_ref_usage, PARSE_OPT_NO_INTERNAL_HELP);
 212
 213        if (exclude_arg)
 214                return exclude_existing(exclude_existing_arg);
 215
 216        pattern = argv;
 217        if (!*pattern)
 218                pattern = NULL;
 219
 220        if (verify) {
 221                if (!pattern)
 222                        die("--verify requires a reference");
 223                while (*pattern) {
 224                        unsigned char sha1[20];
 225
 226                        if (!prefixcmp(*pattern, "refs/") &&
 227                            resolve_ref(*pattern, sha1, 1, NULL)) {
 228                                if (!quiet)
 229                                        show_one(*pattern, sha1);
 230                        }
 231                        else if (!quiet)
 232                                die("'%s' - not a valid ref", *pattern);
 233                        else
 234                                return 1;
 235                        pattern++;
 236                }
 237                return 0;
 238        }
 239
 240        if (show_head)
 241                head_ref(show_ref, NULL);
 242        for_each_ref(show_ref, NULL);
 243        if (!found_match) {
 244                if (verify && !quiet)
 245                        die("No match");
 246                return 1;
 247        }
 248        return 0;
 249}