From: Junio C Hamano Date: Mon, 16 Jun 2014 19:18:48 +0000 (-0700) Subject: Merge branch 'rs/read-ref-at' X-Git-Tag: v2.1.0-rc0~106 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/bb0ced758153412afc4af07fcadd983c364b4f67?ds=inline;hp=-c Merge branch 'rs/read-ref-at' * rs/read-ref-at: refs.c: change read_ref_at to use the reflog iterators --- bb0ced758153412afc4af07fcadd983c364b4f67 diff --combined refs.c index ceeeec645c,f1afec5a46..ca620f07c1 --- a/refs.c +++ b/refs.c @@@ -1611,7 -1611,6 +1611,7 @@@ int peel_ref(const char *refname, unsig struct warn_if_dangling_data { FILE *fp; const char *refname; + const struct string_list *refnames; const char *msg_fmt; }; @@@ -1626,12 -1625,8 +1626,12 @@@ static int warn_if_dangling_symref(cons return 0; resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL); - if (!resolves_to || strcmp(resolves_to, d->refname)) + if (!resolves_to + || (d->refname + ? strcmp(resolves_to, d->refname) + : !string_list_has_string(d->refnames, resolves_to))) { return 0; + } fprintf(d->fp, d->msg_fmt, refname); fputc('\n', d->fp); @@@ -1644,18 -1639,6 +1644,18 @@@ void warn_dangling_symref(FILE *fp, con data.fp = fp; data.refname = refname; + data.refnames = NULL; + data.msg_fmt = msg_fmt; + for_each_rawref(warn_if_dangling_symref, &data); +} + +void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames) +{ + struct warn_if_dangling_data data; + + data.fp = fp; + data.refname = NULL; + data.refnames = refnames; data.msg_fmt = msg_fmt; for_each_rawref(warn_if_dangling_symref, &data); } @@@ -2016,6 -1999,7 +2016,6 @@@ int dwim_log(const char *str, int len, *log = NULL; for (p = ref_rev_parse_rules; *p; p++) { - struct stat st; unsigned char hash[20]; char path[PATH_MAX]; const char *ref, *it; @@@ -2024,9 -2008,12 +2024,9 @@@ ref = resolve_ref_unsafe(path, hash, 1, NULL); if (!ref) continue; - if (!stat(git_path("logs/%s", path), &st) && - S_ISREG(st.st_mode)) + if (reflog_exists(path)) it = path; - else if (strcmp(ref, path) && - !stat(git_path("logs/%s", ref), &st) && - S_ISREG(st.st_mode)) + else if (strcmp(ref, path) && reflog_exists(ref)) it = ref; else continue; @@@ -2444,7 -2431,7 +2444,7 @@@ static int curate_packed_ref_fn(struct return 0; } -static int repack_without_refs(const char **refnames, int n) +int repack_without_refs(const char **refnames, int n) { struct ref_dir *packed; struct string_list refs_to_delete = STRING_LIST_INIT_DUP; @@@ -2943,135 -2930,120 +2943,133 @@@ int create_symref(const char *ref_targe return 0; } - static char *ref_msg(const char *line, const char *endp) - { - const char *ep; - line += 82; - ep = memchr(line, '\n', endp - line); - if (!ep) - ep = endp; - return xmemdupz(line, ep - line); + struct read_ref_at_cb { + const char *refname; + unsigned long at_time; + int cnt; + int reccnt; + unsigned char *sha1; + int found_it; + + unsigned char osha1[20]; + unsigned char nsha1[20]; + int tz; + unsigned long date; + char **msg; + unsigned long *cutoff_time; + int *cutoff_tz; + int *cutoff_cnt; + }; + + static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1, + const char *email, unsigned long timestamp, int tz, + const char *message, void *cb_data) + { + struct read_ref_at_cb *cb = cb_data; + + cb->reccnt++; + cb->tz = tz; + cb->date = timestamp; + + if (timestamp <= cb->at_time || cb->cnt == 0) { + if (cb->msg) + *cb->msg = xstrdup(message); + if (cb->cutoff_time) + *cb->cutoff_time = timestamp; + if (cb->cutoff_tz) + *cb->cutoff_tz = tz; + if (cb->cutoff_cnt) + *cb->cutoff_cnt = cb->reccnt - 1; + /* + * we have not yet updated cb->[n|o]sha1 so they still + * hold the values for the previous record. + */ + if (!is_null_sha1(cb->osha1)) { + hashcpy(cb->sha1, nsha1); + if (hashcmp(cb->osha1, nsha1)) + warning("Log for ref %s has gap after %s.", + cb->refname, show_date(cb->date, cb->tz, DATE_RFC2822)); + } + else if (cb->date == cb->at_time) + hashcpy(cb->sha1, nsha1); + else if (hashcmp(nsha1, cb->sha1)) + warning("Log for ref %s unexpectedly ended on %s.", + cb->refname, show_date(cb->date, cb->tz, + DATE_RFC2822)); + hashcpy(cb->osha1, osha1); + hashcpy(cb->nsha1, nsha1); + cb->found_it = 1; + return 1; + } + hashcpy(cb->osha1, osha1); + hashcpy(cb->nsha1, nsha1); + if (cb->cnt > 0) + cb->cnt--; + return 0; + } + + static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1, + const char *email, unsigned long timestamp, + int tz, const char *message, void *cb_data) + { + struct read_ref_at_cb *cb = cb_data; + + if (cb->msg) + *cb->msg = xstrdup(message); + if (cb->cutoff_time) + *cb->cutoff_time = timestamp; + if (cb->cutoff_tz) + *cb->cutoff_tz = tz; + if (cb->cutoff_cnt) + *cb->cutoff_cnt = cb->reccnt; + hashcpy(cb->sha1, osha1); + if (is_null_sha1(cb->sha1)) + hashcpy(cb->sha1, nsha1); + /* We just want the first entry */ + return 1; } int read_ref_at(const char *refname, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt) { - const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec; - char *tz_c; - int logfd, tz, reccnt = 0; - struct stat st; - unsigned long date; - unsigned char logged_sha1[20]; - void *log_mapped; - size_t mapsz; + struct read_ref_at_cb cb; - logfile = git_path("logs/%s", refname); - logfd = open(logfile, O_RDONLY, 0); - if (logfd < 0) - die_errno("Unable to read log '%s'", logfile); - fstat(logfd, &st); - if (!st.st_size) - die("Log %s is empty.", logfile); - mapsz = xsize_t(st.st_size); - log_mapped = xmmap(NULL, mapsz, PROT_READ, MAP_PRIVATE, logfd, 0); - logdata = log_mapped; - close(logfd); + memset(&cb, 0, sizeof(cb)); + cb.refname = refname; + cb.at_time = at_time; + cb.cnt = cnt; + cb.msg = msg; + cb.cutoff_time = cutoff_time; + cb.cutoff_tz = cutoff_tz; + cb.cutoff_cnt = cutoff_cnt; + cb.sha1 = sha1; + + for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb); + + if (!cb.reccnt) + die("Log for %s is empty.", refname); + if (cb.found_it) + return 0; + + for_each_reflog_ent(refname, read_ref_at_ent_oldest, &cb); - lastrec = NULL; - rec = logend = logdata + st.st_size; - while (logdata < rec) { - reccnt++; - if (logdata < rec && *(rec-1) == '\n') - rec--; - lastgt = NULL; - while (logdata < rec && *(rec-1) != '\n') { - rec--; - if (*rec == '>') - lastgt = rec; - } - if (!lastgt) - die("Log %s is corrupt.", logfile); - date = strtoul(lastgt + 1, &tz_c, 10); - if (date <= at_time || cnt == 0) { - tz = strtoul(tz_c, NULL, 10); - if (msg) - *msg = ref_msg(rec, logend); - if (cutoff_time) - *cutoff_time = date; - if (cutoff_tz) - *cutoff_tz = tz; - if (cutoff_cnt) - *cutoff_cnt = reccnt - 1; - if (lastrec) { - if (get_sha1_hex(lastrec, logged_sha1)) - die("Log %s is corrupt.", logfile); - if (get_sha1_hex(rec + 41, sha1)) - die("Log %s is corrupt.", logfile); - if (hashcmp(logged_sha1, sha1)) { - warning("Log %s has gap after %s.", - logfile, show_date(date, tz, DATE_RFC2822)); - } - } - else if (date == at_time) { - if (get_sha1_hex(rec + 41, sha1)) - die("Log %s is corrupt.", logfile); - } - else { - if (get_sha1_hex(rec + 41, logged_sha1)) - die("Log %s is corrupt.", logfile); - if (hashcmp(logged_sha1, sha1)) { - warning("Log %s unexpectedly ended on %s.", - logfile, show_date(date, tz, DATE_RFC2822)); - } - } - munmap(log_mapped, mapsz); - return 0; - } - lastrec = rec; - if (cnt > 0) - cnt--; - } - - rec = logdata; - while (rec < logend && *rec != '>' && *rec != '\n') - rec++; - if (rec == logend || *rec == '\n') - die("Log %s is corrupt.", logfile); - date = strtoul(rec + 1, &tz_c, 10); - tz = strtoul(tz_c, NULL, 10); - if (get_sha1_hex(logdata, sha1)) - die("Log %s is corrupt.", logfile); - if (is_null_sha1(sha1)) { - if (get_sha1_hex(logdata + 41, sha1)) - die("Log %s is corrupt.", logfile); - } - if (msg) - *msg = ref_msg(logdata, logend); - munmap(log_mapped, mapsz); - - if (cutoff_time) - *cutoff_time = date; - if (cutoff_tz) - *cutoff_tz = tz; - if (cutoff_cnt) - *cutoff_cnt = reccnt; return 1; } +int reflog_exists(const char *refname) +{ + struct stat st; + + return !lstat(git_path("logs/%s", refname), &st) && + S_ISREG(st.st_mode); +} + +int delete_reflog(const char *refname) +{ + return remove_path(git_path("logs/%s", refname)); +} + static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data) { unsigned char osha1[20], nsha1[20]; @@@ -3269,9 -3241,9 +3267,9 @@@ static struct ref_lock *update_ref_lock if (!lock) { const char *str = "Cannot lock the ref '%s'."; switch (onerr) { - case MSG_ON_ERR: error(str, refname); break; - case DIE_ON_ERR: die(str, refname); break; - case QUIET_ON_ERR: break; + case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break; + case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break; + case UPDATE_REFS_QUIET_ON_ERR: break; } } return lock; @@@ -3284,118 -3256,15 +3282,118 @@@ static int update_ref_write(const char if (write_ref_sha1(lock, sha1, action) < 0) { const char *str = "Cannot update the ref '%s'."; switch (onerr) { - case MSG_ON_ERR: error(str, refname); break; - case DIE_ON_ERR: die(str, refname); break; - case QUIET_ON_ERR: break; + case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break; + case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break; + case UPDATE_REFS_QUIET_ON_ERR: break; } return 1; } return 0; } +/** + * Information needed for a single ref update. Set new_sha1 to the + * new value or to zero to delete the ref. To check the old value + * while locking the ref, set have_old to 1 and set old_sha1 to the + * value or to zero to ensure the ref does not exist before update. + */ +struct ref_update { + unsigned char new_sha1[20]; + unsigned char old_sha1[20]; + int flags; /* REF_NODEREF? */ + int have_old; /* 1 if old_sha1 is valid, 0 otherwise */ + struct ref_lock *lock; + int type; + const char refname[FLEX_ARRAY]; +}; + +/* + * Data structure for holding a reference transaction, which can + * consist of checks and updates to multiple references, carried out + * as atomically as possible. This structure is opaque to callers. + */ +struct ref_transaction { + struct ref_update **updates; + size_t alloc; + size_t nr; +}; + +struct ref_transaction *ref_transaction_begin(void) +{ + return xcalloc(1, sizeof(struct ref_transaction)); +} + +static void ref_transaction_free(struct ref_transaction *transaction) +{ + int i; + + for (i = 0; i < transaction->nr; i++) + free(transaction->updates[i]); + + free(transaction->updates); + free(transaction); +} + +void ref_transaction_rollback(struct ref_transaction *transaction) +{ + ref_transaction_free(transaction); +} + +static struct ref_update *add_update(struct ref_transaction *transaction, + const char *refname) +{ + size_t len = strlen(refname); + struct ref_update *update = xcalloc(1, sizeof(*update) + len + 1); + + strcpy((char *)update->refname, refname); + ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc); + transaction->updates[transaction->nr++] = update; + return update; +} + +void ref_transaction_update(struct ref_transaction *transaction, + const char *refname, + unsigned char *new_sha1, unsigned char *old_sha1, + int flags, int have_old) +{ + struct ref_update *update = add_update(transaction, refname); + + hashcpy(update->new_sha1, new_sha1); + update->flags = flags; + update->have_old = have_old; + if (have_old) + hashcpy(update->old_sha1, old_sha1); +} + +void ref_transaction_create(struct ref_transaction *transaction, + const char *refname, + unsigned char *new_sha1, + int flags) +{ + struct ref_update *update = add_update(transaction, refname); + + assert(!is_null_sha1(new_sha1)); + hashcpy(update->new_sha1, new_sha1); + hashclr(update->old_sha1); + update->flags = flags; + update->have_old = 1; +} + +void ref_transaction_delete(struct ref_transaction *transaction, + const char *refname, + unsigned char *old_sha1, + int flags, int have_old) +{ + struct ref_update *update = add_update(transaction, refname); + + update->flags = flags; + update->have_old = have_old; + if (have_old) { + assert(!is_null_sha1(old_sha1)); + hashcpy(update->old_sha1, old_sha1); + } +} + int update_ref(const char *action, const char *refname, const unsigned char *sha1, const unsigned char *oldval, int flags, enum action_on_err onerr) @@@ -3411,7 -3280,7 +3409,7 @@@ static int ref_update_compare(const voi { const struct ref_update * const *u1 = r1; const struct ref_update * const *u2 = r2; - return strcmp((*u1)->ref_name, (*u2)->ref_name); + return strcmp((*u1)->refname, (*u2)->refname); } static int ref_update_reject_duplicates(struct ref_update **updates, int n, @@@ -3419,15 -3288,15 +3417,15 @@@ { int i; for (i = 1; i < n; i++) - if (!strcmp(updates[i - 1]->ref_name, updates[i]->ref_name)) { + if (!strcmp(updates[i - 1]->refname, updates[i]->refname)) { const char *str = "Multiple updates for ref '%s' not allowed."; switch (onerr) { - case MSG_ON_ERR: - error(str, updates[i]->ref_name); break; - case DIE_ON_ERR: - die(str, updates[i]->ref_name); break; - case QUIET_ON_ERR: + case UPDATE_REFS_MSG_ON_ERR: + error(str, updates[i]->refname); break; + case UPDATE_REFS_DIE_ON_ERR: + die(str, updates[i]->refname); break; + case UPDATE_REFS_QUIET_ON_ERR: break; } return 1; @@@ -3435,21 -3304,26 +3433,21 @@@ return 0; } -int update_refs(const char *action, const struct ref_update **updates_orig, - int n, enum action_on_err onerr) +int ref_transaction_commit(struct ref_transaction *transaction, + const char *msg, enum action_on_err onerr) { int ret = 0, delnum = 0, i; - struct ref_update **updates; - int *types; - struct ref_lock **locks; const char **delnames; + int n = transaction->nr; + struct ref_update **updates = transaction->updates; - if (!updates_orig || !n) + if (!n) return 0; /* Allocate work space */ - updates = xmalloc(sizeof(*updates) * n); - types = xmalloc(sizeof(*types) * n); - locks = xcalloc(n, sizeof(*locks)); delnames = xmalloc(sizeof(*delnames) * n); /* Copy, sort, and reject duplicate refs */ - memcpy(updates, updates_orig, sizeof(*updates) * n); qsort(updates, n, sizeof(*updates), ref_update_compare); ret = ref_update_reject_duplicates(updates, n, onerr); if (ret) @@@ -3457,44 -3331,35 +3455,44 @@@ /* Acquire all locks while verifying old values */ for (i = 0; i < n; i++) { - locks[i] = update_ref_lock(updates[i]->ref_name, - (updates[i]->have_old ? - updates[i]->old_sha1 : NULL), - updates[i]->flags, - &types[i], onerr); - if (!locks[i]) { + struct ref_update *update = updates[i]; + + update->lock = update_ref_lock(update->refname, + (update->have_old ? + update->old_sha1 : NULL), + update->flags, + &update->type, onerr); + if (!update->lock) { ret = 1; goto cleanup; } } /* Perform updates first so live commits remain referenced */ - for (i = 0; i < n; i++) - if (!is_null_sha1(updates[i]->new_sha1)) { - ret = update_ref_write(action, - updates[i]->ref_name, - updates[i]->new_sha1, - locks[i], onerr); - locks[i] = NULL; /* freed by update_ref_write */ + for (i = 0; i < n; i++) { + struct ref_update *update = updates[i]; + + if (!is_null_sha1(update->new_sha1)) { + ret = update_ref_write(msg, + update->refname, + update->new_sha1, + update->lock, onerr); + update->lock = NULL; /* freed by update_ref_write */ if (ret) goto cleanup; } + } /* Perform deletes now that updates are safely completed */ - for (i = 0; i < n; i++) - if (locks[i]) { - delnames[delnum++] = locks[i]->ref_name; - ret |= delete_ref_loose(locks[i], types[i]); + for (i = 0; i < n; i++) { + struct ref_update *update = updates[i]; + + if (update->lock) { + delnames[delnum++] = update->lock->ref_name; + ret |= delete_ref_loose(update->lock, update->type); } + } + ret |= repack_without_refs(delnames, delnum); for (i = 0; i < delnum; i++) unlink_or_warn(git_path("logs/%s", delnames[i])); @@@ -3502,10 -3367,12 +3500,10 @@@ cleanup: for (i = 0; i < n; i++) - if (locks[i]) - unlock_ref(locks[i]); - free(updates); - free(types); - free(locks); + if (updates[i]->lock) + unlock_ref(updates[i]->lock); free(delnames); + ref_transaction_free(transaction); return ret; } diff --combined t/t1400-update-ref.sh index 4e2459afc5,953dacc8e9..0218e96366 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@@ -235,7 -235,7 +235,7 @@@ test_expect_success 'rm -f o e && git rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e && test '"$B"' = $(cat o) && - test "warning: Log .git/logs/'"$m has gap after $gd"'." = "$(cat e)"' + test "warning: Log for ref '"$m has gap after $gd"'." = "$(cat e)"' test_expect_success \ 'Query "master@{2005-05-26 23:38:00}" (middle of history)' \ 'rm -f o e && @@@ -253,7 -253,7 +253,7 @@@ test_expect_success 'rm -f o e && git rev-parse --verify "master@{2005-05-28}" >o 2>e && test '"$D"' = $(cat o) && - test "warning: Log .git/logs/'"$m unexpectedly ended on $ld"'." = "$(cat e)"' + test "warning: Log for ref '"$m unexpectedly ended on $ld"'." = "$(cat e)"' rm -f .git/$m .git/logs/$m expect @@@ -350,28 -350,22 +350,28 @@@ test_expect_success 'stdin fails on unk grep "fatal: unknown command: unknown $a" err ' -test_expect_success 'stdin fails on badly quoted input' ' +test_expect_success 'stdin fails on unbalanced quotes' ' echo "create $a \"master" >stdin && test_must_fail git update-ref --stdin err && grep "fatal: badly quoted argument: \\\"master" err ' -test_expect_success 'stdin fails on arguments not separated by space' ' +test_expect_success 'stdin fails on invalid escape' ' + echo "create $a \"ma\zter\"" >stdin && + test_must_fail git update-ref --stdin err && + grep "fatal: badly quoted argument: \\\"ma\\\\zter\\\"" err +' + +test_expect_success 'stdin fails on junk after quoted argument' ' echo "create \"$a\"master" >stdin && test_must_fail git update-ref --stdin err && - grep "fatal: expected SP but got: master" err + grep "fatal: unexpected character after quoted argument: \\\"$a\\\"master" err ' test_expect_success 'stdin fails create with no ref' ' echo "create " >stdin && test_must_fail git update-ref --stdin err && - grep "fatal: create line missing " err + grep "fatal: create: missing " err ' test_expect_success 'stdin fails create with bad ref name' ' @@@ -383,19 -377,19 +383,19 @@@ test_expect_success 'stdin fails create with no new value' ' echo "create $a" >stdin && test_must_fail git update-ref --stdin err && - grep "fatal: create $a missing " err + grep "fatal: create $a: missing " err ' test_expect_success 'stdin fails create with too many arguments' ' echo "create $a $m $m" >stdin && test_must_fail git update-ref --stdin err && - grep "fatal: create $a has extra input: $m" err + grep "fatal: create $a: extra input: $m" err ' test_expect_success 'stdin fails update with no ref' ' echo "update " >stdin && test_must_fail git update-ref --stdin err && - grep "fatal: update line missing " err + grep "fatal: update: missing " err ' test_expect_success 'stdin fails update with bad ref name' ' @@@ -407,19 -401,19 +407,19 @@@ test_expect_success 'stdin fails update with no new value' ' echo "update $a" >stdin && test_must_fail git update-ref --stdin err && - grep "fatal: update $a missing " err + grep "fatal: update $a: missing " err ' test_expect_success 'stdin fails update with too many arguments' ' echo "update $a $m $m $m" >stdin && test_must_fail git update-ref --stdin err && - grep "fatal: update $a has extra input: $m" err + grep "fatal: update $a: extra input: $m" err ' test_expect_success 'stdin fails delete with no ref' ' echo "delete " >stdin && test_must_fail git update-ref --stdin err && - grep "fatal: delete line missing " err + grep "fatal: delete: missing " err ' test_expect_success 'stdin fails delete with bad ref name' ' @@@ -431,13 -425,13 +431,13 @@@ test_expect_success 'stdin fails delete with too many arguments' ' echo "delete $a $m $m" >stdin && test_must_fail git update-ref --stdin err && - grep "fatal: delete $a has extra input: $m" err + grep "fatal: delete $a: extra input: $m" err ' test_expect_success 'stdin fails verify with too many arguments' ' echo "verify $a $m $m" >stdin && test_must_fail git update-ref --stdin err && - grep "fatal: verify $a has extra input: $m" err + grep "fatal: verify $a: extra input: $m" err ' test_expect_success 'stdin fails option with unknown name' ' @@@ -464,24 -458,6 +464,24 @@@ test_expect_success 'stdin create ref w test_cmp expect actual ' +test_expect_success 'stdin succeeds with quoted argument' ' + git update-ref -d $a && + echo "create $a \"$m\"" >stdin && + git update-ref --stdin expect && + git rev-parse $a >actual && + test_cmp expect actual +' + +test_expect_success 'stdin succeeds with escaped character' ' + git update-ref -d $a && + echo "create $a \"ma\\163ter\"" >stdin && + git update-ref --stdin expect && + git rev-parse $a >actual && + test_cmp expect actual +' + test_expect_success 'stdin update ref creates with zero old value' ' echo "update $b $m $Z" >stdin && git update-ref --stdin stdin && test_must_fail git update-ref --stdin err && - grep "fatal: invalid old value for ref $c: does-not-exist" err && + grep "fatal: update $c: invalid : does-not-exist" err && test_must_fail git rev-parse --verify -q $c ' test_expect_success 'stdin create ref fails with bad new value' ' echo "create $c does-not-exist" >stdin && test_must_fail git update-ref --stdin err && - grep "fatal: invalid new value for ref $c: does-not-exist" err && + grep "fatal: create $c: invalid : does-not-exist" err && test_must_fail git rev-parse --verify -q $c ' test_expect_success 'stdin create ref fails with zero new value' ' echo "create $c " >stdin && test_must_fail git update-ref --stdin err && - grep "fatal: create $c given zero new value" err && + grep "fatal: create $c: zero " err && test_must_fail git rev-parse --verify -q $c ' @@@ -556,7 -532,7 +556,7 @@@ test_expect_success 'stdin delete ref f test_expect_success 'stdin delete ref fails with zero old value' ' echo "delete $a " >stdin && test_must_fail git update-ref --stdin err && - grep "fatal: delete $a given zero old value" err && + grep "fatal: delete $a: zero " err && git rev-parse $m >expect && git rev-parse $a >actual && test_cmp expect actual @@@ -697,7 -673,7 +697,7 @@@ test_expect_success 'stdin -z fails on test_expect_success 'stdin -z fails create with no ref' ' printf $F "create " >stdin && test_must_fail git update-ref -z --stdin err && - grep "fatal: create line missing " err + grep "fatal: create: missing " err ' test_expect_success 'stdin -z fails create with bad ref name' ' @@@ -709,7 -685,7 +709,7 @@@ test_expect_success 'stdin -z fails create with no new value' ' printf $F "create $a" >stdin && test_must_fail git update-ref -z --stdin err && - grep "fatal: create $a missing " err + grep "fatal: create $a: unexpected end of input when reading " err ' test_expect_success 'stdin -z fails create with too many arguments' ' @@@ -721,39 -697,25 +721,39 @@@ test_expect_success 'stdin -z fails update with no ref' ' printf $F "update " >stdin && test_must_fail git update-ref -z --stdin err && - grep "fatal: update line missing " err + grep "fatal: update: missing " err +' + +test_expect_success 'stdin -z fails update with too few args' ' + printf $F "update $a" "$m" >stdin && + test_must_fail git update-ref -z --stdin err && + grep "fatal: update $a: unexpected end of input when reading " err ' test_expect_success 'stdin -z fails update with bad ref name' ' - printf $F "update ~a" "$m" >stdin && + printf $F "update ~a" "$m" "" >stdin && test_must_fail git update-ref -z --stdin err && grep "fatal: invalid ref format: ~a" err ' +test_expect_success 'stdin -z emits warning with empty new value' ' + git update-ref $a $m && + printf $F "update $a" "" "" >stdin && + git update-ref -z --stdin err && + grep "warning: update $a: missing , treating as zero" err && + test_must_fail git rev-parse --verify -q $a +' + test_expect_success 'stdin -z fails update with no new value' ' printf $F "update $a" >stdin && test_must_fail git update-ref -z --stdin err && - grep "fatal: update $a missing " err + grep "fatal: update $a: unexpected end of input when reading " err ' test_expect_success 'stdin -z fails update with no old value' ' printf $F "update $a" "$m" >stdin && test_must_fail git update-ref -z --stdin err && - grep "fatal: update $a missing \\[\\] NUL" err + grep "fatal: update $a: unexpected end of input when reading " err ' test_expect_success 'stdin -z fails update with too many arguments' ' @@@ -765,7 -727,7 +765,7 @@@ test_expect_success 'stdin -z fails delete with no ref' ' printf $F "delete " >stdin && test_must_fail git update-ref -z --stdin err && - grep "fatal: delete line missing " err + grep "fatal: delete: missing " err ' test_expect_success 'stdin -z fails delete with bad ref name' ' @@@ -777,7 -739,7 +777,7 @@@ test_expect_success 'stdin -z fails delete with no old value' ' printf $F "delete $a" >stdin && test_must_fail git update-ref -z --stdin err && - grep "fatal: delete $a missing \\[\\] NUL" err + grep "fatal: delete $a: unexpected end of input when reading " err ' test_expect_success 'stdin -z fails delete with too many arguments' ' @@@ -795,7 -757,7 +795,7 @@@ test_expect_success 'stdin -z fails ver test_expect_success 'stdin -z fails verify with no old value' ' printf $F "verify $a" >stdin && test_must_fail git update-ref -z --stdin err && - grep "fatal: verify $a missing \\[\\] NUL" err + grep "fatal: verify $a: unexpected end of input when reading " err ' test_expect_success 'stdin -z fails option with unknown name' ' @@@ -854,7 -816,7 +854,7 @@@ test_expect_success 'stdin -z update re test_expect_success 'stdin -z update ref fails with bad old value' ' printf $F "update $c" "$m" "does-not-exist" >stdin && test_must_fail git update-ref -z --stdin err && - grep "fatal: invalid old value for ref $c: does-not-exist" err && + grep "fatal: update $c: invalid : does-not-exist" err && test_must_fail git rev-parse --verify -q $c ' @@@ -872,14 -834,14 +872,14 @@@ test_expect_success 'stdin -z create re git update-ref -d "$c" && printf $F "create $c" "does-not-exist" >stdin && test_must_fail git update-ref -z --stdin err && - grep "fatal: invalid new value for ref $c: does-not-exist" err && + grep "fatal: create $c: invalid : does-not-exist" err && test_must_fail git rev-parse --verify -q $c ' -test_expect_success 'stdin -z create ref fails with zero new value' ' +test_expect_success 'stdin -z create ref fails with empty new value' ' printf $F "create $c" "" >stdin && test_must_fail git update-ref -z --stdin err && - grep "fatal: create $c given zero new value" err && + grep "fatal: create $c: missing " err && test_must_fail git rev-parse --verify -q $c ' @@@ -903,7 -865,7 +903,7 @@@ test_expect_success 'stdin -z delete re test_expect_success 'stdin -z delete ref fails with zero old value' ' printf $F "delete $a" "$Z" >stdin && test_must_fail git update-ref -z --stdin err && - grep "fatal: delete $a given zero old value" err && + grep "fatal: delete $a: zero " err && git rev-parse $m >expect && git rev-parse $a >actual && test_cmp expect actual @@@ -961,7 -923,7 +961,7 @@@ test_expect_success 'stdin -z update re test_expect_success 'stdin -z update refs fails with wrong old value' ' git update-ref $c $m && - printf $F "update $a" "$m" "$m" "update $b" "$m" "$m" "update $c" "" "$Z" >stdin && + printf $F "update $a" "$m" "$m" "update $b" "$m" "$m" "update $c" "$m" "$Z" >stdin && test_must_fail git update-ref -z --stdin err && grep "fatal: Cannot lock the ref '"'"'$c'"'"'" err && git rev-parse $m >expect &&