Git development
 help / color / mirror / Atom feed
* Reftable reflog timezone encoding differs from specification
@ 2026-09-28  7:00 Josh McKinney
  2026-09-28 12:13 ` Patrick Steinhardt
  0 siblings, 1 reply; 5+ messages in thread
From: Josh McKinney @ 2026-09-28  7:00 UTC (permalink / raw)
  To: git

Hi,

Git 2.55.0 appears to store reftable reflog timezone offsets as signed
HHMM integers, whereas the specification requires signed minutes.

https://git-scm.com/docs/reftable#_log_record states:

    "tz_offset is the absolute number of minutes from GMT the
    committer was at the time of the update."

The specification also gives GMT+0230 as an example encoded as 150.

I reproduced the discrepancy on macOS arm64 by creating a SHA-1
reftable repository and committing with this date:

    2026-09-27T12:00:00+05:30

An independent Python/zlib inspection of the resulting reftable found:

    Stored timezone bytes: 02 12 = 530
    Expected signed minutes: 01 4a = 330

Both HEAD and refs/heads/main reflog entries contained 530. Git reads
its own entries back correctly as +05:30, so its writer and reader
appear internally consistent, but disagree with the specification.

Here is a reproducer using only Git and Python's standard library:

(
    set -eu
    proof_dir=$(mktemp -d)
    cd "$proof_dir"
    echo "Repository retained at: $proof_dir"

    export GIT_CONFIG_NOSYSTEM=1
    export GIT_CONFIG_GLOBAL=/dev/null
    export GIT_AUTHOR_DATE='2026-09-27T12:00:00+05:30'
    export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"

    git --version
    git init --initial-branch=main --object-format=sha1 \
        --ref-format=reftable example

    git -C example \
        -c user.name=Example \
        -c user.email=example@example.com \
        -c commit.gpgsign=false \
        -c core.logAllRefUpdates=true \
        commit --allow-empty -m "Timezone example"

    git -C example reflog show --format='%gD' \
        --date=iso-strict refs/heads/main

    python3 - <<'PY'
from pathlib import Path
import zlib

directory = Path("example/.git/reftable")
for name in (directory / "tables.list").read_text().splitlines():
    table = (directory / name).read_bytes()
    assert table[:5] == b"REFT\x01"
    footer = table[-68:]
    log_start = int.from_bytes(footer[48:56], "big")
    if not log_start:
        continue

    assert table[log_start:log_start + 1] == b"g"
    log = zlib.decompress(table[log_start + 4:])

    # Fixture-specific: email, variable-length time, then timezone.
    email = b"example@example.com"
    search_from = 0
    while (position := log.find(email, search_from)) != -1:
        position += len(email)
        while log[position] & 0x80:
            position += 1
        position += 1

        raw = log[position:position + 2]
        offset = int.from_bytes(raw, "big", signed=True)
        print(f"Timezone bytes: {raw.hex(' ')}; integer: {offset}")
        search_from = position + 2
PY
)

The relevant output is:

    refs/heads/main@{2026-09-27T12:00:00+05:30}
    Timezone bytes: 02 12; integer: 530
    Timezone bytes: 02 12; integer: 530

Is this a known discrepancy? Which representation should interoperable
implementations use? If either the implementation or specification
changes, how should existing tables be interpreted, given that values
such as 330 are valid under both interpretations?

Given that Git consistently writes and reads HHMM values, I suspect the practical resolution is to update the specification to match existing behavior. Are there other implementations or compatibility considerations that would prevent that?

Thanks,
Josh

-- 
Josh McKinney
joshka.net

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-09-29  7:10 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-28  7:00 Reftable reflog timezone encoding differs from specification Josh McKinney
2026-09-28 12:13 ` Patrick Steinhardt
2026-09-28 14:42   ` Junio C Hamano
2026-09-28 16:02     ` Josh McKinney
2026-09-29  7:10       ` Patrick Steinhardt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox