Offer to adopt an enclosing git repository when opening a subfolder

When a workspace folder isn't a git repo itself but sits inside one, ask
the user to confirm before using the parent repository for Sync, rather
than silently operating on whichever repo git walks up to find.

- gitsync: add repo_root() via `git rev-parse --show-toplevel`
- app: detect_repo() distinguishes repo-root / parent-repo / no-repo;
  a confirmation dialog adopts or declines the parent repo; git_init
  tracks repo_root and clears any pending prompt
- Sync button hover names the enclosing repo when one is adopted

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
landon
2026-07-28 11:58:47 -05:00
parent 496bbfd298
commit b31ac0c6c8
3 changed files with 151 additions and 5 deletions
+20 -1
View File
@@ -1,4 +1,4 @@
use std::path::Path;
use std::path::{Path, PathBuf};
use std::process::Command;
/// Result of a git operation: combined human-readable log for display.
@@ -33,6 +33,25 @@ pub fn is_repo(workspace: &Path) -> bool {
ok && out.contains("true")
}
/// The top-level directory of the git work tree containing `workspace`, if any.
///
/// This walks up the directory tree the way git itself does, so it finds a
/// repository whose `.git` lives in a *parent* of `workspace`, not just one
/// rooted at `workspace`.
pub fn repo_root(workspace: &Path) -> Option<PathBuf> {
let (ok, out) = run_git(workspace, &["rev-parse", "--show-toplevel"]);
if !ok {
return None;
}
// run_git prepends a `$ git …` echo line; the toplevel path is the first
// real output line.
let line = out
.lines()
.find(|l| !l.starts_with("$ git") && !l.trim().is_empty())?;
let path = PathBuf::from(line.trim());
(!path.as_os_str().is_empty()).then_some(path)
}
/// True if a remote named `origin` is configured.
fn has_origin(workspace: &Path) -> bool {
let (ok, out) = run_git(workspace, &["remote"]);