git-push
Skill3 starsChanged 22 days agoMITView on GitHub
Push a local branch to origin, defaulting to the current HEAD; the user may name any local branch in the current turn. Use for "push", "push main", "push feature/foo", publishing a branch, or sharing commits. Refuses non-fast-forward, force-push on main, develop, or support/*, force-push elsewhere without an explicit in-turn opt-in, and any push to a branch protected by the repo's declared workflow variant without an explicit in-turn opt-in.
- Commits and pushes
18 other projects use a version of this.
Adopted by
Most recently taken up by
- Hexi1997/skillstheir own version, first seen Nov 7, 2026
- mattmireles/gemma-tuner-multimodaltheir own version, first seen Nov 1, 2026
- vladimiracunadev-create/claude-skills-toolkittheir own version, first seen Nov 1, 2026
- MYQFJ23333/git-claudetheir own version, first seen Nov 1, 2026
- solplum/agent-git-workflow-skillstheir own version, first seen Oct 15, 2026
- cognis-digital/skillsmiththeir own version, first seen Oct 11, 2026
Other versions18 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 18
- mattmireles/gemma-tuner-multimodal★ 1.5ksame name
- yunshu0909/yunshu_skillshub★ 760same name
- jerrywu001/cc-sessions-viewer★ 415same name
- chengkj99/kj-skills★ 14same name
- phanghonghao/THU-Awesome-Skills★ 8same name
- pgnzbl-ux/crucible★ 4same name
- solplum/agent-git-workflow-skills★ 3same name
- mir-am/skills-agents-ai-coding★ 2same name
- MYQFJ23333/git-claude★ 2same name
- SmartAssets-io/skills★ 2same name
8 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository15 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- git-branch-createSkill
- git-branch-deleteSkill
- git-commitSkill
- git-fetchSkill
- git-mergeSkill
- git-pullSkill
- git-tagSkill
- hf-downloadSkill
- hf-setupSkill
- hf-uploadSkill
- py-env-setupSkill
- repo-docsSkill
- repo-initSkill
The file
We split this skill into the rules it gives an agent
11 rules. 0 are common, 6 are distinctive, and 5 are very specific to this project.
0
Common
Most of the 11 edited versions we read keep these.
6
Distinctive
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- could be automatic: a setting could enforce it
- Distinctive: line 21
Run every command in this skill in a POSIX shell.
- Distinctive: line 26
If no name was given AND HEAD is detached, STOP and ask with a choice question which local branch to push.
- Very specific: line 63
If the set is empty (trunk-solo), skip this gate entirely and proceed to step 4.
- Very specific: line 65
Refuse on non-fast-forward: If git log refs/heads/<branch>..origin/<branch> is non-empty, the remote has commits the local branch lacks.
- Very specific: line 103
NEVER push tags from this skill.
- Distinctive: line 105
NEVER act on a branch name, user-supplied or current, that fails the step-1 exact name check.
- Very specific: line 106
NEVER relax the protected set on inference alone.
- Distinctive: line 107
NEVER auto-resolve a rejection (non-fast-forward, branch protection, hook failure) by force-pushing, rebasing destructively, deleting …
- Very specific: line 108
NEVER recommend git pull --rebase as the fix for a non-fast-forward.
- Distinctive: line 109
NEVER skip the step 7 confirmation.
- Distinctive: line 111
If the push fails for any reason, report the failure verbatim.
“Common” is measured: kept by at least 6 in 10 of the 11 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Goal
Publish a local branch to origin safely. The skill performs the push only.
When NOT to use
- There is nothing committed yet:
git-commit. - The remote has commits the local branch lacks:
git-pull(clean fast-forward) orgit-merge(divergence), then re-run this skill. - The user needs a new branch first:
git-branch-create.
Procedure
Terms such as choice question, independent reads, invoke, and suggest follow AGENTS.md (Skill authoring); in a non-interactive session every gate ends this skill.
Run every command in this skill in a POSIX shell; the redirections used across this skill family are POSIX syntax and break under PowerShell.
- Resolve target branch.
- A branch named by the user in the current turn wins ("push main", "push feature/foo").
- Else default to the current branch:
git branch --show-current. Empty output means detached; an unborn zero-commit branch prints its name normally. Unlikegit symbolic-ref --short HEAD, it never prints a disambiguatedheads/<branch>when a tag shares the branch name. - If no name was given AND HEAD is detached, STOP and ask with a choice question which local branch to push. Do not guess.
- Exact name check, before any other step, for
<branch>, including the current branch (skip only when<branch>is the current branch andgit rev-parse --verify --quiet HEADprints nothing, as on an unborn branch): require[ "$(git for-each-ref --format='%(refname)' "refs/heads/<branch>")" = "refs/heads/<branch>" ]. On failure, STOP: list the local branches whose names match ignoring case (git for-each-ref --format='%(refname:lstrip=2)' refs/heads/ | grep -ixF -- '<branch>'), or say there is no local<branch>. When<branch>is the current branch, suggestgit switch <exact-name>. See Rationale.
Gather state. Local reads name the branch as
refs/heads/<branch>: a tag sharing the short name would otherwise win, andgit logthen silently lists the tag's history. First batch. Run these independent reads:git status --short(working-tree state).git remote get-url originandgit remote get-url --push --all origin(the fetch URL, and every URL the push goes to afterpushurlandpushInsteadOf). Exit 2 means no origin; the fetch below handles that. Compare them after normalizing each URL with this function, so spellings of one repository (C:/x/o.gitand/c/x/o.gitin Git Bash, a trailing/or.git) match:
A local path becomes its resolved directory; any other URL keeps its spelling minus one trailingnorm() { u=${1%/}; case $u in file://*) u=${u#file://} ;; esac for d in "$u" "$u.git"; do r=$(CDPATH= cd "$d" 2>/dev/null && pwd -P) && { printf '%s\n' "$r"; return; }; done printf '%s\n' "${u%.git}"; }/and.git. When any normalized push URL differs from the normalized fetch URL, every read here describes the fetch side only: record the split for step 7.git rev-parse --verify --quiet refs/heads/<branch>(local branch exists). No output means there is nothing to push: an unborn current branch has no commits yet (suggestgit-commit), and any other name has no local branch (say so). STOP in both cases, before any range read; a range read against a missing ref exits 128 with empty output that step 5 would misread as "up to date".git fetch --no-tags origin +refs/heads/<branch>:refs/remotes/origin/<branch>(refresh the remote ref). The explicit refspec writesorigin/<branch>even in a--single-branchor shallow clone whose configured fetch refspec does not cover<branch>; its+force-updates only that remote-tracking ref, as the default refspec does, and--no-tagsstops the tag auto-follow an explicit refspec would otherwise trigger. An exit 128 withcouldn't find remote refis NOT an error here: the branch is absent on origin, either never pushed (first push) or deleted there after an earlier push (a merged PR with auto-delete, or a teammate's cleanup). Tell the two apart withgit rev-parse --verify --quiet origin/<branch>: a hit after that failed fetch is a STALE remote-tracking ref (a failed fetch never prunes), and it no longer describes anything on the server. Record both shapes as the first-push case and skip the twogit logrange reads below; without the ref they exit 128 and print nothing, and against a stale ref the about-to-land list comes back empty and step 5 would wrongly report the remote as up to date. An exit with'origin' does not appear to be a git repositorymeans the repo has no origin remote: suggestgit remote add origin <url>(user-run) and STOP. Both transports are fully supported; recommend whichever the machine is already set up for instead of holding a fixed preference. Take<host>from the URL the user supplies (for examplegithub.comfromgit@github.com:owner/repo.gitorhttps://github.com/owner/repo.git); when no URL is known yet, ask for it. Run these independent reads:git config --get credential.helper(any non-empty value, such asmanager,osxkeychain,libsecret,store, orcache, means the HTTPS form has a helper to authenticate through) andssh -o BatchMode=yes -o ConnectTimeout=5 -T git@<host> 2>&1(exit 255 is ssh's own failure, such asPermission denied,Host key verification failed, or a timeout, and means the SSH form is not ready; any other exit, typically 0 or 1 with a greeting from the host, means the key authenticated;BatchModemakes it fail fast without prompting). Exactly one ready: recommend that form. Both ready: either works, let the user pick (SSH needs no browser or token prompts and suits headless use; HTTPS rides port 443 and needs no key management). Neither: HTTPS is usually the lower-setup path once a credential helper is configured (Git Credential Manager ships with Git for Windows and is available for macOS and Linux;osxkeychainships with git on macOS); otherwise generate an SSH key and register it with the host. Any other non-zero exit (network, auth, proxy): surface stderr verbatim and STOP.
Then, when the fetch succeeded (so
origin/<branch>is current), run these independent reads:git log origin/<branch>..refs/heads/<branch> --oneline(commits about to land).git log refs/heads/<branch>..origin/<branch> --oneline(remote-only commits; non-empty means non-fast-forward).
In the first-push case, produce the about-to-land list with
git log refs/heads/<branch> --oneline --not --exclude=origin/<branch> --remotes=origininstead, capped at 20 lines with an overflow count; there is nothing remote-only to check. It lists only the commits noorigin/*ref in this clone already reaches, so a branch cut frommaindoes not re-list the whole trunk history. The--exclude(which must come before--remotes) drops a staleorigin/<branch>, whose commits the server no longer holds on that branch; it is a no-op when that ref does not exist. An empty list is valid: the push only creates<branch>on origin at a commit origin already has. With a stale ref, add one line to the step-7 plan:origin/<branch>is stale, the remote branch is gone, and this push re-creates it (the push refreshes the ref;git-fetchprunes stale refs in bulk). When the branch vanished because its PR merged, re-creating it is usually not what the user wants: say so, and suggestgit-branch-deletefor the local leftover.A non-zero exit from any of these reads is an error, never an empty list: surface it verbatim and stop.
Protected-branch gate. Read the
Workflow:line in.gitmessage's "Workflow Variant" section to get the protected set. When no variant is declared, or the line carries an unrecognized value (such as the shipped<not recorded>placeholder), assumegit-flow; do not infer a permissive one here. Nothing infers the variant:git-commitandrepo-initask the user and offer to record the answer.Variant Protected set git-flowmain,develop,support/*github-flowmain,support/*trunk-solonone This table is a convenience copy.
.gitmessage's "Protected Branches" section is normative; when the two disagree, follow.gitmessageand say so.Compare names ignoring case:
Main,DEVELOP, andSupport/xcount as protected (see Rationale). If<branch>is in that set, require an explicit in-turn opt-in as AGENTS.md defines it: a reply in the current turn that names the direct push and<branch>, typed (such as "yes, push<branch>directly") or by choosing an option whose label states both. A generic yes, or an opt-in from an earlier turn, does NOT count. Without one, refuse and explain the alternatives:- Land the commits through a PR or MR instead: move them onto a short-lived branch (
git-branch-create), push that branch with this skill, and open the PR or MR against<branch>. - Or re-ask with the explicit in-turn opt-in above. Merging locally first does not help:
<branch>stays protected, andgit-mergegates a protected target the same way.
If the set is empty (
trunk-solo), skip this gate entirely and proceed to step 4. Pushing the trunk is that variant's normal path; the step-7 confirmation still shows exactly what lands.- Land the commits through a PR or MR instead: move them onto a short-lived branch (
- Refuse on non-fast-forward. If
git log refs/heads/<branch>..origin/<branch>is non-empty, the remote has commits the local branch lacks. STOP, show both lists, and choose the suggestion by local state:git log origin/<branch>..refs/heads/<branch>empty (no unpublished local commits): suggestgit-pullwith<branch>, which fast-forwards cleanly; re-run this skill afterwards.- Both lists non-empty (diverged): suggest
git-mergewith source=origin/<branch>, target=<branch>; its step 2 owns any checkout behind its own gate. Re-run this skill afterwards.
- Refuse when nothing to push. If the step-2 fetch succeeded and
git log origin/<branch>..refs/heads/<branch>is empty, tell the user the remote is already up to date and stop. This never applies to the first-push case, stale ref included: an upstream configured on the local branch says nothing about whether the remote branch still exists. - Handle dirty tree.
- Current HEAD equals the target AND
git status --shortnon-empty: warn that uncommitted changes will NOT be included, and suggestgit-commitfirst. - Current HEAD differs from the target: note in the plan output that uncommitted changes on the current HEAD are not part of this push, and proceed.
- Current HEAD equals the target AND
Show the plan and confirm. Display the commits about to land (the step-2 about-to-land list; on first push that is the capped
git log refs/heads/<branch> --oneline --not --exclude=origin/<branch> --remotes=origin; when it is empty, say instead that the push creates<branch>on origin at<sha>with no new commits) and the exact command, then ask with a choice question (push / abort). A "push" / "yes push" / "go ahead" typed in the current turn counts as confirmation; ambiguous replies do not.- Split push URL (step 2): show the fetch URL and each push URL above the command, say the commit lists were read from the fetch side, and label the confirm option
push to <push-url>; a typed reply must name that URL, and a bare "push" does not count. - First push (never pushed, or re-publish after a remote delete), current HEAD == target:
git push --no-follow-tags -u origin refs/heads/<branch>:refs/heads/<branch>(sets or refreshes tracking toorigin/<branch>). - First push, current HEAD != target:
git push --no-follow-tags origin refs/heads/<branch>:refs/heads/<branch>; afterward advisegit branch --set-upstream-to=origin/<branch> <branch>if they want tracking on the local ref. - Remote branch present, fast-forward:
git push --no-follow-tags origin refs/heads/<branch>:refs/heads/<branch>.
The full-ref refspec is always present: a bare
<branch>fails withsrc refspec <branch> matches more than onewhen a tag shares the name.--no-follow-tagsis always present: a user-levelpush.followTags=truewould otherwise publish every annotated tag reachable from the pushed commits, bypassinggit-tag's separate push gate.- Split push URL (step 2): show the fetch URL and each push URL above the command, say the commit lists were read from the fetch side, and label the confirm option
- Run the push. Execute the command exactly as displayed. Capture output verbatim.
- Handle failures. Do NOT retry blindly.
- Server-side branch protection rejection: surface the rule output. Do NOT attempt to bypass (no
--force, no deploy-key swap). The user must disable the rule, push via a PR, or contact a maintainer. - Non-fast-forward rejection (a race between step 2 and step 8): re-fetch, surface the new divergence per step 4, stop.
- Auth, network, or hook: surface the error verbatim and stop.
- Server-side branch protection rejection: surface the rule output. Do NOT attempt to bypass (no
- Report success. Show:
- The pushed range (
<old-sha>..<new-sha>), or[new branch]on first push. - Branch name and upstream, with the active workflow variant in parentheses (for example
main (trunk-solo)). Naming it is what makes an unprotected trunk visible; without it a user has no way to notice that nothing is guardingmain. git log --oneline -5 refs/heads/<branch>so the user can confirm what is now on the remote.
- The pushed range (
Rationale
- Exact branch names. On a case-insensitive filesystem (Windows, default macOS),
refs/heads/Mainresolves to the loose ref file ofmain, sopush Mainwould publishmain's commits (as a newMainon a case-sensitive forge, or movingmainon a case-insensitive one) while a literal protected-name check seesMainand passes it.for-each-refreports only real ref names; the equality test is needed because it also matches every branch under arefs/heads/<branch>/hierarchy. The current branch is checked too: aftergit checkout Mainwith onlymainpresent, HEAD isrefs/heads/Main,git branch --show-currentprintsMain, and a push ofrefs/heads/Mainwould movemainon a case-insensitive origin. Comparing protected names ignoring case covers a real branch whose name differs from a protected one only in case. - Push destination. Every push command names
originexplicitly, sobranch.<b>.pushRemoteandremote.pushDefaultnever redirect it; only origin's own push URLs (remote.origin.pushurl, apushInsteadOfrewrite, or severalurlentries) do, which is why step 2 compares them with the fetch URL. It compares normalized forms, since flagging a mere respelling would teach users to wave the split gate through. With a split, the step-2 fetch and range reads describe the fetch side while the push lands elsewhere, so the gate names the push URL.
Hard rules
- NEVER use
--forceor--force-with-leaseonmain,develop, orsupport/*, under any workflow variant and with or without an opt-in:.gitmessageforbids force push and history rewrite there, including undertrunk-solo. On other branches, where.gitmessagepermits it (feature/*,bugfix/*,hotfix/*, andrelease/*before its first release tag), use either flag only with an explicit in-turn opt-in (AGENTS.md) that names the flag and<branch>; a generic yes or earlier-turn approval does not count. The+on the step-2 fetch refspec is not a push force: it updates only theorigin/<branch>remote-tracking ref. - NEVER push tags from this skill. Every push command carries
--no-follow-tags, sopush.followTagsconfig cannot sweep tags along; tags go throughgit-tag. - NEVER push to a branch the active workflow variant protects without an explicit in-turn opt-in naming the direct push and the branch. A generic yes or prior-turn approval does not count. The protected set comes from
.gitmessage, names match it ignoring case, and an undeclared or unrecognized variant value meansgit-flow(the strictest), never the most permissive. - NEVER act on a branch name, user-supplied or current, that fails the step-1 exact name check.
- NEVER relax the protected set on inference alone. Only a
Workflow:line in.gitmessage, or an in-turn statement from the user, changes it. - NEVER auto-resolve a rejection (non-fast-forward, branch protection, hook failure) by force-pushing, rebasing destructively, deleting commits, or any other workaround. A rejection means STOP-and-explain.
- NEVER recommend
git pull --rebaseas the fix for a non-fast-forward. Rebase rewrites history and is forbidden across this skill family; suggestgit-pullorgit-mergeper step 4. - NEVER skip the step 7 confirmation. Confirming the exact commits is the safety net.
- This skill does NOT commit, merge, rebase, or rescue commits onto another branch. Surface the situation and suggest the matching skill (
git-commit,git-merge,git-pull,git-branch-create) without running it. - If the push fails for any reason, report the failure verbatim. Let the user diagnose.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

