Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 19 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
- **🔄 Integration-ready:** Works seamlessly with other DevOps workflows
- **💪 Force push options:** Support for `--force` and `--force-with-lease` when needed
- **🔀 Pull request integration:** Perfect companion for automated PR workflows
- **🎯 Deterministic branch reset:** Optionally reset target branches to a chosen base branch before committing
- **🧩 Empty commit support:** Optionally create empty commits for no-diff automation flows
- **🛡️ Rebase conflict control:** Choose strict failure or legacy best-effort behavior on rebase conflicts


## 🔗 Related Actions
Expand Down Expand Up @@ -65,18 +68,22 @@ This action supports three tag levels for flexible versioning:


### 🔧 Input Parameters
| Input Variable | Required | Default | Description |
|-----------------------|----------|------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `github_token` | Yes | `""` | Personal Access Token for GitHub for pushing the code. |
| `add_timestamp` | No | `false` | Whether to add the timestamp to a new branch name. Uses format `%Y-%m-%dT%H-%M-%SZ`. |
| `amend` | No | `false` | Whether to make an amendment to the previous commit (`--amend`). Can be combined with `commit_message` to change the commit message. |
| `commit_prefix` | No | `""` | Prefix added to commit message. Combines with `commit_message`. |
| `commit_message` | No | `""` | Commit message to set. Combines with `commit_prefix`. Can be used with `amend` to change the commit message. |
| `force` | No | `false` | Whether to use force push (`--force`). Use only when you need to overwrite remote changes. Potentially dangerous. |
| `force_with_lease` | No | `false` | Whether to use force push with lease (`--force-with-lease`). Safer than `force` as it checks for remote changes. Set `fetch-depth: 0` for `actions/checkout`. |
| `no_edit` | No | `false` | Whether to not edit commit message when using amend (`--no-edit`). |
| `organization_domain` | No | `github.com` | GitHub Enterprise domain name. |
| `target_branch` | No | *current branch* | Name of a new branch to push the code into. Creates branch if not existing unless there are no changes and `amend` is false. |
| Input Variable | Required | Default | Description |
|---------------------------|----------|------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `github_token` | Yes | `""` | Personal Access Token for GitHub for pushing the code. |
| `add_timestamp` | No | `false` | Whether to add the timestamp to a new branch name. Uses format `%Y-%m-%dT%H-%M-%SZ`. |
| `amend` | No | `false` | Whether to make an amendment to the previous commit (`--amend`). Can be combined with `commit_message` to change the commit message. |
| `commit_prefix` | No | `""` | Prefix added to commit message. Combines with `commit_message`. |
| `commit_message` | No | `""` | Commit message to set. Combines with `commit_prefix`. Can be used with `amend` to change the commit message. |
| `force` | No | `false` | Whether to use force push (`--force`). Use only when you need to overwrite remote changes. Potentially dangerous. |
| `force_with_lease` | No | `false` | Whether to use force push with lease (`--force-with-lease`). Safer than `force` as it checks for remote changes. Set `fetch-depth: 0` for `actions/checkout`. |
| `base_branch` | No | `""` | Base branch used to sync or reset `target_branch`. When empty, the action auto-detects `main`/`master` or origin HEAD. |
| `reset_target_branch` | No | `false` | Whether to hard-reset `target_branch` to `origin/base_branch` before committing. Recommended for deterministic release branches. |
| `allow_empty_commit` | No | `false` | Whether to create an empty commit when there are no file changes. Useful for workflows that must open a PR with no file diff. |
| `fail_on_rebase_conflict` | No | `true` | Whether to fail the action if rebase onto `base_branch` conflicts. Set to `false` to keep legacy best-effort rebase behavior. |
| `no_edit` | No | `false` | Whether to not edit commit message when using amend (`--no-edit`). |
| `organization_domain` | No | `github.com` | GitHub Enterprise domain name. |
| `target_branch` | No | *current branch* | Name of a new branch to push the code into. Creates branch if not existing unless there are no changes and `amend` is false. |


