mergetopus is a tool that helps teams follow a structured workflow for very large merges by splitting one risky merge into parallelizable tasks:
- one integration branch for trivial/non-conflicting merge results
- optional slice branches for selected conflicted files
It follows and extends the workflow in ext/Invoke-TheMergetopus.ps1.
The core idea is collaborative merge execution:
- one person initializes the merge plan (
mergetopus) - multiple developers resolve different slice branches in parallel (
mergetopus resolve) - a coordinator monitors progress and next actions (
mergetopus status) - once promoted, temporary branches are cleaned up (
mergetopus cleanup)
What It Does
When you run mergetopus <source>, it:
- Checks that you're in a Git repo with a clean working tree.
- Creates an integration branch (
_mmm/<target>/<source>/integration). - Runs
git merge --no-commitagainst the source. - Commits everything that merged cleanly into the integration branch.
- Resets conflicted files back to
oursin that same commit โ they'll be dealt with in slices. - Lets you group conflicts into slices (via TUI or
--select-paths). Any conflicts you don't explicitly group get their own one-file slice. - Creates a branch per slice from the merge base, with the source-side version of each file and a commit message noting where it came from.
From there, use mergetopus resolve to work through each slice with your merge tool (see Resolving Conflicts).
Collaborative Workflow (Large Merges)
Use the commands together as a repeatable team process.
- Plan and split the merge (
mergetopus).
# from your target branch (for example: main)
mergetopus feature/very-large-changeWhat this does:
- creates
_mmm/<target>/<source>/integration - records non-conflicting merge results
- creates per-conflict slice branches (
_mmm/<target>/<source>/sliceN)
- Resolve slices in parallel (
mergetopus resolve).
# each developer picks a slice and resolves it
mergetopus resolve _mmm/main/feature_very-large-change/slice1
mergetopus resolve --commit _mmm/main/feature_very-large-change/slice1Each slice is merged into the integration branch with --no-commit, resolved with your merge tool, and optionally committed.
- Track progress and next actions (
mergetopus status).
mergetopus status feature/very-large-change
Status reports merged vs pending slices and suggests what to do next.
- Promote and clean up (
mergetopus cleanup).
- after all slices are merged, optionally create a
kokomecosnapshot branch (a consolidated merge commit for promotion) - merge the chosen final branch into your target branch using normal Git policy
- run cleanup to remove obsolete integration/slice branches
mergetopus cleanup
Complex Merge Diagrams
- Hard merge without slicing (everything blocked in one place):
main: A---B---C
\
feature: D---E---F
attempt:
main + feature -> conflicts in many files at once
result:
- one large conflict-resolution task
- hard to parallelize
- mergetopus split strategy (parallel conflict handling):
remembered HEAD: C
main: A---B---C
\
source feature: D---E---F
integration branch:
_mmm/main/feature/integration C---M(partial: only non-conflicting files)
|
+-- conflicted files reset to ours in M
slice branches from merge-base(C,F):
_mmm/main/feature/slice1 B---S1 (explicit group: fileA,fileB)
_mmm/main/feature/slice2 B---S2 (explicit group: fileC)
_mmm/main/feature/slice3 B---S3 (auto singleton for unassigned fileD)
_mmm/main/feature/slice4 B---S4 (auto singleton for unassigned fileE)
- After resolution and optional consolidation:
integration after merging slices:
_mmm/main/feature/integration C---M---(merge S1)---(merge S2)---(merge S3)---(merge S4)
optional non-destructive consolidation output:
_mmm/main/feature/kokomeco
\---MC (single merge-commit snapshot branch)
notes:
- integration history stays intact
- consolidated branch is created for review/promotion
Example: Large Team Merge across LTS Versions
Scenario
Five developers maintain a project with multiple long-term stable (LTS) release branches:
| Developer | Role | Active branches |
|---|---|---|
| Wendy Corduroy | Developer | LTS_v17, LTS_v32 |
| Gideon Gleeful | Developer | LTS_v17, LTS_v32 |
| Dipper Pines | Developer | LTS_v32, main |
| Mabel Pines | Team lead (Gideon's) | LTS_v32, main |
| Stan Pines | Senior integrator | LTS_v42 (merges forward) |
Wendy and Gideon make overlapping changes to config.toml and engine.rs
in LTS_v17. All four developers contribute to LTS_v32. Dipper and Mabel also
work on main. Stan regularly merges from older LTS versions into newer ones
and eventually upstream to main.
Branch Topology
Wendy: v17 pooling Gideon: v17 hardening
(config, engine, (config line 2,
api, utils) engine line 2)
LTS_v17: M0 โโโโโโโโ W1 โโโโโโโโโโโโโโโโโโ G1
โ
โ Wendy: v32 Gideon: v32 Dipper: v32 Mabel: v32
โ retry caching debug logging auth + logging
LTS_v32: M0 โโโโโโโโ W2 โโโโโโโโ G2 โโโโโโโโ D1 โโโโโโโโโโโโ MB1
โ
โ Stan: v42
โ baseline
LTS_v42: M0 โโโโโโโโ S1 โโโโโโ kokomecoโ โโโโโโ kokomecoโ โโโโ ยทยทยท
โ โ โ
โ merge LTS_v17 merge LTS_v32
โ
โ Dipper: Mabel:
โ metrics telemetry
main: M0 โโโโโโโโ D2 โโโโโโโโ MB2 โโโโโโโโโโโโโโโ ยทยทยท โโโโโโ merge kokomecoโ
Cascade Merge Steps
Stan merges LTS_v17 โ LTS_v42, then LTS_v32 โ LTS_v42, then LTS_v42 โ main.
Step 1: LTS_v17 โ LTS_v42
# Stan initiates the merge from LTS_v42
mergetopus LTS_v17 --select-paths config.toml,engine.rsLTS_v42 HEAD (Stan) LTS_v17 (Wendy + Gideon)
โ โ
โโโโ mergetopus LTS_v17 โโโโ โ
โ โ
_mmm/LTS_v42/LTS_v17/ โ โ
integration โโโ partial merge (auto-merged files,
โ conflicts reset to ours)
โ
โโโ slice1: config.toml + engine.rs โ explicit group (--select-paths)
โ โ Mabel resolves (contains Gideon's overlapping changes)
โ
โโโ slice2: api.rs
โ โ Dipper resolves (Wendy's caching changes)
โ
โโโ slice3: utils.rs
โ Dipper resolves (Wendy's format changes)
After all slices resolved + consolidated:
_mmm/LTS_v42/LTS_v17/kokomeco
parent 1: original LTS_v42 HEAD (Stan's baseline)
parent 2: LTS_v17 tip (Wendy + Gideon)
tree: final integration state
Step 2: LTS_v32 โ LTS_v42
# After promoting kokomecoโ into LTS_v42 git checkout LTS_v42 && git merge _mmm/LTS_v42/LTS_v17/kokomeco mergetopus LTS_v32 --select-paths engine.rs,utils.rs
_mmm/LTS_v42/LTS_v32/
integration
โ
โโโ slice1: engine.rs + utils.rs โ Mabel resolves (Gideon/Mabel changes)
โ
โโโ slice2: api.rs โ Dipper resolves (Wendy's retry logic)
โ
โโโ slice3: config.toml โ Dipper resolves (Wendy/Dipper's config)
After consolidation:
_mmm/LTS_v42/LTS_v32/kokomeco
parent 1: LTS_v42 (post kokomecoโ)
parent 2: LTS_v32 tip
Step 3: LTS_v42 โ main
# After promoting kokomecoโ
git checkout main
mergetopus LTS_v42Only config.toml and engine.rs conflict (main never changed api.rs or
utils.rs from the initial commit, so those auto-merge).
Blame Verification on kokomeco
After the full cascade, git blame on the final kokomeco correctly traces
through the merge parents back to the original authors โ no squash, no
history rewrite:
git blame _mmm/LTS_v42/LTS_v17/kokomeco -- config.toml:
max_connections = 200 โ Wendy Corduroy ("Wendy: v17 connection pooling and caching")
timeout = 60 โ Gideon Gleeful ("Gideon: v17 timeout hardening and error handling")
git blame _mmm/LTS_v42/LTS_v17/kokomeco -- engine.rs:
fn init() { wendy_pool(); } โ Wendy Corduroy
fn process() { gideon_errors(); } โ Gideon Gleeful
git blame _mmm/LTS_v42/LTS_v17/kokomeco -- api.rs:
fn handle() { wendy_caching(); } โ Wendy Corduroy
git blame _mmm/LTS_v42/LTS_v17/kokomeco -- utils.rs:
fn format() { wendy_format(); } โ Wendy Corduroy
git blame _mmm/main/LTS_v42/kokomeco -- engine.rs:
fn init() { mabel_auth(); } โ Mabel Pines (from LTS_v32)
fn process() { gideon_cache(); } โ Gideon Gleeful (from LTS_v32)
git blame _mmm/main/LTS_v42/kokomeco -- api.rs:
fn handle() { wendy_retry(); } โ Wendy Corduroy (from LTS_v32, auto-merged)
The kokomeco merge topology preserves correct line-blame because:
- each kokomeco has the source branch as merge parent 2
- unchanged lines from the source side trace through that parent to original commits
- the cascade (kokomecoโ โ kokomecoโ โ kokomecoโ) maintains a clean ancestry chain
- no squash or history rewrite occurs โ only clean merge snapshots
This scenario is exercised by the integration test
lts_cascade_merge_preserves_authorship_in_kokomecointests/test_suite_d.rs.
Existing Topology Handling
If the integration branch already exists, mergetopus:
- discovers related slice branches (
_mmm/<safe-current>/<safe-source>/sliceN) - checks whether each slice is merged into the integration branch
- reports merged/pending status
- when all slices are merged, offers optional consolidation
Consolidation is non-destructive: it creates a separate branch _mmm/<safe-current>/<safe-source>/kokomeco with a single merge commit snapshot.
Why "kokomeco" Exists
kokomeco stands for KOrrekt KOnsoliderter MErge COmmit (German: "correctly consolidated merge commit").
The name is intentional: the consolidation step is not a squash and not a history rewrite of the integration branch. Instead, Mergetopus creates a separate merge-commit snapshot branch with merge parents derived from:
- the remembered target-branch head (before the merge workflow started)
- the original merge source commit
and with the final resolved tree copied from the integration branch.
Why this matters for git blame:
- A plain squash-style consolidation would collapse ownership and often attribute many lines to the integrator commit.
- Kokomeco keeps a proper merge ancestry edge to the original source side, so line-blame can continue to follow where unchanged lines actually came from.
- Temporary integration/slice execution history can be cleaned up later, while the promoted branch still shows where changes came from in the final merge history.
Installation
Download the mergetopus binary for your platform from the GitHub Releases page and place it in a directory on your system's PATH.
On Windows with Chocolatey:
choco install mergetopus.portable
Platform Support
mergetopus works on Windows, macOS, and Linux.
During resolve, Mergetopus sets the same environment variables (LOCAL, BASE, REMOTE, MERGED) across platforms so merge tool configuration stays portable.
On Windows, merge tools are invoked directly (without cmd /c) to avoid quoting/whitespace proxy issues.
On Unix-like systems (macOS, Linux), command execution uses sh -c.
When running inside a Git worktree on Windows, mergetopus ensures core.longpaths=true
for the repository so deep path merges remain usable.
Worktree Behavior
Mergetopus adapts its worktree behavior based on your existing setup:
- If your repository has no additional worktrees, Mergetopus keeps the existing default behavior (no automatic worktree creation).
- If your repository already uses worktrees, Mergetopus prefers running branch operations in branch-specific worktree directories.
- When Mergetopus needs to create a branch worktree and no suitable branch worktree exists yet, it infers a base directory from existing worktree paths.
- If a common base cannot be inferred, Mergetopus falls back to the parent of the repository root.
This keeps non-worktree workflows unchanged while making branch checkouts easier in repositories that already use worktrees.
Branch Naming Conventions
-
Integration branches follow the pattern
_mmm/<safe-original-branch>/<safe-source-branch>/integration. These are temporary working branches that hold the merge result with auto-merged files staged and conflicted files reset to "ours".- Example:
_mmm/main/feature/integration,_mmm/develop/release_v1/integration
- Example:
-
Slice branches follow the pattern
_mmm/<safe-original-branch>/<safe-source-branch>/slice<N>whereNis a number (1, 2, 3, ...). These are temporary per-conflict branches for resolving individual conflict groups.- Example:
_mmm/main/feature/slice1,_mmm/main/feature/slice2
- Example:
-
Kokomeco branches follow the pattern
_mmm/<safe-original-branch>/<safe-source-branch>/kokomeco. These are optional output branches created after all slices are merged, containing a single merge-commit snapshot.- Example:
_mmm/main/feature/kokomeco
- Example:
The safe-* components use the same sanitization rules as before: characters outside [0-9A-Za-z._-] are replaced with _.
Branch Filtering
When selecting a source branch for mergetopus:
- Slice branches (
_mmm/.../slice<N>) are automatically filtered out from the branch picker. They should only be used withmergetopus resolve, never as a source for a new merge. - Only non-slice branches are available for selection, reducing accidental misuse.
Integration Branch Redirection
If you select an integration branch as the source, mergetopus detects this
and asks what you want to do:
- Switch to this integration branch and view its status โ checks out the integration branch and shows slice progress.
- Create a new merge targeting this integration branch โ checks out the original target branch and merges the actual source, creating a fresh integration branch.
Example: if you select _mmm/main/feature/integration, mergetopus will
recognize that the target was main and the source was feature, then let
you pick between viewing existing status or starting a new merge of feature
into main.
Usage
Interactive source selection (branch picker overlay shown):
mergetopus
Provide source branch explicitly:
mergetopus feature/refactor-auth
Explicit conflict grouping by path list:
# Put explicit paths into one grouped slice; all remaining conflicts become one-file slices
mergetopus feature/refactor-auth --select-paths src/a.rs,src/b.rsInteractive conflict grouping (with F3 opening your configured diff.tool, or the inline 3-way view when no diff.tool is set) when --select-paths is not provided:
mergetopus feature/refactor-auth
Auto-confirm consolidation prompt:
mergetopus feature/refactor-auth --yes
Non-interactive CI/CD mode (no TUI):
# SOURCE is required in quiet mode mergetopus feature/refactor-auth --quiet # Quiet mode + explicit conflict grouping mergetopus feature/refactor-auth --quiet --select-paths src/a.rs,src/b.rs # Quiet mode + auto-consolidate when eligible mergetopus feature/refactor-auth --quiet --yes # Show slice/integration progress status mergetopus status feature/refactor-auth # Cleanup temporary integration/slice branches (interactive confirmation) mergetopus cleanup # Take over an already in-progress manual merge mergetopus HERE
Takeover mode for an in-progress merge:
# Optional: non-interactive explicit grouping for remaining conflicts
mergetopus --quiet --select-paths src/big/file1.cs,src/big/file2.cs HEREStatus Reporting
Use mergetopus status to inspect an integration branch and its slice progress.
# Status by source ref mergetopus status feature/refactor-auth # Status by integration branch name mergetopus status _mmm/main/feature_refactor-auth/integration
The status output includes:
- integration branch
- source ref and source SHA (when derivable from integration history)
- merged/pending slice counts
- pending slice details with detected affected paths (when available)
- suggested next commands
Resolving Conflicts
After mergetopus has created slice branches, use resolve to merge a selected
slice into its corresponding integration branch with --no-commit, then open
each conflicted file in your configured merge tool one-by-one:
# Interactive slice branch picker (TUI) mergetopus resolve # Resolve a specific slice branch directly mergetopus resolve _mmm/main/feature/slice1 # Non-interactive (--quiet requires an explicit branch) mergetopus resolve --quiet _mmm/main/feature/slice1 # Create a commit automatically after staging mergetopus resolve --commit _mmm/main/feature/slice1
What resolve does
- Derives the corresponding integration branch from the slice branch name.
- Checks out the integration branch and starts
git merge --no-commit <slice>. - For each currently conflicted file, derives from the Git graph:
LOCALas the integration branchHEADbefore the mergeREMOTEas the slice branch tipBASEasmerge-base(LOCAL, REMOTE)
- Writes three temporary files per conflicted path:
LOCALโ the file at the integration branch sideBASEโ the file at the common ancestor (merge-base)REMOTEโ the file at the slice branch side
- Executes the configured merge tool with
LOCAL,BASE,REMOTE, andMERGEDset as environment variables (same convention asgit mergetool). The command is executed via the appropriate shell:
- Windows: invoked directly without
cmd /c - Unix-like systems (macOS, Linux):
sh -c <cmd>
MERGED points to the conflicted working-tree file on the integration branch,
so the tool writes the resolution directly into the repository.
6. Stages each resolved file.
7. Optional: if --commit is passed, creates the merge commit on the integration branch.
Take Over In-Progress Merge (HERE)
Use mergetopus HERE when you already started a regular git merge manually,
resolved some conflicts, and want Mergetopus to take over only the remaining
unresolved conflicts.
Typical scenario:
- You run
git merge <source>on your target branch. - Git stops with conflicts.
- You manually resolve some files.
- You run
mergetopus HEREto continue using slice workflow for what remains.
Behavior of HERE:
- requires an active merge (
MERGE_HEADmust exist) - preserves already-resolved file content from your working tree/index
- aborts the manual merge and rebuilds canonical Mergetopus integration state
- creates slices only for still-unresolved conflict paths
- opens normal conflict grouping flow (or uses
--select-pathsin quiet mode)
Command examples:
# Interactive takeover mergetopus HERE # Quiet takeover with explicit grouping for remaining conflicts mergetopus --quiet --select-paths src/module/a.rs,src/module/b.rs HERE
Configuring the merge tool
mergetopus resolve reads the merge tool from Git config. Set it once in
your global or repository config:
# Choose a tool name git config merge.tool vimdiff # Provide the shell command template. # $LOCAL, $BASE, $REMOTE, $MERGED are expanded at runtime. git config mergetool.vimdiff.cmd 'vimdiff "$LOCAL" "$BASE" "$REMOTE" -c "wincmd J" "$MERGED"'
Some common examples:
| Tool | Example mergetool.<name>.cmd |
|---|---|
| vimdiff | vimdiff "$LOCAL" "$BASE" "$REMOTE" -c "wincmd J" "$MERGED" |
| nvim | nvim -d "$LOCAL" "$REMOTE" "$MERGED" |
| code (VS Code) | code --wait --merge "$LOCAL" "$REMOTE" "$BASE" "$MERGED" |
| meld | meld "$LOCAL" "$BASE" "$REMOTE" --output "$MERGED" |
| kdiff3 | kdiff3 "$BASE" "$LOCAL" "$REMOTE" -o "$MERGED" |
Any tool that reads $LOCAL, $BASE, $REMOTE and writes its result to
$MERGED will work.
Configuring optional F3 diff tool
In the conflict selector, F3 behaves as follows:
- if
git config diff.toolis set,F3launches that difftool for the selected conflicted file - if
diff.toolis not set,F3opens the built-in inline 3-way diff overlay
Example:
git config diff.tool vscode
git config difftool.vscode.cmd 'code --wait --diff "$LOCAL" "$REMOTE"'TUI Keybindings
When --quiet is not set, TUI is used for source branch picking (if SOURCE
is omitted), conflict selection (if --select-paths is omitted), and slice
branch selection for resolve (if BRANCH is omitted).
Conflict selector:
Arrow Up/Down: move cursorTab: switch panen: create new explicit sliceSpace: assign/move highlighted conflict into currently selected sliceu: unassign highlighted conflict (it will become default one-file slice)d: delete selected explicit slice (its files become unassigned)F3: open configured difftool for selected file (or inline 3-way diff ifdiff.toolis not set)Enter: apply selectionEsc: close overlay or cancel selectorq: cancel selector
Source branch picker / Slice branch picker:
- Type text to filter branch list
Arrow Up/Down: moveEnter: selectEscorq: cancel
3-way diff overlay:
Up/Down: scrollPageUp/PageDown: fast scrollHome/End: jump to top/bottomEsc: close overlay
Commit Metadata
Slice commits include informational trailers that document where each file came from:
Source-RefSource-CommitSource-PathSource-Path-CommitCo-authored-by(when source-side author info is available)
These trailers are purely metadata for humans and tools like mergetopus status.
Kokomeco creation does not depend on them โ it works entirely from git history
and the integration branch tree.
Authorship Clarification
A squash or consolidation commit does not preserve per-commit author lineage by itself.
mergetopus keeps attribution info using co-author trailers on slice commits
and preserves original integration/slice history by writing consolidated output
to a separate branch. The kokomeco branch itself gets correct git blame
through its merge parents, not through trailers.
Safety Notes
- No destructive reset/rewrite is performed automatically.
- Consolidation requires explicit confirmation (unless
--yesis used). --quietdisables TUI interactions; provideSOURCEexplicitly for CI/CD runs.- Integration branch is not rewritten by default.
License
MIT. See LICENSE.

























