Created
July 3, 2026 13:37
-
-
Save raymondbutcher/af50bd63e40c0ffc6dc1e63707eebb2b to your computer and use it in GitHub Desktop.
docker desktop virtiofs bug when using git rebase
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/sh | |
| # rebase.sh — runs a git rebase, either natively or inside Docker. | |
| # | |
| # Runs a plain `git rebase` with no external tools or file modifications. | |
| # If the racy-git/VirtioFS bug is present, the rebase will fail with: | |
| # error: Your local changes to the following files would be overwritten by merge | |
| # ...despite git status showing clean. | |
| set -e | |
| cd "${REPO_DIR:-/repo}" | |
| echo "=== Initial status ===" | |
| git status | |
| echo "" | |
| echo "=== Running: git checkout feature; git rebase main ===" | |
| git checkout feature | |
| REBASE_OUT=$(git rebase main 2>&1) && REBASE_STATUS=0 || REBASE_STATUS=$? | |
| printf '%s\n' "$REBASE_OUT" | |
| if [ "$REBASE_STATUS" -eq 0 ]; then | |
| echo "" | |
| echo "Rebase succeeded — bug not reproduced." | |
| elif printf '%s\n' "$REBASE_OUT" | grep -q "would be overwritten by merge"; then | |
| echo "" | |
| echo "████ REPRODUCED ████" | |
| echo "" | |
| echo "git status at failure:" | |
| git status | |
| else | |
| echo "" | |
| echo "Rebase failed for a different reason (not the racy-git bug)." | |
| echo "" | |
| echo "git status at failure:" | |
| git status | |
| exit 1 | |
| fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # reproduce.sh — demonstrates a git rebase failure inside Docker on macOS. | |
| # | |
| # What happened | |
| # ───────────── | |
| # During a plain `git rebase` run inside a Docker container (repo bind-mounted | |
| # from a macOS host), the rebase failed with: | |
| # | |
| # error: Your local changes to the following files would be overwritten by merge | |
| # | |
| # ...even though `git status` showed "nothing to commit, working tree clean." | |
| # No external tools were running or modifying files. | |
| # | |
| # Suspected cause | |
| # ─────────────── | |
| # Git uses file mtimes to detect whether working-tree files differ from the | |
| # index (the "racy git" problem). Docker bind mounts on macOS (VirtioFS) have | |
| # coarser timestamp resolution (~1 second) than native Linux filesystems | |
| # (nanoseconds). When git rapidly applies commits during a rebase, the | |
| # coarsened mtimes may confuse racy-git detection. | |
| # | |
| # The root cause has not been confirmed, but the bug is reproducible via this | |
| # script. | |
| # | |
| # The script runs rebase.sh twice against a fresh repo each time: | |
| # Run 1 — natively on the host (control): rebase should succeed. | |
| # Run 2 — inside Docker with a VirtioFS bind mount: rebase should fail. | |
| # | |
| # Usage | |
| # ───── | |
| # bash reproduce.sh | |
| # | |
| # The test repo is left at ./racy-test-repo after the run for inspection. | |
| set -euo pipefail | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| REPO="$SCRIPT_DIR/racy-test-repo" | |
| command -v docker >/dev/null 2>&1 || { echo "Error: docker not found"; exit 1; } | |
| command -v git >/dev/null 2>&1 || { echo "Error: git not found"; exit 1; } | |
| # ── Run 1: native ──────────────────────────────────────────────────────────── | |
| echo "=== Setting up repository ===" | |
| bash "$SCRIPT_DIR/setup-repo.sh" "$REPO" | |
| echo "" | |
| git -C "$REPO" log --oneline --all --graph | |
| echo "" | |
| echo "=== Run 1: native — expect success ===" | |
| echo "" | |
| REPO_DIR="$REPO" sh "$SCRIPT_DIR/rebase.sh" | |
| # ── Run 2: Docker ──────────────────────────────────────────────────────────── | |
| echo "" | |
| echo "=== Setting up repository ===" | |
| bash "$SCRIPT_DIR/setup-repo.sh" "$REPO" | |
| echo "" | |
| git -C "$REPO" log --oneline --all --graph | |
| echo "" | |
| echo "=== Run 2: inside Docker (bind-mounted from host) — expect failure ===" | |
| echo "" | |
| docker run --rm \ | |
| -v "$REPO:/repo" \ | |
| -v "$SCRIPT_DIR/rebase.sh:/rebase.sh" \ | |
| -w /repo \ | |
| docker/sandbox-templates:claude-code \ | |
| sh /rebase.sh |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # setup-repo.sh — creates the test git repository used by reproduce.sh. | |
| # | |
| # Usage: bash setup-repo.sh <repo-path> | |
| set -euo pipefail | |
| REPO="${1:?Usage: setup-repo.sh <repo-path>}" | |
| rm -rf "$REPO" | |
| mkdir -p "$REPO/internal/fuse" | |
| cd "$REPO" | |
| git init -q | |
| git config user.email "test@example.com" | |
| git config user.name "Test" | |
| # Commit A — shared base | |
| cat > internal/fuse/mount.go << 'EOF' | |
| package fuse | |
| // Mount attaches the filesystem. | |
| func Mount(path string) error { | |
| return nil | |
| } | |
| EOF | |
| git add -A | |
| git commit -q -m "A: initial" | |
| A=$(git rev-parse HEAD) | |
| # Commit B — main branch diverges (separate file to avoid rebase conflicts) | |
| cat > internal/fuse/unmount.go << 'EOF' | |
| package fuse | |
| // Unmount detaches the filesystem. | |
| func Unmount(path string) error { | |
| return nil | |
| } | |
| EOF | |
| git add -A | |
| git commit -q -m "B: main adds Unmount" | |
| # Feature branch: two commits off A | |
| git checkout -q -b feature "$A" | |
| cat >> internal/fuse/mount.go << 'EOF' | |
| // Remount re-applies options without detaching. | |
| func Remount(path string) error { | |
| return nil | |
| } | |
| EOF | |
| git add -A | |
| git commit -q -m "C: feature adds Remount" | |
| cat >> internal/fuse/mount.go << 'EOF' | |
| // Stat returns filesystem statistics. | |
| func Stat(path string) error { | |
| return nil | |
| } | |
| EOF | |
| git add -A | |
| git commit -q -m "D: feature adds Stat" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment