From d283f72fbb6acc398c5c8e085b712f47276995aa Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Mon, 10 Nov 2025 10:58:12 +0000 Subject: [PATCH] Add Git Auto-Sync Architecture documentation --- docs/GIT-AUTO-SYNC-ARCHITECTURE.md | 225 +++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 docs/GIT-AUTO-SYNC-ARCHITECTURE.md diff --git a/docs/GIT-AUTO-SYNC-ARCHITECTURE.md b/docs/GIT-AUTO-SYNC-ARCHITECTURE.md new file mode 100644 index 00000000..e41a1a04 --- /dev/null +++ b/docs/GIT-AUTO-SYNC-ARCHITECTURE.md @@ -0,0 +1,225 @@ +# Git Auto-Sync Architecture - Complete Picture + +## Overview +This document explains how the automatic git synchronization works between Gitea (bare repository) and the VPS working copy. + +## Architecture Components + +### 1. **Bare Repository (Gitea Server)** +- **Host Path**: `/data/app/gitea/git/repositories/salman/igny8.git` +- **Container Path**: `/data/git/repositories/salman/igny8.git` +- **Type**: Bare repository (no working tree) +- **Purpose**: Stores all commits, branches, and git history +- **Access**: Served by Gitea at `https://git.igny8.com/salman/igny8.git` + +### 2. **Working Copy (VPS Deployment)** +- **Host Path**: `/data/app/igny8` +- **Container Path**: `/deploy/igny8` (mounted from host) +- **Type**: Full git repository with working tree +- **Purpose**: Live application code that runs on VPS +- **Remotes**: + - `origin`: `https://git.igny8.com/salman/igny8.git` (HTTPS remote) + - `deploy`: `/data/git/repositories/salman/igny8.git` (local bare repo path) + +### 3. **Docker Volume Mount** +```yaml +# From docker-compose.yml +volumes: + - ./gitea:/data # Gitea data directory + - /data/app/igny8:/deploy/igny8:rw # Mount working copy into container +``` + +This mount makes the VPS working copy accessible inside the Gitea container at `/deploy/igny8`. + +### 4. **Post-Receive Hook** +- **Location**: `/data/app/gitea/git/repositories/salman/igny8.git/hooks/post-receive` +- **Trigger**: Automatically runs after every `git push` to Gitea +- **Execution Context**: Runs inside Gitea container, in the bare repository directory + +## Complete Flow: Push to Auto-Update + +### Step 1: Developer Pushes to Gitea +```bash +git push origin main +# or +git push https://git.igny8.com/salman/igny8.git main +``` + +### Step 2: Gitea Receives Push +- Push arrives at Gitea server +- Gitea validates and stores commits in bare repository +- Bare repo is updated: `/data/app/gitea/git/repositories/salman/igny8.git` + +### Step 3: Post-Receive Hook Executes +The hook runs automatically with this context: +- **Current Directory**: `/data/git/repositories/salman/igny8.git` (bare repo) +- **Environment**: Inside Gitea container +- **Access**: Can access both bare repo and mounted working copy + +### Step 4: Hook Logic Breakdown + +```bash +# 1. Define paths +DEPLOY_DIR="/deploy/igny8" # Working copy (mounted from host) +SOURCE_REPO="$(pwd)" # Bare repo: /data/git/repositories/salman/igny8.git + +# 2. Check if working copy is a git repository +if [ -d "$DEPLOY_DIR/.git" ]; then + # Working copy exists and is a full git repo + + # 3. Set up git command with explicit paths + GIT_CMD="git --git-dir=$DEPLOY_DIR/.git --work-tree=$DEPLOY_DIR" + + # 4. Add 'deploy' remote if it doesn't exist + # This points to the bare repo (no network needed, direct file access) + if ! $GIT_CMD remote get-url deploy >/dev/null 2>&1; then + $GIT_CMD remote add deploy "$SOURCE_REPO" + fi + + # 5. Fetch latest commits from bare repo + # Fetches directly from file system, no HTTPS/SSH needed + $GIT_CMD fetch deploy main + + # 6. Reset working tree to latest commit + # This updates all files in /data/app/igny8 to match latest commit + $GIT_CMD reset --hard remotes/deploy/main + + # 7. Update origin/main tracking branch + # This is CRITICAL: tells git that local HEAD matches origin/main + # Without this, git status shows "ahead" even though files are synced + $GIT_CMD update-ref refs/remotes/origin/main HEAD + +else + # First time setup: working copy doesn't exist yet + # Checkout files from bare repo to create working copy + export GIT_DIR="$SOURCE_REPO" + export GIT_WORK_TREE="$DEPLOY_DIR" + git checkout -f main +fi +``` + +### Step 5: Result +- **Files Updated**: All files in `/data/app/igny8` now match latest commit +- **Git Status**: Shows "up to date with origin/main" (no phantom commits) +- **Application**: If using volume mounts, containers see changes immediately + +## Why This Design? + +### Problem We Solved +1. **Phantom Commits**: Previously, hook updated files but not git metadata, causing git to think local was "ahead" +2. **Manual Sync Required**: Had to manually `git pull` after every push +3. **Sync Issues**: Working tree and git metadata were out of sync + +### Solution +1. **Direct File System Access**: Hook uses `deploy` remote pointing to bare repo path (no network overhead) +2. **Complete Sync**: Updates both files AND git metadata (tracking branches) +3. **Automatic**: Runs on every push, no manual intervention needed + +## Key Git Concepts Used + +### 1. Bare Repository +- Repository without working tree +- Only contains `.git` contents (objects, refs, etc.) +- Used by servers to store code without checking out files + +### 2. Working Tree +- Directory with actual files you can edit +- Has `.git` directory with repository metadata +- This is what developers work with + +### 3. Remote Tracking Branches +- `refs/remotes/origin/main`: Git's record of what `origin/main` was last time we fetched +- When we update this to match HEAD, git knows we're in sync +- Without updating this, git compares HEAD to stale tracking branch → shows "ahead" + +### 4. Git Reset --hard +- Moves HEAD to specified commit +- Updates working tree to match that commit +- Discards any local changes (force update) + +## File System Layout + +``` +Host System: +├── /data/app/gitea/ +│ └── git/repositories/salman/igny8.git/ (bare repo) +│ ├── objects/ (all commits, trees, blobs) +│ ├── refs/ (branches, tags) +│ └── hooks/ +│ └── post-receive (auto-sync hook) +│ +└── /data/app/igny8/ (working copy) + ├── .git/ (git metadata) + │ ├── config (has 'origin' and 'deploy' remotes) + │ └── refs/ + │ └── remotes/ + │ ├── origin/main (tracking branch) + │ └── deploy/main (tracking branch) + ├── backend/ (actual application files) + ├── frontend/ + └── ... + +Inside Gitea Container: +├── /data/git/repositories/salman/igny8.git/ (same as host bare repo) +└── /deploy/igny8/ (mounted from /data/app/igny8) + └── (same contents as host /data/app/igny8) +``` + +## Checking Status + +### On Host VPS +```bash +cd /data/app/igny8 +git status # Should show "up to date with origin/main" +git log --oneline -5 # See recent commits +git remote -v # See remotes (origin + deploy) +``` + +### Hook Logs +```bash +docker exec gitea cat /data/gitea/log/hooks.log +``` + +### Manual Sync (if needed) +```bash +cd /data/app/igny8 +git pull origin main # Pull from HTTPS remote +# OR +git fetch deploy main # Fetch from local bare repo +git reset --hard remotes/deploy/main +``` + +## Troubleshooting + +### Issue: "Your branch is ahead of origin/main" +**Cause**: Hook didn't update `refs/remotes/origin/main` tracking branch +**Fix**: Hook should run `git update-ref refs/remotes/origin/main HEAD` (already in hook) + +### Issue: Files not updating after push +**Check**: +1. Hook logs: `docker exec gitea cat /data/gitea/log/hooks.log` +2. Hook executable: `ls -la /data/app/gitea/git/repositories/salman/igny8.git/hooks/post-receive` +3. Mount exists: `docker exec gitea ls -la /deploy/igny8` + +### Issue: Hook not running +**Check**: +1. Gitea logs: `docker logs gitea --tail 50` +2. Hook file exists and is executable +3. Push actually succeeded (check Gitea web UI) + +## Summary + +**The Complete Flow:** +1. Developer pushes → Gitea bare repo updated +2. Post-receive hook triggers automatically +3. Hook fetches from bare repo (via `deploy` remote) +4. Hook resets working copy to latest commit +5. Hook updates tracking branch metadata +6. VPS working copy is now in sync +7. Application containers see updated files (via volume mounts) + +**Key Innovation:** +- Uses local file system path (`deploy` remote) instead of HTTPS +- Updates both files AND git metadata +- Fully automatic, no manual steps required +