fix: workspaces rm
This commit is contained in:
parent
42d034eef2
commit
c152b4a899
1 changed files with 78 additions and 9 deletions
|
|
@ -669,6 +669,60 @@ def has_unpushed_commits(repo_path: Path, branch: str) -> bool:
|
|||
return count > 0
|
||||
|
||||
|
||||
def is_branch_integrated_into_main(
|
||||
repo_path: Path,
|
||||
branch: str,
|
||||
main_ref: str = "origin/main",
|
||||
) -> bool:
|
||||
"""
|
||||
Heuristic: is `branch`'s content already in `main_ref`?
|
||||
|
||||
Returns True if:
|
||||
- branch tip is an ancestor of main_ref (normal merge / FF / rebase-before-merge), OR
|
||||
- the branch tip's tree hash appears somewhere in main_ref's history
|
||||
(common for squash merges and cherry-picks).
|
||||
|
||||
This does not know about PRs, only Git history.
|
||||
"""
|
||||
# Make sure we have latest main (best-effort; ignore fetch errors)
|
||||
_code, _out, _err = run_cmd(["git", "fetch", "--quiet", "origin"], cwd=repo_path)
|
||||
|
||||
# 1) Simple case: branch is ancestor of main_ref
|
||||
code, _out, _err = run_cmd(
|
||||
["git", "merge-base", "--is-ancestor", branch, main_ref],
|
||||
cwd=repo_path,
|
||||
)
|
||||
if code == 0:
|
||||
return True
|
||||
|
||||
# 2) Squash / cherry-pick heuristic: same tree exists on main_ref
|
||||
code, out, err = run_cmd(
|
||||
["git", "show", "-s", "--format=%T", branch],
|
||||
cwd=repo_path,
|
||||
)
|
||||
if code != 0:
|
||||
console.print(
|
||||
f"[red]Failed to get tree for branch {branch} in {repo_path.name}:[/red]\n{err}"
|
||||
)
|
||||
return False
|
||||
|
||||
branch_tree = out.strip()
|
||||
if not branch_tree:
|
||||
return False
|
||||
|
||||
code, out, err = run_cmd(
|
||||
["git", "log", "--format=%T", main_ref],
|
||||
cwd=repo_path,
|
||||
)
|
||||
if code != 0:
|
||||
console.print(
|
||||
f"[red]Failed to walk {main_ref} in {repo_path.name}:[/red]\n{err}"
|
||||
)
|
||||
return False
|
||||
|
||||
main_trees = {line.strip() for line in out.splitlines() if line.strip()}
|
||||
return branch_tree in main_trees
|
||||
|
||||
@app.command("rm")
|
||||
def remove_workspace(
|
||||
ctx: typer.Context,
|
||||
|
|
@ -687,6 +741,11 @@ def remove_workspace(
|
|||
"-f",
|
||||
help="Force removal even if there is dirty or unpushed work.",
|
||||
),
|
||||
main_ref: str = typer.Option(
|
||||
"origin/main",
|
||||
"--main-ref",
|
||||
help="Ref to consider as the integration target (default: origin/main).",
|
||||
),
|
||||
):
|
||||
"""
|
||||
Remove a workspace:
|
||||
|
|
@ -732,13 +791,19 @@ def remove_workspace(
|
|||
for wt in worktrees:
|
||||
status = get_git_status(wt)
|
||||
branch = get_current_branch(wt) or "?"
|
||||
repo = find_repo_for_worktree(wt, repos_dir)
|
||||
|
||||
integrated = False
|
||||
if repo is not None and branch != "?":
|
||||
integrated = is_branch_integrated_into_main(wt, branch, main_ref=main_ref)
|
||||
|
||||
if status.dirty:
|
||||
problems.append(f"{wt.name}: dirty working tree on '{branch}'")
|
||||
repo = find_repo_for_worktree(wt, repos_dir)
|
||||
if repo is not None and branch != "?":
|
||||
|
||||
# Only care about "unpushed commits" if the branch is NOT integrated into main.
|
||||
if repo is not None and branch != "?" and not integrated:
|
||||
if has_unpushed_commits(wt, branch):
|
||||
problems.append(f"{wt.name}: unpushed commits on '{branch}'")
|
||||
|
||||
if problems and not force:
|
||||
console.print(
|
||||
"[red]Refusing to remove workspace; found dirty or unpushed work:[/red]"
|
||||
|
|
@ -770,10 +835,13 @@ def remove_workspace(
|
|||
continue
|
||||
|
||||
console.print(f"Removing worktree for [bold]{display_name}[/bold]...")
|
||||
code, _out, err = run_cmd(
|
||||
["git", "worktree", "remove", "--force" if force else "--detach", str(wt)],
|
||||
cwd=repo,
|
||||
)
|
||||
|
||||
args = ["git", "worktree", "remove"]
|
||||
if force:
|
||||
args.append("--force")
|
||||
args.append(str(wt))
|
||||
|
||||
code, _out, err = run_cmd(args, cwd=repo)
|
||||
if code != 0:
|
||||
console.print(
|
||||
f"[red]Failed to remove worktree {display_name} via git:[/red]\n{err}"
|
||||
|
|
@ -785,9 +853,8 @@ def remove_workspace(
|
|||
)
|
||||
shutil.rmtree(wt)
|
||||
elif wt.exists():
|
||||
# git worktree remove should delete the directory; if not, clean up.
|
||||
# git worktree remove *should* delete the directory; if not, clean up.
|
||||
shutil.rmtree(wt)
|
||||
|
||||
# Finally, remove the workspace directory itself
|
||||
shutil.rmtree(ws_dir)
|
||||
console.print(
|
||||
|
|
@ -1435,3 +1502,5 @@ app.add_typer(tmux_app)
|
|||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue