{"record":{"id":"39cc75fda376ae33","repo":"xai-org/grok-build","slug":"working-tree-is-dirty-commit-changes-first-or-us","errorCode":null,"errorMessage":"Working tree is dirty. Commit changes first, or use --force.","messagePattern":"Working tree is dirty\\. Commit changes first, or use --force\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-pager/src/plugin_cmd.rs","lineNumber":742,"sourceCode":"        Ok(ManifestLoadResult::NotFound) => bail!(\"No plugin.json found in {path}.\"),\n        Err(e) => bail!(\"Failed to load manifest: {e}\"),\n    };\n\n    let tag = format!(\n        \"v{}\",\n        version\n            .strip_prefix('v')\n            .or_else(|| version.strip_prefix('V'))\n            .unwrap_or(&version)\n    );\n\n    if !force {\n        let out = std::process::Command::new(\"git\")\n            .args([\"status\", \"--porcelain\"])\n            .current_dir(&root)\n            .output()?;\n        if !out.stdout.is_empty() {\n            bail!(\"Working tree is dirty. Commit changes first, or use --force.\");\n        }\n    }\n\n    if dry_run {\n        println!(\"Would create tag: {tag}\");\n        if push {\n            println!(\"Would push tag to remote.\");\n        }\n        return Ok(());\n    }\n\n    let mut cmd = std::process::Command::new(\"git\");\n    cmd.args([\"tag\", &tag]);\n    if force {\n        cmd.arg(\"--force\");\n    }\n    let out = cmd.current_dir(&root).output()?;\n    if !out.status.success() {","sourceCodeStart":724,"sourceCodeEnd":760,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-pager/src/plugin_cmd.rs#L724-L760","documentation":"Before creating a tag, `grok plugin tag` runs `git status --porcelain` in the plugin directory; if output is non-empty the working tree is dirty and the command bails, unless --force is passed. This prevents tagging uncommitted state.","triggerScenarios":"Running `grok plugin tag <dir>` without --force while the git repo in <dir> has uncommitted or untracked changes.","commonSituations":"Edited plugin files but forgot to commit; untracked generated files showing in porcelain; mid-development tagging attempt.","solutions":["Commit or stash your changes, then re-run the tag command","Re-run with `--force` to tag anyway","Add untracked generated files to .gitignore so porcelain output is clean"],"exampleFix":"// before\ngrok plugin tag .\n// after (either)\ngit add -A && git commit -m \"release 1.0.0\"\ngrok plugin tag .\n// or\n grok plugin tag . --force","handlingStrategy":"validation","validationCode":"fn worktree_clean(root: &std::path::Path) -> bool {\n    std::process::Command::new(\"git\")\n        .args([\"status\", \"--porcelain\"])\n        .current_dir(root)\n        .output()\n        .map(|o| o.stdout.is_empty())\n        .unwrap_or(false)\n}\n// if !worktree_clean(&root) { commit/stash or pass --force }","typeGuard":"fn is_clean_repo(root: &std::path::Path) -> bool {\n    root.join(\".git\").exists()\n        && std::process::Command::new(\"git\")\n            .args([\"status\", \"--porcelain\"])\n            .current_dir(root)\n            .output()\n            .map(|o| o.status.success() && o.stdout.is_empty())\n            .unwrap_or(false)\n}","tryCatchPattern":"match cmd_tag(dir, false, false, false) {\n    Err(e) if e.to_string().contains(\"Working tree is dirty\") => {\n        eprintln!(\"Commit changes or re-run with --force\");\n    }\n    Ok(_) => {}\n    Err(e) => return Err(e),\n}","preventionTips":["Commit or stash before tagging; never tag uncommitted work","Add generated files to .gitignore so status stays clean","Run `git status --porcelain` as a preflight in release scripts"],"tags":["git","dirty-worktree","plugin","cli"],"backgroundTag":"git-dirty-working-tree","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}