"Move to cloud" falsely reports uncommitted/unpushed changes on a clean, fully pushed repo (Commit & push loops)

Open 💬 0 comments Opened Jul 13, 2026 by jnankaniblu

Summary

The "Move to cloud" dialog in the Claude Code desktop app persistently reports "Your branch has uncommitted or unpushed changes. Ask Claude to commit and push them, then try again." on a repository that is provably clean and fully pushed by every check real git offers. The dialog's "Commit & push" button sends the canned prompt ("Commit my uncommitted changes with a sensible message and push the current branch to the remote") to the session, which then finds nothing to commit — an infinite loop with no way to complete the move.

Environment

  • Claude desktop app: 1.20186.1 (macOS)
  • macOS 14.4.1 (23E224), Apple Silicon (arm64)
  • Repo: private GitHub repo, HTTPS remote, single working tree, no submodules
  • gh authenticated; Claude GitHub App installed with All repositories access

Expected

With a clean working tree and the current branch in sync with its upstream, "Move to cloud" should proceed.

Actual

The dialog blocks every attempt. Clicking Commit & push repeatedly dispatches the canned commit prompt; the session verifies there is nothing to commit (6+ times over the course of debugging).

Evidence of clean state

All of the following held at every attempt:

$ git status --porcelain          # empty
$ git rev-parse HEAD @{u}         # identical SHAs
$ git stash list                  # empty
$ git worktree list               # single worktree
$ git submodule status            # none
$ git diff-index HEAD --          # empty (no stat-dirty entries either)
$ git update-index --refresh      # no-op

Causes systematically ruled out during debugging

Each item below was verified or fixed, and the dialog still appeared afterwards:

  1. Uncommitted / stat-dirty files — none; index refreshed explicitly.
  2. Unpushed commits on the current branch — HEAD == @{u} throughout.
  3. Local branches missing on the remote — two local-only branches were pushed with -u; all local branches now track in-sync remotes.
  4. Missing upstream tracking on main — fixed (git branch -u origin/main main).
  5. Unusual default branch — remote default branch was changed to the session's current branch; no effect.
  6. Stale app cache — two fresh commits + pushes (including an empty commit) created new HEAD SHAs and push events; no effect.
  7. Claude GitHub App access — installed, All repositories, read/write code permissions (screenshot verified).
  8. Office lock files (~$*.pptx / ~$*.docx, gitignored via ~$*) — present initially; documents closed, lock files gone; no effect.
  9. Files ignored only by the global ignore file.claude/settings.local.json was ignored solely by ~/.config/git/ignore (**/.claude/settings.local.json); the rule was added to the repo's own .gitignore and committed in case the checker skips global excludes; no effect.
  10. Enclosing git repo — no .git in any parent directory; git rev-parse --show-toplevel matches the session's working directory exactly.
  11. .gitignore encoding — no CRLF or hidden characters (verified with cat -etv).

Remaining ignored-but-present paths are the ordinary kind every repo has (.venv/, __pycache__/, .DS_Store, node_modules/, a models/*/ directory pattern, one !.env.example negation).

Notes for triage

  • Whatever check the dialog performs disagrees with git itself. Candidates worth auditing: a non-git ignore matcher (patterns in play: ~$*, models/*/, *.py[cod], !.env.example), treatment of ignored/untracked classification, or a cached repo snapshot that pushes don't invalidate.
  • The "Commit & push" fallback should probably detect the nothing-to-commit case and surface the checker's actual file list instead of looping.

Happy to provide more diagnostics on request.

View original on GitHub ↗