From 0ee5711dcd7d536dcca742ea30d6c45d5b350327 Mon Sep 17 00:00:00 2001 From: "Waylon S. Walker" Date: Tue, 25 Nov 2025 19:12:44 -0600 Subject: [PATCH] status and diff commands --- workspaces.py | 103 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 4 deletions(-) diff --git a/workspaces.py b/workspaces.py index 4545ec5..a9bb772 100755 --- a/workspaces.py +++ b/workspaces.py @@ -983,7 +983,7 @@ def commit_workspace( ) else: console.print( - f" [green]Created WIP commit in {wt.name}:[/green] '{message}'" + f" [green]Created commit in {wt.name}:[/green] '{message}'" ) @@ -1091,8 +1091,12 @@ def status_workspace( title, desc = read_workspace_readme(ws_dir) + # if desc: + # console.print(Panel(desc, title=title)) + table = Table( - title=f"Status for workspace '{title}' (dir: {ws_dir.name})", + title=f"Status for workspace:'{title}'\n(dir: {ws_dir.name})\n\n{desc}", + title_justify="left", show_lines=False, ) table.add_column("Repo (dir)", style="bold") @@ -1100,6 +1104,11 @@ def status_workspace( table.add_column("Ahead/Behind/Dirty") table.add_column("Changed Files", justify="right") + files_table = Table(show_lines=False) + files_table.add_column("Repo (dir)", style="bold") + files_table.add_column("File", justify="left") + files_table.add_column("Status", justify="right") + worktrees: List[Path] = [] for child in sorted(p for p in ws_dir.iterdir() if p.is_dir()): if child.name.lower() == "readme.md": @@ -1123,10 +1132,96 @@ def status_workspace( str(len(changes)), ) + if changes: + for f, s in changes: + files_table.add_row(wt.name, f, s) + console.print(table) - if desc: - console.print(Panel(desc, title="Workspace description")) + console.print("\nFiles with changes:") + if files_table.rows: + console.print(files_table) + +@app.command("diff") +def diff_workspace( + ctx: typer.Context, + workspace: Optional[str] = typer.Option( + None, + "--workspace", + "-w", + help=( + "Workspace directory name to diff. " + "If omitted, uses the workspace containing the current directory." + ), + ), + staged: bool = typer.Option( + False, + "--staged", + "-s", + help="Show only staged changes (git diff --cached).", + ), +): + """ + Show git diff for all repos in the workspace. + + - If --staged: uses `git diff --cached` (only staged changes). + - Otherwise: uses `git diff` (staged + unstaged). + """ + _settings, _repos_dir, workspaces_dir = get_ctx_paths(ctx) + ws_dir = find_workspace_dir(workspaces_dir, workspace) + if not ws_dir.exists(): + console.print(f"[red]Workspace '{ws_dir.name}' does not exist at {ws_dir}[/red]") + raise typer.Exit(1) + + title, _desc = read_workspace_readme(ws_dir) + console.print( + f"Showing diffs for workspace [bold]{title or ws_dir.name}[/bold] (dir: {ws_dir.name})" + ) + + # Collect worktrees (repos) in this workspace + worktrees: List[Path] = [] + for child in sorted(p for p in ws_dir.iterdir() if p.is_dir()): + if child.name.lower() == "readme.md": + continue + if not ensure_git_repo(child): + continue + worktrees.append(child) + + if not worktrees: + console.print(f"[yellow]No repos in workspace {ws_dir.name}[/yellow]") + raise typer.Exit(0) + + any_diffs = False + + for wt in worktrees: + # Quick check: skip repos with no changes at all + changes = git_status_porcelain(wt) + if not changes: + continue + + any_diffs = True + console.rule(f"[bold]{wt.name}[/bold]") + + cmd = ["git", "diff"] + if staged: + cmd.append("--cached") + + code, out, err = run_cmd(cmd, cwd=wt) + if code != 0: + console.print( + f"[red]Failed to get diff for {wt.name}:[/red]\n{err}" + ) + continue + + if not out.strip(): + console.print("[dim]No diff output.[/dim]") + else: + # Use markup=False so diff characters like [ ] don't get eaten by Rich + console.print(out, markup=False) + + if not any_diffs: + console.print("[green]No changes to diff in any repo.[/green]") + def not_in_tmux() -> bool: """Return True if not inside tmux or zellij."""