7.9 KiB
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 pushto 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/igny8now 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
- Phantom Commits: Previously, hook updated files but not git metadata, causing git to think local was "ahead"
- Manual Sync Required: Had to manually
git pullafter every push - Sync Issues: Working tree and git metadata were out of sync
Solution
- Direct File System Access: Hook uses
deployremote pointing to bare repo path (no network overhead) - Complete Sync: Updates both files AND git metadata (tracking branches)
- Automatic: Runs on every push, no manual intervention needed
Key Git Concepts Used
1. Bare Repository
- Repository without working tree
- Only contains
.gitcontents (objects, refs, etc.) - Used by servers to store code without checking out files
2. Working Tree
- Directory with actual files you can edit
- Has
.gitdirectory with repository metadata - This is what developers work with
3. Remote Tracking Branches
refs/remotes/origin/main: Git's record of whatorigin/mainwas 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:
- Hook logs:
docker exec gitea cat /data/gitea/log/hooks.log - Hook executable:
ls -la /data/app/gitea/git/repositories/salman/igny8.git/hooks/post-receive - Mount exists:
docker exec gitea ls -la /deploy/igny8
Issue: Hook not running
Check:
- Gitea logs:
docker logs gitea --tail 50 - Hook file exists and is executable
- Push actually succeeded (check Gitea web UI)
Summary
The Complete Flow:
- Developer pushes → Gitea bare repo updated
- Post-receive hook triggers automatically
- Hook fetches from bare repo (via
deployremote) - Hook resets working copy to latest commit
- Hook updates tracking branch metadata
- VPS working copy is now in sync
- Application containers see updated files (via volume mounts)
Key Innovation:
- Uses local file system path (
deployremote) instead of HTTPS - Updates both files AND git metadata
- Fully automatic, no manual steps required