### 📤 Output Parameters
Expand Down
16 changes: 16 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ inputs:
description: Whether to use force push with lease (--force-with-lease). Safer than force as it checks for remote changes.
required: false
default: "false"
base_branch:
description: Base branch name used for branch sync/reset (defaults to auto-detected main/master).
required: false
default: ""
reset_target_branch:
description: Whether to hard-reset target branch to origin/base_branch before committing.
required: false
default: "false"
allow_empty_commit:
description: Whether to allow creating an empty commit when there are no file changes.
required: false
default: "false"
fail_on_rebase_conflict:
description: Whether to fail when branch rebase onto base branch conflicts.
required: false
default: "true"
no_edit:
description: Whether to not edit commit message when using amend
required: false
Expand Down
87 changes: 58 additions & 29 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ set -e
RET_CODE=0

echo "Inputs:"
echo " add_timestamp: ${INPUT_ADD_TIMESTAMP}"
echo " amend: ${INPUT_AMEND}"
echo " commit_prefix: ${INPUT_COMMIT_PREFIX}"
echo " commit_message: ${INPUT_COMMIT_MESSAGE}"
echo " force: ${INPUT_FORCE}"
echo " force_with_lease: ${INPUT_FORCE_WITH_LEASE}"
echo " no_edit: ${INPUT_NO_EDIT}"
echo " organization_domain: ${INPUT_ORGANIZATION_DOMAIN}"
echo " target_branch: ${INPUT_TARGET_BRANCH}"
echo " add_timestamp: ${INPUT_ADD_TIMESTAMP}"
echo " amend: ${INPUT_AMEND}"
echo " commit_prefix: ${INPUT_COMMIT_PREFIX}"
echo " commit_message: ${INPUT_COMMIT_MESSAGE}"
echo " force: ${INPUT_FORCE}"
echo " force_with_lease: ${INPUT_FORCE_WITH_LEASE}"
echo " base_branch: ${INPUT_BASE_BRANCH}"
echo " reset_target_branch: ${INPUT_RESET_TARGET_BRANCH}"
echo " allow_empty_commit: ${INPUT_ALLOW_EMPTY_COMMIT}"
echo " fail_on_rebase_conflict: ${INPUT_FAIL_ON_REBASE_CONFLICT}"
echo " no_edit: ${INPUT_NO_EDIT}"
echo " organization_domain: ${INPUT_ORGANIZATION_DOMAIN}"
echo " target_branch: ${INPUT_TARGET_BRANCH}"

# Require github_token
if [[ -z "${GITHUB_TOKEN}" ]]; then
Expand All @@ -40,6 +44,13 @@ get_current_branch() {
printf '%s' "${branch}"
}

input_true() {
case "${1:-}" in
true|TRUE|True|1|yes|YES|Yes|on|ON|On) return 0 ;;
*) return 1 ;;
esac
}

# Get changed files
git add -A
FILES_CHANGED=$(git diff --staged --name-status)
Expand All @@ -50,7 +61,7 @@ else
fi

SKIP_BRANCH_CREATION=false
if [[ -z ${FILES_CHANGED} && "${INPUT_AMEND}" != "true" ]]; then
if [[ -z ${FILES_CHANGED} && "${INPUT_AMEND}" != "true" && -z "${INPUT_TARGET_BRANCH}" ]] && ! input_true "${INPUT_ALLOW_EMPTY_COMMIT}"; then
SKIP_BRANCH_CREATION=true
BRANCH="$(get_current_branch)"
echo -e "\n[INFO] No changes to commit and amend disabled; skipping branch creation."
Expand Down Expand Up @@ -81,15 +92,18 @@ if [[ "${SKIP_BRANCH_CREATION}" != "true" ]]; then
# Check if remote branch exists
REMOTE_BRANCH_EXISTS=$(git ls-remote --heads origin "${BRANCH}" 2>/dev/null | wc -l)

