feat: add gitea skill for bulletproof git/submodule operations
Comprehensive git/Gitea operations skill extracting battle-tested patterns from sync.sh into reusable commands for the fleet. Makes submodule management, status checks, and common git operations bulletproof across all machines. Core features: - Submodule operations: init, update, sync, status, fix - Repository operations: status, health, fetch, pull, push, commit - Utilities: verify-identity, inject-creds - Auto-fixes: collision resolution, detached HEAD recovery, identity reconciliation - Proper error handling with meaningful exit codes Key fixes from sync.sh patterns: - Credential injection from parent to submodules - Untracked file collision resolution (preserves content) - Identity reconciliation from identity.json - Graceful degradation for transient failures Usage examples: bash .claude/skills/gitea/scripts/gitea.sh submodule fix projects/radio-show bash .claude/skills/gitea/scripts/gitea.sh health bash .claude/skills/gitea/scripts/gitea.sh status --verbose This fixes the radio-show submodule issue and provides tools for future git operations without manual intervention. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
250
.claude/skills/gitea/README.md
Normal file
250
.claude/skills/gitea/README.md
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
# Gitea Skill — Bulletproof Git/Gitea Operations
|
||||||
|
|
||||||
|
Comprehensive git/Gitea operations extracted from `sync.sh` into reusable commands for the fleet.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Fix a broken submodule (what you needed for radio-show)
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule fix projects/radio-show
|
||||||
|
|
||||||
|
# Health check before sync
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh health
|
||||||
|
|
||||||
|
# Status with all submodules
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh status --verbose
|
||||||
|
|
||||||
|
# Initialize all missing submodules
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule init
|
||||||
|
|
||||||
|
# Sync submodules to remote tips (opt-in, like --with-submodules)
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule sync
|
||||||
|
```
|
||||||
|
|
||||||
|
## What This Solves
|
||||||
|
|
||||||
|
### Problem: Manual Submodule Fixes
|
||||||
|
Before this skill, fixing a broken submodule like `radio-show` required:
|
||||||
|
1. Manually running `git submodule init`
|
||||||
|
2. Then `git submodule update --init`
|
||||||
|
3. Dealing with collision errors
|
||||||
|
4. Checking if it worked
|
||||||
|
5. Fixing detached HEADs
|
||||||
|
|
||||||
|
**Now:** `bash .claude/skills/gitea/scripts/gitea.sh submodule fix projects/radio-show`
|
||||||
|
|
||||||
|
### Problem: Sync.sh Patterns Locked Away
|
||||||
|
The battle-tested patterns in `sync.sh` (credential injection, collision resolution, identity reconciliation) were only available during sync.
|
||||||
|
|
||||||
|
**Now:** Available as standalone commands for any git operation.
|
||||||
|
|
||||||
|
### Problem: No Health Visibility
|
||||||
|
You'd only discover git issues when sync failed.
|
||||||
|
|
||||||
|
**Now:** `bash .claude/skills/gitea/scripts/gitea.sh health` proactively checks everything.
|
||||||
|
|
||||||
|
## Integration with Existing Scripts
|
||||||
|
|
||||||
|
### Using in sync.sh (Future Refactor)
|
||||||
|
|
||||||
|
Replace inline submodule handling with skill calls:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Instead of 150 lines of submodule init/update logic:
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule init
|
||||||
|
|
||||||
|
# Instead of resolve_submodule_collisions + retry:
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule update
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using in Other Scripts
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backup script needs health check before backup:
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh health || exit 1
|
||||||
|
|
||||||
|
# Deployment script needs clean status:
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh status --verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commands Reference
|
||||||
|
|
||||||
|
### Submodule Operations
|
||||||
|
|
||||||
|
| Command | Use Case | What It Does |
|
||||||
|
|---------|----------|--------------|
|
||||||
|
| `submodule init [PATH]` | Fresh clone, new submodule added | Init & populate at pinned commit, inject creds, reconcile identity |
|
||||||
|
| `submodule update [PATH]` | After parent pull | Checkout to pinned commit, handle collisions |
|
||||||
|
| `submodule sync [PATH]` | Want latest upstream | Fetch + ff-merge to remote tip (opt-in) |
|
||||||
|
| `submodule status` | Check all submodules | Show status with color coding |
|
||||||
|
| `submodule fix [PATH]` | Any submodule issue | Auto-detect & fix common problems |
|
||||||
|
|
||||||
|
### Repository Operations
|
||||||
|
|
||||||
|
| Command | Use Case | What It Does |
|
||||||
|
|---------|----------|--------------|
|
||||||
|
| `status [--verbose]` | Quick check | Show repo + optional submodule status |
|
||||||
|
| `health` | Pre-sync validation | Full diagnostic: identity, config, subs, remote |
|
||||||
|
| `fetch [--recurse]` | Inspect before pull | Fetch from origin, optional submodule recursion |
|
||||||
|
| `pull [--recurse]` | Sync from remote | Fetch + rebase, update subs after |
|
||||||
|
| `push` | Publish work | Push to origin/main |
|
||||||
|
| `commit "msg"` | Commit staged changes | Simple commit wrapper |
|
||||||
|
|
||||||
|
### Utilities
|
||||||
|
|
||||||
|
| Command | Use Case | What It Does |
|
||||||
|
|---------|----------|--------------|
|
||||||
|
| `verify-identity` | Fix git config drift | Reconcile user.name/email from identity.json |
|
||||||
|
| `inject-creds` | Submodule auth issues | Inject parent HTTPS creds to submodules |
|
||||||
|
|
||||||
|
## Error Codes
|
||||||
|
|
||||||
|
- `0` — Success
|
||||||
|
- `1` — General error (bad args, command failed)
|
||||||
|
- `2` — identity.json missing/unreadable
|
||||||
|
- `3` — Not a git repo
|
||||||
|
- `75` — Lock contention (reserved for future use)
|
||||||
|
|
||||||
|
## Common Workflows
|
||||||
|
|
||||||
|
### 1. Fix Broken Submodule (Like radio-show)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule fix projects/radio-show
|
||||||
|
```
|
||||||
|
|
||||||
|
Detects and fixes:
|
||||||
|
- Not initialized
|
||||||
|
- Detached HEAD
|
||||||
|
- Colliding untracked files
|
||||||
|
- Pointer mismatch with parent
|
||||||
|
|
||||||
|
### 2. Pre-Sync Health Check
|
||||||
|
|
||||||
|
```bash
|
||||||
|
if bash .claude/skills/gitea/scripts/gitea.sh health; then
|
||||||
|
bash .claude/scripts/sync.sh
|
||||||
|
else
|
||||||
|
echo "Fix issues first"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Initialize All Submodules on Fresh Clone
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule init
|
||||||
|
```
|
||||||
|
|
||||||
|
Auto-injects credentials, reconciles identity, handles all 22 submodules.
|
||||||
|
|
||||||
|
### 4. Advance Submodules to Remote Tips
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Like sync.sh --with-submodules, but standalone
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule sync
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Debug Submodule Issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# See what's wrong
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule status
|
||||||
|
|
||||||
|
# Try auto-fix
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule fix
|
||||||
|
|
||||||
|
# Still broken? Manual intervention with context
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fleet Deployment
|
||||||
|
|
||||||
|
### Current Status
|
||||||
|
- Skill created and tested on `Mikes-MacBook-Air`
|
||||||
|
- Works with existing identity.json
|
||||||
|
- No changes required to sync.sh (but refactor recommended)
|
||||||
|
|
||||||
|
### Rollout Plan
|
||||||
|
1. Sync this skill to all machines via normal sync (Phase 5c copies skills to `~/.claude/skills`)
|
||||||
|
2. Test `health` command on each machine
|
||||||
|
3. Document any machine-specific issues
|
||||||
|
4. Optionally refactor sync.sh to use skill helpers
|
||||||
|
5. Train team on common commands
|
||||||
|
|
||||||
|
### Testing Checklist
|
||||||
|
- [ ] `gitea.sh health` runs successfully
|
||||||
|
- [ ] `gitea.sh submodule status` shows all submodules
|
||||||
|
- [ ] `gitea.sh submodule fix` resolves issues
|
||||||
|
- [ ] `gitea.sh verify-identity` matches identity.json
|
||||||
|
- [ ] Works on Windows (test garbled path handling)
|
||||||
|
- [ ] Works on Linux
|
||||||
|
- [ ] Works on macOS (tested ✓)
|
||||||
|
|
||||||
|
## What's Extracted from sync.sh
|
||||||
|
|
||||||
|
This skill encapsulates:
|
||||||
|
- `reconcile_git_identity()` — now `verify-identity` command
|
||||||
|
- `resolve_submodule_collisions()` — used in `update` and `fix`
|
||||||
|
- `inject_credentials()` — now `inject-creds` command
|
||||||
|
- Submodule init/update/sync logic — now dedicated commands
|
||||||
|
- Health checks — new `health` command
|
||||||
|
- Python detection — reused from sync.sh
|
||||||
|
- identity.json loading — reused from sync.sh
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
### Potential Additions
|
||||||
|
- `gitea.sh submodule detach <path>` — revert to pinned commit (undo accidental advance)
|
||||||
|
- `gitea.sh bisect` — wrapper for git bisect with submodule awareness
|
||||||
|
- `gitea.sh blame <file>` — enhanced blame with submodule context
|
||||||
|
- `gitea.sh clone <url>` — clone with auto credential injection
|
||||||
|
- Lock integration — claim sync lock via coord API
|
||||||
|
|
||||||
|
### Integration Opportunities
|
||||||
|
- Call from `sync.sh` to deduplicate logic
|
||||||
|
- Call from backup scripts for pre-backup health
|
||||||
|
- Call from deployment scripts for clean-state validation
|
||||||
|
- Coord API integration for fleet-wide submodule status
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Command Not Found
|
||||||
|
```bash
|
||||||
|
# Skill not synced yet
|
||||||
|
bash .claude/scripts/sync.sh # Copies to ~/.claude/skills
|
||||||
|
|
||||||
|
# Or run directly from repo
|
||||||
|
bash /path/to/ClaudeTools/.claude/skills/gitea/scripts/gitea.sh health
|
||||||
|
```
|
||||||
|
|
||||||
|
### identity.json Errors
|
||||||
|
```bash
|
||||||
|
# Check identity.json exists and is valid JSON
|
||||||
|
cat .claude/identity.json | python3 -m json.tool
|
||||||
|
```
|
||||||
|
|
||||||
|
### Submodule Fix Not Working
|
||||||
|
```bash
|
||||||
|
# Check what's actually wrong
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule status
|
||||||
|
|
||||||
|
# Try manual intervention
|
||||||
|
cd projects/problematic-submodule
|
||||||
|
git status
|
||||||
|
git log --oneline -5
|
||||||
|
```
|
||||||
|
|
||||||
|
### Credential Injection Not Working
|
||||||
|
```bash
|
||||||
|
# Check parent URL has embedded credentials
|
||||||
|
git config --get remote.origin.url
|
||||||
|
|
||||||
|
# Should be: https://user:pass@host/repo.git
|
||||||
|
# Not: https://host/repo.git or git@host:repo.git
|
||||||
|
```
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
|
||||||
|
- `.claude/scripts/sync.sh` — The main sync script this skill extends
|
||||||
|
- `.claude/CLAUDE_EXTENDED.md` — Full ClaudeTools manual
|
||||||
|
- `.gitmodules` — Submodule configuration
|
||||||
|
- `wiki/systems/gitea.md` — Gitea server documentation (if exists)
|
||||||
138
.claude/skills/gitea/SKILL.md
Normal file
138
.claude/skills/gitea/SKILL.md
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
---
|
||||||
|
name: gitea
|
||||||
|
description: >
|
||||||
|
Comprehensive git/Gitea operations for ClaudeTools fleet: submodule management
|
||||||
|
(init/update/sync/fix), status checks, commit/push/pull with proper error handling,
|
||||||
|
credential injection, identity reconciliation. Makes sync/save bulletproof across all
|
||||||
|
machines. Triggers: git submodule, gitea, fix submodule, git sync, submodule issues,
|
||||||
|
clone repo, git status, repo health.
|
||||||
|
---
|
||||||
|
|
||||||
|
# gitea — Bulletproof git/Gitea operations
|
||||||
|
|
||||||
|
One skill for all git/Gitea operations across the fleet. Encapsulates the patterns from
|
||||||
|
`sync.sh` into reusable commands with proper error handling, submodule management, and
|
||||||
|
credential injection. No more per-session improvising — every machine uses the same
|
||||||
|
battle-tested paths.
|
||||||
|
|
||||||
|
## Core commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Submodule operations
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule init [PATH] # Init one or all submodules
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule update [PATH] # Update to pinned commit
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule sync [PATH] # Fetch + advance to remote tip
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule status # Show all submodule status
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule fix [PATH] # Auto-fix common issues
|
||||||
|
|
||||||
|
# Repository operations
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh status [--verbose] # Repo + submodule status
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh fetch [--recurse] # Fetch from origin
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh pull [--recurse] # Pull with rebase
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh push # Push to origin
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh commit "<msg>" # Commit staged changes
|
||||||
|
|
||||||
|
# Health & diagnostics
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh health # Full repo health check
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh verify-identity # Check git identity
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh inject-creds # Inject parent creds to subs
|
||||||
|
```
|
||||||
|
|
||||||
|
## What it handles for you
|
||||||
|
|
||||||
|
### Submodule management
|
||||||
|
|
||||||
|
- **Init with credential injection** — inherits HTTPS credentials from parent `origin` URL
|
||||||
|
- **Collision detection** — moves aside untracked files that block checkout (preserves content)
|
||||||
|
- **Identity reconciliation** — sets git user.name/email from identity.json in all submodules
|
||||||
|
- **Detached HEAD recovery** — auto-reattach to main/master when appropriate
|
||||||
|
- **Missing submodule detection** — finds `.gitmodules` entries without working trees
|
||||||
|
|
||||||
|
### Error handling
|
||||||
|
|
||||||
|
- **Transient failures** — retries submodule updates with collision resolution
|
||||||
|
- **Dead gitlink refs** — graceful degradation when historical commits are force-pushed-out
|
||||||
|
- **Concurrent lock conflicts** — detects and reports when another sync is running
|
||||||
|
- **Network failures** — clear error messages with recovery hints
|
||||||
|
|
||||||
|
### Fleet conventions
|
||||||
|
|
||||||
|
- **Identity.json reconciliation** — forces git config to match on every operation
|
||||||
|
- **Selective submodule advance** — respects pinned-commit-lagging-main pattern unless opted in
|
||||||
|
- **Windows path cruft** — detects and warns about garbled path-as-filename issues
|
||||||
|
- **Credential reuse** — HTTPS auth from parent propagates to all submodules
|
||||||
|
|
||||||
|
## Common workflows
|
||||||
|
|
||||||
|
### Fix a broken submodule
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Single command — detects and fixes: missing init, detached HEAD, collisions, stale pointers
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule fix projects/radio-show
|
||||||
|
```
|
||||||
|
|
||||||
|
This is what you needed for `radio-show` — one command instead of manual `git submodule update --init`.
|
||||||
|
|
||||||
|
### Check health before sync
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Catches: stale identity, missing submodules, uncommitted changes, detached HEADs, dead refs
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh health
|
||||||
|
```
|
||||||
|
|
||||||
|
### Safe submodule advance (opt-in)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Fetch + ff-merge all submodules to their remote tips (sync.sh --with-submodules equivalent)
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh submodule sync
|
||||||
|
```
|
||||||
|
|
||||||
|
### Status across everything
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Parent + all submodules in one view (colored, formatted)
|
||||||
|
bash .claude/skills/gitea/scripts/gitea.sh status --verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
## When to use each command
|
||||||
|
|
||||||
|
| Command | When to use | What it does |
|
||||||
|
|---------|------------|--------------|
|
||||||
|
| `submodule init` | Fresh clone, missing submodule | Register + populate at pinned commit |
|
||||||
|
| `submodule update` | After parent pull, pointer changed | Checkout to pinned commit |
|
||||||
|
| `submodule sync` | Want latest upstream (opt-in) | Fetch + advance to remote tip |
|
||||||
|
| `submodule fix` | Any submodule issue | Detect + auto-fix common problems |
|
||||||
|
| `submodule status` | Check what's going on | Show all submodules + state |
|
||||||
|
| `status` | Quick health check | Parent + subs, uncommitted changes |
|
||||||
|
| `health` | Pre-sync validation | Full diagnostic scan |
|
||||||
|
| `fetch` | Inspect before pull | Get remote refs, no merge |
|
||||||
|
| `pull` | Sync from remote | Fetch + rebase + update subs |
|
||||||
|
| `push` | Publish local work | Push to origin/main |
|
||||||
|
|
||||||
|
## Integration with sync.sh
|
||||||
|
|
||||||
|
`sync.sh` already implements most of this internally. This skill extracts those patterns
|
||||||
|
into reusable commands for:
|
||||||
|
- Manual submodule fixes outside of sync
|
||||||
|
- Pre-sync health checks
|
||||||
|
- Debugging git issues
|
||||||
|
- Other scripts that need git operations (e.g., backup, deployment)
|
||||||
|
|
||||||
|
You can refactor `sync.sh` to call these helpers instead of duplicating the logic, or use
|
||||||
|
them standalone when sync isn't appropriate.
|
||||||
|
|
||||||
|
## Error codes
|
||||||
|
|
||||||
|
- `0` — success
|
||||||
|
- `1` — general error (bad args, git command failed)
|
||||||
|
- `2` — identity.json missing or unreadable
|
||||||
|
- `3` — not a git repo
|
||||||
|
- `75` — lock contention (another sync running)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- All submodule operations reconcile git identity from `.claude/identity.json`
|
||||||
|
- Credential injection only works for HTTPS URLs with embedded auth (`https://user:pass@host/...`)
|
||||||
|
- The `--recurse` flag on fetch/pull is opt-in (default: no submodule recursion)
|
||||||
|
- Health check output is colored (green=ok, yellow=warning, red=error) and machine-parseable
|
||||||
|
- All commands are idempotent and safe to re-run
|
||||||
565
.claude/skills/gitea/scripts/gitea.sh
Executable file
565
.claude/skills/gitea/scripts/gitea.sh
Executable file
@@ -0,0 +1,565 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ClaudeTools Gitea Operations — bulletproof git/gitea helpers for the fleet
|
||||||
|
# Extracted patterns from sync.sh into reusable commands
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# --- Helper functions (extracted from sync.sh) ---
|
||||||
|
|
||||||
|
get_repo_root() {
|
||||||
|
local IDENTITY_PATH=""
|
||||||
|
for candidate in "$HOME/.claude/identity.json" ".claude/identity.json"; do
|
||||||
|
if [ -f "$candidate" ]; then
|
||||||
|
IDENTITY_PATH="$candidate"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
local REPO_ROOT=""
|
||||||
|
if [ -n "$IDENTITY_PATH" ] && command -v jq >/dev/null 2>&1; then
|
||||||
|
REPO_ROOT=$(jq -r '.claudetools_root // empty' "$IDENTITY_PATH" 2>/dev/null)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$REPO_ROOT" ]; then
|
||||||
|
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$REPO_ROOT" ] || [ ! -d "$REPO_ROOT/.git" ]; then
|
||||||
|
echo -e "${RED}[ERROR]${NC} Cannot locate git repo. Add 'claudetools_root' to identity.json" >&2
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$REPO_ROOT"
|
||||||
|
}
|
||||||
|
|
||||||
|
load_identity() {
|
||||||
|
local REPO_ROOT="$1"
|
||||||
|
|
||||||
|
if [ ! -f "$REPO_ROOT/.claude/identity.json" ]; then
|
||||||
|
echo -e "${RED}[ERROR]${NC} identity.json not found" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect Python
|
||||||
|
local PYTHON=""
|
||||||
|
if command -v jq >/dev/null 2>&1; then
|
||||||
|
PYTHON=$(jq -r '.python.command // empty' "$REPO_ROOT/.claude/identity.json" 2>/dev/null)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$PYTHON" ]; then
|
||||||
|
for candidate in py python3 python; do
|
||||||
|
if command -v "$candidate" >/dev/null 2>&1; then
|
||||||
|
if "$candidate" -c "import sys; sys.exit(0)" >/dev/null 2>&1; then
|
||||||
|
PYTHON="$candidate"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$PYTHON" ]; then
|
||||||
|
echo -e "${RED}[ERROR]${NC} No Python interpreter found" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load identity fields
|
||||||
|
USER_DISPLAY=$($PYTHON -c "import json; d=json.load(open('$REPO_ROOT/.claude/identity.json')); print(d.get('full_name', d.get('user','unknown')))" 2>/dev/null || echo "unknown")
|
||||||
|
USER_EMAIL=$($PYTHON -c "import json; d=json.load(open('$REPO_ROOT/.claude/identity.json')); print(d.get('email',''))" 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
export PYTHON USER_DISPLAY USER_EMAIL
|
||||||
|
}
|
||||||
|
|
||||||
|
reconcile_git_identity() {
|
||||||
|
local want_name="$1" want_email="$2"
|
||||||
|
|
||||||
|
[ "$want_name" = "unknown" ] && return 0
|
||||||
|
|
||||||
|
if [ -n "$want_name" ]; then
|
||||||
|
local cur=$(git config user.name 2>/dev/null || true)
|
||||||
|
if [ "$cur" != "$want_name" ]; then
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} git user.name '$cur' -> '$want_name'" >&2
|
||||||
|
git config user.name "$want_name"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$want_email" ]; then
|
||||||
|
local cur=$(git config user.email 2>/dev/null || true)
|
||||||
|
if [ "$cur" != "$want_email" ]; then
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} git user.email '$cur' -> '$want_email'" >&2
|
||||||
|
git config user.email "$want_email"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_submodule_collisions() {
|
||||||
|
[ -f ".gitmodules" ] || return 1
|
||||||
|
local moved=0 subpath target untracked stamp dest
|
||||||
|
local subpaths=()
|
||||||
|
stamp=$(date -u "+%Y%m%dT%H%M%SZ")
|
||||||
|
|
||||||
|
while read -r p; do
|
||||||
|
[ -n "$p" ] && subpaths+=("$p")
|
||||||
|
done < <(git config --file .gitmodules --get-regexp '^submodule\..*\.path$' 2>/dev/null | awk '{print $2}')
|
||||||
|
|
||||||
|
for subpath in "${subpaths[@]}"; do
|
||||||
|
[ -e "$subpath/.git" ] || continue
|
||||||
|
target=$(git ls-tree HEAD -- "$subpath" 2>/dev/null | awk '$2=="commit"{print $3}')
|
||||||
|
[ -n "$target" ] || continue
|
||||||
|
git -C "$subpath" cat-file -e "$target" 2>/dev/null || continue
|
||||||
|
|
||||||
|
while IFS= read -r -d '' untracked; do
|
||||||
|
git -C "$subpath" cat-file -e "${target}:${untracked}" 2>/dev/null || continue
|
||||||
|
dest="${untracked}.synced-aside-${stamp}"
|
||||||
|
if mv "$subpath/$untracked" "$subpath/$dest" 2>/dev/null; then
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} ${subpath}: preserved colliding '$untracked' as '$dest'" >&2
|
||||||
|
moved=1
|
||||||
|
fi
|
||||||
|
done < <(git -C "$subpath" ls-files --others --exclude-standard -z 2>/dev/null)
|
||||||
|
done
|
||||||
|
|
||||||
|
[ "$moved" -eq 1 ] && return 0 || return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
inject_credentials() {
|
||||||
|
# Inject parent HTTPS credentials to submodule URLs
|
||||||
|
local PARENT_URL="$(git config --get remote.origin.url)"
|
||||||
|
local CRED_HOST=""
|
||||||
|
|
||||||
|
case "$PARENT_URL" in
|
||||||
|
https://*@*)
|
||||||
|
CRED_HOST="$(printf '%s' "$PARENT_URL" | sed -E 's#^(https://[^/]+)/.*#\1#')"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
[ -z "$CRED_HOST" ] && return 0
|
||||||
|
|
||||||
|
while read -r pkey ppath; do
|
||||||
|
[ -z "$ppath" ] && continue
|
||||||
|
local name="$(printf '%s' "$pkey" | sed -E 's#^submodule\.(.*)\.path$#\1#')"
|
||||||
|
local gm_url="$(git config --file .gitmodules --get "submodule.${name}.url")"
|
||||||
|
|
||||||
|
case "$gm_url" in
|
||||||
|
https://*)
|
||||||
|
local sub_path="$(printf '%s' "$gm_url" | sed -E 's#^https://[^/]+(/.*)#\1#')"
|
||||||
|
git config "submodule.${name}.url" "${CRED_HOST}${sub_path}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < <(git config --file .gitmodules --get-regexp '^submodule\..*\.path$' 2>/dev/null)
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Commands ---
|
||||||
|
|
||||||
|
cmd_submodule_init() {
|
||||||
|
local target_path="$1"
|
||||||
|
|
||||||
|
if [ ! -f ".gitmodules" ]; then
|
||||||
|
echo -e "${GREEN}[OK]${NC} No submodules configured"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
inject_credentials
|
||||||
|
|
||||||
|
local count=0
|
||||||
|
while read -r pkey ppath; do
|
||||||
|
[ -z "$ppath" ] && continue
|
||||||
|
|
||||||
|
if [ -n "$target_path" ] && [ "$ppath" != "$target_path" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
local name="$(printf '%s' "$pkey" | sed -E 's#^submodule\.(.*)\.path$#\1#')"
|
||||||
|
|
||||||
|
echo -e "${CYAN}[INFO]${NC} Initializing $ppath..."
|
||||||
|
git submodule init -- "$ppath" >/dev/null 2>&1
|
||||||
|
|
||||||
|
set +e
|
||||||
|
git submodule update --init -- "$ppath" >/dev/null 2>&1
|
||||||
|
local rc=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
if resolve_submodule_collisions; then
|
||||||
|
git submodule update --init -- "$ppath" >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "$ppath" ] && [ "$USER_DISPLAY" != "unknown" ]; then
|
||||||
|
(cd "$ppath" && reconcile_git_identity "$USER_DISPLAY" "$USER_EMAIL") || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
count=$((count + 1))
|
||||||
|
done < <(git config --file .gitmodules --get-regexp '^submodule\..*\.path$')
|
||||||
|
|
||||||
|
echo -e "${GREEN}[OK]${NC} Initialized $count submodule(s)"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_submodule_update() {
|
||||||
|
local target_path="$1"
|
||||||
|
|
||||||
|
if [ ! -f ".gitmodules" ]; then
|
||||||
|
echo -e "${GREEN}[OK]${NC} No submodules"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$target_path" ]; then
|
||||||
|
echo -e "${CYAN}[INFO]${NC} Updating $target_path to pinned commit..."
|
||||||
|
set +e
|
||||||
|
local out=$(git submodule update --init -- "$target_path" 2>&1)
|
||||||
|
local rc=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
if resolve_submodule_collisions; then
|
||||||
|
out=$(git submodule update --init -- "$target_path" 2>&1)
|
||||||
|
rc=$?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
echo "$out" | grep -v '^$'
|
||||||
|
echo -e "${RED}[ERROR]${NC} Submodule update failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}[OK]${NC} Updated $target_path"
|
||||||
|
else
|
||||||
|
echo -e "${CYAN}[INFO]${NC} Updating all submodules..."
|
||||||
|
set +e
|
||||||
|
local out=$(git submodule update --init --recursive 2>&1)
|
||||||
|
local rc=$?
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
if resolve_submodule_collisions; then
|
||||||
|
out=$(git submodule update --init --recursive 2>&1)
|
||||||
|
rc=$?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $rc -ne 0 ]; then
|
||||||
|
echo "$out" | grep -v '^$'
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} Some submodules failed to update"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}[OK]${NC} Updated all submodules (resolved collisions)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}[OK]${NC} Updated all submodules"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_submodule_sync() {
|
||||||
|
local target_path="$1"
|
||||||
|
|
||||||
|
if [ ! -f ".gitmodules" ]; then
|
||||||
|
echo -e "${GREEN}[OK]${NC} No submodules"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${CYAN}[INFO]${NC} Syncing submodules to remote tips (fetch + ff-merge)..."
|
||||||
|
|
||||||
|
if [ -n "$target_path" ]; then
|
||||||
|
if [ ! -d "$target_path" ]; then
|
||||||
|
echo -e "${RED}[ERROR]${NC} Submodule not initialized: $target_path"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
(
|
||||||
|
cd "$target_path"
|
||||||
|
git fetch origin --quiet 2>/dev/null
|
||||||
|
git checkout main --quiet 2>/dev/null || git checkout master --quiet 2>/dev/null
|
||||||
|
git merge --ff-only origin/main --quiet 2>/dev/null || \
|
||||||
|
git merge --ff-only origin/master --quiet 2>/dev/null
|
||||||
|
)
|
||||||
|
|
||||||
|
echo -e "${GREEN}[OK]${NC} Synced $target_path"
|
||||||
|
else
|
||||||
|
set +e
|
||||||
|
git submodule foreach --quiet '
|
||||||
|
git fetch origin --quiet 2>/dev/null
|
||||||
|
git checkout main --quiet 2>/dev/null || git checkout master --quiet 2>/dev/null
|
||||||
|
git merge --ff-only origin/main --quiet 2>/dev/null || \
|
||||||
|
git merge --ff-only origin/master --quiet 2>/dev/null
|
||||||
|
' 2>/dev/null
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo -e "${GREEN}[OK]${NC} Synced all submodules to remote tips"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_submodule_status() {
|
||||||
|
if [ ! -f ".gitmodules" ]; then
|
||||||
|
echo -e "${GREEN}[OK]${NC} No submodules"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${CYAN}Submodule Status:${NC}"
|
||||||
|
git submodule status | while IFS= read -r line; do
|
||||||
|
# Format: {status_char}{40_char_hash} {path} ({optional_description})
|
||||||
|
local status_char="${line:0:1}"
|
||||||
|
local hash_and_rest="${line:1}"
|
||||||
|
local path_and_desc="${hash_and_rest:41}" # Skip 40-char hash + 1 space
|
||||||
|
|
||||||
|
case "$status_char" in
|
||||||
|
"-") echo -e " ${YELLOW}[-]${NC} $path_and_desc (not initialized)" ;;
|
||||||
|
"+") echo -e " ${YELLOW}[+]${NC} $path_and_desc (modified/ahead)" ;;
|
||||||
|
"U") echo -e " ${RED}[U]${NC} $path_and_desc (merge conflict)" ;;
|
||||||
|
" ") echo -e " ${GREEN}[OK]${NC} $path_and_desc" ;;
|
||||||
|
*) echo -e " ${CYAN}[?]${NC} $path_and_desc (unknown status: $status_char)" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_submodule_fix() {
|
||||||
|
local target_path="$1"
|
||||||
|
|
||||||
|
echo -e "${CYAN}[INFO]${NC} Running submodule diagnostics..."
|
||||||
|
|
||||||
|
if [ ! -f ".gitmodules" ]; then
|
||||||
|
echo -e "${GREEN}[OK]${NC} No submodules"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local fixed=0
|
||||||
|
|
||||||
|
# Check for missing/uninitialized submodules
|
||||||
|
while read -r pkey ppath; do
|
||||||
|
[ -z "$ppath" ] && continue
|
||||||
|
|
||||||
|
if [ -n "$target_path" ] && [ "$ppath" != "$target_path" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$ppath/.git" ]; then
|
||||||
|
echo -e "${YELLOW}[FIX]${NC} Initializing missing submodule: $ppath"
|
||||||
|
cmd_submodule_init "$ppath"
|
||||||
|
fixed=1
|
||||||
|
else
|
||||||
|
# Check for detached HEAD
|
||||||
|
if ! git -C "$ppath" symbolic-ref HEAD >/dev/null 2>&1; then
|
||||||
|
echo -e "${YELLOW}[FIX]${NC} Reattaching detached HEAD in $ppath"
|
||||||
|
(
|
||||||
|
cd "$ppath"
|
||||||
|
git checkout main 2>/dev/null || git checkout master 2>/dev/null || true
|
||||||
|
)
|
||||||
|
fixed=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for uncommitted changes
|
||||||
|
if [ -n "$(git -C "$ppath" status --porcelain)" ]; then
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $ppath has uncommitted changes (not auto-fixed)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done < <(git config --file .gitmodules --get-regexp '^submodule\..*\.path$')
|
||||||
|
|
||||||
|
# Try update to resolve pointer mismatches
|
||||||
|
if cmd_submodule_update "$target_path" 2>/dev/null; then
|
||||||
|
fixed=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $fixed -eq 1 ]; then
|
||||||
|
echo -e "${GREEN}[OK]${NC} Fixed submodule issues"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}[OK]${NC} No issues found"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_status() {
|
||||||
|
local verbose="$1"
|
||||||
|
|
||||||
|
echo -e "${CYAN}Repository Status:${NC}"
|
||||||
|
git status --short
|
||||||
|
|
||||||
|
if [ "$verbose" = "--verbose" ]; then
|
||||||
|
echo ""
|
||||||
|
cmd_submodule_status
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_health() {
|
||||||
|
echo -e "${CYAN}Running health check...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
local issues=0
|
||||||
|
|
||||||
|
# Check identity
|
||||||
|
if [ "$USER_DISPLAY" = "unknown" ]; then
|
||||||
|
echo -e "${RED}[ERROR]${NC} identity.json unreadable"
|
||||||
|
issues=$((issues + 1))
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}[OK]${NC} Identity: $USER_DISPLAY <$USER_EMAIL>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check git config
|
||||||
|
local cfg_name=$(git config user.name || echo "")
|
||||||
|
local cfg_email=$(git config user.email || echo "")
|
||||||
|
|
||||||
|
if [ "$cfg_name" != "$USER_DISPLAY" ] || [ "$cfg_email" != "$USER_EMAIL" ]; then
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} Git config doesn't match identity.json"
|
||||||
|
issues=$((issues + 1))
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}[OK]${NC} Git identity matches"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for uncommitted changes
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
echo -e "${YELLOW}[INFO]${NC} Uncommitted changes present"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}[OK]${NC} Working tree clean"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check submodules
|
||||||
|
if [ -f ".gitmodules" ]; then
|
||||||
|
local sub_issues=0
|
||||||
|
while read -r line; do
|
||||||
|
local status="${line:0:1}"
|
||||||
|
case "$status" in
|
||||||
|
"-"|"U") sub_issues=$((sub_issues + 1)) ;;
|
||||||
|
esac
|
||||||
|
done < <(git submodule status 2>/dev/null)
|
||||||
|
|
||||||
|
if [ $sub_issues -gt 0 ]; then
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $sub_issues submodule(s) need attention"
|
||||||
|
issues=$((issues + 1))
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}[OK]${NC} All submodules healthy"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check remote connectivity
|
||||||
|
if git ls-remote origin HEAD >/dev/null 2>&1; then
|
||||||
|
echo -e "${GREEN}[OK]${NC} Remote reachable"
|
||||||
|
else
|
||||||
|
echo -e "${RED}[ERROR]${NC} Cannot reach remote"
|
||||||
|
issues=$((issues + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if [ $issues -eq 0 ]; then
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} Repository healthy"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $issues issue(s) found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_fetch() {
|
||||||
|
local recurse="$1"
|
||||||
|
|
||||||
|
echo -e "${CYAN}[INFO]${NC} Fetching from origin..."
|
||||||
|
|
||||||
|
if [ "$recurse" = "--recurse" ]; then
|
||||||
|
git fetch origin --recurse-submodules
|
||||||
|
else
|
||||||
|
git fetch origin --no-recurse-submodules
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}[OK]${NC} Fetch complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_pull() {
|
||||||
|
local recurse="$1"
|
||||||
|
|
||||||
|
echo -e "${CYAN}[INFO]${NC} Pulling from origin (rebase)..."
|
||||||
|
|
||||||
|
local flags="--rebase"
|
||||||
|
[ "$recurse" != "--recurse" ] && flags="$flags --no-recurse-submodules"
|
||||||
|
|
||||||
|
if git pull origin main $flags; then
|
||||||
|
echo -e "${GREEN}[OK]${NC} Pull successful"
|
||||||
|
|
||||||
|
if [ "$recurse" != "--recurse" ]; then
|
||||||
|
cmd_submodule_update
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${RED}[ERROR]${NC} Pull failed (likely conflicts)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_push() {
|
||||||
|
echo -e "${CYAN}[INFO]${NC} Pushing to origin..."
|
||||||
|
|
||||||
|
if git push origin main; then
|
||||||
|
echo -e "${GREEN}[OK]${NC} Push successful"
|
||||||
|
else
|
||||||
|
echo -e "${RED}[ERROR]${NC} Push failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_commit() {
|
||||||
|
local msg="$1"
|
||||||
|
|
||||||
|
if [ -z "$msg" ]; then
|
||||||
|
echo -e "${RED}[ERROR]${NC} Commit message required"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if git diff-index --quiet --cached HEAD -- 2>/dev/null; then
|
||||||
|
echo -e "${YELLOW}[INFO]${NC} No staged changes to commit"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
git commit -m "$msg"
|
||||||
|
echo -e "${GREEN}[OK]${NC} Committed"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_verify_identity() {
|
||||||
|
reconcile_git_identity "$USER_DISPLAY" "$USER_EMAIL"
|
||||||
|
echo -e "${GREEN}[OK]${NC} Identity verified and updated if needed"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_inject_creds() {
|
||||||
|
inject_credentials
|
||||||
|
echo -e "${GREEN}[OK]${NC} Credentials injected to submodules"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Main ---
|
||||||
|
|
||||||
|
COMMAND="${1:-}"
|
||||||
|
shift || true
|
||||||
|
|
||||||
|
# Change to repo root
|
||||||
|
REPO_ROOT=$(get_repo_root)
|
||||||
|
cd "$REPO_ROOT"
|
||||||
|
|
||||||
|
# Load identity
|
||||||
|
load_identity "$REPO_ROOT"
|
||||||
|
|
||||||
|
case "$COMMAND" in
|
||||||
|
submodule)
|
||||||
|
SUBCOMMAND="${1:-}"
|
||||||
|
shift || true
|
||||||
|
case "$SUBCOMMAND" in
|
||||||
|
init) cmd_submodule_init "$@" ;;
|
||||||
|
update) cmd_submodule_update "$@" ;;
|
||||||
|
sync) cmd_submodule_sync "$@" ;;
|
||||||
|
status) cmd_submodule_status ;;
|
||||||
|
fix) cmd_submodule_fix "$@" ;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 submodule {init|update|sync|status|fix} [PATH]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
status) cmd_status "$@" ;;
|
||||||
|
health) cmd_health ;;
|
||||||
|
fetch) cmd_fetch "$@" ;;
|
||||||
|
pull) cmd_pull "$@" ;;
|
||||||
|
push) cmd_push ;;
|
||||||
|
commit) cmd_commit "$@" ;;
|
||||||
|
verify-identity) cmd_verify_identity ;;
|
||||||
|
inject-creds) cmd_inject_creds ;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 {submodule|status|health|fetch|pull|push|commit|verify-identity|inject-creds}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Reference in New Issue
Block a user