Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

git-ship

Two bash scripts that collapse the squash-merge PR workflow into one command each — ship a change, and tidy up afterward.

./git-ship.sh "Fix typo in hero copy"   # branch → commit → push → open PR
./git-sync.sh                           # re-sync main, delete merged branches

Built for the pattern most small teams and solo projects land on: work on main, squash-merge PRs, let a host like Vercel or Netlify build a preview per PR and production on merge.

The problem

Squash-merge is pleasant right up until you look at your local clone. Merging rewrites your commits into a single new SHA on main, so afterward git considers both your local main and the feature branch diverged from origin — even though the work has already shipped. The fix is a fetch, a reset --hard, a branch delete, and remembering which branches were actually merged.

git-sync.sh does that. git-ship.sh handles the other end: getting a change from your working tree into an open PR without thinking about branch naming, staging, or gh pr create flags.

Install

Drop both scripts anywhere in a repo (a scripts/ directory is the usual home) and make them executable:

curl -O https://raw.githubusercontent.com/conching/git-ship/main/git-ship.sh
curl -O https://raw.githubusercontent.com/conching/git-ship/main/git-sync.sh
chmod +x git-ship.sh git-sync.sh

Both cd to the repo root themselves, so they run from any subdirectory.

Requires: bash, git, and the gh CLI authenticated against the repo (gh auth status to check).

git-ship.sh "<subject>"

Run it whenever you've finished a change.

  1. If you're on main, syncs to origin/main and cuts a branch named fix/<slugified-subject>-<disambiguator>. If you're already on a feature branch, it uses that one.
  2. Stages everything (git add -A) and commits with your subject.
  3. Pushes with --force-with-lease.
  4. Opens a PR against main — or, if one is already open for the branch, reports its URL instead of erroring.
./git-ship.sh "Fix typo in hero copy"
# → creates branch fix/fix-typo-in-hero-copy-1234
# → commits, pushes, opens PR, prints the URL

An optional second argument becomes the PR body:

./git-ship.sh "Add rate limiting" "Caps the public API at 60 req/min per IP."

Because it re-pushes with --force-with-lease, running it again after review feedback updates the same PR rather than opening a second one.

git-sync.sh

Run it anytime — it's idempotent.

  1. Stashes uncommitted work if there is any.
  2. Resets local main to origin/main.
  3. Asks GitHub which of your local feature branches have merged PRs, and deletes those.
  4. Pops the stash back.

Safety

These scripts run reset --hard, so the sharp edges got attention:

  • Uncommitted work is stashed before any reset, in both scripts, and popped afterward. The stash is tagged with a timestamp so it's findable if something goes sideways mid-run.
  • --force-with-lease, never --force — a push aborts if someone else pushed to your branch in the meantime.
  • Preflight checks for gh being installed and authenticated, with the fix command printed on failure.
  • set -euo pipefail throughout, so a failed step stops the script rather than cascading.
  • Only branches with merged PRs are deleted, confirmed against GitHub — not inferred from local state.

Assumptions

Worth knowing before you adopt them:

  • Your default branch is main.
  • You squash-merge PRs.
  • Feature branches are yours alone — --force-with-lease is appropriate for a branch nobody else commits to, and not for a shared one.
  • git-ship.sh stages all pending changes. It is not the tool for splitting a working tree into separate commits.

License

MIT

About

Two bash scripts that collapse the squash-merge PR workflow into one command each — ship a change, and tidy up afterward.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages