chore: add Entire CLI, .claude config, CLAUDE.md auto-generation
Enable auto-commit tracking, git-sync hooks, session recovery, and anonymous identity for the new repo. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
583
.entire/git-sync.sh
Executable file
583
.entire/git-sync.sh
Executable file
@@ -0,0 +1,583 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# git-sync.sh — Multi-machine git sync for Entire CLI projects
|
||||
# =============================================================================
|
||||
# Usage:
|
||||
# bash .entire/git-sync.sh pull # SessionStart: fetch + rebase
|
||||
# bash .entire/git-sync.sh push # post-commit: push to remote (background)
|
||||
# bash .entire/git-sync.sh status # Diagnostics
|
||||
# bash .entire/git-sync.sh cleanup # Remove old backup tags
|
||||
# =============================================================================
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
SYNC_DIR=".entire/tmp"
|
||||
SYNC_LOG="$SYNC_DIR/sync.log"
|
||||
SYNC_LOCK="$SYNC_DIR/sync.lock"
|
||||
NETWORK_TIMEOUT=30
|
||||
BACKUP_TAG_PREFIX="sync/backup"
|
||||
DAILY_TAG_PREFIX="sync/daily"
|
||||
BACKUP_MAX_AGE_DAYS=7
|
||||
DAILY_MAX_AGE_DAYS=30
|
||||
ARCHIVE_INTERVAL_DAYS=3
|
||||
ARCHIVE_BRANCH="archive/snapshots"
|
||||
ARCHIVE_LOCAL_DIR=".entire/tmp/archives"
|
||||
STALE_LOCK_SECONDS=120
|
||||
|
||||
# --- Logging ---
|
||||
log() {
|
||||
mkdir -p "$SYNC_DIR"
|
||||
local ts
|
||||
ts=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo "[$ts] $*" >> "$SYNC_LOG"
|
||||
}
|
||||
|
||||
log_and_echo() {
|
||||
log "$@"
|
||||
echo "[git-sync] $*"
|
||||
}
|
||||
|
||||
# --- Lock management ---
|
||||
acquire_lock() {
|
||||
mkdir -p "$SYNC_DIR"
|
||||
|
||||
# Remove stale lock
|
||||
if [ -f "$SYNC_LOCK" ]; then
|
||||
local lock_pid lock_age
|
||||
lock_pid=$(cat "$SYNC_LOCK" 2>/dev/null || echo "")
|
||||
lock_age=$(( $(date +%s) - $(stat -c %Y "$SYNC_LOCK" 2>/dev/null || stat -f %m "$SYNC_LOCK" 2>/dev/null || echo 0) ))
|
||||
|
||||
if [ -n "$lock_pid" ] && ! kill -0 "$lock_pid" 2>/dev/null; then
|
||||
log "Removing lock from dead process (PID: $lock_pid)"
|
||||
rm -f "$SYNC_LOCK"
|
||||
elif [ "$lock_age" -gt "$STALE_LOCK_SECONDS" ]; then
|
||||
log "Removing stale lock (age: ${lock_age}s)"
|
||||
rm -f "$SYNC_LOCK"
|
||||
else
|
||||
log "Lock held by process $lock_pid (age: ${lock_age}s), skipping"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo $$ > "$SYNC_LOCK"
|
||||
return 0
|
||||
}
|
||||
|
||||
release_lock() {
|
||||
rm -f "$SYNC_LOCK"
|
||||
}
|
||||
|
||||
# --- Detect current branch ---
|
||||
current_branch() {
|
||||
git symbolic-ref --short HEAD 2>/dev/null
|
||||
}
|
||||
|
||||
# --- Check if we should sync (only main/master) ---
|
||||
should_sync() {
|
||||
local branch
|
||||
branch=$(current_branch)
|
||||
if [ -z "$branch" ]; then
|
||||
log "Detached HEAD, skipping sync"
|
||||
return 1
|
||||
fi
|
||||
case "$branch" in
|
||||
main|master) return 0 ;;
|
||||
*)
|
||||
log "Branch '$branch' is not main/master, skipping sync"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# --- Detect remote for current branch ---
|
||||
detect_remote() {
|
||||
local branch
|
||||
branch=$(current_branch)
|
||||
local remote
|
||||
remote=$(git config "branch.${branch}.remote" 2>/dev/null)
|
||||
if [ -n "$remote" ]; then
|
||||
echo "$remote"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Fallback: try first available remote
|
||||
remote=$(git remote | head -1)
|
||||
if [ -n "$remote" ]; then
|
||||
echo "$remote"
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# --- Create backup tag ---
|
||||
create_backup_tag() {
|
||||
local ts
|
||||
ts=$(date '+%Y%m%d-%H%M%S')
|
||||
local tag="${BACKUP_TAG_PREFIX}/pre-rebase/${ts}"
|
||||
git tag "$tag" HEAD 2>/dev/null && log "Backup tag: $tag"
|
||||
}
|
||||
|
||||
# --- Daily snapshot tag with summary ---
|
||||
create_daily_tag() {
|
||||
local today
|
||||
today=$(date '+%Y-%m-%d')
|
||||
local tag="${DAILY_TAG_PREFIX}/${today}"
|
||||
|
||||
# Skip if today's tag already exists
|
||||
if git tag -l "$tag" 2>/dev/null | grep -q .; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Find yesterday's tag or the most recent daily tag
|
||||
local prev_ref=""
|
||||
prev_ref=$(git tag -l "${DAILY_TAG_PREFIX}/*" 2>/dev/null | sort -r | head -1)
|
||||
|
||||
# Build summary of commits since last daily tag (or last 24h)
|
||||
local summary=""
|
||||
if [ -n "$prev_ref" ]; then
|
||||
summary=$(git log --oneline "${prev_ref}..HEAD" 2>/dev/null | head -20)
|
||||
else
|
||||
summary=$(git log --oneline --since="yesterday" 2>/dev/null | head -20)
|
||||
fi
|
||||
|
||||
if [ -z "$summary" ]; then
|
||||
summary="(no new commits)"
|
||||
fi
|
||||
|
||||
local commit_count
|
||||
if [ -n "$prev_ref" ]; then
|
||||
commit_count=$(git rev-list --count "${prev_ref}..HEAD" 2>/dev/null || echo 0)
|
||||
else
|
||||
commit_count=$(git log --oneline --since="yesterday" 2>/dev/null | wc -l)
|
||||
fi
|
||||
|
||||
# Create annotated tag with summary as message
|
||||
local msg
|
||||
msg=$(printf "Daily snapshot %s (%s commits)\n\n%s" "$today" "$commit_count" "$summary")
|
||||
git tag -a "$tag" -m "$msg" HEAD 2>/dev/null && log "Daily tag: $tag ($commit_count commits)"
|
||||
}
|
||||
|
||||
# --- Periodic archive: full snapshot to archive/snapshots branch ---
|
||||
create_periodic_archive() {
|
||||
local today
|
||||
today=$(date '+%Y-%m-%d')
|
||||
local branch
|
||||
branch=$(current_branch)
|
||||
local project_name
|
||||
project_name=$(basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)")
|
||||
|
||||
# Check if archive branch exists, get last archive date
|
||||
local last_archive_date="0000-00-00"
|
||||
if git rev-parse --verify "$ARCHIVE_BRANCH" &>/dev/null; then
|
||||
# Get date from last commit message on archive branch
|
||||
last_archive_date=$(git log "$ARCHIVE_BRANCH" -1 --format="%s" 2>/dev/null | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' | head -1 || echo "0000-00-00")
|
||||
fi
|
||||
|
||||
# Check interval: skip if last archive is less than ARCHIVE_INTERVAL_DAYS ago
|
||||
if [ "$last_archive_date" != "0000-00-00" ]; then
|
||||
local last_epoch today_epoch
|
||||
last_epoch=$(date -d "$last_archive_date" +%s 2>/dev/null || date -j -f '%Y-%m-%d' "$last_archive_date" +%s 2>/dev/null || echo 0)
|
||||
today_epoch=$(date +%s)
|
||||
local diff_days=$(( (today_epoch - last_epoch) / 86400 ))
|
||||
if [ "$diff_days" -lt "$ARCHIVE_INTERVAL_DAYS" ]; then
|
||||
log "Archive skipped: last archive $last_archive_date ($diff_days days ago, interval=${ARCHIVE_INTERVAL_DAYS})"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if there are new commits since last archive
|
||||
local new_commits=0
|
||||
if git rev-parse --verify "$ARCHIVE_BRANCH" &>/dev/null; then
|
||||
# Find the source commit hash stored in last archive
|
||||
local last_source_hash
|
||||
last_source_hash=$(git log "$ARCHIVE_BRANCH" -1 --format="%b" 2>/dev/null | sed -n 's/^Source: \([a-f0-9]*\).*/\1/p' | head -1)
|
||||
if [ -n "$last_source_hash" ] && git rev-parse --verify "$last_source_hash" &>/dev/null; then
|
||||
new_commits=$(git rev-list --count "${last_source_hash}..HEAD" 2>/dev/null || echo 0)
|
||||
else
|
||||
new_commits=1 # Can't determine, assume changes
|
||||
fi
|
||||
else
|
||||
new_commits=$(git rev-list --count HEAD 2>/dev/null || echo 1)
|
||||
fi
|
||||
|
||||
if [ "$new_commits" -eq 0 ]; then
|
||||
log "Archive skipped: 0 new commits since last archive"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_and_echo "Creating archive ($new_commits new commits since last snapshot)..."
|
||||
|
||||
local short_hash
|
||||
short_hash=$(git rev-parse --short HEAD 2>/dev/null)
|
||||
local archive_name="${project_name}_${today}_${short_hash}.tar.gz"
|
||||
|
||||
# Create archive in temp
|
||||
local tmpdir
|
||||
tmpdir=$(mktemp -d)
|
||||
|
||||
if ! git archive --format=tar.gz --prefix="${project_name}/" -o "${tmpdir}/${archive_name}" HEAD 2>>"$SYNC_LOG"; then
|
||||
log "Archive creation failed"
|
||||
rm -rf "$tmpdir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local size
|
||||
size=$(du -h "${tmpdir}/${archive_name}" | cut -f1)
|
||||
|
||||
# Build changelog
|
||||
local changelog="${tmpdir}/CHANGELOG.md"
|
||||
local prev_tag
|
||||
prev_tag=$(git tag -l "${DAILY_TAG_PREFIX}/*" 2>/dev/null | sort -r | head -1)
|
||||
|
||||
{
|
||||
echo "# Archive ${today} (${branch}@${short_hash})"
|
||||
echo ""
|
||||
echo "**Project**: ${project_name}"
|
||||
echo "**Branch**: ${branch}"
|
||||
echo "**Commit**: ${short_hash} ($(git log -1 --format='%s' HEAD 2>/dev/null))"
|
||||
echo "**Size**: ${size}"
|
||||
echo "**Date**: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
|
||||
echo ""
|
||||
echo "## Commits since last archive"
|
||||
echo ""
|
||||
|
||||
if git rev-parse --verify "$ARCHIVE_BRANCH" &>/dev/null; then
|
||||
local last_src
|
||||
last_src=$(git log "$ARCHIVE_BRANCH" -1 --format="%b" 2>/dev/null | sed -n 's/^Source: \([a-f0-9]*\).*/\1/p' | head -1)
|
||||
if [ -n "$last_src" ] && git rev-parse --verify "$last_src" &>/dev/null; then
|
||||
git log --oneline "${last_src}..HEAD" 2>/dev/null | while read -r line; do
|
||||
echo "- ${line}"
|
||||
done
|
||||
else
|
||||
git log --oneline -20 HEAD 2>/dev/null | while read -r line; do
|
||||
echo "- ${line}"
|
||||
done
|
||||
fi
|
||||
else
|
||||
git log --oneline -50 HEAD 2>/dev/null | while read -r line; do
|
||||
echo "- ${line}"
|
||||
done
|
||||
fi
|
||||
} > "$changelog"
|
||||
|
||||
# Save local copy
|
||||
mkdir -p "$ARCHIVE_LOCAL_DIR"
|
||||
cp "${tmpdir}/${archive_name}" "$ARCHIVE_LOCAL_DIR/"
|
||||
cp "${tmpdir}/CHANGELOG.md" "${ARCHIVE_LOCAL_DIR}/CHANGELOG_${today}.md"
|
||||
log "Local archive copy: ${ARCHIVE_LOCAL_DIR}/${archive_name}"
|
||||
|
||||
# Cleanup local archives older than 30 days
|
||||
local cutoff
|
||||
cutoff=$(date -d "-30 days" '+%Y-%m-%d' 2>/dev/null || \
|
||||
date -v "-30d" '+%Y-%m-%d' 2>/dev/null || echo "0000-00-00")
|
||||
for f in "${ARCHIVE_LOCAL_DIR}/${project_name}_"*.tar.gz; do
|
||||
[ -f "$f" ] || continue
|
||||
local fdate
|
||||
fdate=$(echo "$f" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' | tail -1)
|
||||
if [ -n "$fdate" ] && [ "$fdate" \< "$cutoff" ]; then
|
||||
rm -f "$f"
|
||||
# Also remove matching changelog
|
||||
rm -f "${ARCHIVE_LOCAL_DIR}/CHANGELOG_${fdate}.md"
|
||||
fi
|
||||
done
|
||||
|
||||
# Switch to archive branch (orphan if new), commit, switch back
|
||||
local original_branch
|
||||
original_branch=$(current_branch)
|
||||
local archive_stashed=false
|
||||
|
||||
# Safety trap: restore branch on interrupt
|
||||
archive_cleanup() {
|
||||
if [ "$(current_branch 2>/dev/null)" != "$original_branch" ]; then
|
||||
git checkout "$original_branch" 2>/dev/null || true
|
||||
fi
|
||||
if [ "$archive_stashed" = true ]; then
|
||||
git stash pop 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
trap archive_cleanup INT TERM
|
||||
|
||||
if ! git diff --quiet 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then
|
||||
git stash push -m "archive-stash" 2>/dev/null && archive_stashed=true
|
||||
fi
|
||||
|
||||
if git rev-parse --verify "$ARCHIVE_BRANCH" &>/dev/null; then
|
||||
git checkout "$ARCHIVE_BRANCH" 2>>"$SYNC_LOG"
|
||||
else
|
||||
git checkout --orphan "$ARCHIVE_BRANCH" 2>>"$SYNC_LOG"
|
||||
git rm -rf . 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Copy archive and changelog
|
||||
cp "${tmpdir}/${archive_name}" .
|
||||
cp "${tmpdir}/CHANGELOG.md" .
|
||||
git add "${archive_name}" CHANGELOG.md 2>/dev/null
|
||||
|
||||
# Commit with source hash in body for tracking
|
||||
local full_hash
|
||||
full_hash=$(git rev-parse "$original_branch" 2>/dev/null || echo "$short_hash")
|
||||
git commit -m "snapshot ${today} (${branch}@${short_hash}, ${size})" \
|
||||
-m "Source: ${full_hash}" \
|
||||
2>>"$SYNC_LOG"
|
||||
|
||||
# Push archive branch
|
||||
local remote
|
||||
remote=$(detect_remote) || true
|
||||
if [ -n "$remote" ]; then
|
||||
timeout "$NETWORK_TIMEOUT" git push "$remote" "$ARCHIVE_BRANCH" 2>>"$SYNC_LOG" || \
|
||||
log "Archive branch push failed"
|
||||
fi
|
||||
|
||||
# Switch back and remove trap
|
||||
git checkout "$original_branch" 2>>"$SYNC_LOG"
|
||||
if [ "$archive_stashed" = true ]; then
|
||||
git stash pop 2>/dev/null || true
|
||||
fi
|
||||
trap - INT TERM
|
||||
|
||||
rm -rf "$tmpdir"
|
||||
log_and_echo "Archive committed to ${ARCHIVE_BRANCH}: ${archive_name} (${size}, ${new_commits} commits)"
|
||||
}
|
||||
|
||||
# --- Push tags to remote ---
|
||||
push_tags() {
|
||||
local remote
|
||||
remote=$(detect_remote) || return 0
|
||||
|
||||
# Push only sync/* tags (not all tags)
|
||||
local tag
|
||||
while IFS= read -r tag; do
|
||||
[ -z "$tag" ] && continue
|
||||
timeout "$NETWORK_TIMEOUT" git push "$remote" "refs/tags/$tag" 2>>"$SYNC_LOG" || \
|
||||
log "Failed to push tag: $tag"
|
||||
done < <(git tag -l "sync/*" 2>/dev/null)
|
||||
log "Sync tags pushed to $remote"
|
||||
}
|
||||
|
||||
# --- PULL: fetch + rebase ---
|
||||
do_pull() {
|
||||
if ! should_sync; then
|
||||
echo "[git-sync] Skipped (not on main/master)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local remote
|
||||
remote=$(detect_remote) || {
|
||||
echo "[git-sync] No remote configured, skipping pull"
|
||||
return 0
|
||||
}
|
||||
|
||||
local branch
|
||||
branch=$(current_branch)
|
||||
|
||||
if ! acquire_lock; then
|
||||
echo "[git-sync] Another sync in progress, skipping"
|
||||
return 0
|
||||
fi
|
||||
trap release_lock EXIT
|
||||
|
||||
# Fetch with timeout
|
||||
log "Fetching from $remote..."
|
||||
if ! timeout "$NETWORK_TIMEOUT" git fetch "$remote" "$branch" 2>>"$SYNC_LOG"; then
|
||||
log_and_echo "Fetch failed (network timeout or error), continuing with local state"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Create daily snapshot tag and periodic archive after fetch (has latest remote state)
|
||||
create_daily_tag
|
||||
create_periodic_archive
|
||||
|
||||
# Check if we're behind
|
||||
local behind
|
||||
behind=$(git rev-list --count "HEAD..${remote}/${branch}" 2>/dev/null || echo 0)
|
||||
if [ "$behind" -eq 0 ]; then
|
||||
log_and_echo "Already up to date with ${remote}/${branch}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_and_echo "Behind ${remote}/${branch} by $behind commit(s), syncing..."
|
||||
|
||||
# Create backup tag before rebase
|
||||
create_backup_tag
|
||||
|
||||
# Stash dirty changes if any
|
||||
local stashed=false
|
||||
if ! git diff --quiet 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then
|
||||
log "Stashing dirty working tree..."
|
||||
git stash push -m "git-sync auto-stash $(date '+%Y%m%d-%H%M%S')" 2>>"$SYNC_LOG" && stashed=true
|
||||
fi
|
||||
|
||||
# Rebase
|
||||
if git rebase "${remote}/${branch}" 2>>"$SYNC_LOG"; then
|
||||
log_and_echo "Rebased successfully onto ${remote}/${branch} (+$behind commits)"
|
||||
else
|
||||
log_and_echo "WARNING: Rebase conflict! Aborting rebase, backup tag preserved"
|
||||
git rebase --abort 2>/dev/null
|
||||
if [ "$stashed" = true ]; then
|
||||
git stash pop 2>>"$SYNC_LOG" || true
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Restore stashed changes
|
||||
if [ "$stashed" = true ]; then
|
||||
if git stash pop 2>>"$SYNC_LOG"; then
|
||||
log "Stash restored successfully"
|
||||
else
|
||||
log_and_echo "WARNING: Stash pop conflict! Your changes are saved in git stash"
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- PUSH: push to remote ---
|
||||
do_push() {
|
||||
if ! should_sync; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local remote
|
||||
remote=$(detect_remote) || return 0
|
||||
|
||||
local branch
|
||||
branch=$(current_branch)
|
||||
|
||||
if ! acquire_lock; then
|
||||
return 0
|
||||
fi
|
||||
trap release_lock EXIT
|
||||
|
||||
log "Pushing to ${remote}/${branch}..."
|
||||
if timeout "$NETWORK_TIMEOUT" git push "$remote" "$branch" 2>>"$SYNC_LOG"; then
|
||||
log "Push successful"
|
||||
# Push tags to remote after successful push
|
||||
push_tags
|
||||
else
|
||||
log "Push failed (likely behind remote). Will reconcile at next session pull."
|
||||
fi
|
||||
}
|
||||
|
||||
# --- STATUS: diagnostics ---
|
||||
do_status() {
|
||||
local branch
|
||||
branch=$(current_branch)
|
||||
echo "[git-sync] Status"
|
||||
echo " Branch: ${branch:-DETACHED}"
|
||||
|
||||
if ! should_sync; then
|
||||
echo " Sync: DISABLED (not on main/master)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local remote
|
||||
remote=$(detect_remote) || {
|
||||
echo " Remote: NONE"
|
||||
echo " Sync: DISABLED (no remote)"
|
||||
return 0
|
||||
}
|
||||
echo " Remote: $remote"
|
||||
|
||||
# Fetch silently for status check
|
||||
timeout "$NETWORK_TIMEOUT" git fetch "$remote" "$branch" 2>/dev/null
|
||||
|
||||
local ahead behind
|
||||
ahead=$(git rev-list --count "${remote}/${branch}..HEAD" 2>/dev/null || echo "?")
|
||||
behind=$(git rev-list --count "HEAD..${remote}/${branch}" 2>/dev/null || echo "?")
|
||||
echo " Ahead: $ahead | Behind: $behind"
|
||||
|
||||
local backup_count daily_count
|
||||
backup_count=$(git tag -l "${BACKUP_TAG_PREFIX}/*" 2>/dev/null | wc -l)
|
||||
daily_count=$(git tag -l "${DAILY_TAG_PREFIX}/*" 2>/dev/null | wc -l)
|
||||
echo " Backup tags: $backup_count"
|
||||
echo " Daily snapshots: $daily_count"
|
||||
|
||||
# Show recent daily tags with summaries
|
||||
local latest_daily
|
||||
latest_daily=$(git tag -l "${DAILY_TAG_PREFIX}/*" 2>/dev/null | sort -r | head -3)
|
||||
if [ -n "$latest_daily" ]; then
|
||||
echo " Recent daily snapshots:"
|
||||
echo "$latest_daily" | while read -r dtag; do
|
||||
local dmsg
|
||||
dmsg=$(git tag -n1 "$dtag" 2>/dev/null | sed "s|^${dtag}\s*||")
|
||||
echo " $dtag — $dmsg"
|
||||
done
|
||||
fi
|
||||
|
||||
# Show archive branch info
|
||||
if git rev-parse --verify "$ARCHIVE_BRANCH" &>/dev/null; then
|
||||
local archive_count
|
||||
archive_count=$(git log "$ARCHIVE_BRANCH" --oneline 2>/dev/null | wc -l)
|
||||
echo " Archive branch: $ARCHIVE_BRANCH ($archive_count snapshots)"
|
||||
echo " Recent archives:"
|
||||
git log "$ARCHIVE_BRANCH" --oneline -3 2>/dev/null | sed 's/^/ /'
|
||||
else
|
||||
echo " Archive branch: not created yet (interval: every ${ARCHIVE_INTERVAL_DAYS} days)"
|
||||
fi
|
||||
|
||||
if [ -f "$SYNC_LOG" ]; then
|
||||
echo " Last log entries:"
|
||||
tail -5 "$SYNC_LOG" | sed 's/^/ /'
|
||||
fi
|
||||
|
||||
echo " Sync: ENABLED"
|
||||
}
|
||||
|
||||
# --- CLEANUP: remove old backup tags ---
|
||||
do_cleanup() {
|
||||
local cutoff
|
||||
cutoff=$(date -d "-${BACKUP_MAX_AGE_DAYS} days" '+%Y%m%d' 2>/dev/null || \
|
||||
date -v "-${BACKUP_MAX_AGE_DAYS}d" '+%Y%m%d' 2>/dev/null || echo "00000000")
|
||||
|
||||
local count=0
|
||||
while IFS= read -r tag; do
|
||||
[ -z "$tag" ] && continue
|
||||
# Extract date from tag: sync/backup/pre-rebase/YYYYMMDD-HHMMSS
|
||||
local tag_date
|
||||
tag_date=$(echo "$tag" | grep -oE '[0-9]{8}' | head -1)
|
||||
if [ -n "$tag_date" ] && [ "$tag_date" -lt "$cutoff" ] 2>/dev/null; then
|
||||
git tag -d "$tag" 2>/dev/null
|
||||
count=$((count + 1))
|
||||
fi
|
||||
done < <(git tag -l "${BACKUP_TAG_PREFIX}/*" 2>/dev/null)
|
||||
|
||||
# Clean old daily tags (older than DAILY_MAX_AGE_DAYS)
|
||||
local daily_cutoff
|
||||
daily_cutoff=$(date -d "-${DAILY_MAX_AGE_DAYS} days" '+%Y-%m-%d' 2>/dev/null || \
|
||||
date -v "-${DAILY_MAX_AGE_DAYS}d" '+%Y-%m-%d' 2>/dev/null || echo "0000-00-00")
|
||||
|
||||
while IFS= read -r tag; do
|
||||
[ -z "$tag" ] && continue
|
||||
local tag_date
|
||||
tag_date=$(echo "$tag" | sed "s|${DAILY_TAG_PREFIX}/||")
|
||||
if [ "$tag_date" \< "$daily_cutoff" ] 2>/dev/null; then
|
||||
git tag -d "$tag" 2>/dev/null
|
||||
count=$((count + 1))
|
||||
fi
|
||||
done < <(git tag -l "${DAILY_TAG_PREFIX}/*" 2>/dev/null)
|
||||
|
||||
log_and_echo "Cleaned up $count old backup/daily tag(s)"
|
||||
|
||||
# Trim sync log if > 1000 lines
|
||||
if [ -f "$SYNC_LOG" ]; then
|
||||
local lines
|
||||
lines=$(wc -l < "$SYNC_LOG")
|
||||
if [ "$lines" -gt 1000 ]; then
|
||||
tail -500 "$SYNC_LOG" > "${SYNC_LOG}.tmp" && mv "${SYNC_LOG}.tmp" "$SYNC_LOG"
|
||||
log "Trimmed sync log from $lines to 500 lines"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Main ---
|
||||
case "${1:-}" in
|
||||
pull) do_pull ;;
|
||||
push) do_push ;;
|
||||
status) do_status ;;
|
||||
cleanup) do_cleanup ;;
|
||||
*)
|
||||
echo "Usage: bash .entire/git-sync.sh {pull|push|status|cleanup}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user