Skip to main content

Structured git workflow CLI: conventional commits, trunk-based branches, GitHub PR lifecycle

Project description

git-clerk

A structured git workflow CLI for conventional commits, trunk-based branching, and a clean GitHub PR lifecycle — all from the command line.

Philosophy

git-clerk is built on three practices that reinforce each other: trunk-based development, conventional commits, and squash-merge-only history. Short-lived branches stay close to main. Squash merges keep main's history linear and readable — one commit per feature. Conventional commit types make that history meaningful at a glance. git-clerk connects all three as a unit: you name your branch feat/user-auth once, and every commit message, PR title, and release tag follows from that single decision.

Opinions

git-clerk is intentionally opinionated. These constraints are not configurable:

  • GitHub only — PR and release operations rely on gh. GitLab and Bitbucket are not supported.
  • Squash mergesship always squash-merges to keep main's history linear.
  • Single trunkmain is the only integration branch. develop, release/*, and similar long-lived branches are out of scope. The trunk name is not configurable — repositories using a different default branch are not supported.
  • Conventional commits — branch names must follow type/scope using one of the 11 standard types.

If your workflow diverges from any of these, git-clerk is not the right tool.

Prerequisites

Local

  • Python 3.10+
  • uv for installation
  • GitHub CLI (gh) 2.0 or later, authenticated to your GitHub account

Repository configuration

git-clerk assumes the repository is configured to match its workflow. Without this, the tool still works but its guarantees don't hold — anyone can bypass conventions by using git and gh directly.

Ask your infra or platform team to configure:

  • Squash merges only — disable merge commits and rebase merges so main stays linear
  • Branch protection on main — require pull requests before merging; disallow direct pushes
  • Required status checks — require CI to pass before a PR can be merged; this makes git clerk ship's CI gate structural rather than advisory

Installation

uv tool install git-clerk

To use it as git clerk (recommended) rather than git-clerk, register a git alias:

git config --global alias.clerk '!git-clerk'

After this, git clerk prints help and git clerk commit --help (any subcommand) works as expected. Note that git clerk --help will not work — git intercepts --help before running the alias and tries to open a man page. Use git clerk or git-clerk --help for top-level help instead.

Workflow walkthrough

Here is a complete cycle from starting a feature to tagging a release.

1. Create a branch

git clerk branch feat/user-auth

Fetches the latest origin/main and creates feat/user-auth from it. The branch name is the only thing you decide upfront — type and scope flow into every subsequent command automatically.

2. Do your work, then commit

git clerk commit -A "add login form"

Stages all changes and commits with the message feat(user-auth): add login form. The type and scope come from the branch name — you only write the description.

For commits that need more context, pass the body as a second argument or open your editor with -e:

git clerk commit -A "add login form" "Supports email and SSO providers."
# → commits with inline body (useful in scripts and LLM workflows)

git clerk commit -A -e "add login form"
# → opens $EDITOR for the body, then commits

You can commit as many times as you want. Only the squash commit that lands on main is permanent.

3. Open a PR

git clerk pr "Add login form"

Pushes the branch with upstream tracking set, creates the PR against main, prints the URL, then watches CI checks until they complete. You can share the URL while CI is still running.

By default no body is added. To add one:

git clerk pr "Add login form" "Adds email/password and SSO login. Closes #42."
# inline body

git clerk pr -e "Add login form"
# opens $EDITOR for the body

4. Ship it

Once CI is green:

git clerk ship

Shows you the PR title and number, asks for confirmation, then: squash-merges into main, deletes the remote branch, switches to local main, pulls, and force-deletes the local branch. You end up on a clean, up-to-date main in one step.

5. Tag a release

git clerk release

Detects your versioning scheme from existing tags, computes the next version, shows you the tag, and asks for confirmation before pushing. On a fresh repo with no tags, it prompts you to choose CalVer or SemVer.

Commands

branch TYPE/scope

Fetches the latest origin/main and creates a new branch from it.

git clerk branch feat/user-auth     # → feat/user-auth
git clerk branch fix/payment-api    # → fix/payment-api
git clerk branch chore/deps         # → chore/deps

The branch name is the only decision you make upfront. Every subsequent commit and pr command reads the type and scope from it — you never repeat yourself.

The fetch happens before branch creation, so you always start from the latest main regardless of how long ago you last pulled. If the branch already exists locally, git will error — delete it first or choose a different name.

commit DESCRIPTION [BODY]

Creates a conventional commit by reading the type and scope from the current branch name. The commit message header is always type(scope): description — you only supply the description.

git clerk commit "add login form"
# → feat(user-auth): add login form

Body

By default there is no body — most commits don't need one. There are two ways to add one:

# Inline — pass the body as a second positional argument.
# Useful in scripts and LLM-driven workflows.
git clerk commit "add login form" "Supports email and SSO providers."

# Interactive — open $EDITOR. Save and exit to use the body; quit without
# saving to abort.
git clerk commit -e "add login form"

An empty string passed as BODY is treated the same as no body — only a non-empty string is included in the commit.

Options

Flag Description
-A Stage all changes (git add -A) before committing
-e Open $EDITOR to write the commit body interactively
-t TYPE Override the type inferred from the branch name
-s SCOPE Override the scope inferred from the branch name

The -t and -s overrides are for cases where the commit type or scope differs from the branch — for example, bumping a lockfile on a feat branch:

git clerk commit -t chore "update lockfile"    # chore(user-auth): update lockfile
git clerk commit -s auth-core "fix token TTL"  # feat(auth-core): fix token TTL

pr TITLE [BODY]

Pushes the current branch to origin (with upstream tracking), creates a GitHub PR against main with a conventional title derived from the branch name, prints the PR URL, then watches CI checks until they complete.

The PR title is constructed the same way as a commit header: type(scope): title. You supply only the human-readable title.

Body

By default no body is added. There are two ways to add one:

# Inline — pass the body as a second positional argument.
# Useful in scripts and LLM-driven workflows.
git clerk pr "Add login form" "Adds email/password and SSO login. Closes #42."

# Interactive — open $EDITOR. Save and exit to use the body;
# quit without saving to abort.
git clerk pr -e "Add login form"

An empty string passed as BODY is treated the same as no body.

Options

Flag Description
-e Open $EDITOR to write the PR body interactively
-t TYPE Override the type in the PR title
-s SCOPE Override the scope in the PR title

The PR URL is printed to stdout as soon as the PR is created, before CI checks begin — you can share it while checks are still running. If checks fail, the run ends with a non-zero exit code.

ship

Squash-merges the current branch's PR and brings your local environment back to a clean state on main. Must be run from the feature branch, not from main.

git clerk ship

Displays the PR title and number, asks for confirmation, then executes in order:

  1. Squash-merges the PR into main
  2. Deletes the remote branch
  3. Switches to local main
  4. Pulls latest from origin/main
  5. Force-deletes the local branch

You end up on a clean, up-to-date main in one step.

Options

Flag Description
-y / --yes Skip the confirmation prompt
-u BRANCH After shipping, switch to BRANCH and merge origin/main into it

-u is for cases where you paused work on one branch to ship a dependency first:

# Shipping fix/tech-debt while feat/user-auth is parked
git clerk ship -u feat/user-auth
# → ships fix/tech-debt, then switches to feat/user-auth and merges origin/main

-y is useful in automated contexts where you want to ship without interactive confirmation:

git clerk ship -y

watch

Re-attaches to CI checks for the current branch's PR. Useful when you want to check in on CI after navigating away from the terminal during a pr run.

git clerk watch

release

Tags the current tip of origin/main and pushes the tag. Supports CalVer and SemVer.

git-clerk fetches the latest tags, computes the next version, shows you what it's about to create, and asks for confirmation before pushing anything.

git clerk release                      # auto-detect scheme, prompt for bump if SemVer
git clerk release --semver --bump minor
git clerk release --semver --bump major
git clerk release --calver

Scheme detection

If the repository already has version tags, git-clerk detects the scheme automatically — --calver and --semver are not needed. If no tags exist yet, git-clerk prompts you to choose interactively. Pass --calver or --semver to skip the prompt.

If both CalVer and SemVer tags are found (e.g. after a scheme migration), git-clerk exits with an error. Pass --calver or --semver explicitly to proceed.

Options

Flag Description
--calver Use calendar versioning
--semver Use semantic versioning
--bump patch|minor|major SemVer component to increment; prompted if not provided (ignored for CalVer)
-y / --yes Skip the confirmation prompt

Versioning schemes

CalVer — vYYYY.MM.N

Tags are tied to the calendar month, with a sequential counter that resets each month:

v2026.06.1
v2026.06.2
v2026.07.1   ← new month, counter resets

Good fit for projects that ship continuously and want version numbers that communicate when something was released.

SemVer — vMAJOR.MINOR.PATCH

Standard semantic versioning. The first tag on a repo with no prior tags starts at v0.1.0.

v0.1.0 → v0.1.1   (patch)
v0.1.1 → v0.2.0   (minor)
v0.2.0 → v1.0.0   (major)

The tag is always placed on origin/main, so run release after shipping all PRs for the version.

Branch naming

All commands that read from the branch name (commit, pr) expect the format type/scope:

feat/user-auth
fix/payment-timeout
chore/upgrade-deps
docs/api-reference

The type must be one of the standard conventional commit types: build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test. The scope must start with a letter or digit and may contain letters, digits, hyphens, and underscores — spaces and special characters are not allowed. Both are validated by branch before the branch is created, and by commit and pr when reading the current branch name.

License

MIT

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_clerk-0.2.1.tar.gz (25.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_clerk-0.2.1-py3-none-any.whl (13.0 kB view details)

Uploaded Python 3

File details

Details for the file git_clerk-0.2.1.tar.gz.

File metadata

  • Download URL: git_clerk-0.2.1.tar.gz
  • Upload date:
  • Size: 25.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for git_clerk-0.2.1.tar.gz
Algorithm Hash digest
SHA256 8e0416cf6f86da8f18c43e15c8f197559e49c2453add6ee645ab3e18b7e4dde5
MD5 09c0145deaa4df2996351c306924b9c0
BLAKE2b-256 aabc256e324dfd865da2ab8983a59870837ae515ecef377bd1b4ce935d768ba4

See more details on using hashes here.

Provenance

The following attestation bundles were made for git_clerk-0.2.1.tar.gz:

Publisher: publish.yml on nicobc/git-clerk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file git_clerk-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: git_clerk-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 13.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for git_clerk-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 154a10296bc4bb8035ddc8c8ded8ae82e36fa031452f69cc55dc634bf1f06d35
MD5 aed7b4268626a772dfaf8993c006d57c
BLAKE2b-256 a0868a45ad6f9284ec7f348c864b46b37c4f8dd1dab958e0ee8f79561f2aebb3

See more details on using hashes here.

Provenance

The following attestation bundles were made for git_clerk-0.2.1-py3-none-any.whl:

Publisher: publish.yml on nicobc/git-clerk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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