oneie
Version:
Build apps, websites, and AI agents in English. Zero-interaction setup for AI agents (Claude Code, Cursor, Windsurf). Download to your computer, run in the cloud, deploy to the edge. Open source and free forever.
474 lines (348 loc) • 9.37 kB
Markdown
# Pull Before Push: The Anti-Divergence Guide
**CRITICAL RULE:** Always pull before push. This prevents branch divergence forever.
## The Problem
Branch divergence happens when:
```
Local: 5daac2b4 → b871c846 (your commit)
Remote: 5daac2b4 → c0430725 (someone else's commit)
↑
Same parent, but different children
→ DIVERGED! (requires manual resolution)
```
**How it happens:**
1. Remote gets a commit you don't have
2. You make a local commit
3. You try to push
4. Git says "rejected - remote is ahead"
5. You're now in "diverged" state
**Time to recover:** 5-10 minutes (pull, resolve conflicts, push again)
## The Solution: Pull Before Push
```bash
# Always use this sequence:
git add -A
git commit -m "Your message"
git pull origin main # ← ALWAYS pull first
git push origin main # ← Then push
```
**Why it works:**
- If remote is ahead, you get their commits
- If you're ahead, pull is a no-op (fast-forward)
- If both diverged, pull merges and you fix conflicts once
- After pull, your branch is always in sync
- Push always succeeds (never rejected)
## How ONE Platform Enforces This
We have **THREE LAYERS** of protection:
### Layer 1: Git Config (Local)
```bash
git config pull.ff only
```
- Forces fast-forward only pulls
- Git refuses to pull if it would create divergence
- You get immediate feedback if something's wrong
### Layer 2: Claude Hooks (.claude/hooks/)
#### push.sh
```bash
./.claude/hooks/push.sh main # or: all, one, web, oneie
```
- Automatically pulls before pushing
- Tries fast-forward first, falls back to merge
- Shows progress: pull → push
#### commit-push.sh
```bash
./.claude/hooks/commit-push.sh "Your message"
```
- All-in-one: stage → commit → pull → push
- Same pull logic as push.sh
- Fully automated workflow
### Layer 3: Git Hook (Server-Side)
```bash
.git/hooks/pre-push
```
- Final safety check before ANY push
- Runs automatically on `git push`
- Detects divergence and prevents push if not synced
- Suggests fix: "git pull origin main"
## Workflows
### Workflow 1: Using commit-push.sh (Easiest)
**Command:**
```bash
./.claude/hooks/commit-push.sh "Your commit message"
```
**What happens:**
1. ✅ Stages all changes
2. ✅ Creates commit
3. ✅ Pulls latest from remote
4. ✅ Pushes to origin
5. ✅ Shows summary
**Result:** Always synced, zero divergence.
### Workflow 2: Using push.sh
**Commands:**
```bash
git add -A
git commit -m "Your message"
./.claude/hooks/push.sh main
```
**What happens:**
1. ✅ You create commits locally
2. ✅ push.sh pulls before pushing
3. ✅ push.sh pushes all your commits
**Result:** Always synced, zero divergence.
### Workflow 3: Manual (Most Control)
**Commands:**
```bash
git add -A
git commit -m "Your message"
git pull origin main
git push origin main
```
**What happens:**
1. ✅ You do everything manually
2. ✅ pre-push hook validates before push
3. ✅ pre-push hook prevents bad pushes
**Result:** Manual control, still safe.
## What If Divergence Already Happened?
### Symptom
```
error: failed to push some refs
hint: Updates were rejected because the tip of your current branch is behind
```
### Fix
```bash
# 1. Pull to get remote commits
git pull origin main
# 2. Resolve conflicts if they occur
# (edit conflicted files)
# 3. Complete merge
git add -A
git commit -m "Merge remote changes"
# 4. Push (now safe)
git push origin main
```
**Time:** 5-10 minutes
## Git Config Details
### Current Settings
```bash
git config pull.ff only
```
### What This Does
- ✅ `git pull` succeeds if fast-forward possible
- ❌ `git pull` fails if would create divergence
- Forces you to explicitly handle divergence
### Verify It's Set
```bash
git config --get pull.ff
# Output: only
```
### If You Want to Change
```bash
# Allow merges on divergence (less safe)
git config pull.ff false
# Restore safety (recommended)
git config pull.ff only
```
## Hook Details
### pre-push Hook
**Location:** `.git/hooks/pre-push`
**What it does:**
1. Fetches latest from remote
2. Checks if local and remote are in sync
3. If diverged: prevents push, suggests fix
4. If synced: allows push
5. Shows progress with colors
**Triggers:** Automatically on `git push` (all types)
**Can't bypass:** This hook is local, you control it
### .claude/hooks/push.sh
**Location:** `.claude/hooks/push.sh`
**Usage:**
```bash
./.claude/hooks/push.sh [all|main|one|web|oneie]
```
**Options:**
- `all` - Push to all remotes (main, one, web, oneie)
- `main` - Push only main repo (one-ie/one)
- `one` - Push only /one subtree (one-ie/ontology)
- `web` - Push only /web subtree (one-ie/web)
- `oneie` - Push only one.ie directory
**Pull logic:**
```bash
git pull origin main --ff-only # Try fast-forward
# If that fails:
git pull origin main --no-rebase # Fall back to merge
```
### .claude/hooks/commit-push.sh
**Location:** `.claude/hooks/commit-push.sh`
**Usage:**
```bash
./.claude/hooks/commit-push.sh "Your commit message"
```
**Workflow:**
1. Check for changes
2. Stage all changes
3. Create commit
4. Pull latest (with fallback to merge)
5. Push to origin
6. Show summary
**Benefits:**
- One command instead of 4
- Automatic pull-before-push
- Clear progress output
- Shows commit hash and file count
## Testing the Setup
### Test 1: Verify Config
```bash
git config --get pull.ff
# Expected: only
```
### Test 2: Verify Hooks Exist
```bash
ls -la .git/hooks/pre-push
# Expected: -rwxr-xr-x (executable)
ls -la .claude/hooks/push.sh
# Expected: -rwxr-xr-x (executable)
ls -la .claude/hooks/commit-push.sh
# Expected: -rwxr-xr-x (executable)
```
### Test 3: Simulate Push
```bash
# Create a test commit
echo "test" > test.txt
git add test.txt
./.claude/hooks/commit-push.sh "test: verify push workflow"
# If successful, hooks are working!
# Clean up
git reset --soft HEAD~1
git rm test.txt
git checkout test.txt
```
## Common Scenarios
### Scenario 1: Fast Development (Multiple Commits)
```bash
# Commit 1
git add file1.js
git commit -m "feat: add feature 1"
# Commit 2
git add file2.js
git commit -m "feat: add feature 2"
# Push both together (pull happens once)
./.claude/hooks/push.sh main
```
**Result:** Both commits pushed, no divergence.
### Scenario 2: Concurrent Work
**Person A:**
```bash
git add feature-a.js
./.claude/hooks/commit-push.sh "feat: feature A"
# Pushes successfully
```
**Person B (at same time):**
```bash
git add feature-b.js
./.claude/hooks/commit-push.sh "feat: feature B"
# push.sh pulls A's commit, then pushes B
```
**Result:** Both commits pushed in order, no conflicts.
### Scenario 3: Long-Running Branch
```bash
# Day 1
git commit -m "work in progress"
./.claude/hooks/commit-push.sh "wip: day 1"
# Day 2 (remote got other commits)
git commit -m "more work"
./.claude/hooks/commit-push.sh "wip: day 2"
# push.sh pulls day-1 commits, pushes day-2
```
**Result:** Always synced despite long time between commits.
## Troubleshooting
### Problem: Pre-push hook blocks push
**Message:**
```
PUSH REJECTED: Branches have diverged!
```
**Cause:** Remote and local have different commits.
**Fix:**
```bash
git pull origin main
git push origin main
```
### Problem: Merge conflicts during pull
**Message:**
```
CONFLICT (content): Merge conflict in file.js
```
**Cause:** You and someone else edited same file.
**Fix:**
```bash
# 1. Open conflicted file
vim file.js
# 2. Resolve conflicts (look for <<<<, ====, >>>>)
# 3. Mark resolved
git add file.js
# 4. Complete merge
git commit -m "Merge remote changes"
# 5. Push
git push origin main
```
### Problem: Hook script not executable
**Message:**
```
permission denied: .claude/hooks/push.sh
```
**Fix:**
```bash
chmod +x .claude/hooks/push.sh
chmod +x .claude/hooks/commit-push.sh
chmod +x .git/hooks/pre-push
```
### Problem: Config not working
**Verify:**
```bash
git config --get pull.ff
# Should return: only
```
**If not set:**
```bash
git config pull.ff only
```
## Philosophy
**Why we do this:**
1. **Prevention > Recovery**
- Preventing divergence (5 seconds) < Fixing divergence (10 minutes)
2. **Safety First**
- Triple-layer protection means mistakes are impossible
- Hooks auto-correct most issues
3. **Simple Rule**
- One rule: Always pull before push
- Easy to remember, easy to implement
4. **Automation**
- Hooks do the pulling automatically
- You just run one command
## Quick Reference
| Task | Command | Time | Safety |
|------|---------|------|--------|
| Quick commit & push | `./.claude/hooks/commit-push.sh "msg"` | 10s | ✅✅✅ |
| Push existing commits | `./.claude/hooks/push.sh main` | 5s | ✅✅✅ |
| Manual (full control) | `git pull && git push` | 5s | ✅✅ |
| No hooks (dangerous) | `git push origin main` | 2s | ✅ (only pre-push hook) |
## Success Criteria
✅ **You've successfully implemented anti-divergence if:**
- [ ] `git config pull.ff only` is set
- [ ] `.git/hooks/pre-push` exists and is executable
- [ ] `.claude/hooks/push.sh` pulls before pushing
- [ ] `.claude/hooks/commit-push.sh` pulls before pushing
- [ ] You always use `commit-push.sh` or `push.sh` (never raw `git push`)
- [ ] No more divergence errors when pushing
**Remember:** Pull before push, every time. It's that simple. 🔒