workspace management cli
Find a file
Waylon S. Walker 6d2de70926 chore/fix-issues-on-workspace (#10)
Reviewed-on: https://git.wayl.one/waylon/workspaces/pulls/10
Co-authored-by: Waylon S. Walker <waylon@waylonwalker.com>
Co-committed-by: Waylon S. Walker <waylon@waylonwalker.com>
2025-12-02 13:06:21 -06:00
src/workspaces feat/workspace-tmux-remove (#3) 2025-11-26 12:46:08 -06:00
.gitignore fix: tmux sessions wont attach with . in name 2025-11-26 13:18:25 -06:00
justfile chore/fix-issues-on-workspace (#10) 2025-12-02 13:06:21 -06:00
prompt.md add readme and prompt 2025-11-25 12:45:13 -06:00
pyproject.toml chore/fix-issues-on-workspace (#10) 2025-12-02 13:06:21 -06:00
README.md chore/fix-issues-on-workspace (#10) 2025-12-02 13:06:21 -06:00
uv.lock fill in pyproject.toml (#4) 2025-11-27 08:34:23 -06:00
workspaces.py chore/fix-issues-on-workspace (#10) 2025-12-02 13:06:21 -06:00

Workspaces

Workspaces is a workspace management tool meant to keep your cloned repos clean, and check out worktrees into a workspace for focused work on a task that would require multiple repos.

Installation

workspaces.py is currently fully self contained, you can download the file to your ~/.local/bin or let uv manage it.

alias ws='uv run 
https://git.waylonwalker.com/waylon/workspaces/raw/branch/main/workspaces.py -W work'
alias gs='uv run 
https://git.waylonwalker.com/waylon/workspaces/raw/branch/main/workspaces.py -W git'

Workflow

My workflow for working with workspaces starts with creating a new workspace.

> gs create
Workspace name (can contain spaces): my new feature
Workspace description (): Showing workspace cration.
Created workspace title='my new feature' dir='my-new-feature' at 
/home/u_walkews/git.workspaces/my-new-feature

Now you should have the directory layout.

git
  workspaces   main
git.workspaces
  my-new-feature
    readme.md

Now I want to add my repos to the workspace.

> gs add
Branch 'my/new-feature' does not exist in workspaces; creating it.
Added repo workspaces to workspace my new feature (dir: my-new-feature) on branch
'my/new-feature' at /home/u_walkews/git.workspaces/my-new-feature/workspaces

After adding the repos, the directory layout will now look like this.

git
  workspaces   main
git.workspaces
  my-new-feature
    readme.md
    workspaces   my/new-feature (worktree)

Now I can begin work in the workspace by attaching to it. I use tmux, and gs attach gives me a tmux session for the repo in the workspace.

> gs attach

You can see the session by listing out tmux sessions.

> tmux ls
ω|my-new-feature|workspaces: 1 windows (created Wed Jun 12 14:23:45 2024) [80x24]

While I work on the project there are some helpers to commit and push all repos at once. I typically use git directly, but there are good use cases for these.

gs commit
gs wip
gs push

Once the work is done, I can remove the workspace and clean up all the worktrees.

> gs rm
Remove workspace my new feature (dir: my-new-feature) and clean up its worktrees? 
(n): y
Removing worktree for workspaces...
Removed workspace title='my new feature' dir='my-new-feature'

Usage


 Usage: workspaces.py [OPTIONS] COMMAND [ARGS]...

 Workspace management tool

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --workspaces-name     -W      TEXT  Logical name for this workspace set      │
│                                     (e.g. 'git', 'work', 'personal').        │
│                                     Overrides WORKSPACES_NAME env. Defaults  │
│                                     to 'git'.                                │
│ --install-completion                Install completion for the current       │
│                                     shell.                                   │
│ --show-completion                   Show completion for the current shell,   │
│                                     to copy it or customize the              │
│                                     installation.                            │
│ --help                              Show this message and exit.              │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ───────────────────────────────────────────────────────────────────╮
│ list          List all workspaces.                                           │
│ create        Create a new workspace.                                        │
│ list-repos    List repos and branches in the current (or specified)          │
│               workspace.                                                     │
│ add-repo      Add a repo to a workspace.                                     │
│ rm            Remove a workspace and its worktrees.                          │
│ wip           Create WIP commits for changed files in workspace repos.       │
│ commit        Create commits with specified message for workspace repos.     │
│ push          For each repo in the workspace, run 'git push <remote>         │
│               <current-branch>'.                                             │
│ status        Show detailed status for the current (or specified) workspace. │
│ diff          Show git diff for all repos in the workspace.                  │
│ clear         Clear empty workspaces.                                        │
│ clean         Show detailed status for the current (or specified) workspace. │
│ move-staged   Create a patch for staged changes and move them to another     │
│               workspace.                                                     │
│ attach        Attach to a workspace.                                         │
│ docs          Generate full documentation for all commands.                  │
│ tmux          tmux commands                                                  │
╰──────────────────────────────────────────────────────────────────────────────╯

Commands

list


 Usage: workspaces.py list [OPTIONS]

 List all workspaces.

 Shows: - workspace directory name - workspace description (from README
 markdown, everything after H1) - included repos with git status indicators

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --help          Show this message and exit.                                  │
╰──────────────────────────────────────────────────────────────────────────────╯

create


 Usage: workspaces.py create [OPTIONS]

 Create a new workspace.

 - Asks for name and description if not provided. - Workspace directory uses a
 slugified version of the name. - README uses the original name as heading with
 description.

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --name         -n      TEXT  Name of the new workspace (display name; can    │
│                              contain spaces).                                │
│ --description  -d      TEXT  Description of the workspace. Will be written   │
│                              into readme.md.                                 │
│ --help                       Show this message and exit.                     │
╰──────────────────────────────────────────────────────────────────────────────╯

list-repos


 Usage: workspaces.py list-repos [OPTIONS]

 List repos and branches in the current (or specified) workspace.

 Shows: - repo directory name - current branch - ahead/behind/dirty indicators

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --workspace  -w      TEXT  Workspace directory name to inspect. If omitted,  │
│                            uses the workspace containing the current         │
│                            directory.                                        │
│ --help                     Show this message and exit.                       │
╰──────────────────────────────────────────────────────────────────────────────╯

add-repo


 Usage: workspaces.py add-repo [OPTIONS]

 Add a repo to a workspace.

 - Lists all directories in repos_dir as repos. - Uses iterfzf to pick repo if
 --repo not given. - Creates a worktree for a branch derived from the workspace
 name   into workspace_dir / repo_name.

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --workspace  -w      TEXT  Workspace directory name to add repo to. If       │
│                            omitted, uses the workspace containing the        │
│                            current directory.                                │
│ --repo       -r      TEXT  Name of repo (directory under repos_dir). If      │
│                            omitted, uses iterfzf to pick from repos_dir.     │
│ --help                     Show this message and exit.                       │
╰──────────────────────────────────────────────────────────────────────────────╯

rm


 Usage: workspaces.py rm [OPTIONS]

 Remove a workspace and its worktrees.

 - For each repo worktree in the workspace:   * Check for dirty work or
 unpushed commits.   * If any found and not --force, abort.   * Otherwise, run
 'git worktree remove'. - Finally, delete the workspace directory.

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --workspace  -w      TEXT  Workspace directory name to remove. If omitted,   │
│                            uses the workspace containing the current         │
│                            directory.                                        │
│ --force      -f            Force removal even if there is dirty or unpushed  │
│                            work.                                             │
│ --main-ref           TEXT  Ref to consider as the integration target         │
│                            (default: origin/main).                           │
│                                                        │
│ --help                     Show this message and exit.                       │
╰──────────────────────────────────────────────────────────────────────────────╯

wip


 Usage: workspaces.py wip [OPTIONS]

 Create WIP commits for changed files in workspace repos.

 - Show list of changed files. - Ask whether to stage all, none, or pick some
 files. - Stage chosen files. - Make commit: 'wip: <workspace display name>'.

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --workspace  -w      TEXT  Workspace directory name to WIP-commit. If        │
│                            omitted, uses the workspace containing the        │
│                            current directory.                                │
│ --help                     Show this message and exit.                       │
╰──────────────────────────────────────────────────────────────────────────────╯

commit


 Usage: workspaces.py commit [OPTIONS]

 Create commits with specified message for workspace repos.

 - Show list of changed files. - Ask whether to stage all, none, or pick some
 files. - Stage chosen files.

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --workspace  -w      TEXT  Workspace directory name to WIP-commit. If        │
│                            omitted, uses the workspace containing the        │
│                            current directory.                                │
│ --message    -m      TEXT  Commit message to use.                            │
│ --help                     Show this message and exit.                       │
╰──────────────────────────────────────────────────────────────────────────────╯

push


 Usage: workspaces.py push [OPTIONS]

 For each repo in the workspace, run 'git push <remote> <current-branch>'.

 Skips repos with no current branch or when push fails.

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --workspace  -w      TEXT  Workspace directory name to push. If omitted,     │
│                            uses the workspace containing the current         │
│                            directory.                                        │
│ --remote     -r      TEXT  Remote name to push to (default: origin).         │
│                                                             │
│ --help                     Show this message and exit.                       │
╰──────────────────────────────────────────────────────────────────────────────╯

status


 Usage: workspaces.py status [OPTIONS]

 Show detailed status for the current (or specified) workspace.

 For each repo in the workspace: - repo directory name - branch -
 ahead/behind/dirty indicator - number of changed files

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --workspace  -w      TEXT  Workspace directory name to show status for. If   │
│                            omitted, uses the workspace containing the        │
│                            current directory.                                │
│ --help                     Show this message and exit.                       │
╰──────────────────────────────────────────────────────────────────────────────╯

diff


 Usage: workspaces.py diff [OPTIONS]

 Show git diff for all repos in the workspace.

 Uses rich Syntax highlighter for diffs.

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --workspace  -w      TEXT  Workspace directory name to diff. If omitted,     │
│                            uses the workspace containing the current         │
│                            directory.                                        │
│ --staged     -s            Show only staged changes (git diff --cached).     │
│ --help                     Show this message and exit.                       │
╰──────────────────────────────────────────────────────────────────────────────╯

clear


 Usage: workspaces.py clear [OPTIONS]

 Clear empty workspaces.

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --dry-run  -n        Show what would be deleted, but don't actually delete.  │
│ --help               Show this message and exit.                             │
╰──────────────────────────────────────────────────────────────────────────────╯

clean


 Usage: workspaces.py clean [OPTIONS]

 Show detailed status for the current (or specified) workspace.

 For each repo in the workspace: - look up all local branches that are merged
 into main - ask user to delete local branches that are merged into main - look
 up all remote branches that are merged into main - ask user to delete remote
 branches that are merged into main

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --workspace  -w      TEXT  Workspace directory name to diff. If omitted,     │
│                            uses the workspace containing the current         │
│                            directory.                                        │
│ --dry-run    -n            Print what would be deleted, but don't actually   │
│                            delete.                                           │
│ --help                     Show this message and exit.                       │
╰──────────────────────────────────────────────────────────────────────────────╯

move-staged


 Usage: workspaces.py move-staged [OPTIONS]

 Create a patch for staged changes and move them to another workspace.

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --workspace  -w      TEXT  Workspace directory name to diff. If omitted,     │
│                            uses the workspace containing the current         │
│                            directory.                                        │
│ --help                     Show this message and exit.                       │
╰──────────────────────────────────────────────────────────────────────────────╯

attach


 Usage: workspaces.py attach [OPTIONS]

 Attach to a workspace.

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --workspace  -w      TEXT  Workspace directory name to show status for. If   │
│                            omitted, uses the workspace containing the        │
│                            current directory.                                │
│ --repo       -r      TEXT  Repo name to attach to. If omitted, uses iterfzf  │
│                            to pick.                                          │
│ --help                     Show this message and exit.                       │
╰──────────────────────────────────────────────────────────────────────────────╯

tmux


 Usage: workspaces.py tmux [OPTIONS] COMMAND [ARGS]...

 tmux commands

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --help          Show this message and exit.                                  │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ───────────────────────────────────────────────────────────────────╮
│ attach          Attach or create a tmux session for a repo in a workspace.   │
│ list-sessions   List tmux workspace sessions.                                │
│ remove          Remove tmux workspace sessions.                              │
╰──────────────────────────────────────────────────────────────────────────────╯

tmux attach


 Usage: workspaces.py tmux attach [OPTIONS]

 Attach or create a tmux session for a repo in a workspace.

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --workspace  -w      TEXT  Workspace directory name to show status for. If   │
│                            omitted, uses the workspace containing the        │
│                            current directory.                                │
│ --repo       -r      TEXT  Repo name to attach to. If omitted, uses iterfzf  │
│                            to pick.                                          │
│ --help                     Show this message and exit.                       │
╰──────────────────────────────────────────────────────────────────────────────╯

tmux list-sessions


 Usage: workspaces.py tmux list-sessions [OPTIONS]

 List tmux workspace sessions.

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --workspace  -w      TEXT  Workspace directory name to show status for. If   │
│                            omitted, uses the workspace containing the        │
│                            current directory.                                │
│ --help                     Show this message and exit.                       │
╰──────────────────────────────────────────────────────────────────────────────╯

tmux remove


 Usage: workspaces.py tmux remove [OPTIONS]

 Remove tmux workspace sessions.

╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --workspace  -w      TEXT  Workspace directory name to show status for. If   │
│                            omitted, uses the workspace containing the        │
│                            current directory.                                │
│ --dry-run    -d            Don't actually remove the session.                │
│ --help                     Show this message and exit.                       │
╰──────────────────────────────────────────────────────────────────────────────╯

directory layout

repos_dir
  repo1
  repo2
  repo3
  ...
workspaces_dir
  workspace1
    readme.md
    repo1 (worktree)
    repo2 (worktree)
  workspace2
    readme.md
    repo2 (worktree)
    repo3 (worktree)
  ...