# 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