git-pushing
Safely stage, commit, and push intended git changes with conventional commit messages. Use for ordinary non-release pushes when explicitly asked to push, save work remotely, or share a completed change.
Install
npx skills add https://github.com/sickn33/agentic-awesome-skills/tree/main/skills/git-pushing
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install sickn33-agentic-awesome-skills@llmmart
git clone https://github.com/sickn33/agentic-awesome-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole sickn33/agentic-awesome-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Git Push Workflow
Stage only intended changes, create a conventional commit, and push to the remote branch.
When to Use
Automatically activate when the user:
- Explicitly asks to push changes ("push this", "commit and push")
- Mentions saving work to remote ("save to github", "push to remote")
- Completes a feature and wants to share it
- Says phrases like "let's push this up" or "commit these changes"
Safety Gates
Before staging, inspect git status --short --branch, confirm the intended files, and fetch the upstream branch when a concurrent push is plausible. Do not absorb unrelated dirty files.
Read repository policy before choosing the destination branch. If main or master is protected, or the repository defines a maintainer command such as merge:batch, create or use a topic branch and finish through the required pull-request checks. A user request to “push to main” describes the desired final state; it does not authorize bypassing server-side protection. Never keep retrying a direct push after a protected-branch rejection.
The helper requires an empty live index and a conventional commit message before it stages anything. It locks the live index, builds and validates the commit in an isolated temporary index, rejects -- without paths, and atomically updates the branch only if its parent is unchanged.
The helper honors branch.<name>.pushRemote, remote.pushDefault, and the branch's configured upstream, in that order. For a new branch without those settings, it requires origin and establishes origin/<branch>. It rejects detached HEAD and invalid remote configurations before staging.
Do not use this skill for a maintainer merge batch, canonical synchronization, versioned repository release, tag publication, or a repository with an explicit merge:batch, release:prepare, or release:publish workflow. Use that repository's maintainer/release flow instead; it owns pull-request evidence, protected-branch checks, generated files, tags, and publication verification.
Workflow
Use the helper only after the safety gates pass. Resolve the installed directory that contains this SKILL.md and substitute its absolute path for <skill-directory> below; do not assume the current working directory is the catalog repository. With no paths the helper stages all current changes, so use that form only when every dirty file belongs to the requested commit:
bash "<skill-directory>/scripts/smart_commit.sh"
With custom message:
bash "<skill-directory>/scripts/smart_commit.sh" "feat: add feature"
To stage only named files, pass them after --:
bash "<skill-directory>/scripts/smart_commit.sh" "fix: scope change" -- path/to/file
The helper handles isolated staging, commit creation, and push; it does not replace validation, release tooling, or a rebase required by an advanced upstream branch.
Limitations
- The helper currently requires Git's
filesref backend; it rejectsreftablerepositories before creating a commit because their refs cannot use the filesystem lock protocol. - Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
Files (agentic-awesome-skills)
-
scripts
-
smart_commit.sh 7 KB
#!/bin/bash set -euo pipefail # Always inspect and update the repository's real index. An inherited alternate # index could otherwise make the safety check observe different staged content. unset GIT_INDEX_FILE CONVENTIONAL_PATTERN='^(feat|fix|ref|refactor|perf|docs|test|build|ci|chore|style|meta|license|revert)(\([A-Za-z0-9._/-]+\))?(!)?: .+' MESSAGE="${1:-chore: update code}" shift || true validate_message() { local subject=$1 if [[ ! "$subject" =~ $CONVENTIONAL_PATTERN ]]; then echo "Commit message must use the conventional '<type>(<scope>): <subject>' format." >&2 exit 1 fi } validate_message "$MESSAGE" PATH_MODE=false if [[ "${1:-}" == "--" ]]; then PATH_MODE=true shift elif [[ "$#" -gt 0 ]]; then echo "Pass selected paths after an explicit -- separator." >&2 exit 1 fi if [[ "$PATH_MODE" == true && "$#" -eq 0 ]]; then echo "The -- separator requires at least one path." >&2 exit 1 fi BRANCH=$(git symbolic-ref --quiet --short HEAD) || { echo "Refusing to commit from a detached HEAD." >&2 exit 1 } REF_FORMAT=$(git rev-parse --show-ref-format 2>/dev/null || true) if [[ -z "$REF_FORMAT" || "$REF_FORMAT" == "--show-ref-format" ]]; then REF_FORMAT=$(git config --get extensions.refStorage || true) REF_FORMAT=${REF_FORMAT:-files} fi if [[ "$REF_FORMAT" != files ]]; then echo "Refusing to run with ref backend '$REF_FORMAT'; safe branch locking currently requires the files backend." >&2 exit 1 fi PUSH_REMOTE=$(git config --get "branch.$BRANCH.pushRemote" || true) if [[ -z "$PUSH_REMOTE" ]]; then PUSH_REMOTE=$(git config --get remote.pushDefault || true) fi FETCH_REMOTE=$(git config --get "branch.$BRANCH.remote" || true) MERGE_REF=$(git config --get "branch.$BRANCH.merge" || true) if [[ -z "$PUSH_REMOTE" ]]; then PUSH_REMOTE=${FETCH_REMOTE:-origin} fi if [[ "$PUSH_REMOTE" == "." ]] || ! git remote get-url --push "$PUSH_REMOTE" >/dev/null 2>&1; then echo "Configured push remote '$PUSH_REMOTE' does not exist or is not pushable." >&2 exit 1 fi PUSH_BRANCH=$BRANCH if [[ "$PUSH_REMOTE" == "$FETCH_REMOTE" && -n "$MERGE_REF" ]]; then if [[ "$MERGE_REF" != refs/heads/* ]]; then echo "Configured upstream '$MERGE_REF' is not a pushable branch ref." >&2 exit 1 fi PUSH_BRANCH=${MERGE_REF#refs/heads/} fi HAS_UPSTREAM=false if git rev-parse --verify --quiet '@{upstream}' >/dev/null; then HAS_UPSTREAM=true fi SET_UPSTREAM=false if [[ "$HAS_UPSTREAM" == false && "$PUSH_REMOTE" == origin ]]; then SET_UPSTREAM=true fi GIT_DIR=$(git rev-parse --absolute-git-dir) GIT_COMMON_DIR=$(git rev-parse --path-format=absolute --git-common-dir) LIVE_INDEX="$GIT_DIR/index" LIVE_INDEX_LOCK="$LIVE_INDEX.lock" BRANCH_REF="$GIT_COMMON_DIR/refs/heads/$BRANCH" BRANCH_REF_LOCK="$BRANCH_REF.lock" TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/smart-commit.XXXXXX") TEMP_INDEX="$TEMP_DIR/index" MESSAGE_FILE="$TEMP_DIR/COMMIT_EDITMSG" LOCK_HELD=false BRANCH_LOCK_HELD=false cleanup() { rm -rf "$TEMP_DIR" if [[ "$LOCK_HELD" == true ]]; then rm -f "$LIVE_INDEX_LOCK" fi if [[ "$BRANCH_LOCK_HELD" == true ]]; then rm -f "$BRANCH_REF_LOCK" fi } trap cleanup EXIT if ! (set -o noclobber; : > "$LIVE_INDEX_LOCK") 2>/dev/null; then echo "The Git index is busy; refusing to race another staging operation." >&2 exit 1 fi LOCK_HELD=true if ! git diff --cached --quiet; then echo "Refusing to commit because the index already contains staged changes." >&2 echo "Commit or unstage the existing index, then retry with the intended paths." >&2 exit 1 fi PARENT=$(git rev-parse --verify HEAD) if [[ -f "$LIVE_INDEX" ]]; then cp "$LIVE_INDEX" "$TEMP_INDEX" else GIT_INDEX_FILE="$TEMP_INDEX" git read-tree "$PARENT" fi if [[ "$PATH_MODE" == true ]]; then GIT_INDEX_FILE="$TEMP_INDEX" git add -- "$@" else GIT_INDEX_FILE="$TEMP_INDEX" git add -A fi if GIT_INDEX_FILE="$TEMP_INDEX" git diff --cached --quiet; then echo "No changes staged for commit." >&2 exit 1 fi EXPECTED_TREE=$(GIT_INDEX_FILE="$TEMP_INDEX" git write-tree) printf '%s\n' "$MESSAGE" > "$MESSAGE_FILE" GIT_INDEX_FILE="$TEMP_INDEX" git hook run --ignore-missing pre-commit if [[ $(GIT_INDEX_FILE="$TEMP_INDEX" git write-tree) != "$EXPECTED_TREE" ]]; then echo "Pre-commit hooks changed the isolated index; review those changes before retrying." >&2 exit 1 fi GIT_INDEX_FILE="$TEMP_INDEX" git hook run --ignore-missing prepare-commit-msg -- "$MESSAGE_FILE" message GIT_INDEX_FILE="$TEMP_INDEX" git hook run --ignore-missing commit-msg -- "$MESSAGE_FILE" if [[ $(GIT_INDEX_FILE="$TEMP_INDEX" git write-tree) != "$EXPECTED_TREE" ]]; then echo "Commit hooks changed the isolated index; review those changes before retrying." >&2 exit 1 fi validate_message "$(head -n 1 "$MESSAGE_FILE")" if [[ $(git config --bool commit.gpgsign || true) == true ]]; then CREATED_COMMIT=$(git commit-tree -S "$EXPECTED_TREE" -p "$PARENT" -F "$MESSAGE_FILE") else CREATED_COMMIT=$(git commit-tree "$EXPECTED_TREE" -p "$PARENT" -F "$MESSAGE_FILE") fi if ! git update-ref "refs/heads/$BRANCH" "$CREATED_COMMIT" "$PARENT"; then echo "The branch changed concurrently; refusing to replace or push it." >&2 exit 1 fi mkdir -p "$(dirname "$BRANCH_REF_LOCK")" if ! (set -o noclobber; : > "$BRANCH_REF_LOCK") 2>/dev/null; then CURRENT_BRANCH_COMMIT=$(git rev-parse --verify "refs/heads/$BRANCH") if [[ "$CURRENT_BRANCH_COMMIT" != "$CREATED_COMMIT" ]]; then GIT_INDEX_FILE="$TEMP_INDEX" git read-tree "$CURRENT_BRANCH_COMMIT" fi cp "$TEMP_INDEX" "$LIVE_INDEX_LOCK" mv -f "$LIVE_INDEX_LOCK" "$LIVE_INDEX" LOCK_HELD=false echo "The branch ref is busy after commit creation; refusing to continue to hooks or push." >&2 exit 1 fi BRANCH_LOCK_HELD=true CURRENT_BRANCH_COMMIT=$(git rev-parse --verify "refs/heads/$BRANCH") if [[ "$CURRENT_BRANCH_COMMIT" != "$CREATED_COMMIT" ]]; then GIT_INDEX_FILE="$TEMP_INDEX" git read-tree "$CURRENT_BRANCH_COMMIT" cp "$TEMP_INDEX" "$LIVE_INDEX_LOCK" mv -f "$LIVE_INDEX_LOCK" "$LIVE_INDEX" LOCK_HELD=false echo "The branch changed before its ref lock was acquired; refusing to push." >&2 exit 1 fi if ! GIT_INDEX_FILE="$TEMP_INDEX" git hook run --ignore-missing post-commit; then echo "Warning: post-commit hook failed after the commit was created; continuing with a consistent index." >&2 fi CURRENT_BRANCH_COMMIT=$(git rev-parse --verify "refs/heads/$BRANCH") if [[ "$CURRENT_BRANCH_COMMIT" != "$CREATED_COMMIT" ]]; then GIT_INDEX_FILE="$TEMP_INDEX" git read-tree "$CURRENT_BRANCH_COMMIT" cp "$TEMP_INDEX" "$LIVE_INDEX_LOCK" mv -f "$LIVE_INDEX_LOCK" "$LIVE_INDEX" LOCK_HELD=false echo "The branch changed after commit creation; refusing to push another commit." >&2 exit 1 fi cp "$TEMP_INDEX" "$LIVE_INDEX_LOCK" mv -f "$LIVE_INDEX_LOCK" "$LIVE_INDEX" LOCK_HELD=false PUSH_REFSPEC="$CREATED_COMMIT:refs/heads/$PUSH_BRANCH" git push "$PUSH_REMOTE" "$PUSH_REFSPEC" if [[ "$SET_UPSTREAM" == true ]]; then git config "branch.$BRANCH.remote" "$PUSH_REMOTE" git config "branch.$BRANCH.merge" "refs/heads/$PUSH_BRANCH" fi rm -f "$BRANCH_REF_LOCK" BRANCH_LOCK_HELD=false echo "✅ Successfully pushed $BRANCH to $PUSH_REMOTE/$PUSH_BRANCH"
-
-
SKILL.md 3.6 KB
--- name: git-pushing description: "Safely stage, commit, and push intended git changes with conventional commit messages. Use for ordinary non-release pushes when explicitly asked to push, save work remotely, or share a completed change." risk: critical source: community date_added: "2026-02-27" --- # Git Push Workflow Stage only intended changes, create a conventional commit, and push to the remote branch. ## When to Use Automatically activate when the user: - Explicitly asks to push changes ("push this", "commit and push") - Mentions saving work to remote ("save to github", "push to remote") - Completes a feature and wants to share it - Says phrases like "let's push this up" or "commit these changes" ## Safety Gates Before staging, inspect `git status --short --branch`, confirm the intended files, and fetch the upstream branch when a concurrent push is plausible. Do not absorb unrelated dirty files. Read repository policy before choosing the destination branch. If `main` or `master` is protected, or the repository defines a maintainer command such as `merge:batch`, create or use a topic branch and finish through the required pull-request checks. A user request to “push to main” describes the desired final state; it does not authorize bypassing server-side protection. Never keep retrying a direct push after a protected-branch rejection. The helper requires an empty live index and a conventional commit message before it stages anything. It locks the live index, builds and validates the commit in an isolated temporary index, rejects `--` without paths, and atomically updates the branch only if its parent is unchanged. The helper honors `branch.<name>.pushRemote`, `remote.pushDefault`, and the branch's configured upstream, in that order. For a new branch without those settings, it requires `origin` and establishes `origin/<branch>`. It rejects detached HEAD and invalid remote configurations before staging. Do not use this skill for a maintainer merge batch, canonical synchronization, versioned repository release, tag publication, or a repository with an explicit `merge:batch`, `release:prepare`, or `release:publish` workflow. Use that repository's maintainer/release flow instead; it owns pull-request evidence, protected-branch checks, generated files, tags, and publication verification. ## Workflow Use the helper only after the safety gates pass. Resolve the installed directory that contains this `SKILL.md` and substitute its absolute path for `<skill-directory>` below; do not assume the current working directory is the catalog repository. With no paths the helper stages all current changes, so use that form only when every dirty file belongs to the requested commit: ```bash bash "<skill-directory>/scripts/smart_commit.sh" ``` With custom message: ```bash bash "<skill-directory>/scripts/smart_commit.sh" "feat: add feature" ``` To stage only named files, pass them after `--`: ```bash bash "<skill-directory>/scripts/smart_commit.sh" "fix: scope change" -- path/to/file ``` The helper handles isolated staging, commit creation, and push; it does not replace validation, release tooling, or a rebase required by an advanced upstream branch. ## Limitations - The helper currently requires Git's `files` ref backend; it rejects `reftable` repositories before creating a commit because their refs cannot use the filesystem lock protocol. - Use this skill only when the task clearly matches the scope described above. - Do not treat the output as a substitute for environment-specific validation, testing, or expert review. - Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.