git-publish
Push stacked atomic Pull/Merge Requests.
What is this?
git-publish is a Python script that manages your stacked commits into separate PR (GitHub) / MR (GitLab). It assigns a change ID to each commit in the commit message through a commit message hook. When calling git-publish directly, each commit will be pushed to a dedicated remote branch and PR / MR will be created targeting the right branch to retain the commit history. If commits are amended, git-publish will refer to the change ID to find the associated branch and keep everything in order.
Get started
Install git-publish for everyday use:
$ uv tool install git-publish
For local development of this repo:
$ uv build
$ uv tool install . -e
Set the required environment variables (add to your shell profile) or rely on the token resolver:
GITHUB_TOKEN— required for GitHub projects (repo scope sufficient for PRs). The tool will also acceptGH_TOKEN, or fall back togh auth tokenand Git credential helper.GITLAB_TOKEN— required for GitLab projects (api scope)GITLAB_URL— optional GitLab instance URL; default:https://gitlab.comGITPUBLISH_BRANCH_PREFIX— optional branch/id prefix; defaults to your OS usernameGITPUBLISH_CHANGE_ID_PREFIX— prefix used in commit messages for change ids; defaults toChange-Id:
You can also drop these in a local .env file at the repo root; it will be auto‑loaded when you run the tool:
# .env
GITHUB_TOKEN=ghp_...
GITLAB_TOKEN=glpat-...
GITLAB_URL=https://gitlab.example.com
GITPUBLISH_BRANCH_PREFIX=alice
GITPUBLISH_CHANGE_ID_PREFIX=Change-Id:
Run it in your repository:
$ git-publish
Tips:
- You can also run it as a Git subcommand:
git publish(Git will invokegit-publishon PATH). - Make sure you are on one of the main branches (
main,master,development,develop) and that it is up-to-date with its tracking branch. - After commit, check the commit message contains a line like
Change-Id: user/1a2b(or your chosen prefix).
Hooks
When you run the tool, it installs a commit-msg hook at .git/hooks/commit-msg that ensures a Change‑Id is present in every commit.
- Update: re-run
git-publishto validate the existing hook content. - Remove:
rm .git/hooks/commit-msg.
Safety
This tool will:
- Stash and later unstash your working tree when dirty
- Force‑push ephemeral branches (one per commit)
- Delete temporary local branches after publishing
Make sure your main branch is clean and tracking the correct remote.
Development tasks
Using the task runner:
$ uv run task lint # ruff (check) + pyright
$ uv run task format # ruff --fix
$ uv run task test # pytest
Troubleshooting
- "Branch is not up‑to‑date with its tracking branch" →
git fetchthengit pull --ff-only. - "Must be on a main branch" → switch to
main,master,development, ordevelop. - "Empty GITHUB_TOKEN/GITLAB_TOKEN" → export in your shell or add to
.env.
Dev containers and CI-friendly token resolution
git-publish now resolves the GitHub token in this order:
GITHUB_TOKEN(orGH_TOKEN) environment variablegh auth tokenif the GitHub CLI is installed and logged in- Git credential helper (
git credential fillforgithub.com)
This works well in VS Code tasks and dev containers where environment variables may not be present.
Dev container task example:
{
"version": "2.0.0",
"tasks": [
{
"label": "git-publish",
"type": "shell",
"command": "bash",
"args": [
"-lc",
"GITHUB_TOKEN=$(gh auth token 2>/dev/null || true) git publish"
],
"problemMatcher": []
}
]
}
Alternatively, add a .env file in the workspace (not committed) with GITHUB_TOKEN=... which the tool auto‑loads.
Example output
git-publish info: My feature commit
🔗 https://github.com/owner/repo/pull/123
Next
- Add nested blocking dependencies (available in GitLab 16.6+)
Metadata
Release files for git-publish 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| git_publish-0.1.0.tar.gz | 49.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| git_publish-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 59.9 kB
Release files / git_publish-0.1.0.tar.gz
| Download URL | git_publish-0.1.0.tar.gz |
|---|---|
| Size | 49.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
90ebddd4e7741bf57ecf7b5cb6f5621d2c928be6cd9f45aee0c046fda985f780
|
|
BLAKE2b-256 checksum How to use checksums |
aa61ddb5cb418518e4d353e7c6b630c64ebecbbac9beb9f87a863c709065989c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 28, 2025.
Transparency logRelease files / git_publish-0.1.0-py3-none-any.whl
| Download URL | git_publish-0.1.0-py3-none-any.whl |
|---|---|
| Size | 10.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8141628b8d48853b3fee44fb7b4aafbedd66ded91b768e7d1d6b75a4c1f978bd
|
|
BLAKE2b-256 checksum How to use checksums |
89d8dfaafb001d266bc890d3f1d27a3e994bff4e1c93a689aeaacebed57f6769
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 28, 2025.
Transparency log