InkdownInkdown
Start writing

Claude-Code

62 files·4 subfolders

Shared Workspace

Claude-Code
codex

15-git-internals

Shared from "Claude-Code" on Inkdown

Git Internals & Code Intelligence

Overview

Claude Code has deep Git integration - it tracks changes, generates diffs, attributes commits, and uses Git status for context. This system is critical for file operations and context understanding.

Plain text
┌─────────────────────────────────────────────────────────────────────────────┐
│                    GIT INTEGRATION ARCHITECTURE                            │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐  │
│  │                     GIT OPERATIONS LAYER                             │  │
│  │                                                                      │  │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐               │  │
│  │  │  Status  │  │   Diff   │  │   Show   │  │   Log    │               │  │
│  │  │          │  │          │  │          │  │          │               │  │
│  │  │ modified │  │ staged   │  │ commit   │  │ recent   │               │  │
│  │  │ untracked│  │ unstaged │  │ details  │  │ history  │               │  │
│  │  │ branch   │  │ hunk     │  │ files    │  │ blame    │               │  │
│  │  └──────────┘  └──────────┘  └──────────┘  └──────────┘               │  │
│  │                                                                      │  │
│  └─────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐  │
│  │                   ATTRIBUTION & TRACKING                             │  │
│  │                                                                      │  │
│  │  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐           │  │
│  │  │ File History │───►│   Attribution│───►│   Suggest    │           │  │
│  │  │   (who/when) │    │ (Claude edits)│    │   Reviewers  │           │  │
│  │  └──────────────┘    └──────────────┘    └──────────────┘           │  │
│  │                                                                      │  │
│  └─────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐  │
│  │                    SAFETY LAYER                                      │  │
│  │                                                                      │  │
│  │  Pre-edit hooks:                                                     │  │
│  │  - Stash uncommitted changes                                         │  │
│  │  - Snapshot file state                                               │  │
│  │  - Track in .claude/snapshots/                                      │  │
│  │                                                                      │  │
│  │  Post-edit hooks:                                                    │  │
│  │  - Check for conflicts                                               │  │
│  │  - Validate changes                                                  │  │
│  │  - Record attribution                                                │  │
│  │                                                                      │  │
│  └─────────────────────────────────────────────────────────────────────┘  │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
0000_start_here_index_and_recommended_reading_order.md
0100_project_overview_tech_stack_runtime_modes_and_folder_map.md
0200_startup_flow_entry_points_and_cold_start_sequence.md
0300_codebase_modules_layers_state_models_and_schemas.md
0400_system_architecture_and_design_rationale.md
0500_interactive_repl_request_flow_end_to_end.md
0600_headless_sdk_and_print_mode_request_flow_end_to_end.md
0700_mcp_integration_connection_and_tool_call_flow.md
0800_external_services_sdks_storage_and_local_dependencies.md
0900_environment_variables_settings_feature_flags_and_failure_modes.md
1000_non_obvious_patterns_gotchas_and_debugging_traps.md
1100_full_codebase_file_inventory_grouped_by_directory.md
kimi
00-overview.md
01-entrypoints.md
02-state-management.md
03-query-system.md
04-tools-system.md
05-tasks-system.md
06-ui-components.md
07-bridge-remote.md
08-services.md
09-skills-plugins.md
10-commands.md
11-testing-architecture.md
12-permission-system.md
13-build-system.md
14-ink-internals.md
15-git-internals.md
16-context-compaction.md
17-vim-mode.md
18-mailbox-notifications.md
19-session-persistence.md
20-hooks-system.md
21-error-recovery.md
README.md
qwen
00-overview.md
01-entry-points.md
02-query-engine.md
03-tools-and-tasks.md
04-commands-and-skills.md
05-state-management.md
06-ink-rendering.md
07-bridge-remote.md
08-mcp-services.md
09-services-overview.md
10-multi-agent.md
11-system-prompt-constants.md
12-tool-interface.md
13-memory-system.md
14-buddy-companion.md
15-keybindings.md
16-stop-hooks.md
17-vim-mode.md
18-upstreamproxy.md
19-cost-tracking-history.md
20-contexts-styles-onboarding.md
21-hooks.md
22-screens.md
tweets-explain
claude-code-memory-analysis.md
compact
memory-system
agentic-architecture

