Merge rules

Some files collect a line from almost every pull request: a changelog, a status page, a JSON ledger of work items. Two pull requests that each add to one of them conflict, even though nothing they did disagrees. Declare how such a file merges, once, in your repository's .gitattributes, and Gluecron settles those conflicts on every merge.

Copy, paste, commit

# .gitattributes — at the root of your default branch
CHANGELOG.md            merge=union
docs/STATUS.md          merge=gluecron-append-log
docs/LEDGER.json        merge=gluecron-json-by-id
data/features.json      merge=gluecron-json-by-id merge-key=slug
config/generated.lock   merge=ours
*.png                   binary

Rules take effect for pull requests into a branch once the line is on that branch. Your repository's Gates → Settings → Merge rules lists the rules in force and what each one does.

What each rule does

"Base branch" below is the branch the pull request merges into. Its side always goes first, so every way a pull request can be merged produces the same file.

LineWhen both sides changed the file and the lines conflict
merge=unionBoth sides' lines are kept: the base branch's, then the pull request's. git's own union merge. Good for changelogs and lists where order does not matter much.
merge=gluecron-append-logIf both sides only added text at the end, both additions are kept, the base branch's first (the same addition on both sides is kept once). Any other change (an edit, a deletion, an insertion higher up) stays a conflict.
merge=gluecron-json-by-idA JSON array of items, or an object holding one ({"version": 1, "items": [...]}), or an object whose values are the items. Items are matched by their id field (or the field merge-key= names; for an object of items, the property name). Different items changed or added on each side are all kept. The same item changed differently on both sides, changed on one side and deleted on the other, or reordered by the pull request, stays a conflict. The file keeps its own layout byte for byte; a file in a layout Gluecron cannot reproduce exactly stays a conflict rather than being reformatted.
merge=oursWhere lines conflict, the base branch's lines win. Changes that do not conflict are kept from both sides. Use it for generated files the base branch regenerates.
merge=binary, -merge, binaryAlways a conflict. Someone keeps one version on the Conflicts tab.
merge=text, mergegit's line merge. Conflicts are left for a person, and structured (code-aware) merge is not tried.
any other merge=<name>A merge program from someone's own git config. Gluecron never runs one; the file stays a conflict.

Patterns follow git: *.md matches at any depth, /CHANGELOG.md only at the root, docs/** everything under docs/. A later line overrides an earlier one, a .gitattributes in a subdirectory overrides the root one, and !merge removes a rule. Quoted patterns are not supported on a line that sets a merge rule.

Where it applies

The same rule, with the same result, on Update branch, the merge preview CI builds, the merge train, the merge itself, and the Conflicts tab. The pull request page lists a file it settled as Settled by your merge rule (.gitattributes: docs/STATUS.md → append-log). Merge commits record it as rule(gitattributes:append-log), and a commit from the Conflicts tab carries the trailer Conflict-Resolution: docs/STATUS.md=rule(gitattributes:append-log). CI runs on the merged result like on any other merge.

Safety

  • Rules are read from the branch the pull request merges into, never from the pull request. A pull request that adds a rule cannot use it for itself; the rule applies to later pull requests once it is merged.
  • A rule that cannot settle a file exactly leaves it a conflict. Nothing is guessed.
  • .gitattributes files over 64 KiB, with more than 2,000 pattern lines, or with a pattern over 1,024 characters are not used, and the files under them stay conflicts.
  • No program named in a repository ever runs on Gluecron's servers.

Examples

A changelog

CHANGELOG.md merge=union

The base branch added - Fix login and the pull request added - Add export under ## Unreleased: the merged file has both, the base branch's first.

A status page that only grows at the end

docs/STATUS.md merge=gluecron-append-log

For a page where new entries go at the top, use merge=union instead: append-log only settles additions at the end. With union, a line both sides changed (a "Last updated" stamp, say) is kept twice, once per side.

A JSON ledger

docs/handoff/LEDGER.json merge=gluecron-json-by-id
{
  "version": 1,
  "items": [
    { "id": "auth-hardening", "state": "done" },
    { "id": "previews", "state": "in-progress" }
  ]
}

One pull request sets auth-hardening to done, another adds a new item: both are kept. Two pull requests that set previews to different states still conflict, because only a person can say which is right.

Compared with other hosts

A custom merge driver is a program named in a developer's own git config, which a repository cannot carry, so a hosted merge does not run one. GitHub Support has written that "GitHub doesn't consider user-defined .gitattributes files", and GitHub community discussion #9288, asking for merge=union on pull requests, has been open since 2021. Gluecron applies union and ours on the server, adds the two drivers above, and never runs a program from the repository.