Tooling gotcha — do NOT trust RTK-filtered git log/--graph for history rewriting

When RTK (the token-optimizing CLI proxy that auto-rewrites git …rtk git … via a Claude Code hook) filters git log output — especially git log --oneline --graph — it drops merge commits from the displayed list. The result is a deceptively linear-looking graph: a merge commit gets collapsed out of view, so a child commit appears to descend from the merge’s first parent’s parent. Trusting this for a squash / rebase / reset operates on the wrong commit range and can lose work.

This gotcha surfaced during git history surgery on esign_css (update/customization/instacash-2026-05-27 branch). See esign-css-instacash-orphan-history for the broader esign_css instacash history shape.

The symptom (concrete case)

RTK-filtered git log --oneline --graph rendered an apparently linear chain and collapsed merge commit 27e6cc7 (a real two-parent merge) out of the displayed list. This made it look like:

  • 9cce25f’s parent was 5d5b407

…when its actual parent was the merge 27e6cc7. Operating on the visible “parent” would have squashed/reset across the merge boundary and dropped a parent’s worth of history.

Why it happens

RTK’s output filter elides commit lines it treats as noise; merge commits’ own list lines get filtered, so a merge silently disappears from --oneline/--graph even though it is structurally present in the DAG.

It also hits plain git log -1 (added 2026-08-11)

git log -1 right after committing a merge shows the WRONG commit

Confirmed on FKITDEV-9197 (CIB / portal_css devel update): immediately after creating merge commit b1a4bc94, git log -1 displayed 56a63bd0 — devel’s tip, i.e. the merge’s second parent. The merge line was filtered out, so -1 surfaced the next visible commit.

The failure is maximally misleading at exactly the moment you are checking “did my commit land, and is it the right one?“. You conclude the commit failed, or that you committed onto the wrong base, and start undoing correct work.

Verify a merge commit with plumbing or a raw run instead:

rtk proxy git log -1 --format='%H %P %s'
git rev-parse HEAD                    # the sha you just created
git rev-list --parents -n 1 HEAD      # 3 shas = a merge, and shows both parents

Same family as the other silently-wrong RTK git modes catalogued in rtk-mangles-curl-and-pipes (git show <ref>:<file> | grep reporting real matches as absent, diff -u with a wrong exit code, git diff --no-index rendering everything as new, blanked ${PIPESTATUS[0]}).

Reliable patterns for git surgery under RTK

Use parent-aware / plumbing commands that survive filtering

These expose the real DAG even when a merge’s own log line is filtered out.

Expose parents in the log itself (the %p field shows actual parent SHAs, so merges are visible by their 2+ parents even if their own list line is filtered):

git log --pretty='%h | %p | %s'

Filter-proof plumbing (porcelain-free, RTK leaves these intact):

git rev-list --parents -n 1 <sha>      # one commit's parents (2+ SHAs = a merge)
git rev-list --count A..B              # exact commit count in a range
git rev-parse <sha>                    # resolve a ref to a full SHA
git rev-parse '<sha>^{tree}'           # the tree (content) hash at a commit

rtk proxy <cmd> runs a raw, unfiltered command — but the user may prefer you avoid it; reach for the parent-aware / plumbing commands above first.

Verify a squash/rebase preserved content — compare TREE hashes

For Agents

A squash or rebase must change history only, never content. Prove zero content change by comparing the tree hash before and after — an identical tree hash is cryptographic proof the working content is byte-identical.

git rev-parse 'HEAD^{tree}'   # capture BEFORE the operation
# … perform the squash / rebase …
git rev-parse 'HEAD^{tree}'   # must equal the captured value

If the two tree hashes match, only the history was restructured. If they differ, content changed — stop and investigate.

Reusable technique — non-interactive squash of the latest N commits

git rebase -i is unavailable in this harness, so squash the latest N commits with a soft reset instead:

git branch backup/<name> HEAD        # 1. backup ref first — trivial rollback
git diff --cached --quiet            # 2. guard: nothing staged (must exit 0)
ORIG=$(git rev-parse 'HEAD^{tree}')  #    capture original tree hash
git reset --soft <base>              # 3. move HEAD back, keep all changes staged
git commit -m "<msg>"                # 4. one new commit on top of <base>
test "$(git rev-parse 'HEAD^{tree}')" = "$ORIG" && echo OK   # 5. tree-hash equality
  • <base> is the commit you want the single squashed commit to sit on top of — derive it from git log --pretty='%h | %p | %s' or git rev-list --parents, not from a filtered --graph.
  • Step 1 (backup ref) makes rollback git reset --hard backup/<name>.
  • Step 2 ensures the squash doesn’t silently absorb unrelated staged changes.
  • Step 5 is the proof gate — identical tree hash means zero content change, history-only restructure.
  • esign-css-instacash-orphan-history — esign_css customization/instacash is a single squashed orphan; same family of “the usual git tooling misleads you here” hazards (no merge base, meaningless rev-list --count A..B).
  • RTK — the token-optimizing proxy whose output filtering causes this gotcha.
  • instacash-update-2026-05-27-status — the 2026-05-27 InstaCash devel-update wave during which this surgery happened.
  • FKITDEV-9197 — the CIB / portal_css devel update where the git log -1 case was found.
  • rtk-mangles-curl-and-pipes — the wider catalogue of silently-wrong RTK commands.
  • esign — esign_css / eSign service overview.