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> |
||
|---|---|---|
| src/workspaces | ||
| .gitignore | ||
| justfile | ||
| prompt.md | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
| workspaces.py | ||
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)
...