push: fix --force-if-includes when remote-tracking ref has no reflog

Since 99a1f9ae10 (push: add reflog check for "--force-if-includes",
2020-10-03), is_reachable_in_reflog() looks for the remote tip in the
local branch's reflog and stops at entries older than the newest entry
of the remote-tracking ref's reflog. That timestamp comes from a
callback of refs_for_each_reflog_ent_reverse(), which never runs when
the remote-tracking ref has no reflog, so the variable stays
uninitialized.

With the files backend a remote-tracking ref that "git clone" created
has no reflog until it moves. On my machine the leftover value exceeded
any real timestamp, so the walk stopped at the first entry and the push
was rejected with "remote ref updated since checkout" though nothing on
the remote had changed.

That stopping point assumes an entry older than the last recorded move
of the remote-tracking ref cannot be the one we want. The record itself
can be missing: never written, deleted, or expired by gc. Initialize
the timestamp to zero for a missing record. timestamp_t is unsigned, so
nothing compares older and the walk stops only at the remote tip or at
the end of the local reflog. "Now" brings the bug straight back. A
fixed age narrows it: the push is rejected when the remote tip is
recorded only past the first entry older than that age and nothing
collected reaches it.

When the remote tip is not in the local reflog at all, a stopped walk
and a full one fall back to the same merge-base check, and the full one
hands it more entries.

Signed-off-by: Aleksei Sviridkin <f@lex.la>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
main
Aleksei Sviridkin 2026-09-29 12:13:18 +03:00 committed by Junio C Hamano
parent e9019fcafe
commit c84ed3a0ab
2 changed files with 17 additions and 1 deletions

View File

@ -2751,7 +2751,7 @@ static int check_and_collect_until(const char *refname UNUSED,
*/
static int is_reachable_in_reflog(const char *local, const struct ref *remote)
{
timestamp_t date;
timestamp_t date = 0;
struct commit *commit;
struct commit **chunk;
struct check_and_collect_until_cb_data cb;

View File

@ -396,4 +396,20 @@ test_expect_success '"--force-if-includes" should allow deletes' '
)
'

test_expect_success '"--force-if-includes" should allow forced update when remote-tracking ref has no reflog' '
setup_src_dup_dst &&
test_when_finished "rm -fr dst src dup" &&
(
cd src &&
git switch branch &&
git pull --rebase origin branch &&
# the bug needs a remote-tracking ref with no reflog, and
# the fetch above wrote one
git reflog expire --expire=all refs/remotes/origin/branch &&
git reset --hard HEAD^ &&
test_commit I &&
git push --force-if-includes --force-with-lease="branch"
)
'

test_done