Core Files

FilePurpose
utils/git.tsCore Git operations
utils/git/parse.tsGit output parsing
utils/git/diff.tsDiff generation
utils/git/status.tsStatus parsing
utils/fileHistory.tsFile history tracking
utils/commitAttribution.tsCommit tracking
tools/shared/gitOperationTracking.tsPre/post edit hooks

Git Status

Status Parsing
TypeScript
// utils/git/status.ts
export type GitStatus = {
  branch: string
  ahead: number
  behind: number

  staged: FileChange[]
  unstaged: FileChange[]
  untracked: string[]

  isClean: boolean
}

export type FileChange = {
  path: string
  status: 'added' | 'modified' | 'deleted' | 'renamed'
  originalPath?: string  // For renames
}

export async function getGitStatus(cwd: string): Promise<GitStatus> {
  // porcelain = machine-readable format
  const result = await spawn('git', ['status', '--porcelain=v2', '-b'], { cwd })

  return parsePorcelainStatus(result.output)
}

function parsePorcelainStatus(output: string): GitStatus {
  const lines = output.split('\n')
  const status: GitStatus = {
    branch: 'main',
    ahead: 0,
    behind: 0,
    staged: [],
    unstaged: [],
    untracked: [],
    isClean: true,
  }

  for (const line of lines) {
    if (line.startsWith('# branch.head ')) {
      status.branch = line.slice('# branch.head '.length)
    }

    if (line.startsWith('# branch.ab ')) {
      const [, ahead, behind] = line.split(' ')
      status.ahead = parseInt(ahead.slice(1))  // +N
      status.behind = parseInt(behind.slice(1)) // -N
    }

    if (line.startsWith('1 ')) {
      // Ordinary change
      const [, xy, sub, m1, m2, mW, h1, h2, path] = line.split(' ')
      const [x, y] = xy.split('')

      if (x !== '.') {
        status.staged.push({ path, status: parseStatus(x) })
      }
      if (y !== '.') {
        status.unstaged.push({ path, status: parseStatus(y) })
      }
    }

    if (line.startsWith('? ')) {
      // Untracked
      status.untracked.push(line.slice(2))
    }
  }

  status.isClean =
    status.staged.length === 0 &&
    status.unstaged.length === 0 &&
    status.untracked.length === 0

  return status
}
Status in Context
TypeScript
// utils/queryContext.ts
export async function buildGitContext(cwd: string): Promise<string> {
  const status = await getGitStatus(cwd)

  if (status.isClean) {
    return `Git: On branch ${status.branch}, working tree clean`
  }

  let context = `Git: On branch ${status.branch}`

  if (status.staged.length > 0) {
    context += `\nStaged changes:\n`
    for (const file of status.staged) {
      context += `  ${file.status}: ${file.path}\n`
    }
  }

  if (status.unstaged.length > 0) {
    context += `\nUnstaged changes:\n`
    for (const file of status.unstaged) {
      context += `  ${file.status}: ${file.path}\n`
    }
  }

  return context
}

Diff Generation

