Skip to main content

Re-parent a branch by merge instead of rebase: the git merge equivalent of `git rebase --onto`, without rewriting history.

Project description

git-merge-onto

Re-parent a branch by merge instead of rebase. It is the git merge equivalent of git rebase --onto: it moves your branch onto a new base and drops an old one, without rewriting history and without a force-push.

git merge-onto <new> <old>

Run on the branch you want to move (HEAD). It performs a 3-way merge of <new> into HEAD whose merge base is forced to merge-base(HEAD, <old>). The result keeps HEAD's own changes, drops the content it shared with its old parent <old>, and records <new> as a real ancestor. The new tip is a descendant of the old one, so the branch updates as a fast-forward push.

Why not just git merge?

A plain git merge <new> picks its base from the commit graph, and for a re-parent that base is wrong in one of two ways:

  • Too low. When <new> already contains <old>'s content but not its commit (the usual case after <old> was squash-merged into <new>), the natural base sits below <old>, so the merge re-applies <old> and conflicts on every hunk the squash reshaped. -X ours papers over this but also silently discards real changes that landed on <new>.
  • Too high. When the new parent transitively contains HEAD's own commit (a stack reorder), the natural base is HEAD itself, so the merge fast-forwards and silently drops your branch's change, leaving an empty diff.

Forcing the base to merge-base(HEAD, <old>) is correct in both. That base is the one thing git porcelain cannot express, which is the whole reason this tool exists.

Install

# one-off, no install
uvx git-merge-onto <new> <old>

# or install so `git merge-onto` works as a git subcommand
uv tool install git-merge-onto
git merge-onto <new> <old>

It is a small, dependency-free Python package. Requires Python 3.9+ and git 2.13+.

Example

You branched feature off develop, then branched followup off feature. feature gets squash-merged into develop. Now move followup onto develop, dropping feature, keeping followup's own commits and its review history:

git switch followup
git merge-onto develop feature
git push            # fast-forward, no --force
gh pr edit followup --base develop

A clean merge commits straight away. A conflict is left in your working tree like any merge: edit the files, git add -A, git commit.

Flags

flag meaning
-m, --message <msg> commit message for a clean merge
--quiet do not echo the executed git commands
--version print the version

How it works

git merge-onto <new> <old> reduces to a single plumbing call:

git merge-recursive <merge-base(HEAD, old)> -- HEAD <new>

On a clean merge it writes the commit (parents [HEAD, new]) using the standard MERGE_HEAD machinery, so resolving a conflict is just git add then git commit, exactly like a normal merge. Nothing is rewritten and nothing is force-pushed.

Companion tools

git-merge-onto is the shared primitive behind two stacked-PR tools:

  • stack-mv rearranges open PR stacks before they land (reorder, move between stacks, insert a prerequisite).
  • autorestack-action re-homes descendant PRs after an ancestor lands.

Both re-parent by merge, never by rebase. This package isolates the one operation git itself cannot do.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

git_merge_onto-0.1.0.tar.gz (6.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

git_merge_onto-0.1.0-py3-none-any.whl (8.4 kB view details)

Uploaded Python 3

File details

Details for the file git_merge_onto-0.1.0.tar.gz.

File metadata

  • Download URL: git_merge_onto-0.1.0.tar.gz
  • Upload date:
  • Size: 6.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for git_merge_onto-0.1.0.tar.gz
Algorithm Hash digest
SHA256 87225ff8ca897bdc6f5784f6ecda47ea1476848cb33b14ae5b03dc4a048ab7ae
MD5 83c596f05d08950a1dba6486e943f630
BLAKE2b-256 1607f0e0883d4f4903fbdf438a146b69b109efee38a9357ff9bd2a1ea4e82ac2

See more details on using hashes here.

File details

Details for the file git_merge_onto-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: git_merge_onto-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.24 {"installer":{"name":"uv","version":"0.11.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for git_merge_onto-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f912ee58f8f44d1f7ec04067c152a0ba58348a13f1f83053671ece3650d965ab
MD5 774a4897326340246a15cc5da07ed865
BLAKE2b-256 6b280848ccd3e503825a86ee86a95bdebd3ebeb593284a16ad70bbfad944a800

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page