#!/usr/bin/env bash # git-sync — add/commit/pull --rebase/push a single repo. # Safe: rebases onto remote, never merges, never force-pushes; conflicts are # reported (rebase aborted), not auto-resolved. # Usage: git-sync [commit-message] # Invoked by the /git-sync slash command, once per configured project repo. set -uo pipefail dir=${1:?usage: git-sync [message]} msg=${2:-} # expand leading ~ (not expanded inside variables) so $HOME-based paths work case "$dir" in "~") dir="$HOME" ;; "~/"*) dir="$HOME/${dir#"~/"}" ;; esac ok() { printf '\033[1;32m✓ %s\033[0m\n' "$*"; } # green note() { printf '\033[1;36m· %s\033[0m\n' "$*"; } # cyan warn() { printf '\033[1;33m⚠ %s\033[0m\n' "$*"; } # yellow g() { git -C "$dir" "$@"; } label=$(basename "$dir") if ! g rev-parse --is-inside-work-tree >/dev/null 2>&1; then warn "$label: kein Git-Repo — übersprungen." exit 0 fi branch=$(g rev-parse --abbrev-ref HEAD) # --- commit working-tree changes ------------------------------------------- if [[ -n "$(g status --porcelain)" ]]; then g add -A [[ -z "$msg" ]] && msg="git-sync: $(date '+%Y-%m-%d %H:%M')" if g commit --quiet -m "$msg"; then note "$label: committed — $msg" else warn "$label: commit fehlgeschlagen." exit 0 fi else note "$label: keine Änderungen." fi # --- pull --rebase, then push (no force, no merge) ------------------------- if ! g remote get-url origin >/dev/null 2>&1; then note "$label: kein origin-Remote — push übersprungen." exit 0 fi if g rev-parse --abbrev-ref --symbolic-full-name '@{u}' >/dev/null 2>&1; then # integrate remote first: rebase keeps history linear, no merge commits g fetch --quiet 2>/dev/null || true behind=$(g rev-list --count 'HEAD..@{u}' 2>/dev/null || echo 0) if [[ "$behind" -gt 0 ]]; then if g pull --rebase --quiet 2>/dev/null; then note "$label: $behind Commit(s) per Rebase integriert." else g rebase --abort >/dev/null 2>&1 || true warn "$label: Pull/Rebase-Konflikt — bitte manuell lösen. Push übersprungen." exit 0 fi fi ahead=$(g rev-list --count '@{u}..HEAD' 2>/dev/null || echo 0) if [[ "$ahead" -eq 0 ]]; then note "$label: nichts zu pushen." elif g push --quiet 2>/dev/null; then ok "$label: $ahead Commit(s) gepusht ($branch)." else warn "$label: push abgelehnt — Remote voraus. Manuell prüfen." fi else if g push --quiet -u origin "$branch" 2>/dev/null; then ok "$label: Upstream gesetzt und gepusht ($branch)." else warn "$label: kein Upstream, push fehlgeschlagen — einmal manuell pushen." fi fi