Unified Diff Format
TypeScript
// utils/git/diff.ts
export async function generateDiff(options: DiffOptions): Promise<string> {
  const args = ['diff']

  if (options.staged) {
    args.push('--staged')
  }

  if (options.unstaged) {
    args.push()  // default
  }

  if (options.files) {
    args.push('--', ...options.files)
  }

  // Always use unified format with context
  args.push('-U3')  // 3 lines of context

  const result = await spawn('git', args, { cwd: options.cwd })
  return result.output
}
Diff Parsing
TypeScript
// Parse unified diff
export function parseDiff(diff: string): DiffHunk[] {
  const hunks: DiffHunk[] = []
  let currentHunk: DiffHunk | null = null
  let oldLine = 0
  let newLine = 0

  const lines = diff.split('\n')

  for (const line of lines) {
    // Hunk header: @@ -oldStart,oldCount +newStart,newCount @@
    if (line.startsWith('@@')) {
      const match = line.match(/@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@/)
      if (match) {
        oldLine = parseInt(match[1])
        newLine = parseInt(match[3])

        currentHunk = {
          oldStart: oldLine,
          newStart: newLine,
          lines: [],
        }
        hunks.push(currentHunk)
      }
      continue
    }

    if (!currentHunk) continue

    // Context line (space)
    if (line.startsWith(' ')) {
      currentHunk.lines.push({
        type: 'context',
        oldLine: oldLine++,
        newLine: newLine++,
        content: line.slice(1),
      })
    }

    // Deletion (-)
    if (line.startsWith('-')) {
      currentHunk.lines.push({
        type: 'delete',
        oldLine: oldLine++,
        content: line.slice(1),
      })
    }

    // Addition (+)
    if (line.startsWith('+')) {
      currentHunk.lines.push({
        type: 'add',
        newLine: newLine++,
        content: line.slice(1),
      })
    }
  }

  return hunks
}

File History

Tracking File Changes
TypeScript
// utils/fileHistory.ts
export type FileHistoryEntry = {
  commit: string
  author: string
  date: Date
  message: string
  changeType: 'add' | 'modify' | 'delete'
}

export async function getFileHistory(
  filePath: string,
  options: { limit?: number } = {}
): Promise<FileHistoryEntry[]> {
  const args = [
    'log',
    '--follow',  // Track renames
    '--format=%H|%an|%ai|%s',
    '--name-status',
    options.limit ? `-n${options.limit}` : '-n10',
    '--',
    filePath,
  ]

  const result = await spawn('git', args)
  return parseLogOutput(result.output)
}
File History State
TypeScript
// utils/fileHistory.ts
export type FileHistoryState = {
  // Path -> history entries
  history: Map<string, FileHistoryEntry[]>

  // Path -> last snapshot
  snapshots: Map<string, string>
}

export function createFileHistoryState(): FileHistoryState {
  return {
    history: new Map(),
    snapshots: new Map(),
  }
}

Commit Attribution

Tracking Claude's Edits
TypeScript
// utils/commitAttribution.ts
export type AttributionState = {
  // Files Claude has modified this session
  modifiedFiles: Set<string>

  // Original content of modified files (for undo)
  originalContent: Map<string, string>

  // Suggested commit message based on changes
  suggestedMessage?: string
}

export function createEmptyAttributionState(): AttributionState {
  return {
    modifiedFiles: new Set(),
    originalContent: new Map(),
  }
}

// Called before each edit
export function recordAttribution(
  state: AttributionState,
  filePath: string,
  originalContent: string
): AttributionState {
  return {
    ...state,
    modifiedFiles: new Set([...state.modifiedFiles, filePath]),
    originalContent: new Map([
      ...state.originalContent,
      [filePath, originalContent],
    ]),
  }
}
Pre/Post Edit Hooks
TypeScript
// tools/shared/gitOperationTracking.ts
export async function executeWithGitTracking<T>(
  operation: () => Promise<T>,
  options: GitTrackingOptions
): Promise<T> {
  // 1. Pre-operation: snapshot state
  const preState = await snapshotState(options.files)

  // 2. Execute operation
  const result = await operation()

  // 3. Post-operation: check what changed
  const postState = await snapshotState(options.files)

  // 4. Record attribution
  for (const file of options.files) {
    if (preState[file] !== postState[file]) {
      recordAttribution(file, preState[file])
    }
  }

  // 5. Check for conflicts
  await checkForConflicts(options.files)

  return result
}

Safety Snapshots

