git/list[1] front-page[2] threads[3] people[4] search[5] about
wed 2026-10-07 16:55 UTC

[PATCH v4] push: fix --force-if-includes when remote-tracking ref has no reflog

From
Aleksei Sviridkin <f@lex.la>
Date
Sep 29, 2026, 09:13 UTC
Message-ID
<20260929091319.86392-1-f@lex.la>
In-Reply-To
<20260905171330.34646-1-f@lex.la>

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>
---
Changes since v3:
- log message rewritten. Two things in it were wrong, not just
  unclear: it read as if a walk that stops at the cut-off skips the
  merge-base check, and it said there is "no such moment" when what is
  missing is the record of it.
- test uses setup_src_dup_dst and expires only the remote-tracking
  reflog.

t5533 passes 24/24 with the fix on files and on reftable, and the new test fails on both without it. That failure is only reliable when built with

	make CFLAGS_APPEND=-ftrivial-auto-var-init=pattern

otherwise the stack may hold a small number, as on your machine. So CI would not catch this going uninitialized again.

One detail the expire hides: on files it leaves no reflog at all, on reftable an empty one. The callback does not run either way.

The batch size growth looks worth its own patch. Not touched here.
 remote.c            |  2 +-
 t/t5533-push-cas.sh | 16 ++++++++++++++++
 2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/remote.c b/remote.c
index 00723b385e..6d301698ca 100644
--- a/remote.c
+++ b/remote.c
@@ -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;
diff --git a/t/t5533-push-cas.sh b/t/t5533-push-cas.sh
index cba26a872d..c9aaeec8d1 100755
--- a/t/t5533-push-cas.sh
+++ b/t/t5533-push-cas.sh
@@ -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
-- 
2.55.0
Previous: Tyler CiprianiNext: Junio C Hamano
Message 25 of 27 in “push: fix --force-if-includes when remote-tracking ref has no reflog”
  1. push: fix --force-if-includes when remote-tracking ref has no reflogAleksei Sviridkin, Sep 3, 2026
  2. Junio C HamanoSep 3, 2026
  3. Aleksei SviridkinSep 3, 2026
  4. Junio C HamanoSep 3, 2026
  5. Aleksei SviridkinSep 3, 2026
  6. Kristoffer HaugsbakkSep 4, 2026
  7. push: fix --force-if-includes when remote-tracking ref has no reflogAleksei Sviridkin, Sep 4, 2026
  8. Junio C HamanoSep 4, 2026
  9. Junio C HamanoSep 4, 2026
  10. push: fix --force-if-includes when remote-tracking ref has no reflogAleksei Sviridkin, Sep 5, 2026
  11. Aleksei SviridkinSep 5, 2026
  12. Junio C HamanoSep 6, 2026
  13. Kristoffer HaugsbakkSep 6, 2026
  14. Aleksei SviridkinSep 6, 2026
  15. Junio C HamanoSep 6, 2026
  16. Thomas BachemSep 7, 2026
  17. Weijie YuanSep 7, 2026
  18. Junio C HamanoSep 8, 2026
  19. Aleksei SviridkinSep 9, 2026
  20. Junio C HamanoSep 10, 2026
  21. Aleksei SviridkinSep 10, 2026
  22. Tyler CiprianiSep 25, 2026
  23. Junio C HamanoSep 25, 2026
  24. Tyler CiprianiSep 29, 2026
  25. push: fix --force-if-includes when remote-tracking ref has no reflogAleksei Sviridkin, Sep 29, 2026
  26. Junio C HamanoSep 29, 2026
  27. Junio C HamanoSep 29, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.