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