Creating Snapshots
TypeScript
// utils/snapshots.ts
export async function createSnapshot(
  files: string[],
  description: string
): Promise<SnapshotId> {
  const snapshotId = generateSnapshotId()
  const snapshotDir = join(SNAPSHOTS_DIR, snapshotId)

  await mkdir(snapshotDir, { recursive: true })

  for (const file of files) {
    const content = await readFile(file, 'utf-8')
    const relativePath = relative(process.cwd(), file)
    const snapshotPath = join(snapshotDir, relativePath)

    await mkdir(dirname(snapshotPath), { recursive: true })
    await writeFile(snapshotPath, content)
  }

  // Write metadata
  await writeFile(
    join(snapshotDir, 'meta.json'),
    JSON.stringify({
      created: Date.now(),
      description,
      files,
    })
  )

  return snapshotId
}
Restoring Snapshots
TypeScript
export async function restoreSnapshot(snapshotId: SnapshotId): Promise<void> {
  const snapshotDir = join(SNAPSHOTS_DIR, snapshotId)
  const metaPath = join(snapshotDir, 'meta.json')

  const meta = JSON.parse(await readFile(metaPath, 'utf-8'))

  for (const file of meta.files) {
    const relativePath = relative(process.cwd(), file)
    const snapshotPath = join(snapshotDir, relativePath)
    const content = await readFile(snapshotPath, 'utf-8')

    await writeFile(file, content)
  }
}

Rewind (Undo) System

Plain text
┌─────────────────────────────────────────────────────────────────┐
│                    REWIND SYSTEM                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  User: "/rewind" or Ctrl+Z                                     │
│        │                                                        │
│        ▼                                                        │
│  ┌─────────────┐                                                │
│  │ Show        │  Display recent operations                      │
│  │ operations  │  1. Edit src/index.ts                         │
│  │             │  2. Create src/utils.ts                       │
│  │             │  3. Bash: npm install                         │
│  └──────┬──────┘                                                │
│         │                                                        │
│         ▼                                                        │
│  ┌─────────────┐                                                │
│  │ User picks  │  "Undo operation #1"                           │
│  │ operation   │                                                │
│  └──────┬──────┘                                                │
│         │                                                        │
│         ▼                                                        │
│  ┌─────────────┐                                                │
│  │ Restore     │  - Git checkout -- file                        │
│  │ snapshot    │  - Or apply reverse diff                       │
│  │             │                                                │
│  └──────┬──────┘                                                │
│         │                                                        │
│         ▼                                                        │
│  ┌─────────────┐                                                │
│  │ Update      │  Remove from attribution state                  │
│  │ state       │                                                │
│  └─────────────┘                                                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Git Context in Queries

TypeScript
// How git status affects query context
export async function buildSystemContext(
  cwd: string,
  options: ContextOptions
): Promise<string> {
  const parts: string[] = []

  // Git status (if in repo)
  if (await isGitRepo(cwd)) {
    const status = await getGitStatus(cwd)
    parts.push(formatGitStatus(status))

    // Recent commits
    const recentCommits = await getRecentCommits(5)
    parts.push(`\nRecent commits:\n${recentCommits.join('\n')}`)

    // Changed files context
    if (!status.isClean) {
      const diff = await generateDiff({ cwd, staged: false })
      parts.push(`\nUnstaged changes:\n${truncate(diff, 2000)}`)
    }
  }

  return parts.join('\n')
}

Key Concepts

  1. Porcelain Format: Machine-readable Git output (--porcelain)
  2. Attribution: Track which files Claude modified (for /commit)
  3. Snapshots: Pre-edit backups for /rewind
  4. Unified Diff: Standard format with 3 lines context
  5. Safety First: Check Git status before dangerous operations

Debugging Git

Bash
# See what Git info Claude has
claude /git status

# Debug Git integration
CLAUDE_CODE_DEBUG_GIT=1 claude

# Check attribution state
claude /config attribution