79 lines
2.6 KiB
Bash
Executable File
79 lines
2.6 KiB
Bash
Executable File
#!/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 <dir> [commit-message]
|
|
# Invoked by the /git-sync slash command, once per configured project repo.
|
|
set -uo pipefail
|
|
|
|
dir=${1:?usage: git-sync <dir> [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
|