From cedaf4ce0409d8a44ba6f8db5e3edc78d893754f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 22 May 2026 18:51:30 +0000 Subject: [PATCH 1/2] Make _clone_nvim_config idempotent Previously rmtree'd ~/.config/nvim on every full bootstrap run, destroying any user edits, plugin state, and undo history. Now skips when the directory is already a clone of the expected repo, and refuses to touch unfamiliar contents (different remote, or non-git directory). --- bootstrap_environment.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/bootstrap_environment.py b/bootstrap_environment.py index 9d04d18..e17e287 100755 --- a/bootstrap_environment.py +++ b/bootstrap_environment.py @@ -1290,28 +1290,38 @@ def _clone_nvim_config() -> None: print(f"\n[Neovim] Setting up configuration from {repo_url} ...") if config_dir.exists(): - print(f" Removing existing configuration at {config_dir} ...") - shutil.rmtree(config_dir) + if (config_dir / ".git").is_dir(): + remote = subprocess.run( + ["git", "-C", str(config_dir), "remote", "get-url", "origin"], + capture_output=True, text=True, check=False, + ) + current = remote.stdout.strip() + if remote.returncode == 0 and current == repo_url: + print(f" Configuration already cloned at {config_dir} — skipping.") + return + warn(f"{config_dir} has a different git remote ({current!r}); " + f"leaving it untouched. Remove it manually to re-clone.") + return + warn(f"{config_dir} exists but is not a git repo; leaving it untouched. " + f"Remove it manually to clone the Neovim configuration.") + return config_dir.parent.mkdir(parents=True, exist_ok=True) - print(f" Cloning to {config_dir} ...") - # We use a temp clone and then move to ensure we handle the "rename" part of the request - # although cloning directly to 'nvim' is effectively the same. - # The user asked: "clones my nvim config to $HOME/.config/$REPO and renames the repo root folder to just nvim" - repo_name = repo_url.split("/")[-1].replace(".git", "") + repo_name = repo_url.split("/")[-1].removesuffix(".git") temp_clone = config_dir.parent / repo_name - if temp_clone.exists(): shutil.rmtree(temp_clone) + print(f" Cloning to {config_dir} ...") result = run(["git", "clone", repo_url, str(temp_clone)], check=False) if result.returncode != 0: err("Neovim configuration clone failed") return - print(f" Renaming {temp_clone.name} to {config_dir.name} ...") - temp_clone.rename(config_dir) + if temp_clone != config_dir: + print(f" Renaming {temp_clone.name} to {config_dir.name} ...") + temp_clone.rename(config_dir) print(f" Neovim configuration ready at {config_dir}") -- 2.47.3 From a4b855b796a38e7c839336cf1cd2fcbcf88d7ed3 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 22 May 2026 18:55:44 +0000 Subject: [PATCH 2/2] Rename existing nvim config to nvim-N instead of skipping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On re-runs, ~/.config/nvim is moved aside to ~/.config/nvim-1 (or nvim-2, nvim-3, ... — first free number) and a fresh clone is created. End-of-run notices report the backup path so it's visible after the rest of the bootstrap output scrolls by. --- bootstrap_environment.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/bootstrap_environment.py b/bootstrap_environment.py index e17e287..118572c 100755 --- a/bootstrap_environment.py +++ b/bootstrap_environment.py @@ -79,6 +79,18 @@ def write_run_log() -> None: RUN_LOG.write_text("\n".join(lines) + "\n") print(f"\n{len(_issues)} issue(s) logged to: {RUN_LOG}") +_notices: list[str] = [] + +def notice(msg: str) -> None: + _notices.append(msg) + +def print_notices() -> None: + if not _notices: + return + print("\nNotices:") + for n in _notices: + print(f" • {n}") + # ── subprocess helpers ──────────────────────────────────────────────────────── def run( @@ -1290,21 +1302,12 @@ def _clone_nvim_config() -> None: print(f"\n[Neovim] Setting up configuration from {repo_url} ...") if config_dir.exists(): - if (config_dir / ".git").is_dir(): - remote = subprocess.run( - ["git", "-C", str(config_dir), "remote", "get-url", "origin"], - capture_output=True, text=True, check=False, - ) - current = remote.stdout.strip() - if remote.returncode == 0 and current == repo_url: - print(f" Configuration already cloned at {config_dir} — skipping.") - return - warn(f"{config_dir} has a different git remote ({current!r}); " - f"leaving it untouched. Remove it manually to re-clone.") - return - warn(f"{config_dir} exists but is not a git repo; leaving it untouched. " - f"Remove it manually to clone the Neovim configuration.") - return + n = 1 + while (backup := config_dir.with_name(f"nvim-{n}")).exists(): + n += 1 + print(f" Renaming existing {config_dir} → {backup} ...") + config_dir.rename(backup) + notice(f"Previous Neovim config preserved at {backup}") config_dir.parent.mkdir(parents=True, exist_ok=True) @@ -2412,6 +2415,7 @@ def main() -> None: pyenv_thread.join() write_run_log() + print_notices() print("\nDone.") # Final step (user-requested): source ~/.zshrc. -- 2.47.3