A complete guide to version control, branching strategies, team collaboration, inbox processing, and the folder structures that hold it all together — from your first commit to production deployment.
Git is a version control system. It tracks every change you make to your files and lets you go back to any previous version. Think of it as an infinite undo history that also lets multiple people work on the same project simultaneously without overwriting each other's work.
When combined with GSD and Skill Creator, Git becomes the backbone of the entire system. GSD makes atomic commits after every completed task. Skill Creator's post-commit hooks observe those commits to detect patterns. The DACP system uses Git state to determine handoff context. Without Git, none of this works.
If you're coming to Git for the first time, the next section will get you up to speed. If you already know Git, skip to section 03.
Git has three areas that files move between. Understanding these three areas is the single most important concept in Git:
Working Directory Staging Area Repository (your actual files) (what's about to (permanent history) be committed) You edit files here ──→ git add ──→ git commit ──→ Saved forever (pick what (snapshot with to include) a message)
Working directory is just your files on disk. Edit them however you like.
Staging area (also called "index") is where you assemble what you want to save. You pick and choose which changes to include. This is powerful — if you changed 5 files but only want to save 3 of them right now, you stage those 3.
Repository is the permanent record. Once committed, a change is (almost) impossible to lose.
# Create a new git repository in the current folder
git init
# See what's changed since the last commit
git status
# Stage specific files for the next commit
git add src/app.ts src/types.ts
# Stage everything that's changed
git add .
# Commit the staged changes with a message
git commit -m "feat(auth): add login form with email validation"
# See the history of commits
git log --oneline
# See what changed in a file
git diff src/app.ts
When you run git status, Git tells you exactly what's going on:
# Untracked files — Git has never seen these before
Untracked files:
src/new-feature.ts
# Modified — changed since last commit, but not staged yet
Changes not staged for commit:
modified: src/app.ts
# Staged — ready to be committed
Changes to be committed:
new file: src/types.ts
modified: src/utils.ts
# Unstage a file (keep the changes, just remove from staging)
git restore --staged src/app.ts
# Discard changes to a file (go back to last committed version)
git restore src/app.ts
# Go back to a previous commit (look at it, don't change anything)
git log --oneline # find the commit hash
git checkout abc1234 # visit that point in time
git checkout main # come back to the present
A branch is a parallel version of your project. You create a branch, make changes on it, and when you're satisfied, merge those changes back into the main line. The original branch is untouched until you decide to integrate.
Without branches, every change you make affects everyone immediately. If you're building a new feature and it's half-finished, the whole project is in a broken state. Branches let you work in isolation — you can break things, experiment, and iterate without affecting anyone else (or your own stable codebase).
# Create a new branch and switch to it
git checkout -b feature/user-dashboard
# This is shorthand for two commands:
git branch feature/user-dashboard # create it
git checkout feature/user-dashboard # switch to it
# List all branches (* marks the current one)
git branch
* feature/user-dashboard
main
# Switch back to main
git checkout main
Good branch names tell you what the branch is for at a glance:
| Prefix | Purpose | Example |
|---|---|---|
feature/ | New functionality | feature/user-dashboard |
fix/ | Bug fixes | fix/login-redirect-loop |
hotfix/ | Urgent production fixes | hotfix/payment-crash |
chore/ | Maintenance, cleanup | chore/update-dependencies |
docs/ | Documentation | docs/api-reference |
refactor/ | Code restructuring | refactor/extract-auth-module |
When your feature is done, you bring those changes back into main:
# 1. Switch to the branch you want to merge INTO
git checkout main
# 2. Merge the feature branch into main
git merge feature/user-dashboard
# 3. Delete the branch (it's been merged, you don't need it)
git branch -d feature/user-dashboard
If nobody else changed main while you were working, Git does a "fast-forward merge" — it just moves the pointer ahead. No merge commit needed. If main did change, Git creates a merge commit that combines both histories.
A merge conflict happens when two branches changed the same lines in the same file. Git can't automatically decide whose version to keep, so it asks you.
When Git finds a conflict during a merge, it marks the file with conflict markers:
function getGreeting(name: string) {
<<<<<<< HEAD
return `Hello, ${name}! Welcome back.`;
=======
return `Hey ${name}, good to see you!`;
>>>>>>> feature/casual-greetings
}
The file now has three sections:
<<<<<<< HEAD — your current branch's version======= — the dividing line>>>>>>> feature/casual-greetings — the incoming branch's versionYou resolve a conflict by editing the file to be exactly what you want. Remove the conflict markers and choose (or combine) the code:
// Option A: Keep your version
function getGreeting(name: string) {
return `Hello, ${name}! Welcome back.`;
}
// Option B: Keep theirs
function getGreeting(name: string) {
return `Hey ${name}, good to see you!`;
}
// Option C: Combine both (this is the most common choice)
function getGreeting(name: string, casual = false) {
return casual
? `Hey ${name}, good to see you!`
: `Hello, ${name}! Welcome back.`;
}
After editing, tell Git the conflict is resolved:
git add src/greeting.ts # mark as resolved
git commit # complete the merge
If a merge gets too messy and you want to start over:
git merge --abort # go back to before the merge started
Tags mark specific points in your history as important — usually releases. While branches move forward as you add commits, tags are permanent bookmarks.
The standard format is MAJOR.MINOR.PATCH:
| Part | When to Increment | Example |
|---|---|---|
| MAJOR | Breaking changes that require users to update their code | 1.0.0 → 2.0.0 |
| MINOR | New features that are backward-compatible | 1.0.0 → 1.1.0 |
| PATCH | Bug fixes, no new features | 1.1.0 → 1.1.1 |
# Create an annotated tag (recommended — includes message and author)
git tag -a v1.2.0 -m "Release 1.2.0: add user dashboard and API caching"
# List all tags
git tag
# See details about a specific tag
git show v1.2.0
# Check out a specific release
git checkout v1.2.0
Skill Creator uses this exact convention — v1.49.6 means major version 1, minor version 49, patch 6. The CHANGELOG maps each version to its phase count, test count, and shipped features.
A worktree lets you have multiple branches checked out simultaneously in different directories, all sharing the same repository. Instead of switching branches (which replaces all your files), you just open a different folder.
Switching branches is disruptive. Your editor reloads. Your dev server restarts. Your build cache invalidates. Worktrees avoid all of this — each branch lives in its own directory, running independently.
# Your main working directory is already on 'main'
# Create a worktree for a feature branch in a sibling directory
git worktree add ../my-project-feature feature/user-dashboard
# Now you have:
# ~/projects/my-project/ ← main branch
# ~/projects/my-project-feature/ ← feature branch
# Both are the same repository — commits in either appear in both
# List all worktrees
git worktree list
# When done, remove the worktree
git worktree remove ../my-project-feature
Worktrees are especially powerful with GSD because each worktree can run its own GSD session. You can have one agent executing Phase 12 in the main worktree while another agent runs tests on Phase 11's output in a feature worktree. The sessions don't interfere because they're in different directories.
Skill Creator v1.38 formalized this as per-agent worktree isolation: each agent type (exec, verify, scout, main) gets its own worktree with a scoped sandbox profile. Cross-agent read access is denied by default, with only the INTEG (integration) role having read-only access across all worktrees. This prevents a compromised agent from accessing credentials or modifying another agent's work.
For projects with deployment stages, a multi-environment branching strategy keeps unstable code away from users:
feature/* ──→ dev ──→ alpha ──→ beta ──→ main (prod) (wild west) (integration) (internal (external (stable, testing) testing) deployed)
| Branch | Who Uses It | Stability | Merges From |
|---|---|---|---|
feature/* | Individual developers | May be broken | Created from dev |
dev | All developers | Compiles, tests pass | Merged features |
alpha | Internal team | Feature-complete, may have bugs | Promoted from dev |
beta | External testers | Release candidate, minor bugs | Promoted from alpha |
main | End users | Production-ready | Promoted from beta |
# Developer finishes a feature
git checkout dev
git merge feature/new-widget
git branch -d feature/new-widget
# When dev is stable enough for internal testing
git checkout alpha
git merge dev
git tag -a v2.1.0-alpha.1 -m "Alpha 1: widget + dashboard"
# After internal testing passes
git checkout beta
git merge alpha
git tag -a v2.1.0-beta.1 -m "Beta 1: tested internally"
# After beta feedback is addressed
git checkout main
git merge beta
git tag -a v2.1.0 -m "Release 2.1.0"
When a critical bug hits production, you branch directly from main, fix it, merge it back, then forward-merge the fix through beta, alpha, and dev so everyone has it:
git checkout -b hotfix/payment-crash main
# ... fix the bug ...
git checkout main && git merge hotfix/payment-crash
git checkout beta && git merge main
git checkout alpha && git merge main
git checkout dev && git merge main
git branch -d hotfix/payment-crash
main + feature/*. A small team might add dev. The full pipeline is for projects with external users and deployment infrastructure.
GSD doesn't just use Git for storage — Git is part of the execution model. Here's what happens behind the scenes:
After every completed task within a plan, GSD creates a git commit. Not after every phase, not after every milestone — after every task. This gives you an extremely fine-grained history. If task 3 of 5 introduced a regression, you can identify exactly which commit caused it and revert just that change.
Commits follow the Conventional Commits format, enforced by a git hook:
feat(auth): add email validation to login form
fix(api): handle null response from user service
test(dashboard): add integration tests for chart rendering
docs(readme): update installation instructions
The format is type(scope): description, where type is one of: feat, fix, docs, style, refactor, perf, test, build, ci, chore. The validate-commit hook blocks any commit that doesn't match this pattern.
Skill Creator includes a git-state-check script that produces a JSON report of the repository's state at any moment. It detects six states in priority order: CONFLICT (unresolved merge conflicts), MERGING (mid-merge), REBASING (mid-rebase), DETACHED (not on a branch), DIRTY (uncommitted changes), and CLEAN. The report includes branch name, remote tracking info, ahead/behind counts, and lists of staged, unstaged, and untracked files.
This state report feeds into the session hooks and pattern detection system. If you start a GSD session with dirty files, the system knows. If you're mid-rebase, it warns you before starting execution.
On every session start, a hook reads .planning/STATE.md and injects it into the context. This is how GSD remembers where it left off — the hook feeds the agent the current phase, any blockers, and the project configuration. A post-commit hook observes every commit and appends it to sessions.jsonl for pattern analysis.
As of v1.49.5, the repository is organized into zones following Linux FHS conventions. Understanding this layout helps you navigate projects and know where things belong.
| Zone | Path | Purpose |
|---|---|---|
| projects/ | projects/ | Your GSD projects. Each subdirectory is an independent project with its own .planning/ state. External projects can be linked via .sc-config.json. |
| contrib/ | contrib/ | Collaboration zone. upstream/ for PRs you send to other repos. downstream/staging/ for PRs you receive. publishing/ for extracted side projects. |
| packs/ | packs/ | Catalog of educational and domain packs. References source directories in src/. |
| www/ | www/ | Web staging. site/ for generated output, tools/ for web tool sources, staging/ for pre-publish review. |
Every GSD project has a .planning/ directory at its root. This is the state machine:
.planning/ ├── STATE.md # Current position, blockers, context ├── ROADMAP.md # Phase completion status ├── REQUIREMENTS.md # What we're building ├── PROJECT.md # Project definition ├── config.json # GSD mode and settings ├── staging/ # Inbox processing pipeline │ ├── inbox/ # New documents land here │ ├── checking/ # Being processed │ ├── attention/ # Needs human review │ ├── ready/ # Cleared for processing │ ├── aside/ # Deferred │ └── queue.jsonl # Processing queue ├── patterns/ # Observation JSONL logs ├── bus/ # Filesystem message bus ├── security/ # Security profiles and audit └── phases/ # Per-phase plans, summaries, verification ├── phase-001-PLAN.md ├── phase-001-SUMMARY.md ├── phase-001-VERIFICATION.md └── ...
Skills live in two locations, with project-level overriding user-level:
| Scope | Path | Visibility |
|---|---|---|
| User | ~/.claude/skills/ | All your projects |
| Project | .claude/skills/ | This project only |
You don't have to keep all projects inside the Skill Creator repo. The .sc-config.json (gitignored, user-local) lets you reference external directories:
{
"home": "projects",
"external_projects": [
{ "name": "my-saas-app", "path": "/home/user/code/my-saas-app" },
{ "name": "client-site", "path": "/home/user/code/client-site" }
]
}
The sc project list command shows both local and external projects.
The staging pipeline is a document intake system that ensures every piece of input — vision documents, requirements, skill proposals, feature requests — goes through hygiene checking and clarity assessment before entering the build queue.
| State | What Happens |
|---|---|
| inbox | Document arrives. A companion .meta.json file is created alongside it with the submission timestamp, source, and status. Nothing has been checked yet. |
| checking | The hygiene scanner runs. It looks for YAML code execution risks, path traversal patterns, zip extraction dangers, and other security concerns. Content is classified by familiarity (trusted/known/stranger). A hygiene report is generated with an overall risk level. |
| attention | If the hygiene scan flags critical or warning-level risks, the document is moved here and the flow pauses. A human must review the findings before the document can proceed. This is a hard gate — no automatic bypass. |
| ready | The document passed hygiene and clarity assessment. It's been confirmed by the user (the "anything else?" prompt) and is queued for processing. |
| aside | The user deferred this document. It can be resubmitted to inbox later via the aside → inbox transition. |
Not every transition is allowed. The state machine enforces a specific graph:
inbox ──→ checking ──→ attention ──→ ready
│ │ ╲ │ ╲ │
│ │ ╲ │ ╲ │
╰──→ aside ←─╯ ╰──→ aside ╰──→ aside
│ ↑ ↑
╰──→ inbox │ │
(resubmit) ready ──→ checking
(re-check)
Key rules: documents can be set aside from any state. Aside documents can only go back to inbox (start over). Ready documents can be re-checked if something changes. Attention documents can be approved (go to ready), re-checked, or deferred.
After passing hygiene, documents go through a clarity assessment that routes to one of three paths:
The intake flow tracks every step in the metadata file. If the process crashes mid-assessment, resumeIntakeFlow() reads the last completed step and continues from there. You never lose progress through the pipeline.
Even as a solo developer, a structured workflow prevents chaos. Here's the recommended approach:
# 1. Start from main, create a feature branch
git checkout main
git checkout -b feature/notifications
# 2. Initialize a GSD project (if new) or start a new milestone
/gsd:new-project # or /gsd:new-milestone
/gsd:discuss-phase # talk through what you want
/gsd:plan-phase # generate the plan
# 3. Execute — GSD commits atomically after each task
/gsd:execute-phase
# 4. Verify the work
/gsd:verify-work
# 5. When the feature is complete, merge back to main
git checkout main
git merge feature/notifications
git tag -a v1.3.0 -m "Add notification system"
git branch -d feature/notifications
The feature branch protects your main branch. If the feature goes sideways, you can abandon the branch entirely. If it works, you get a clean merge with a complete history of atomic commits.
When multiple people work together, Git's distributed nature becomes essential. Each person has a complete copy of the repository. They push changes to a shared location (a remote) and pull other people's changes down.
# Clone the team repository
git clone https://github.com/team/project.git
# Or if using a shared server (see next section)
git clone ssh://server/repos/project.git
# Push your branch to the remote
git push origin feature/my-feature
# Pull the latest changes from main
git checkout main
git pull origin main
# Keep your feature branch up to date with main
git checkout feature/my-feature
git merge main # or: git rebase main
When a team uses GSD, each developer runs their own GSD sessions on their own feature branches. The .planning/ directory tracks per-person state. The key coordination points:
.claude/skills/) are committed to Git and shared. When one developer creates a useful skill, everyone benefits on the next pull.dev, another team member reviews the changes. GSD's SUMMARY.md and VERIFICATION.md files make reviews faster — the reviewer reads what the agent built and verified instead of reading every line of code.The contrib/ directory is designed for team collaboration: upstream/ holds PRs you're preparing to send to shared repos, downstream/staging/ holds incoming PRs to review, and publishing/ holds side projects extracted for independent release. sc contrib status shows the count in each direction.
Git hooks are scripts that run automatically at specific points in the Git workflow. Skill Creator ships with several:
Runs before every commit is accepted. It parses the commit message and rejects anything that doesn't follow Conventional Commits format. The regex check enforces: valid type prefix, optional scope in parentheses, colon-space separator, subject line under 72 characters. This hook is POSIX-compatible (works on macOS Bash 3.2).
Runs on every session start. It reads .planning/STATE.md and outputs the first 20 lines as a project state reminder. If no .planning/ exists, it suggests running /gsd:new-project. It also reads the project config to display the current mode.
Runs after every successful commit. It appends commit metadata to sessions.jsonl — timestamp, hash, message, files changed. This powers the pattern detection system and the historical trends dashboard (commit type distribution, velocity curves, file hotspots).
Prevents accidental cross-phase work by checking that the current git state aligns with the active phase in .planning/. If you're supposed to be in Phase 12 but the commit references Phase 11 files, the hook warns you.
# GSD installs its hooks automatically during setup
npx get-shit-done-cc@latest # includes hook installation
# Or manually copy hooks into your project
cp project-claude/hooks/validate-commit.sh .claude/hooks/
cp project-claude/hooks/session-state.sh .claude/hooks/
cp project-claude/hooks/post-commit .git/hooks/
v1.38 introduced per-agent worktree isolation as a security boundary. This is relevant for teams running multiple AI agents simultaneously on the same repository.
Each agent type gets its own git worktree with a scoped sandbox profile:
| Agent Type | Worktree | Access |
|---|---|---|
| exec | Own worktree, own branch | Read/write own files only |
| verify | Own worktree, read-only checkout | Read everything, write nothing |
| scout | Own worktree, ephemeral | Read own, no cross-agent access |
| main | Primary worktree | Full access |
| integ | Integration worktree | Read-only across all agent worktrees |
Cross-agent read denial is verified programmatically — an exec agent literally cannot see the verify agent's files. The WorktreeCreate hook auto-generates a sandbox profile (bubblewrap on Linux, Seatbelt on macOS) for each new worktree, ensuring isolation is enforced at the OS level, not just at the application level.
This matters because a compromised or misbehaving agent can't access credentials, modify another agent's work, or exfiltrate data from a neighboring worktree. The isolation is structural — built into the filesystem and enforced by the operating system's process sandbox.
mkdir my-project && cd my-project
git init
# open Claude Code with GSD installed
/gsd:new-project
git add . && git commit -m "chore: initialize project with GSD"
/gsd:pause-work # creates handoff document
git add . && git commit -m "chore: pause work at phase 12 task 3"
# ... days later ...
/gsd:resume-work # reads handoff, continues exactly where you left off
# Feature A in main directory
git checkout -b feature/auth
# Feature B in a separate worktree
git worktree add ../my-project-api feature/api-cache
# Work on either by cd-ing into the right directory
cd ~/projects/my-project/ # auth feature
cd ~/projects/my-project-api/ # api cache feature
# Each can run its own GSD session independently
# Find the commit before the bad phase started
git log --oneline
# Revert all commits from the bad phase
git revert HEAD~5..HEAD # reverts the last 5 commits
# Or, nuclear option: reset to before the phase (destroys history)
git reset --hard abc1234
# ⚠️ Only do this on your own branch, never on shared branches
# Set up a bare repo on NFS
ssh nas "mkdir -p /share/git/project.git && cd /share/git/project.git && git init --bare"
# Each team member
git clone ssh://nas/share/git/project.git
# ... work, commit ...
git push origin feature/my-work
git pull origin main
# Morning: pull latest, update your branch
git checkout main && git pull
git checkout feature/my-work && git merge main
# Work: GSD executes, commits atomically
/gsd:resume-work
# End of day: push your branch
git push origin feature/my-work
# When feature is done: merge to dev, delete branch
git checkout dev && git merge feature/my-work
git push origin dev
git branch -d feature/my-work
| Task | Command |
|---|---|
| Create repo | git init |
| Clone repo | git clone <url> |
| See status | git status |
| See history | git log --oneline |
| Stage files | git add <files> |
| Commit | git commit -m "msg" |
| New branch | git checkout -b name |
| Switch branch | git checkout name |
| Merge | git merge branch |
| Push | git push origin branch |
| Pull | git pull origin branch |
| Tag a release | git tag -a v1.0.0 -m "msg" |
| List branches | git branch |
| Delete branch | git branch -d name |
| Create worktree | git worktree add path branch |
| List worktrees | git worktree list |
| Abort merge | git merge --abort |
| See diff | git diff |
| Stash changes | git stash / git stash pop |