Compare commits
No commits in common. "0ee5711dcd7d536dcca742ea30d6c45d5b350327" and "d5d081f74376fac9354c2c36c42dc1cba952d929" have entirely different histories.
0ee5711dcd
...
d5d081f743
1 changed files with 64 additions and 140 deletions
204
workspaces.py
204
workspaces.py
|
|
@ -868,6 +868,7 @@ def choose_files_for_wip(repo_path: Path, changes: List[Tuple[str, str]]) -> Lis
|
||||||
|
|
||||||
return selected_files
|
return selected_files
|
||||||
|
|
||||||
|
|
||||||
@app.command("wip")
|
@app.command("wip")
|
||||||
def wip_workspace(
|
def wip_workspace(
|
||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
|
|
@ -894,48 +895,9 @@ def wip_workspace(
|
||||||
if not ws_dir.exists():
|
if not ws_dir.exists():
|
||||||
console.print(f"[red]Workspace '{ws_dir.name}' does not exist at {ws_dir}[/red]")
|
console.print(f"[red]Workspace '{ws_dir.name}' does not exist at {ws_dir}[/red]")
|
||||||
raise typer.Exit(1)
|
raise typer.Exit(1)
|
||||||
|
|
||||||
title, _desc = read_workspace_readme(ws_dir)
|
title, _desc = read_workspace_readme(ws_dir)
|
||||||
commit_message = f"wip: {title or ws_dir.name}"
|
commit_message = f"wip: {title or ws_dir.name}"
|
||||||
commit_workspace(ctx, workspace, message=commit_message)
|
|
||||||
|
|
||||||
@app.command("commit")
|
|
||||||
def commit_workspace(
|
|
||||||
ctx: typer.Context,
|
|
||||||
workspace: Optional[str] = typer.Option(
|
|
||||||
None,
|
|
||||||
"--workspace",
|
|
||||||
"-w",
|
|
||||||
help=(
|
|
||||||
"Workspace directory name to WIP-commit. "
|
|
||||||
"If omitted, uses the workspace containing the current directory."
|
|
||||||
),
|
|
||||||
),
|
|
||||||
message: Optional[str] = typer.Option(
|
|
||||||
None,
|
|
||||||
"--message",
|
|
||||||
"-m",
|
|
||||||
help="Commit message to use.",
|
|
||||||
),
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
For each repo in the workspace:
|
|
||||||
|
|
||||||
- Show list of changed files.
|
|
||||||
- Ask whether to stage all, none, or pick some files.
|
|
||||||
- Stage chosen files.
|
|
||||||
"""
|
|
||||||
if not message:
|
|
||||||
console.print(
|
|
||||||
"[red]No commit message provided. Exiting.[/red]",
|
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
raise typer.Exit(1)
|
|
||||||
_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)
|
|
||||||
|
|
||||||
worktrees: List[Path] = []
|
worktrees: List[Path] = []
|
||||||
for child in sorted(p for p in ws_dir.iterdir() if p.is_dir()):
|
for child in sorted(p for p in ws_dir.iterdir() if p.is_dir()):
|
||||||
|
|
@ -976,14 +938,14 @@ def commit_workspace(
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Commit
|
# Commit
|
||||||
code, _out, err = run_cmd(["git", "commit", "-m", message], cwd=wt)
|
code, _out, err = run_cmd(["git", "commit", "-m", commit_message], cwd=wt)
|
||||||
if code != 0:
|
if code != 0:
|
||||||
console.print(
|
console.print(
|
||||||
f"[red]Failed to commit in {wt.name}:[/red]\n{err}"
|
f"[red]Failed to commit in {wt.name}:[/red]\n{err}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
console.print(
|
console.print(
|
||||||
f" [green]Created commit in {wt.name}:[/green] '{message}'"
|
f" [green]Created WIP commit in {wt.name}:[/green] '{commit_message}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1091,12 +1053,8 @@ def status_workspace(
|
||||||
|
|
||||||
title, desc = read_workspace_readme(ws_dir)
|
title, desc = read_workspace_readme(ws_dir)
|
||||||
|
|
||||||
# if desc:
|
|
||||||
# console.print(Panel(desc, title=title))
|
|
||||||
|
|
||||||
table = Table(
|
table = Table(
|
||||||
title=f"Status for workspace:'{title}'\n(dir: {ws_dir.name})\n\n{desc}",
|
title=f"Status for workspace '{title}' (dir: {ws_dir.name})",
|
||||||
title_justify="left",
|
|
||||||
show_lines=False,
|
show_lines=False,
|
||||||
)
|
)
|
||||||
table.add_column("Repo (dir)", style="bold")
|
table.add_column("Repo (dir)", style="bold")
|
||||||
|
|
@ -1104,11 +1062,6 @@ def status_workspace(
|
||||||
table.add_column("Ahead/Behind/Dirty")
|
table.add_column("Ahead/Behind/Dirty")
|
||||||
table.add_column("Changed Files", justify="right")
|
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] = []
|
worktrees: List[Path] = []
|
||||||
for child in sorted(p for p in ws_dir.iterdir() if p.is_dir()):
|
for child in sorted(p for p in ws_dir.iterdir() if p.is_dir()):
|
||||||
if child.name.lower() == "readme.md":
|
if child.name.lower() == "readme.md":
|
||||||
|
|
@ -1132,96 +1085,10 @@ def status_workspace(
|
||||||
str(len(changes)),
|
str(len(changes)),
|
||||||
)
|
)
|
||||||
|
|
||||||
if changes:
|
|
||||||
for f, s in changes:
|
|
||||||
files_table.add_row(wt.name, f, s)
|
|
||||||
|
|
||||||
console.print(table)
|
console.print(table)
|
||||||
|
|
||||||
console.print("\nFiles with changes:")
|
if desc:
|
||||||
if files_table.rows:
|
console.print(Panel(desc, title="Workspace description"))
|
||||||
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:
|
def not_in_tmux() -> bool:
|
||||||
"""Return True if not inside tmux or zellij."""
|
"""Return True if not inside tmux or zellij."""
|
||||||
|
|
@ -1423,5 +1290,62 @@ def attach(
|
||||||
|
|
||||||
app.add_typer(tmux_app)
|
app.add_typer(tmux_app)
|
||||||
|
|
||||||
|
# @app.command()
|
||||||
|
# def main(
|
||||||
|
# dir: Optional[Path] = typer.Argument(
|
||||||
|
# None,
|
||||||
|
# help="Base directory containing projects. If omitted, auto-attach or --start behavior.",
|
||||||
|
# ),
|
||||||
|
# start: bool = typer.Option(
|
||||||
|
# False,
|
||||||
|
# "--start",
|
||||||
|
# help="Start a new session based on the current directory.",
|
||||||
|
# ),
|
||||||
|
# ):
|
||||||
|
# # Replicate initial "DIR=$1" and attach-or-start logic
|
||||||
|
# # If no args and no --start
|
||||||
|
# if dir is None and not start:
|
||||||
|
# if not_in_tmux():
|
||||||
|
# # Try to attach to an existing session
|
||||||
|
# attach_proc = run_tmux(["attach"])
|
||||||
|
# if attach_proc.returncode == 0:
|
||||||
|
# # Successfully attached; exit like the bash script
|
||||||
|
# raise typer.Exit(1)
|
||||||
|
# # If attach failed, fall through to start mode
|
||||||
|
# start = True
|
||||||
|
# else:
|
||||||
|
# # In tmux and no dir/start: nothing to do
|
||||||
|
# raise typer.Exit(1)
|
||||||
|
#
|
||||||
|
# # Figure out session_name and path_name
|
||||||
|
# if start:
|
||||||
|
# path_name = Path.cwd()
|
||||||
|
# session_name = path_name.name.replace(".", "_")
|
||||||
|
# else:
|
||||||
|
# if dir is None:
|
||||||
|
# typer.echo("[ta] DIR argument is required unless --start is used.", err=True)
|
||||||
|
# raise typer.Exit(1)
|
||||||
|
#
|
||||||
|
# dir = dir.expanduser().resolve()
|
||||||
|
# project = pick_project(dir)
|
||||||
|
# if not project:
|
||||||
|
# # cancelled or error
|
||||||
|
# raise typer.Exit(1)
|
||||||
|
#
|
||||||
|
# session_name = project.replace(".", "_")
|
||||||
|
# path_name = (dir / project).resolve()
|
||||||
|
#
|
||||||
|
# typer.echo(f'session name is "{session_name}"')
|
||||||
|
# typer.echo(f"path name is {path_name}")
|
||||||
|
#
|
||||||
|
# if not session_name:
|
||||||
|
# raise typer.Exit(1)
|
||||||
|
#
|
||||||
|
# # Try main attach/create flow; on failure, fall back
|
||||||
|
# ok = create_if_needed_and_attach(session_name, path_name, start_mode=start)
|
||||||
|
# if not ok:
|
||||||
|
# attach_to_first_session()
|
||||||
|
#
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app()
|
app()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue