Git Reference & Version Control¶
Overview¶
Git is a distributed version control system used to track changes in source code. In a homelab context, it serves as the backbone of "Infrastructure as Code" (IaC). By storing docker-compose.yml and configuration files in Git, the entire server state is backed up, versioned, and easily reproducible.
This guide covers the initialization of a local repository, security practices for managing secrets, and daily workflows using both the Command Line Interface (CLI) and VS Code.
In my setup the repo lives on a self-hosted Forgejo server, and a push to main is the deploy: Komodo picks up
the webhook and redeploys only the stacks whose files changed, and Renovate opens PRs for image updates. How that is
built is in the GitOps section. This page is the everyday git side of it.
I don't know much about the other stuff in git. I just use the basics, but this gets me by.
1. Initialization (One-Time Setup)¶
Perform these steps when setting up the machine for the first time to establish the repository.
1.1. Installation & Identity¶
First, ensure Git is installed and configure the user identity. This information is embedded in every commit.
# Install Git
sudo apt update && sudo apt install git -y
# Configure Global Identity
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
# Set default branch to 'main' (Modern Standard)
git config --global init.defaultBranch main
1.2. Initialize Repo¶
Turn the config directory into a tracked repository. One directory per host, one per stack
(hosts/<host>/<stack>/compose.yaml), see Repo layout.
1.3. The Ignore Rules (Critical)¶
A .gitignore file prevents sensitive data (secrets) and system clutter from being uploaded to the remote server.
File Location: ~/homelab/.gitignore
# --- Secrets (NEVER COMMIT) ---
**/.env
**/.env.*
**/secrets/
id_rsa
*.pem
*.key
*.ovpn
# --- System Junk ---
.DS_Store
Thumbs.db
**/.git/ # Ignore nested git repos (prevents submodule errors)
# --- Docker Data (should never be in the repo; bind mounts use /mnt/appdata/...) ---
**/data/
**/logs/
**/cache/
**/mysql/
**/influxdb/
# --- Custom Ignores ---
**/.pastebin
**/.pastebin/
Secret values don't go in git at all, not even in an ignored file in the repo: Komodo writes each stack's .env at
deploy time from its secret Variables. See Secrets.
1.4. Connect to Remote (Forgejo)¶
Link the local folder to the infra repo on your Forgejo server. (A GitHub/Codeberg remote works the same way; I used a private GitHub repo as off-site backup before moving to self-hosted Forgejo.)
# 1. Link Remote
git remote add origin https://git.<YOUR_DOMAIN>/<YOUR_USER>/homelab.git
# 2. First Push
git add .
git commit -m "Initial homelab commit"
git push -u origin main
2. Daily Workflows (CLI)¶
2.1. The "Config Change" Routine¶
Execute this routine whenever a compose.yaml or configuration file is edited.
- Validate the compose file (in the stack directory):
-
No output means the YAML and
${VAR}interpolation are fine. Komodo would fail on the same errors, just later. -
Check Status:
-
Verify: Ensure no
.envfiles appear in the untracked list (Red). -
Stage & Commit:
-
Push:
-
Pushing to
maindeploys. Watch ntfy (or Komodo's Updates list) for the redeploy, or the failure. - Undo a bad change with
git revert <commit>and push again. The same flow deploys the old version.
2.2. Disaster Recovery (Rebuild)¶
One host dies: reinstall Debian + Docker, restore /mnt/appdata from backup, install the Komodo Periphery agent
again and redeploy that host's stacks from Komodo. Secrets come back with them, because they live in Komodo, not on the
host.
The control host dies (Forgejo and Komodo with it): follow Rebuild from scratch. The short version, for a single stack without Komodo:
-
Clone Configs:
-
Restore Secrets (Manual): Since
.envfiles are never in git, recreate them from a password manager (e.g., Bitwarden/Vaultwarden). -
Launch:
3. Security: Secret Detection¶
Objective: Prevent accidental leakage of passwords, API keys, or .env contents into the public Git history.
3.1. Visual Inspection¶
Before staging files, review changes to ensure no hardcoded credentials were added.
| Command | Description |
|---|---|
git diff |
Shows line-by-line changes for unstaged files. |
git diff --staged |
Shows changes for files already staged but not committed. |
3.2. Automated Scanning (Gitleaks via Docker)¶
Use Gitleaks to scan the directory for high-entropy strings (random characters that look like API keys) without installing new software on the host.
# Run inside ~/homelab (scans the git history)
docker run --rm \
-v "$(pwd)":/path \
zricethezav/gitleaks:latest \
git /path -v
# Scan the working files instead (including uncommitted ones)
docker run --rm -v "$(pwd)":/path zricethezav/gitleaks:latest dir /path -v
- Success: "No leaks found."
- Failure: Output lists the specific secret and file location.
4. VS Code Integration¶
4.1. Visualizing Diffs¶
To view diffs directly in VS Code from the terminal:
One-off:
Permanent Configuration:
git config --global diff.tool vscode
git config --global difftool.vscode.cmd 'code --wait --diff $LOCAL $REMOTE'
# Usage:
git difftool
4.2. File Explorer Indicators¶
Understanding the colored badges in the VS Code file explorer.
| Badge | Color (Gruvbox) | Meaning | Action Needed |
|---|---|---|---|
| U | Green | Untracked | File is new. Needs git add. |
| M | Blue | Modified | File is edited but not staged. |
| A | Green | Added | File is staged and ready to commit. |
| D | Red | Deleted | File removed locally. |
| - | Gray | Ignored | Matches .gitignore rule. |
4.3. GUI Workflow¶
The "Happy Path" using the Source Control Tab (Ctrl+Shift+G):
- Edit & Save: Make changes and save (
Ctrl+S). - Review: Click the file under Changes to see the Side-by-Side Diff.
- Stage: Click the
+(Plus) icon next to the file. - Commit: Type a message in the input box Click Commit.
- Sync: Click Sync Changes (Auto Pulls & Pushes).
5. Troubleshooting¶
5.1. "Submodule" Error (Gray Folder on GitHub)¶
Symptom: A project (like a theme) was git cloned inside the main repo. GitHub sees the nested .git folder and treats it as a dead link (submodule).
Fix:
# 1. Remove the nested .git folder
rm -rf path/to/subfolder/.git
# 2. Remove the folder from Git's index (cache) only
git rm --cached path/to/subfolder
# 3. Re-add the folder as standard files
git add .
git commit -m "Converted submodule to standard files"
5.2. Credentials Issues (HTTPS)¶
Symptom: Git prompts for a username/password on every push.
Fix (Cache Credentials):
# Cache credentials in memory for 1 hour (3600 seconds)
git config --global credential.helper 'cache --timeout=3600'
6. Command Cheat Sheet¶
Basic Operations¶
| Action | Command |
|---|---|
| Check State | git status (Always run this first) |
| View Changes | git diff |
| Stage Files | git add . (Stage all) or git add <filename> |
| Commit | git commit -m "Your message here" |
| Push | git push origin main |
| Pull Updates | git pull |
| Undo File Edit | git restore <file> (Discards local changes) |
| View History | git log --oneline --graph --all |
Emergency Fixes¶
| Scenario | Workflow / Command |
|---|---|
| Typos in last commit | git add . git commit --amend -m "New Message" git push -f (only if nothing pulled it yet; on main prefer a new commit, since a push already deployed it) |
| Leaked Secret | git rm --cached .env echo ".env" >> .gitignore git commit -m "rm secret", then rotate the secret: it's still in history (Secrets) |
| Reset to Remote | git fetch origin git reset --hard origin/main (Destructive: makes local match cloud) |