# Improved main branch detection
MAIN_BRANCH="main"
if git show-ref --verify --quiet "refs/remotes/origin/main"; then
# Improved main branch detection with explicit override
MAIN_BRANCH="${INPUT_BASE_BRANCH}"
if [[ -z "${MAIN_BRANCH}" ]]; then
MAIN_BRANCH="main"
elif git show-ref --verify --quiet "refs/remotes/origin/master"; then
MAIN_BRANCH="master"
else
# Try to get default branch from remote HEAD
MAIN_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")
if git show-ref --verify --quiet "refs/remotes/origin/main"; then
MAIN_BRANCH="main"
elif git show-ref --verify --quiet "refs/remotes/origin/master"; then
MAIN_BRANCH="master"
else
# Try to get default branch from remote HEAD
MAIN_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")
fi
fi
echo "[INFO] Detected main branch: ${MAIN_BRANCH}"

Expand All @@ -110,15 +124,28 @@ if [[ "${SKIP_BRANCH_CREATION}" != "true" ]]; then
}
fi

# Ensure branch is up-to-date with main/master (only if they're different branches)
# Ensure branch is synchronized with base branch
if [[ "${BRANCH}" != "${MAIN_BRANCH}" ]] && git show-ref --verify --quiet "refs/remotes/origin/${MAIN_BRANCH}"; then
echo "[INFO] Rebasing branch onto ${MAIN_BRANCH}..."
git rebase "origin/${MAIN_BRANCH}" || {
echo "[WARNING] Rebase onto ${MAIN_BRANCH} failed. This may indicate conflicts."
echo "[INFO] Attempting to abort the rebase and continue without sync..."
git rebase --abort 2>/dev/null || true
echo "[INFO] Branch will remain at its current state without sync to ${MAIN_BRANCH}"
}
if input_true "${INPUT_RESET_TARGET_BRANCH}"; then
echo "[INFO] Hard resetting '${BRANCH}' to origin/${MAIN_BRANCH}..."
git reset --hard "origin/${MAIN_BRANCH}" || {
echo "[ERROR] Failed to hard reset '${BRANCH}' to origin/${MAIN_BRANCH}"
exit 1
}
else
echo "[INFO] Rebasing branch onto ${MAIN_BRANCH}..."
git rebase "origin/${MAIN_BRANCH}" || {
if input_true "${INPUT_FAIL_ON_REBASE_CONFLICT}"; then
echo "[ERROR] Rebase onto ${MAIN_BRANCH} failed and fail_on_rebase_conflict=true"
git rebase --abort 2>/dev/null || true
exit 1
fi
echo "[WARNING] Rebase onto ${MAIN_BRANCH} failed."
echo "[INFO] Attempting to abort the rebase and continue without sync..."
git rebase --abort 2>/dev/null || true
echo "[INFO] Branch will remain at its current state without sync to ${MAIN_BRANCH}"
}
fi
fi
else
echo "[INFO] Remote branch '${BRANCH}' does not exist, creating new branch..."
Expand All @@ -142,10 +169,12 @@ fi

# Create an auto commit
COMMIT_PARAMS=()
COMMIT_PARAMS+=("--allow-empty")
if input_true "${INPUT_ALLOW_EMPTY_COMMIT}"; then
COMMIT_PARAMS+=("--allow-empty")
fi

# Commit if there are changes OR if we're amending (even without changes)
if [[ -n ${FILES_CHANGED} || "${INPUT_AMEND}" == "true" ]]; then
# Commit if there are changes, or amend is requested, or empty commit is allowed
if [[ -n ${FILES_CHANGED} || "${INPUT_AMEND}" == "true" ]] || input_true "${INPUT_ALLOW_EMPTY_COMMIT}"; then
if [[ -n ${FILES_CHANGED} ]]; then
echo "[INFO] Committing changes."
fi
Expand Down
Loading