A minimalist desktop app for managing multiple Git repositories across branches. Switch an entire group of repos to the right branches and pull updates with one click.
Built with Electron and vanilla HTML/CSS/JS. No frameworks, no build tools, no complexity.
- Installation
- Getting Started
- Features
- Import / Export
- Git Safety
- Developer Guide
- File Reference
- Config File
- Troubleshooting
- Download
Git Workspace Manager 1.0.0.exefrom thedist/folder. - Place it anywhere (Desktop, USB drive, etc.).
- Double-click to run. No installation needed.
Prerequisite: Git must be installed and available on your system PATH.
Prerequisites: Node.js (v18+) and Git.
cd git-workspace
npm install
npm startBefore you can create workspaces, the app needs to know where your repos live on disk.
- Click Manage Registry in the sidebar.
- Click Add Repository to pick a single repo folder, or Scan Folder to automatically find all repos inside a parent directory (e.g.
C:\Repositories). - The app reads each repo's
git remote originURL and extracts an identifier likemyorg/my-repo.
A workspace is a named group of repositories, each with an optional target branch.
- Click + New Workspace in the sidebar.
- Give it a name (e.g. "Development", "Staging").
- Check the repos you want to include.
- For each repo, type a branch name (e.g.
development) or leave blank to stay on whatever branch it's currently on. - Click Save.
- Select a workspace from the sidebar.
- Click Sync All.
- The app will check each repo for uncommitted changes. If any are dirty, you'll see a warning and can choose to skip them.
- For each clean repo, it will: fetch all remotes, checkout the target branch (if specified), and pull latest changes.
- Progress and results are shown per-repo in the table.
| Feature | Description |
|---|---|
| Workspace management | Create, edit, and delete named groups of repos with target branches. |
| Sync All | Fetch + checkout + pull for every repo in a workspace, with per-repo progress. |
| Dirty repo warnings | Repos with uncommitted changes are flagged and skipped during sync. |
| Repository registry | Central list mapping org/repo identifiers to local paths. |
| Scan Folder | Batch-add all repos from a parent directory. |
| Import / Export | Move workspace configs between machines without sharing local paths. |
| Status indicators | Green = clean, Red = dirty, Yellow = error/missing. |
- Select a workspace and click Export.
- Choose where to save the
.jsonfile. - The exported file contains only repo identifiers (
org/repo) and branch names. No local paths are included.
Example export file:
{
"name": "Development",
"exportedAt": "2026-03-18T12:00:00Z",
"repos": [
{ "id": "myorg/api-server", "branch": "development" },
{ "id": "myorg/web-client", "branch": "development" },
{ "id": "myorg/shared-lib", "branch": null }
]
}- Click Import Workspace in the sidebar and select a
.jsonfile. - The app checks that every repo in the file exists in your local registry.
- If all repos are found: The workspace is created.
- If any repos are missing: The import is blocked and you'll see a list of missing repos. Add them to your registry first, then try again.
This lets you share workspace configs between machines where the same repos may live at different paths.
This app is designed to be accident-proof. It only runs safe, read-or-pull git operations:
| Allowed | NOT allowed (not in the app at all) |
|---|---|
git fetch --all |
git merge |
git checkout <branch> |
git rebase |
git pull |
git reset |
git status --porcelain |
git push |
git rev-parse --abbrev-ref HEAD |
git clean |
git remote get-url origin |
git stash |
git branch -a |
Any --force flag |
All git commands are executed via Node.js execFile with arguments passed as arrays (not shell strings), preventing command injection. There is no generic "run any git command" function.
git-workspace/
package.json # npm config, Electron version, build settings
main.js # Electron main process
preload.js # Context bridge (main <-> renderer)
git.js # Git command whitelist
index.html # UI markup
styles.css # Styling
renderer.js # UI logic
dist/ # Built executables (after npm run build)
The app follows Electron's standard architecture with three layers:
-
Main process (
main.js) - Runs in Node.js. Handles file I/O, config persistence, native dialogs, and git operations. Exposes functionality to the renderer via IPC handlers. -
Preload (
preload.js) - The bridge. Uses Electron'scontextBridgeto expose a safewindow.apiobject to the renderer. The renderer cannot access Node.js directly. -
Renderer (
renderer.js+index.html+styles.css) - Runs in the browser window. All UI logic: DOM manipulation, event handlers, view switching. Callswindow.api.*methods to talk to the main process.
npm startThis launches Electron and loads the app. Changes to renderer.js, index.html, or styles.css take effect after reloading the window (Ctrl+R). Changes to main.js, preload.js, or git.js require restarting the app.
npm run buildThis uses electron-builder to create a portable .exe in the dist/ folder. The executable bundles the Electron runtime and all source files - no Node.js installation needed on the target machine.
To add new functionality accessible from the UI:
main.js- Add a handler:ipcMain.handle('my-action', async (_e, arg) => { ... })preload.js- Expose it: addmyAction: (arg) => ipcRenderer.invoke('my-action', arg)to thecontextBridgeobjectrenderer.js- Call it:const result = await window.api.myAction(arg)
The only file that runs git commands. Contains a private run() function that calls execFile('git', args, { cwd }) and 7 public functions (gitFetch, gitCheckout, gitPull, gitStatus, gitCurrentBranch, gitRemoteUrl, gitBranchList) plus a parseRepoId helper. To audit git safety, you only need to read this one file.
Handles:
- Config management - reads/writes
config.jsonfrom%APPDATA%/git-workspace/. Config path is lazily initialized after Electron is ready. - IPC handlers -
get-config,save-config,pick-repo-folder,scan-folder,repo-status,repo-branches,sync-repo,export-workspace,import-workspace,check-git. - Window creation - single window, no menu bar, context isolation enabled.
Maps each IPC channel to a method on window.api. This is the complete list of what the renderer can do - nothing more.
Single-page app with:
- Sidebar - workspace list, new workspace button, registry and import buttons.
- Three views - welcome (empty state), workspace (repo table + sync), registry (repo list + add/scan).
- Four modals - workspace create/edit, dirty repo warning, import result, git-not-found overlay.
Dark theme with Catppuccin-inspired colors. Defines CSS variables at :root for easy theming. Covers layout, sidebar, tables, buttons, modals, status indicators, and scrollbars.
All DOM manipulation and event handling. Key functions:
init()- checks git availability, loads config, renders sidebar.renderWorkspace()/fetchRepoStatus()- builds the repo table and fetches live status.syncAll()- orchestrates the sync: checks dirty repos, shows warning, syncs sequentially.openWorkspaceModal()- handles create/edit with registry-based repo picker.importWorkspace()/exportWorkspace()- portable workspace transfer.renderRegistry()/addRepoToRegistry()/scanFolderToRegistry()- registry management.
Location: %APPDATA%/git-workspace/config.json
{
"registry": [
{
"id": "myorg/my-repo",
"localPath": "C:\\Repositories\\my-repo"
}
],
"workspaces": [
{
"id": "ws-1710000000000",
"name": "Development",
"repos": [
{ "registryId": "myorg/my-repo", "branch": "development" },
{ "registryId": "myorg/other-repo", "branch": null }
]
}
]
}- registry - maps
org/repo(from git remote URL) to local filesystem path. - workspaces - each has a unique ID, name, and list of repos referencing the registry.
branch: nullmeans "stay on whatever branch is currently checked out."
If this file becomes corrupt, the app backs it up as config.json.backup and creates a fresh empty config.
| Problem | Solution |
|---|---|
| App shows "Git Not Found" | Install Git and make sure git --version works in your terminal. |
| Repo shows yellow dot in registry | The local path no longer exists or is not a git repo. Update or remove it. |
| Sync skips a repo as "dirty" | That repo has uncommitted changes. Commit or stash them first. |
| Checkout fails during sync | The target branch may not exist. Check the branch name in workspace settings. |
| Config lost between restarts | Make sure you're on version 1.0.0+. Earlier versions had a config path bug. |
| Import fails with missing repos | Add the listed repos to your registry first (Add Repository or Scan Folder). |