#!/usr/bin/env bash # Shared Git Utilities # Sprint: CI/CD Enhancement - Script Consolidation # # Purpose: Common git operations for CI/CD scripts # Usage: source "$(dirname "${BASH_SOURCE[0]}")/lib/git-utils.sh" # Prevent multiple sourcing if [[ -n "${__STELLAOPS_GIT_UTILS_LOADED:-}" ]]; then return 0 fi export __STELLAOPS_GIT_UTILS_LOADED=1 # Source dependencies SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPT_DIR}/logging.sh" 2>/dev/null || true source "${SCRIPT_DIR}/exit-codes.sh" 2>/dev/null || true # ============================================================================ # Repository Information # ============================================================================ # Get repository root directory git_root() { git rev-parse --show-toplevel 2>/dev/null || echo "." } # Check if current directory is a git repository is_git_repo() { git rev-parse --git-dir >/dev/null 2>&1 } # Get current commit SHA (full) git_sha() { git rev-parse HEAD 2>/dev/null } # Get current commit SHA (short) git_sha_short() { git rev-parse --short HEAD 2>/dev/null } # Get current branch name git_branch() { git rev-parse --abbrev-ref HEAD 2>/dev/null } # Get current tag (if HEAD is tagged) git_tag() { git describe --tags --exact-match HEAD 2>/dev/null || echo "" } # Get latest tag git_latest_tag() { git describe --tags --abbrev=0 2>/dev/null || echo "" } # Get remote URL git_remote_url() { local remote="${1:-origin}" git remote get-url "$remote" 2>/dev/null } # Get repository name from remote URL git_repo_name() { local url url=$(git_remote_url "${1:-origin}") basename "$url" .git } # ============================================================================ # Commit Information # ============================================================================ # Get commit message git_commit_message() { local sha="${1:-HEAD}" git log -1 --format="%s" "$sha" 2>/dev/null } # Get commit author git_commit_author() { local sha="${1:-HEAD}" git log -1 --format="%an" "$sha" 2>/dev/null } # Get commit author email git_commit_author_email() { local sha="${1:-HEAD}" git log -1 --format="%ae" "$sha" 2>/dev/null } # Get commit timestamp (ISO 8601) git_commit_timestamp() { local sha="${1:-HEAD}" git log -1 --format="%aI" "$sha" 2>/dev/null } # Get commit timestamp (Unix epoch) git_commit_epoch() { local sha="${1:-HEAD}" git log -1 --format="%at" "$sha" 2>/dev/null } # ============================================================================ # Working Tree State # ============================================================================ # Check if working tree is clean git_is_clean() { [[ -z "$(git status --porcelain 2>/dev/null)" ]] } # Check if working tree is dirty git_is_dirty() { ! git_is_clean } # Get list of changed files git_changed_files() { git status --porcelain 2>/dev/null | awk '{print $2}' } # Get list of staged files git_staged_files() { git diff --cached --name-only 2>/dev/null } # Get list of untracked files git_untracked_files() { git ls-files --others --exclude-standard 2>/dev/null } # ============================================================================ # Diff and History # ============================================================================ # Get files changed between two refs git_diff_files() { local from="${1:-HEAD~1}" local to="${2:-HEAD}" git diff --name-only "$from" "$to" 2>/dev/null } # Get files changed in last N commits git_recent_files() { local count="${1:-1}" git diff --name-only "HEAD~${count}" HEAD 2>/dev/null } # Check if file was changed between two refs git_file_changed() { local file="$1" local from="${2:-HEAD~1}" local to="${3:-HEAD}" git diff --name-only "$from" "$to" -- "$file" 2>/dev/null | grep -q "$file" } # Get commits between two refs git_commits_between() { local from="${1:-HEAD~10}" local to="${2:-HEAD}" git log --oneline "$from".."$to" 2>/dev/null } # ============================================================================ # Tag Operations # ============================================================================ # Create a tag git_create_tag() { local tag="$1" local message="${2:-}" if [[ -n "$message" ]]; then git tag -a "$tag" -m "$message" else git tag "$tag" fi } # Delete a tag git_delete_tag() { local tag="$1" git tag -d "$tag" 2>/dev/null } # Push tag to remote git_push_tag() { local tag="$1" local remote="${2:-origin}" git push "$remote" "$tag" } # List tags matching pattern git_list_tags() { local pattern="${1:-*}" git tag -l "$pattern" 2>/dev/null } # ============================================================================ # Branch Operations # ============================================================================ # Check if branch exists git_branch_exists() { local branch="$1" git show-ref --verify --quiet "refs/heads/$branch" 2>/dev/null } # Check if remote branch exists git_remote_branch_exists() { local branch="$1" local remote="${2:-origin}" git show-ref --verify --quiet "refs/remotes/$remote/$branch" 2>/dev/null } # Get default branch git_default_branch() { local remote="${1:-origin}" git remote show "$remote" 2>/dev/null | grep "HEAD branch" | awk '{print $NF}' } # ============================================================================ # CI/CD Helpers # ============================================================================ # Get version string for CI builds git_ci_version() { local tag tag=$(git_tag) if [[ -n "$tag" ]]; then echo "$tag" else local branch sha branch=$(git_branch | tr '/' '-') sha=$(git_sha_short) echo "${branch}-${sha}" fi } # Check if current commit is on default branch git_is_default_branch() { local current default current=$(git_branch) default=$(git_default_branch) [[ "$current" == "$default" ]] } # Check if running in CI environment git_is_ci() { [[ -n "${CI:-}" ]] || [[ -n "${GITHUB_ACTIONS:-}" ]] || [[ -n "${GITLAB_CI:-}" ]] } # Ensure clean worktree or fail git_require_clean() { if git_is_dirty; then log_error "Working tree is dirty. Commit or stash changes first." return "${EXIT_DIRTY_WORKTREE:-71}" fi }