Skip to content

Commit 755e746

Browse files
peffgitster
authored andcommitted
get_oid_basic(): special-case ref@{n} for oldest reflog entry
The goal of 6436a20 (refs: allow @{n} to work with n-sized reflog, 2021-01-07) was that if we have "n" entries in a reflog, we should still be able to resolve ref@{n} by looking at the "old" value of the oldest entry. Commit 6436a20 tried to put the logic into read_ref_at() by shifting its idea of "n" by one. But we reverted that in the previous commit, since it led to bugs in other callers which cared about the details of the reflog entry we found. Instead, let's put the special case into the caller that resolves @{n}, as it cares only about the oid. read_ref_at() is even kind enough to return the "old" value from the final reflog; it just returns "1" to signal to us that we ran off the end of the reflog. But we can notice in the caller that we read just enough records for that "old" value to be the one we're looking for, and use it. Note that read_ref_at() could notice this case, too, and just return 0. But we don't want to do that, because the caller must be made aware that we only found the oid, not an actual reflog entry (and the call sites in show-branch do care about this). There is one complication, though. When read_ref_at() hits a truncated reflog, it will return the "old" value of the oldest entry only if it is not the null oid. Otherwise, it actually returns the "new" value from that entry! This bit of fudging is due to d1a4489 (avoid null SHA1 in oldest reflog, 2008-07-08), where asking for "ref@{20.years.ago}" for a ref created recently will produce the initial value as a convenience (even though technically it did not exist 20 years ago). But this convenience is only useful for time-based cutoffs. For count-based cutoffs, get_oid_basic() has always simply complained about going too far back: $ git rev-parse HEAD@{20} fatal: log for 'HEAD' only has 16 entries and we should continue to do so, rather than returning a nonsense value (there's even a test in t1508 already which covers this). So let's have the d1a4489 code kick in only when doing timestamp-based cutoffs. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent aa72e73 commit 755e746

File tree

3 files changed

+11
-2
lines changed

3 files changed

+11
-2
lines changed

object-name.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,15 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
10341034
len, str,
10351035
show_date(co_time, co_tz, DATE_MODE(RFC2822)));
10361036
}
1037+
} else if (nth == co_cnt && !is_null_oid(oid)) {
1038+
/*
1039+
* We were asked for the Nth reflog (counting
1040+
* from 0), but there were only N entries.
1041+
* read_ref_at() will have returned "1" to tell
1042+
* us it did not find an entry, but it did
1043+
* still fill in the oid with the "old" value,
1044+
* which we can use.
1045+
*/
10371046
} else {
10381047
if (flags & GET_OID_QUIETLY) {
10391048
exit(128);

refs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1083,7 +1083,7 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid
10831083

10841084
set_read_ref_cutoffs(cb, timestamp, tz, message);
10851085
oidcpy(cb->oid, ooid);
1086-
if (is_null_oid(cb->oid))
1086+
if (cb->at_time && is_null_oid(cb->oid))
10871087
oidcpy(cb->oid, noid);
10881088
/* We just want the first entry */
10891089
return 1;

t/t1508-at-combinations.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ test_expect_success 'create path with @' '
103103
check "@:normal" blob content
104104
check "@:fun@ny" blob content
105105

106-
test_expect_failure '@{1} works with only one reflog entry' '
106+
test_expect_success '@{1} works with only one reflog entry' '
107107
git checkout -B newbranch main &&
108108
git reflog expire --expire=now refs/heads/newbranch &&
109109
git commit --allow-empty -m "first after expiration" &&

0 commit comments

Comments
 (0)