Skip to content

The changelog merges by union, so a sibling merge no longer costs a hand-resolved rebase - #461

Merged
MarkusNeusinger merged 2 commits into
mainfrom
claude/changelog-union-merge
Aug 30, 2026
Merged

The changelog merges by union, so a sibling merge no longer costs a hand-resolved rebase#461
MarkusNeusinger merged 2 commits into
mainfrom
claude/changelog-union-merge

Conversation

@MarkusNeusinger

Copy link
Copy Markdown
Owner

What

.gitattributes declares CHANGELOG.md merge=union. Two PRs that each add bullets under [Unreleased] no longer conflict: git keeps both sides (ours first, then theirs) instead of raising a conflict, so a rebase onto main goes through untouched.

The one thing union cannot judge is a line that BOTH sides changed — it would appear twice. The rule that keeps union safe therefore stands in three places (the file's header, CLAUDE.md, .github/copilot-instructions.md): add a new bullet on top of its category, never rewrite existing lines in passing.

Why

Every merge of one open PR turned the others DIRTY on CHANGELOG.md — four hand-resolved rebases on 2026-08-30 alone — and a DIRTY PR gets no CI run at all (GitHub cannot build the merge commit). Owner's ask: „könnten wir das irgendwie selbstheilen machen".

Verified

With the same attribute set locally (.git/info/attributes) the rebases of #458 and #459 onto main after the #457 merge went through with zero conflicts; the resulting [Unreleased] holds every bullet exactly once (checked by count), gates green on both.

Open question — one thing this cannot promise

Whether GitHub's own mergeability check honours merge=union (it applies its own merge when computing DIRTY/CLEAN; built-in drivers may or may not be respected). If it does, sibling PRs simply stay CLEAN after a merge. If it does not, the PR still shows a conflict in the UI, but the local rebase is one command and needs no hands — still self-healing from our side. The next sibling merge will tell; if GitHub ignores it, the robust next step is changelog fragments (changelog.d/<pr>.md, assembled at release time), which touch no shared file at all.

Every merge of one open PR turned the others DIRTY on CHANGELOG.md (four
hand-resolved rebases on 2026-08-30), and a DIRTY PR gets no CI run.
.gitattributes now declares CHANGELOG.md merge=union: git keeps both
sides' bullets instead of raising a conflict, so a rebase onto main goes
through untouched — verified on the #458 and #459 rebases with the same
attribute set locally. The rule that keeps union safe (add on top of your
category, never rewrite existing lines in passing) stands in the file's
header, CLAUDE.md and copilot-instructions.md.
Copilot AI lite review requested due to automatic review settings August 30, 2026 06:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Multiple docs currently state “never conflict / no longer conflict” as an absolute, but union merges can still duplicate lines and may not be honored by GitHub’s mergeability check, so the wording should be softened to avoid a misleading guarantee.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR configures Git’s built-in merge=union driver for CHANGELOG.md to reduce churn and manual conflict resolution when multiple PRs add [Unreleased] entries, and documents the accompanying “add-only” rule across the repo’s contributor guidance.

Changes:

  • Add CHANGELOG.md merge=union to .gitattributes to make concurrent changelog bullet additions merge without conflicts in local Git merges/rebases.
  • Document the “add on top, don’t rewrite existing lines” rule in CHANGELOG.md, CLAUDE.md, and .github/copilot-instructions.md.
  • Add a [Unreleased] changelog entry describing the change.
File summaries
File Description
.gitattributes Declares CHANGELOG.md merge=union and explains the safe-usage rule.
CHANGELOG.md Documents the union-merge behavior and adds an [Unreleased] entry about the change.
CLAUDE.md Updates contributor guardrails to mention the union merge behavior and the add-only rule.
.github/copilot-instructions.md Mirrors the guardrail update for Copilot-driven contributions.
Review details

Suppressed comments (1)

CHANGELOG.md:32

  • This changelog entry claims sibling PRs "no longer conflict" and that rebases go through "untouched". Union merges can still yield duplicated lines (and GitHub’s DIRTY/CLEAN check may not apply the driver), so the entry should describe this as a conflict-reduction measure for local merges/rebases rather than a guaranteed end to conflicts.
- **The changelog merges by union — sibling PRs no longer conflict in it.**
  Every merge of one open PR used to turn the others `DIRTY` on
  `CHANGELOG.md` (four rebases with hand-resolved conflicts on 2026-08-30
  alone), and a DIRTY PR gets no CI run. `.gitattributes` now declares
  `CHANGELOG.md merge=union`: git keeps both sides' bullets instead of
  • Files reviewed: 4/4 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread CHANGELOG.md Outdated
Comment thread CLAUDE.md Outdated
Comment thread .github/copilot-instructions.md Outdated
Review on #461: no absolute "never conflict" — a local merge or rebase
resolves itself, GitHub's own mergeability check may still flag the PR
until then, and a line changed on both sides appears twice; the header,
the entry, CLAUDE.md and copilot-instructions.md say so in one voice.
Copilot AI review requested due to automatic review settings August 30, 2026 06:09
@MarkusNeusinger MarkusNeusinger changed the title The changelog merges by union, so sibling PRs no longer conflict in it The changelog merges by union, so a sibling merge no longer costs a hand-resolved rebase Aug 30, 2026
@MarkusNeusinger

Copy link
Copy Markdown
Owner Author

Agreed — reworded in one voice across the header, the entry, CLAUDE.md and copilot-instructions.md (and the PR title): a local merge or rebase resolves itself, GitHub's own mergeability check may still flag the PR until the rebase lands, and a line changed on both sides appears twice — hence the add-on-top rule.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The change is a small, well-scoped Git attribute/workflow documentation update with no functional runtime impact and consistent documentation updates across the repo.

Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@MarkusNeusinger
MarkusNeusinger merged commit afa6074 into main Aug 30, 2026
4 checks passed
MarkusNeusinger added a commit that referenced this pull request Aug 30, 2026
… the release cut — sibling merges stop conflicting (#462)

* Changelog fragments: one file per PR under changelog.d/, folded in at the release cut

Every PR used to add its bullets under [Unreleased] of the one shared
CHANGELOG.md, and every sibling merge conflicted the others exactly
there. The union merge driver (#461) heals the local rebase, but
GitHub's mergeability check ignores merge drivers and a branch that
moves changelog lines came out of the rebase duplicated. Removing the
shared spot is the fix: a PR adds changelog.d/<slug>.md in the
changelog's own format and touches nothing else.

tools/changelog (stdlib only): `check [--base REF]` is the CI gate — a
fragment per PR, or a release cut, or data-only, and no bullet written
into [Unreleased] directly; `preview` prints the pending section;
`release X.Y.Z --title …` folds the fragments newest-first under the
new heading, bumps pyproject.toml, uv.lock and CITATION.cff and deletes
the fragments. New CI job "Changelog (fragment)", skipped by the
skip-changelog label. Rules moved in CHANGELOG header, CLAUDE.md,
copilot-instructions, the open-pr gate, .gitattributes, werkzeuge.md,
the glossary and sprachregelung §4.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012TVu1uouGhsjKCEVhWBinX

* Add the PR reference to the fragment

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012TVu1uouGhsjKCEVhWBinX

* The gate's git calls fail loudly

An unfetched base ref made `git diff` fail, which read as an empty diff
and let the PR pass — the one silent pass the gate must not have
(Copilot finding on #462). The gate's calls are now required and raise
with git's stderr; only the fragment-date lookup stays best-effort.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012TVu1uouGhsjKCEVhWBinX

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants