Skip to content

Commit 74522bb

Browse files
committed
Merge branch 'jk/reflog-special-cases-fix'
The logic to access reflog entries by date and number had ugly corner cases at the boundaries, which have been cleaned up. * jk/reflog-special-cases-fix: read_ref_at(): special-case ref@{0} for an empty reflog get_oid_basic(): special-case ref@{n} for oldest reflog entry Revert "refs: allow @{n} to work with n-sized reflog"
2 parents 542d093 + 5edd126 commit 74522bb

File tree

4 files changed

+87
-51
lines changed

4 files changed

+87
-51
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: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,55 +1039,40 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
10391039
const char *message, void *cb_data)
10401040
{
10411041
struct read_ref_at_cb *cb = cb_data;
1042-
int reached_count;
10431042

10441043
cb->tz = tz;
10451044
cb->date = timestamp;
10461045

1047-
/*
1048-
* It is not possible for cb->cnt == 0 on the first iteration because
1049-
* that special case is handled in read_ref_at().
1050-
*/
1051-
if (cb->cnt > 0)
1052-
cb->cnt--;
1053-
reached_count = cb->cnt == 0 && !is_null_oid(ooid);
1054-
if (timestamp <= cb->at_time || reached_count) {
1046+
if (timestamp <= cb->at_time || cb->cnt == 0) {
10551047
set_read_ref_cutoffs(cb, timestamp, tz, message);
10561048
/*
10571049
* we have not yet updated cb->[n|o]oid so they still
10581050
* hold the values for the previous record.
10591051
*/
1060-
if (!is_null_oid(&cb->ooid) && !oideq(&cb->ooid, noid))
1061-
warning(_("log for ref %s has gap after %s"),
1052+
if (!is_null_oid(&cb->ooid)) {
1053+
oidcpy(cb->oid, noid);
1054+
if (!oideq(&cb->ooid, noid))
1055+
warning(_("log for ref %s has gap after %s"),
10621056
cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
1063-
if (reached_count)
1064-
oidcpy(cb->oid, ooid);
1065-
else if (!is_null_oid(&cb->ooid) || cb->date == cb->at_time)
1057+
}
1058+
else if (cb->date == cb->at_time)
10661059
oidcpy(cb->oid, noid);
10671060
else if (!oideq(noid, cb->oid))
10681061
warning(_("log for ref %s unexpectedly ended on %s"),
10691062
cb->refname, show_date(cb->date, cb->tz,
10701063
DATE_MODE(RFC2822)));
1064+
cb->reccnt++;
1065+
oidcpy(&cb->ooid, ooid);
1066+
oidcpy(&cb->noid, noid);
10711067
cb->found_it = 1;
1068+
return 1;
10721069
}
10731070
cb->reccnt++;
10741071
oidcpy(&cb->ooid, ooid);
10751072
oidcpy(&cb->noid, noid);
1076-
return cb->found_it;
1077-
}
1078-
1079-
static int read_ref_at_ent_newest(struct object_id *ooid UNUSED,
1080-
struct object_id *noid,
1081-
const char *email UNUSED,
1082-
timestamp_t timestamp, int tz,
1083-
const char *message, void *cb_data)
1084-
{
1085-
struct read_ref_at_cb *cb = cb_data;
1086-
1087-
set_read_ref_cutoffs(cb, timestamp, tz, message);
1088-
oidcpy(cb->oid, noid);
1089-
/* We just want the first entry */
1090-
return 1;
1073+
if (cb->cnt > 0)
1074+
cb->cnt--;
1075+
return 0;
10911076
}
10921077

10931078
static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
@@ -1099,7 +1084,7 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid
10991084

11001085
set_read_ref_cutoffs(cb, timestamp, tz, message);
11011086
oidcpy(cb->oid, ooid);
1102-
if (is_null_oid(cb->oid))
1087+
if (cb->at_time && is_null_oid(cb->oid))
11031088
oidcpy(cb->oid, noid);
11041089
/* We just want the first entry */
11051090
return 1;
@@ -1122,14 +1107,24 @@ int read_ref_at(struct ref_store *refs, const char *refname,
11221107
cb.cutoff_cnt = cutoff_cnt;
11231108
cb.oid = oid;
11241109

1125-
if (cb.cnt == 0) {
1126-
refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent_newest, &cb);
1127-
return 0;
1128-
}
1129-
11301110
refs_for_each_reflog_ent_reverse(refs, refname, read_ref_at_ent, &cb);
11311111

11321112
if (!cb.reccnt) {
1113+
if (cnt == 0) {
1114+
/*
1115+
* The caller asked for ref@{0}, and we had no entries.
1116+
* It's a bit subtle, but in practice all callers have
1117+
* prepped the "oid" field with the current value of
1118+
* the ref, which is the most reasonable fallback.
1119+
*
1120+
* We'll put dummy values into the out-parameters (so
1121+
* they're not just uninitialized garbage), and the
1122+
* caller can take our return value as a hint that
1123+
* we did not find any such reflog.
1124+
*/
1125+
set_read_ref_cutoffs(&cb, 0, 0, "empty reflog");
1126+
return 1;
1127+
}
11331128
if (flags & GET_OID_QUIETLY)
11341129
exit(128);
11351130
else

refs.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,20 @@ int refs_create_reflog(struct ref_store *refs, const char *refname,
440440
struct strbuf *err);
441441
int safe_create_reflog(const char *refname, struct strbuf *err);
442442

443-
/** Reads log for the value of ref during at_time. **/
443+
/**
444+
* Reads log for the value of ref during at_time (in which case "cnt" should be
445+
* negative) or the reflog "cnt" entries from the top (in which case "at_time"
446+
* should be 0).
447+
*
448+
* If we found the reflog entry in question, returns 0 (and details of the
449+
* entry can be found in the out-parameters).
450+
*
451+
* If we ran out of reflog entries, the out-parameters are filled with the
452+
* details of the oldest entry we did find, and the function returns 1. Note
453+
* that there is one important special case here! If the reflog was empty
454+
* and the caller asked for the 0-th cnt, we will return "1" but leave the
455+
* "oid" field untouched.
456+
**/
444457
int read_ref_at(struct ref_store *refs,
445458
const char *refname, unsigned int flags,
446459
timestamp_t at_time, int cnt,

t/t3202-show-branch.sh

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ test_description='test show-branch'
44

55
. ./test-lib.sh
66

7-
# arbitrary reference time: 2009-08-30 19:20:00
8-
GIT_TEST_DATE_NOW=1251660000; export GIT_TEST_DATE_NOW
9-
107
test_expect_success 'error descriptions on empty repository' '
118
current=$(git branch --show-current) &&
129
cat >expect <<-EOF &&
@@ -187,18 +184,6 @@ test_expect_success 'show branch --merge-base with N arguments' '
187184
test_cmp expect actual
188185
'
189186

190-
test_expect_success 'show branch --reflog=2' '
191-
sed "s/^> //" >expect <<-\EOF &&
192-
> ! [refs/heads/branch10@{0}] (4 years, 5 months ago) commit: branch10
193-
> ! [refs/heads/branch10@{1}] (4 years, 5 months ago) commit: branch10
194-
> --
195-
> + [refs/heads/branch10@{0}] branch10
196-
> ++ [refs/heads/branch10@{1}] initial
197-
EOF
198-
git show-branch --reflog=2 >actual &&
199-
test_cmp actual expect
200-
'
201-
202187
# incompatible options
203188
while read combo
204189
do
@@ -264,4 +249,38 @@ test_expect_success 'error descriptions on orphan branch' '
264249
test_branch_op_in_wt -c new-branch
265250
'
266251

252+
test_expect_success 'setup reflogs' '
253+
test_commit base &&
254+
git checkout -b branch &&
255+
test_commit one &&
256+
git reset --hard HEAD^ &&
257+
test_commit two &&
258+
test_commit three
259+
'
260+
261+
test_expect_success '--reflog shows reflog entries' '
262+
cat >expect <<-\EOF &&
263+
! [branch@{0}] (0 seconds ago) commit: three
264+
! [branch@{1}] (60 seconds ago) commit: two
265+
! [branch@{2}] (2 minutes ago) reset: moving to HEAD^
266+
! [branch@{3}] (2 minutes ago) commit: one
267+
----
268+
+ [branch@{0}] three
269+
++ [branch@{1}] two
270+
+ [branch@{3}] one
271+
++++ [branch@{2}] base
272+
EOF
273+
# the output always contains relative timestamps; use
274+
# a known time to get deterministic results
275+
GIT_TEST_DATE_NOW=$test_tick \
276+
git show-branch --reflog branch >actual &&
277+
test_cmp expect actual
278+
'
279+
280+
test_expect_success '--reflog handles missing reflog' '
281+
git reflog expire --expire=now branch &&
282+
git show-branch --reflog branch >actual &&
283+
test_must_be_empty actual
284+
'
285+
267286
test_done

0 commit comments

Comments
 (0)