Files
igny8/docs/GIT-AUTO-SYNC-ARCHITECTURE.md
2025-11-10 10:58:12 +00:00

7.9 KiB

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

# 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

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

# 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

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

docker exec gitea cat /data/gitea/log/hooks.log

Manual Sync (if needed)

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