{"record":{"id":"4bcb9fccdf997dbd","repo":"xai-org/grok-build","slug":"working-tree-is-not-clean-commit-before-merging-t","errorCode":null,"errorMessage":"working tree is not clean; commit before merging to '{target_branch}'","messagePattern":"working tree is not clean; commit before merging to '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-workspace/src/session/git.rs","lineNumber":3242,"sourceCode":"}\n/// `MergeToMain` (`workspace.git_merge_to_main`): merge the conversation branch\n/// into its target. Merge, never rebase; never force. On conflicts the merge\n/// is aborted and HEAD is restored to `conv_branch` (never leave `MERGE_HEAD`\n/// on the integration branch).\n///\n/// Fetch `origin/<target>` *before* checkout, then fast-forward the local\n/// target so the deployed SHA can never be behind the durable remote tip; a\n/// target that has *diverged* from origin is an error (never a force).\npub async fn merge_to_main(\n    git_root: &Path,\n    conv_branch: &str,\n    target_branch: &str,\n    push: bool,\n) -> Result<GitMergeToMainResult> {\n    ensure_ref_arg_safe(conv_branch, \"session_branch\")?;\n    ensure_ref_arg_safe(target_branch, \"target_branch\")?;\n    let dirty = git_cli(git_root, &[\"status\", \"--porcelain\"]).await?;\n    anyhow::ensure!(\n        dirty.is_empty(),\n        \"working tree is not clean; commit before merging to '{target_branch}'\"\n    );\n    let (fetched, _) = git_cli_raw(\n        git_root,\n        &[\"fetch\", \"origin\", \"--end-of-options\", target_branch],\n    )\n    .await?;\n    checkout_branch(git_root, target_branch, false).await?;\n    if fetched\n        && !git_cli_raw(\n            git_root,\n            &[\"merge-base\", \"--is-ancestor\", \"FETCH_HEAD\", \"HEAD\"],\n        )\n        .await?\n        .0\n    {\n        let (ff, ff_out) = git_cli_raw(git_root, &[\"merge\", \"--ff-only\", \"FETCH_HEAD\"]).await?;","sourceCodeStart":3224,"sourceCodeEnd":3260,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-workspace/src/session/git.rs#L3224-L3260","documentation":"The merge_to_main flow requires a clean working tree before it merges the session branch into the target branch. Before merging it runs `git status --porcelain` and, if any output is present (uncommitted or untracked changes), fails with this error so the merge is never performed on a dirty tree.","triggerScenarios":"Calling the merge-to-target operation (crates/codegen/xai-grok-workspace/src/session/git.rs:3242) while the git_root worktree has modified tracked files, staged-but-uncommitted changes, or untracked files — anything `git status --porcelain` reports.","commonSituations":"An agent/session left generated files or edits in the workspace without committing; the developer forgot to commit before triggering the merge; a tool (formatter, codegen step) wrote files into the repo between the last commit and the merge; .gitignore is missing so build artifacts show as untracked.","solutions":["Commit or stash all changes in the working tree (git add -A && git commit, or git stash), then re-run the operation.","Remove or gitignore untracked build/generated artifacts so `git status --porcelain` is empty.","If the changes belong to the session, commit them on the session branch first — that is what the flow expects.","Run git status --porcelain yourself beforehand and surface a friendly prompt to the user to commit."],"exampleFix":"// before\ngit_ops.merge_to_main(&root, \"session/abc\", \"main\", false).await?; // fails if dirty\n// after\nlet dirty = Command::new(\"git\").args([\"status\", \"--porcelain\"]).output()?;\nif !dirty.stdout.is_empty() {\n    Command::new(\"git\").args([\"add\", \"-A\"]).status()?;\n    Command::new(\"git\").args([\"commit\", \"-m\", \"session work\"]).status()?;\n}\ngit_ops.merge_to_main(&root, \"session/abc\", \"main\", false).await?;","handlingStrategy":"try-catch","validationCode":"let status = Command::new(\"git\").args([\"status\", \"--porcelain\"]).current_dir(&root).output()?;\nanyhow::ensure!(status.stdout.is_empty(), \"commit or stash changes before merging\");","typeGuard":null,"tryCatchPattern":"match git_ops.merge_to_main(&root, &branch, \"main\", false).await {\n    Err(e) if e.to_string().contains(\"working tree is not clean\") => {\n        // prompt user to commit/stash, then retry once\n        prompt_commit_and_retry()?;\n    }\n    other => other?,\n}","preventionTips":["Run git status --porcelain before triggering merges and surface a friendly prompt.","Commit session work on the session branch as part of the workflow, not at merge time.","Add .gitignore entries for generated artifacts so the tree stays clean.","Snapshot or stash automatically before starting long-running sessions."],"tags":["git","working-tree","precondition"],"backgroundTag":"dirty-working-tree","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}