diff --git a/refs.c b/refs.c index 13dc2c3291..03968ad787 100644 --- a/refs.c +++ b/refs.c @@ -882,59 +882,9 @@ struct read_ref_at_cb { int *cutoff_cnt; }; -static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid, - const char *email, timestamp_t timestamp, int tz, - const char *message, void *cb_data) +static void set_read_ref_cutoffs(struct read_ref_at_cb *cb, + timestamp_t timestamp, int tz, const char *message) { - 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]oid so they still - * hold the values for the previous record. - */ - if (!is_null_oid(&cb->ooid)) { - oidcpy(cb->oid, noid); - if (!oideq(&cb->ooid, noid)) - warning(_("log for ref %s has gap after %s"), - cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822))); - } - else if (cb->date == cb->at_time) - oidcpy(cb->oid, noid); - else if (!oideq(noid, cb->oid)) - warning(_("log for ref %s unexpectedly ended on %s"), - cb->refname, show_date(cb->date, cb->tz, - DATE_MODE(RFC2822))); - oidcpy(&cb->ooid, ooid); - oidcpy(&cb->noid, noid); - cb->found_it = 1; - return 1; - } - oidcpy(&cb->ooid, ooid); - oidcpy(&cb->noid, noid); - if (cb->cnt > 0) - cb->cnt--; - return 0; -} - -static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid, - const char *email, timestamp_t 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) @@ -943,6 +893,69 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid *cb->cutoff_tz = tz; if (cb->cutoff_cnt) *cb->cutoff_cnt = cb->reccnt; +} + +static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid, + const char *email, timestamp_t timestamp, int tz, + const char *message, void *cb_data) +{ + struct read_ref_at_cb *cb = cb_data; + int reached_count; + + cb->tz = tz; + cb->date = timestamp; + + /* + * It is not possible for cb->cnt == 0 on the first iteration because + * that special case is handled in read_ref_at(). + */ + if (cb->cnt > 0) + cb->cnt--; + reached_count = cb->cnt == 0 && !is_null_oid(ooid); + if (timestamp <= cb->at_time || reached_count) { + set_read_ref_cutoffs(cb, timestamp, tz, message); + /* + * we have not yet updated cb->[n|o]oid so they still + * hold the values for the previous record. + */ + if (!is_null_oid(&cb->ooid) && !oideq(&cb->ooid, noid)) + warning(_("log for ref %s has gap after %s"), + cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822))); + if (reached_count) + oidcpy(cb->oid, ooid); + else if (!is_null_oid(&cb->ooid) || cb->date == cb->at_time) + oidcpy(cb->oid, noid); + else if (!oideq(noid, cb->oid)) + warning(_("log for ref %s unexpectedly ended on %s"), + cb->refname, show_date(cb->date, cb->tz, + DATE_MODE(RFC2822))); + cb->found_it = 1; + } + cb->reccnt++; + oidcpy(&cb->ooid, ooid); + oidcpy(&cb->noid, noid); + return cb->found_it; +} + +static int read_ref_at_ent_newest(struct object_id *ooid, struct object_id *noid, + const char *email, timestamp_t timestamp, + int tz, const char *message, void *cb_data) +{ + struct read_ref_at_cb *cb = cb_data; + + set_read_ref_cutoffs(cb, timestamp, tz, message); + oidcpy(cb->oid, noid); + /* We just want the first entry */ + return 1; +} + +static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid, + const char *email, timestamp_t timestamp, + int tz, const char *message, void *cb_data) +{ + struct read_ref_at_cb *cb = cb_data; + + set_read_ref_cutoffs(cb, timestamp, tz, message); oidcpy(cb->oid, ooid); if (is_null_oid(cb->oid)) oidcpy(cb->oid, noid); @@ -967,6 +980,11 @@ int read_ref_at(struct ref_store *refs, const char *refname, cb.cutoff_cnt = cutoff_cnt; cb.oid = oid; + if (cb.cnt == 0) { + refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent_newest, &cb); + return 0; + } + refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent, &cb); if (!cb.reccnt) { diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh index dc9fe3cbf1..a7e9b4863d 100755 --- a/t/t1503-rev-parse-verify.sh +++ b/t/t1503-rev-parse-verify.sh @@ -86,8 +86,8 @@ test_expect_success 'fails silently when using -q' ' test_expect_success 'fails silently when using -q with deleted reflogs' ' ref=$(git rev-parse HEAD) && git update-ref --create-reflog -m "message for refs/test" refs/test "$ref" && - git reflog delete --updateref --rewrite refs/test@{0} && - test_must_fail git rev-parse -q --verify refs/test@{0} >error 2>&1 && + git reflog delete --updateref --rewrite refs/test@{1} && + test_must_fail git rev-parse -q --verify refs/test@{1} >error 2>&1 && test_must_be_empty error ' diff --git a/t/t1508-at-combinations.sh b/t/t1508-at-combinations.sh index 4a9964e9dc..e4521b7b97 100755 --- a/t/t1508-at-combinations.sh +++ b/t/t1508-at-combinations.sh @@ -99,4 +99,17 @@ test_expect_success 'create path with @' ' check "@:normal" blob content check "@:fun@ny" blob content +test_expect_success '@{1} works with only one reflog entry' ' + git checkout -B newbranch master && + git reflog expire --expire=now refs/heads/newbranch && + git commit --allow-empty -m "first after expiration" && + test_cmp_rev newbranch~ newbranch@{1} +' + +test_expect_success '@{0} works with empty reflog' ' + git checkout -B newbranch master && + git reflog expire --expire=now refs/heads/newbranch && + test_cmp_rev newbranch newbranch@{0} +' + test_done