Skip to content

A .gitattributes merge driver is retroactively inert: every PR older than the declaration still conflicts #3308

Closed

Description

@noahgift

Measurement

Seven open PRs could not even be updated onto main today — gh pr update-branch refused each with "Cannot update PR branch due to conflicts". The conflict surface, measured across all seven:

File PRs
docs/roadmaps/roadmap.yaml 4
.github/workflows/ci.yml 3
docs/audits/impl-estimates.jsonl 2
crates/aprender-contracts-cli/src/lib.rs, Makefile, scripts/tree_reader_tests.txt 1 each

docs/audits/impl-estimates.jsonl should never conflict. It is strictly append-only — every commit touching it is N insertions, 0 deletions — and #3256 (768e740b1) declared exactly the right thing:

docs/audits/*.jsonl merge=union

It still conflicted.

Mechanism

A .gitattributes merge-driver declaration is retroactively inert. Git reads merge attributes from the side being merged into, not from the side being merged in. A branch cut before the rule landed carries a .gitattributes without it, so the driver is never consulted.

Measured on agent/T-2 (#3005):

$ git show origin/agent/T-2:.gitattributes
docs/roadmaps/roadmap.yaml merge=roadmap      <- no jsonl rule

$ git merge-base --is-ancestor 768e740b1 origin/agent/T-2 ; echo $?
1                                              <- branch predates the declaration

$ git merge origin/main
CONFLICT (content): Merge conflict in docs/audits/impl-estimates.jsonl

The blobs themselves merge perfectly — git merge-file --union on the three stages returns rc=0, 50 rows, 0 markers, every line valid JSON. Nothing was wrong with the data or the driver; the attribute simply never bound.

This is the "declared fix that cannot fire" class. The repo has had the correct declaration since #3256 and every PR older than it kept conflicting anyway, silently, with no signal that the declaration was not in play.

Fix

scripts/ci_resolve_dirty.sh already owns this class for roadmap.yaml: it registers merge.roadmap.driver per invocation so nothing leaks into the shared .git. Bind union the same way:

git -c core.attributesFile=<tmp with `docs/audits/*.jsonl merge=union`> merge ...

core.attributesFile is the lowest-priority attribute source, so it applies only where the branch has no rule of its own — a branch that already carries the declaration is unaffected. Nothing shared is written.

Verified on #3271, whose merge had failed minutes earlier:

merge rc=0
Auto-merging Makefile
Auto-merging docs/audits/impl-estimates.jsonl
Auto-merging docs/roadmaps/roadmap.yaml

49 rows, 0 invalid, 0 duplicate; check_roadmap_ids_unique.sh and check_roadmap_sorted.sh both PASS.

Drained under this diagnosis

#3005, #3271, #3278, #3281 pushed out of CONFLICTING; seven more (#3205, #3238, #3246, #3249, #3259, #3265, #3270) updated onto main. Remaining conflicts (#3134, #3245, #3248) are genuine ci.yml content edits and are not this class.

Activity

  1. added a commit that references this issue on Sep 29, 2026
    03dfa8f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High priority

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions