package e2e import ( "os" "path/filepath" "testing" ) // TestSyncForceOverwritesLocallyModified pins the --force escape hatch: // locally modified managed items are overwritten with the repository // version, upstream-deleted modified items are removed, and unmanaged // local items are never touched. func TestSyncForceOverwritesLocallyModified(t *testing.T) { h := newHarness(t) env := newSyncEnv(t, h) buildWorkRepo(t, h, env.workRepo) env.writeConfig(t, env.workRepo, "") env.run(t) // local drift on a managed item + upstream change to the same item deployed := filepath.Join(env.home, ".config/opencode/commands/deploy.md") os.WriteFile(deployed, []byte("---\nname: Deploy\n---\nlocal drift\n"), 0o644) upstream := filepath.Join(env.workRepo, "commands/deploy.md") os.WriteFile(upstream, []byte("---\nname: Deploy\ndescription: updated\n---\nnew body\n"), 0o644) // an unmanaged local item that must survive --force untouched unmanaged := filepath.Join(env.home, ".config/opencode/commands/mine.md") os.MkdirAll(filepath.Dir(unmanaged), 0o755) os.WriteFile(unmanaged, []byte("---\nname: Mine\n---\nprecious\n"), 0o644) h.git(t, env.workRepo, "add", "-A") h.git(t, env.workRepo, "commit", "-m", "update deploy command") out, err := env.run(t) if err != nil { t.Fatalf("sync failed: %v\n%s", err, out) } if !contains(out, "skipped (local modifications kept): 1") { t.Errorf("default sync must keep the local modification:\n%s", out) } out, err = env.run(t, "--force") if err != nil { t.Fatalf("sync --force failed: %v\n%s", err, out) } if !contains(out, "updated: 1") { t.Errorf("force must update the locally modified item:\n%s", out) } if got := readFile(t, deployed); !contains(got, "new body") { t.Error("force must overwrite the local drift with the repository version") } if got := readFile(t, unmanaged); !contains(got, "precious") { t.Error("force must never touch unmanaged local items") } // upstream deletion of a locally modified item: force removes it os.WriteFile(deployed, []byte("---\nname: Deploy\n---\nmore drift\n"), 0o644) os.Remove(upstream) h.git(t, env.workRepo, "add", "-A") h.git(t, env.workRepo, "commit", "-m", "delete deploy command") out, err = env.run(t) if err != nil { t.Fatalf("sync failed: %v\n%s", err, out) } if !contains(out, "skipped (local modifications kept): 1") { t.Errorf("default sync must keep the modified item after upstream deletion:\n%s", out) } if _, err := os.Stat(deployed); err != nil { t.Fatal("item must still exist before force") } out, err = env.run(t, "--force") if err != nil { t.Fatalf("sync --force failed: %v\n%s", err, out) } if !contains(out, "removed: 1") { t.Errorf("force must remove the locally modified item after upstream deletion:\n%s", out) } if _, err := os.Stat(deployed); !os.IsNotExist(err) { t.Error("forced removal must delete the deployed file") } }