gitconfig-ifcmerge: use --prioritise-local correctly per merge direction

ifcmerge defaults to preserving remote IDs; --prioritise-local preserves local
IDs instead.  For Forgejo merge commits, %A is main (local) so ifcmerge_ours
(--prioritise-local) is the right driver — main's IDs are preserved and the
PR branch is renumbered.  For developer rebases from main, %B is main (remote)
so the plain ifcmerge driver (no flag) preserves main's IDs by default.

Update /etc/gitattributes to use ifcmerge_ours for server-side merges, and
update README with a direction table clarifying which driver to use when.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Bruno Postle 2026-04-25 07:41:49 +01:00
parent 6f23d874d3
commit 8e474f2629
3 changed files with 39 additions and 21 deletions

View file

@ -286,17 +286,25 @@ Or apply the blocks from `server-config/gitconfig-ifcmerge` directly into
### Merge direction
The merge driver is asymmetrical: `ifcmerge` rewrites STEP IDs from `%B`
(theirs) to match `%A` (ours), so `%A`'s ID space is preserved.
The invariant is that step-IDs in `main`/`master` must never be renumbered,
because existing cross-references depend on them. The correct driver depends
on which branch is the "local" (`%A`) side:
For Forgejo, use the **"Merge commit"** strategy (not rebase or squash).
With a merge commit, `%A` is always the base branch (e.g. `main`) and
`%B` is the pull-request branch — so `main`'s STEP IDs are preserved and
existing cross-references remain valid.
| Situation | `%A` (local) | `%B` (remote) | Driver to use |
|---|---|---|---|
| Forgejo "Merge commit" button | `main` | PR branch | `ifcmerge_ours` (`--prioritise-local`) |
| Developer rebasing PR from `main` | PR branch | `main` | `ifcmerge` (default) |
A second driver `ifcmerge_ours` is defined in the config file for cases
where the base branch arrives as `%B`; see comments in
`server-config/gitconfig-ifcmerge` for details.
For Forgejo server-side merges, use the **"Merge commit"** strategy (not
rebase or squash) and configure `.gitattributes` to use `ifcmerge_ours`:
```
*.ifc merge=ifcmerge_ours
```
`ifcmerge` (no flag) is for client-side use by developers updating their
working branch from `main`, where `main` arrives as the remote (`%B`) side
and its IDs are preserved by default.
### Client-side `.gitattributes`

View file

@ -9,4 +9,4 @@
# Committed per-repo .gitattributes files are NOT read by git in bare-repo
# context and are therefore insufficient for the server-side conflict check.
*.ifc merge=ifcmerge
*.ifc merge=ifcmerge_ours

View file

@ -12,22 +12,32 @@
#
# How the merge drivers work
# --------------------------
# ifcmerge rewrites STEP IDs from %B to match %A's ID space, so %A's IDs
# are preserved in the merged result. Use the "Merge commit" strategy in
# Forgejo so that %A is always the base branch (e.g. main).
# The invariant: step-IDs in main/master must never be renumbered, because
# existing cross-references depend on them. ifcmerge always renumbers the
# "remote" side by default; --prioritise-local reverses this and renumbers
# the "remote" side instead. The correct driver depends on which branch is
# checked out when the merge happens:
#
# Two variants are registered for symmetry:
# ifcmerge — base branch is %A (standard merge-commit direction)
# ifcmerge_ours — base branch is %B (rebase direction); swaps %A/%B so
# base-branch IDs are still preserved
# ifcmerge — use when local (%A) is a working/PR branch and
# remote (%B) is main/master. Default behaviour
# preserves remote (main) IDs.
# e.g. rebasing or updating a PR branch from main.
#
# ifcmerge_ours — use when local (%A) is main/master and remote (%B)
# is a working/PR branch. --prioritise-local preserves
# local (main) IDs.
# e.g. Forgejo "Merge commit" button (standard PR merge).
#
# Use the "Merge commit" strategy in Forgejo (not rebase or squash) so that
# %A is always the base branch — ifcmerge_ours is then the correct driver.
[core]
attributesFile = /etc/gitattributes
[merge "ifcmerge"]
name = IFC merge driver (base is ours)
driver = ifcmerge %O %A %B %L
name = IFC merge driver (merging from main into working branch)
driver = ifcmerge %O %A %B %A
[merge "ifcmerge_ours"]
name = IFC merge driver (base is theirs)
driver = ifcmerge %O %B %A %L
name = IFC merge driver (merging PR branch into main)
driver = ifcmerge --prioritise-local %O %A %B %A