| name | merge-conflict-resolving |
| description | You resolve merge conflicts in code repositories, ensuring that the final merged code is functional and free of errors, and adheres to our code quality standards. Covers routine feature-branch merges from master, and modify/delete conflicts where a branch has deleted, split, or renamed a file that master still changes. For Mergify backport cherry-picks onto release/X.X.x, see the `backport-resolution` skill. |
Merge Conflict Resolution
Resolve merge conflicts in this repository. Two situations produce them, and they need different recovery commands — identify which one you are in before touching anything:
| Situation | How it arises | Recovery | Start here |
|---|
| Backport | Mergify cherry-picks a merged PR onto release/X.X.x | git cherry-pick --continue / --abort | backport-resolution skill |
| Feature branch | Your branch merges master to stay current | git merge --abort | Modify/delete conflicts |
This skill is organized by file type (lock files, API reports, changelogs, source code) and applies to both. Backport-specific file types and rules — pnpm-config.json, package.json version precedence, NextVersion.md placement after a release, CI config, and anywhere the rule is "keep the release branch side" — live in the backport-resolution skill.
The highest-risk case is a modify/delete conflict: your branch deleted, split, or renamed a file that master then modified. Git offers "delete it" as a resolution, and taking it silently discards the incoming work with a clean build and green tests. That case has its own workflow and a mandatory review gate.
Lock Files (common/config/rush/pnpm-lock.yaml)
Always regenerate. Never manually edit.
rush update
rush update --full
Commit with: git commit -m "resolve pnpm-lock conflicts"
Examples: PR #8902, PR #8954
API Signature Files (common/api/*.api.md)
Always regenerate. Never manually edit.
These files track the public API surface and are generated by rush extract-api. They conflict when both branches have a different API surface.
rush build
rush extract-api
Also regenerate the export summary files: common/api/summary/*.exports.csv
Example: PR #8986 — backport of integrityCheck() modified common/api/core-backend.api.md
Rush Change Files (common/changes/@itwin/)
Rush change files are JSON files in common/changes/@itwin/<package>/ created by rush change. They usually have unique filenames and rarely conflict.
If they do conflict: Keep both files. If the same file has conflicts, merge the JSON content.
If change files are needed: Generate them non-interactively:
rush change --verify -b origin/<base-branch>
rush change --bulk --message "" --bump-type none -b origin/<base-branch>
Example: PR #8345 — had 30+ rush change files for a multi-package change
Documentation Conflicts (NextVersion.md)
NextVersion.md is the active changelog for the upcoming release. Merge both versions intelligently:
- Extract unique sections from both versions
- Merge into logical category order
- Update table of contents to match headers
- Remove duplicate content
Example: If one branch adds Electron support and another adds Presentation changes, include both sections in the proper order.
Backports follow a different rule once the target release branch has shipped X.X.0 — NextVersion.md stays empty and entries move into X.X.0.md. See the backport-resolution skill.
Verification
rush docs
Avoid:
- Leaving mismatched table of contents
- Keeping duplicate sections
Source Code Conflicts (.ts files)
Possible whenever the same code area was modified on both branches.
Resolution:
- Understand the intent of the incoming change
- Apply the functional change to your branch's version of the code
- Do not blindly accept incoming — your branch may have different surrounding context
Modify/Delete Conflicts on Refactored-Away Files
Not all conflicts come from Mergify. A long-lived feature branch that deletes or splits a file will conflict with every master commit that touches that file, and the conflict recurs on each merge until the branch lands.
CONFLICT (modify/delete): core/backend/src/test/imodel/IModel.test.ts
deleted in HEAD and modified in origin/master.
Never just re-delete the file. Git is telling you master added work that would be silently lost. The deletion is usually correct, but the incoming change must be ported to its new home first.
Workflow
-
Identify exactly what master changed — do not read the whole file. Use the ref for the operation in progress (MERGE_HEAD for a merge, CHERRY_PICK_HEAD for a cherry-pick, REBASE_HEAD for a rebase):
base=$(git merge-base HEAD MERGE_HEAD)
git log --oneline $base..MERGE_HEAD -- <deleted-file>
git show <commit> -- <deleted-file>
-
Classify the incoming change by what kind of file it is, then port it:
| File kind | Incoming change | Action |
|---|
Source (*.ts) | New code added | Port into the module that now owns that responsibility; keep the exported names the incoming code expects |
Source (*.ts) | Existing code modified | Apply the same edit to the migrated implementation |
Source (*.ts) | Code removed | Remove the migrated copy |
| Public API surface | Any of the above | Re-run rush extract-api — a moved export changes common/api/*.api.md |
| Docs / changelog | Entry added or edited | Port the entry into the file that replaced it (see NextVersion.md guidance above) |
| Config / CI | Setting changed | Apply the setting to the file that now carries it; do not assume the old key name survived the split |
| Test | Added, modified, removed | Port into the test file that now owns that feature area — see the test-suite-refactoring skill |
-
Port faithfully, then adapt to the new file's conventions. Keep the behavior identical — only the surrounding plumbing (imports, helper names, module layout) should change. For ported tests specifically, keep the assertions identical and change only the fixture plumbing (shared fixtures, teardown helpers, tracker).
-
Resolve the deletion and stage the port together:
git rm <deleted-file>
git add <file-that-received-the-port>
-
Verify before committing. — a ported test that compiles can still fail on a stale dependency. If exported API moved, also run and inspect the regenerated report.
Incoming changes often carry dependency bumps
A ported test or module may depend on a native addon or package version bumped in the same master commit. The symptom is a confusing assertion failure rather than a dependency error — for example expected undefined to equal 2, because the older BisCore schema lacks a property the new test expects.
Check the manifest against what is installed, then run the Rush update flow:
git show <commit> -- core/backend/package.json
rush update
rush build --to @itwin/core-backend
Avoid:
- Re-deleting the file without inspecting the incoming diff — this silently drops master's work
- Porting a test but leaving the accompanying dependency bump uninstalled
- Assuming a clean build means the port is correct;
tsc emits on type errors by default
Resolution Workflow
-
Identify conflict type: Run git status to see which files need resolution
-
Apply strategy:
- Lock file:
rush update → stage → commit
- API files (common/api/):
rush build → rush extract-api → stage → commit
- Rush change files: Keep both / generate fresh → stage → commit
- NextVersion.md: Merge both versions → stage → commit
- Source code: Apply the incoming functional change to your branch's context → stage → commit
- Modify/delete (file deleted on one side): Port the incoming change to its new home → stage → stop for review (see Review Gate)
- Backport-specific files (
pnpm-config.json, package.json, CI config): See the backport-resolution skill
-
Check for residual conflict markers:
grep -r "<<<<<<< " . --include="*.ts" --include="*.json" --include="*.md" --include="*.yaml" --include="*.yml"
-
Verify: Run rush build and ensure CI passes
-
Finish the operation — for every conflict type except modify/delete, complete it directly. If a modify/delete conflict was resolved, stop for review first (see Review Gate).
- Merge:
git commit using the messages below
- Cherry-pick / rebase:
git add the resolved files, then git cherry-pick --continue / git rebase --continue. Do not create a separate git commit first — the continue step would then be empty or duplicate the change
Commit messages:
- Lock files:
"resolve pnpm-lock conflicts"
Completion Criteria
The resolution is done only when every box is checked:
Review Gate: Modify/Delete Conflicts Only
When a conflict is modify/delete — the file was deleted on one side and modified on the other — stop and ask the user to review before finishing the operation. Every other conflict type can be completed directly once verification passes.
This case alone gets a gate because its worst failure mode is invisible: the incoming change is silently dropped, and the result still builds and passes tests. A green verification run proves the code you kept is correct — it proves nothing about the code you discarded. For content conflicts both sides remain visible in the diff, so a reviewer can see what happened; for modify/delete, the discarded work leaves no trace.
Present a summary and wait for explicit approval:
- Which operation is in progress — merge, cherry-pick (including a Mergify backport), or rebase; this determines the abort and finish commands below
- What was deleted, and by which side
- What the incoming side changed — the specific commits and what they added or modified
- Where each change was ported — the new file and location that now owns it
- What was dropped, if anything — call this out explicitly, even when dropping was clearly correct
- Verification evidence — build, lint, and test results, including test counts before and after
Keep the resolution staged but unfinished while waiting. The tree stays inspectable, git diff --cached shows exactly what will land, and the abort for the operation in progress is still available:
| Operation in progress | Abort | Finish after approval |
|---|
| Merge | git merge --abort | git commit |
| Cherry-pick (backports) | git cherry-pick --abort | git cherry-pick --continue |
| Rebase | git rebase --abort | git rebase --continue |
Never offer git merge --abort during a cherry-pick or rebase — it does not apply to that state. Confirm with git status, which names the operation in progress.
Only after approval, finish the operation and record in the message where the ported work went, so the next person does not think it was lost.
Exception: if the user has already stated they want the resolution completed without review, honor that. Otherwise, ask — including when the resolution looks trivial. A one-file deletion that quietly discarded a new test looks exactly like a one-file deletion that discarded nothing.
Rollback
If resolution goes wrong:
git merge --abort
git rebase --abort
git cherry-pick --abort
git reset --hard ORIG_HEAD
git clean -fd
rush update
Quick Reference
| File Type | Path | Resolution | Key Points |
|---|
| Lock file | common/config/rush/pnpm-lock.yaml | rush update | Never manually edit |
| API signatures | common/api/*.api.md | rush build + rush extract-api | Never manually edit |
| Rush change files | common/changes/@itwin/*/ | Keep both or regenerate | Usually unique filenames, rarely conflict |
| NextVersion.md | docs/changehistory/NextVersion.md | Merge both versions | Backports follow a different rule — see backport-resolution |
| Source code | *.ts | Apply incoming intent to your branch's context | Do not blindly accept incoming |
| Refactored-away file | any (modify/delete) | Port incoming change to its new home, then git rm | Never re-delete blindly; stop for review before committing; check for accompanying dependency bumps |
For Automated Agents
- Check target branch first — Strategy differs for
master vs release/X.X.x
- Not every conflict is a backport — A feature branch merging
master uses git merge; recovery is git merge --abort. Modify/delete conflicts here mean incoming work needs porting, not discarding
- Backports use cherry-pick — Recovery is
git cherry-pick --continue, and the release-branch rules live in the backport-resolution skill
- Parse structured data — Extract version fields from package.json programmatically
- Always check for conflict markers —
grep -r "<<<<<<< " . before committing
- Verify after resolution — Run
rush build, rush extract-api, and check git diff
- Never commit without testing — Ensure no syntax errors or breaking changes
- Match the command to the operation —
git status names it; abort with git merge --abort, git cherry-pick --abort, or git rebase --abort, and finish with git commit, git cherry-pick --continue, or git rebase --continue accordingly
- Never commit a modify/delete resolution without user approval — Stage it, summarize what was ported and what was dropped, then wait. Tests cannot detect discarded work. All other conflict types can be committed directly once verification passes