builtin / update-ref.con commit update-ref --stdin: harmonize error messages (f11b09f)
   1#include "cache.h"
   2#include "refs.h"
   3#include "builtin.h"
   4#include "parse-options.h"
   5#include "quote.h"
   6#include "argv-array.h"
   7
   8static const char * const git_update_ref_usage[] = {
   9        N_("git update-ref [options] -d <refname> [<oldval>]"),
  10        N_("git update-ref [options]    <refname> <newval> [<oldval>]"),
  11        N_("git update-ref [options] --stdin [-z]"),
  12        NULL
  13};
  14
  15static int updates_alloc;
  16static int updates_count;
  17static struct ref_update **updates;
  18
  19static char line_termination = '\n';
  20static int update_flags;
  21
  22static struct ref_update *update_alloc(void)
  23{
  24        struct ref_update *update;
  25
  26        /* Allocate and zero-init a struct ref_update */
  27        update = xcalloc(1, sizeof(*update));
  28        ALLOC_GROW(updates, updates_count + 1, updates_alloc);
  29        updates[updates_count++] = update;
  30
  31        /* Store and reset accumulated options */
  32        update->flags = update_flags;
  33        update_flags = 0;
  34
  35        return update;
  36}
  37
  38/*
  39 * Parse one whitespace- or NUL-terminated, possibly C-quoted argument
  40 * and append the result to arg.  Return a pointer to the terminator.
  41 * Die if there is an error in how the argument is C-quoted.  This
  42 * function is only used if not -z.
  43 */
  44static const char *parse_arg(const char *next, struct strbuf *arg)
  45{
  46        if (*next == '"') {
  47                const char *orig = next;
  48
  49                if (unquote_c_style(arg, next, &next))
  50                        die("badly quoted argument: %s", orig);
  51                if (*next && !isspace(*next))
  52                        die("unexpected character after quoted argument: %s", orig);
  53        } else {
  54                while (*next && !isspace(*next))
  55                        strbuf_addch(arg, *next++);
  56        }
  57
  58        return next;
  59}
  60
  61/*
  62 * Parse the reference name immediately after "command SP".  If not
  63 * -z, then handle C-quoting.  Return a pointer to a newly allocated
  64 * string containing the name of the reference, or NULL if there was
  65 * an error.  Update *next to point at the character that terminates
  66 * the argument.  Die if C-quoting is malformed or the reference name
  67 * is invalid.
  68 */
  69static char *parse_refname(struct strbuf *input, const char **next)
  70{
  71        struct strbuf ref = STRBUF_INIT;
  72
  73        if (line_termination) {
  74                /* Without -z, use the next argument */
  75                *next = parse_arg(*next, &ref);
  76        } else {
  77                /* With -z, use everything up to the next NUL */
  78                strbuf_addstr(&ref, *next);
  79                *next += ref.len;
  80        }
  81
  82        if (!ref.len) {
  83                strbuf_release(&ref);
  84                return NULL;
  85        }
  86
  87        if (check_refname_format(ref.buf, REFNAME_ALLOW_ONELEVEL))
  88                die("invalid ref format: %s", ref.buf);
  89
  90        return strbuf_detach(&ref, NULL);
  91}
  92
  93/*
  94 * The value being parsed is <oldvalue> (as opposed to <newvalue>; the
  95 * difference affects which error messages are generated):
  96 */
  97#define PARSE_SHA1_OLD 0x01
  98
  99/*
 100 * For backwards compatibility, accept an empty string for update's
 101 * <newvalue> in binary mode to be equivalent to specifying zeros.
 102 */
 103#define PARSE_SHA1_ALLOW_EMPTY 0x02
 104
 105/*
 106 * Parse an argument separator followed by the next argument, if any.
 107 * If there is an argument, convert it to a SHA-1, write it to sha1,
 108 * set *next to point at the character terminating the argument, and
 109 * return 0.  If there is no argument at all (not even the empty
 110 * string), return 1 and leave *next unchanged.  If the value is
 111 * provided but cannot be converted to a SHA-1, die.  flags can
 112 * include PARSE_SHA1_OLD and/or PARSE_SHA1_ALLOW_EMPTY.
 113 */
 114static int parse_next_sha1(struct strbuf *input, const char **next,
 115                           unsigned char *sha1,
 116                           const char *command, const char *refname,
 117                           int flags)
 118{
 119        struct strbuf arg = STRBUF_INIT;
 120        int ret = 0;
 121
 122        if (*next == input->buf + input->len)
 123                goto eof;
 124
 125        if (line_termination) {
 126                /* Without -z, consume SP and use next argument */
 127                if (!**next || **next == line_termination)
 128                        return 1;
 129                if (**next != ' ')
 130                        die("%s %s: expected SP but got: %s",
 131                            command, refname, *next);
 132                (*next)++;
 133                *next = parse_arg(*next, &arg);
 134                if (arg.len) {
 135                        if (get_sha1(arg.buf, sha1))
 136                                goto invalid;
 137                } else {
 138                        /* Without -z, an empty value means all zeros: */
 139                        hashclr(sha1);
 140                }
 141        } else {
 142                /* With -z, read the next NUL-terminated line */
 143                if (**next)
 144                        die("%s %s: expected NUL but got: %s",
 145                            command, refname, *next);
 146                (*next)++;
 147                if (*next == input->buf + input->len)
 148                        goto eof;
 149                strbuf_addstr(&arg, *next);
 150                *next += arg.len;
 151
 152                if (arg.len) {
 153                        if (get_sha1(arg.buf, sha1))
 154                                goto invalid;
 155                } else if (flags & PARSE_SHA1_ALLOW_EMPTY) {
 156                        /* With -z, treat an empty value as all zeros: */
 157                        warning("%s %s: missing <newvalue>, treating as zero",
 158                                command, refname);
 159                        hashclr(sha1);
 160                } else {
 161                        /*
 162                         * With -z, an empty non-required value means
 163                         * unspecified:
 164                         */
 165                        ret = 1;
 166                }
 167        }
 168
 169        strbuf_release(&arg);
 170
 171        return ret;
 172
 173 invalid:
 174        die(flags & PARSE_SHA1_OLD ?
 175            "%s %s: invalid <oldvalue>: %s" :
 176            "%s %s: invalid <newvalue>: %s",
 177            command, refname, arg.buf);
 178
 179 eof:
 180        die(flags & PARSE_SHA1_OLD ?
 181            "%s %s: unexpected end of input when reading <oldvalue>" :
 182            "%s %s: unexpected end of input when reading <newvalue>",
 183            command, refname);
 184}
 185
 186
 187/*
 188 * The following five parse_cmd_*() functions parse the corresponding
 189 * command.  In each case, next points at the character following the
 190 * command name and the following space.  They each return a pointer
 191 * to the character terminating the command, and die with an
 192 * explanatory message if there are any parsing problems.  All of
 193 * these functions handle either text or binary format input,
 194 * depending on how line_termination is set.
 195 */
 196
 197static const char *parse_cmd_update(struct strbuf *input, const char *next)
 198{
 199        struct ref_update *update;
 200
 201        update = update_alloc();
 202
 203        update->ref_name = parse_refname(input, &next);
 204        if (!update->ref_name)
 205                die("update: missing <ref>");
 206
 207        if (parse_next_sha1(input, &next, update->new_sha1,
 208                            "update", update->ref_name,
 209                            PARSE_SHA1_ALLOW_EMPTY))
 210                die("update %s: missing <newvalue>", update->ref_name);
 211
 212        update->have_old = !parse_next_sha1(input, &next, update->old_sha1,
 213                                            "update", update->ref_name,
 214                                            PARSE_SHA1_OLD);
 215
 216        if (*next != line_termination)
 217                die("update %s: extra input: %s", update->ref_name, next);
 218
 219        return next;
 220}
 221
 222static const char *parse_cmd_create(struct strbuf *input, const char *next)
 223{
 224        struct ref_update *update;
 225
 226        update = update_alloc();
 227
 228        update->ref_name = parse_refname(input, &next);
 229        if (!update->ref_name)
 230                die("create: missing <ref>");
 231
 232        if (parse_next_sha1(input, &next, update->new_sha1,
 233                            "create", update->ref_name, 0))
 234                die("create %s: missing <newvalue>", update->ref_name);
 235
 236        if (is_null_sha1(update->new_sha1))
 237                die("create %s: zero <newvalue>", update->ref_name);
 238
 239        if (*next != line_termination)
 240                die("create %s: extra input: %s", update->ref_name, next);
 241
 242        return next;
 243}
 244
 245static const char *parse_cmd_delete(struct strbuf *input, const char *next)
 246{
 247        struct ref_update *update;
 248
 249        update = update_alloc();
 250
 251        update->ref_name = parse_refname(input, &next);
 252        if (!update->ref_name)
 253                die("delete: missing <ref>");
 254
 255        if (parse_next_sha1(input, &next, update->old_sha1,
 256                            "delete", update->ref_name, PARSE_SHA1_OLD)) {
 257                update->have_old = 0;
 258        } else {
 259                if (is_null_sha1(update->old_sha1))
 260                        die("delete %s: zero <oldvalue>", update->ref_name);
 261                update->have_old = 1;
 262        }
 263
 264        if (*next != line_termination)
 265                die("delete %s: extra input: %s", update->ref_name, next);
 266
 267        return next;
 268}
 269
 270static const char *parse_cmd_verify(struct strbuf *input, const char *next)
 271{
 272        struct ref_update *update;
 273
 274        update = update_alloc();
 275
 276        update->ref_name = parse_refname(input, &next);
 277        if (!update->ref_name)
 278                die("verify: missing <ref>");
 279
 280        if (parse_next_sha1(input, &next, update->old_sha1,
 281                            "verify", update->ref_name, PARSE_SHA1_OLD)) {
 282                update->have_old = 0;
 283        } else {
 284                hashcpy(update->new_sha1, update->old_sha1);
 285                update->have_old = 1;
 286        }
 287
 288        if (*next != line_termination)
 289                die("verify %s: extra input: %s", update->ref_name, next);
 290
 291        return next;
 292}
 293
 294static const char *parse_cmd_option(struct strbuf *input, const char *next)
 295{
 296        if (!strncmp(next, "no-deref", 8) && next[8] == line_termination)
 297                update_flags |= REF_NODEREF;
 298        else
 299                die("option unknown: %s", next);
 300        return next + 8;
 301}
 302
 303static void update_refs_stdin(void)
 304{
 305        struct strbuf input = STRBUF_INIT;
 306        const char *next;
 307
 308        if (strbuf_read(&input, 0, 1000) < 0)
 309                die_errno("could not read from stdin");
 310        next = input.buf;
 311        /* Read each line dispatch its command */
 312        while (next < input.buf + input.len) {
 313                if (*next == line_termination)
 314                        die("empty command in input");
 315                else if (isspace(*next))
 316                        die("whitespace before command: %s", next);
 317                else if (starts_with(next, "update "))
 318                        next = parse_cmd_update(&input, next + 7);
 319                else if (starts_with(next, "create "))
 320                        next = parse_cmd_create(&input, next + 7);
 321                else if (starts_with(next, "delete "))
 322                        next = parse_cmd_delete(&input, next + 7);
 323                else if (starts_with(next, "verify "))
 324                        next = parse_cmd_verify(&input, next + 7);
 325                else if (starts_with(next, "option "))
 326                        next = parse_cmd_option(&input, next + 7);
 327                else
 328                        die("unknown command: %s", next);
 329
 330                next++;
 331        }
 332
 333        strbuf_release(&input);
 334}
 335
 336int cmd_update_ref(int argc, const char **argv, const char *prefix)
 337{
 338        const char *refname, *oldval, *msg = NULL;
 339        unsigned char sha1[20], oldsha1[20];
 340        int delete = 0, no_deref = 0, read_stdin = 0, end_null = 0, flags = 0;
 341        struct option options[] = {
 342                OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")),
 343                OPT_BOOL('d', NULL, &delete, N_("delete the reference")),
 344                OPT_BOOL( 0 , "no-deref", &no_deref,
 345                                        N_("update <refname> not the one it points to")),
 346                OPT_BOOL('z', NULL, &end_null, N_("stdin has NUL-terminated arguments")),
 347                OPT_BOOL( 0 , "stdin", &read_stdin, N_("read updates from stdin")),
 348                OPT_END(),
 349        };
 350
 351        git_config(git_default_config, NULL);
 352        argc = parse_options(argc, argv, prefix, options, git_update_ref_usage,
 353                             0);
 354        if (msg && !*msg)
 355                die("Refusing to perform update with empty message.");
 356
 357        if (read_stdin) {
 358                if (delete || no_deref || argc > 0)
 359                        usage_with_options(git_update_ref_usage, options);
 360                if (end_null)
 361                        line_termination = '\0';
 362                update_refs_stdin();
 363                return update_refs(msg, updates, updates_count,
 364                                   UPDATE_REFS_DIE_ON_ERR);
 365        }
 366
 367        if (end_null)
 368                usage_with_options(git_update_ref_usage, options);
 369
 370        if (delete) {
 371                if (argc < 1 || argc > 2)
 372                        usage_with_options(git_update_ref_usage, options);
 373                refname = argv[0];
 374                oldval = argv[1];
 375        } else {
 376                const char *value;
 377                if (argc < 2 || argc > 3)
 378                        usage_with_options(git_update_ref_usage, options);
 379                refname = argv[0];
 380                value = argv[1];
 381                oldval = argv[2];
 382                if (get_sha1(value, sha1))
 383                        die("%s: not a valid SHA1", value);
 384        }
 385
 386        hashclr(oldsha1); /* all-zero hash in case oldval is the empty string */
 387        if (oldval && *oldval && get_sha1(oldval, oldsha1))
 388                die("%s: not a valid old SHA1", oldval);
 389
 390        if (no_deref)
 391                flags = REF_NODEREF;
 392        if (delete)
 393                return delete_ref(refname, oldval ? oldsha1 : NULL, flags);
 394        else
 395                return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
 396                                  flags, UPDATE_REFS_DIE_ON_ERR);
 397}