Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@ updates:
directory: "/"
schedule:
interval: "weekly"
# github-actions does not support semver-specific cooldowns.
cooldown:
semver-minor-days: 3
semver-patch-days: 3
semver-major-days: 30
default-days: 3
open-pull-requests-limit: 10
groups:
minor-and-patch:
Expand Down
38 changes: 35 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,43 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

This is a TypeScript CLI tool that provides Git workspace management utilities. The codebase is organized as follows:

- **src/bin/** - CLI entry points for all git commands (git-recent, git-merged, git-workspace, etc.)
- **src/lib/** - Core library functions and utilities shared across commands
- **src/tasks/** - Task implementations for workspace operations (clone, sync)
- **src/bin/** - CLI entry points, one file per installed binary (see CLI Commands below)
- **src/lib/** - Core library functions, grouped by theme, each with an `index.ts` barrel re-exported from `src/lib/index.ts`:
- `item/` - repository domain model (types, link parsing, location, filtering, status)
- `config/` - configuration and CLI plumbing (read/write git workspace config, filtered registry queries, shared commander options)
- `branches/` - git branch operations (list recent, exclude operational, select to checkout/delete)
- `worktrees/` - git worktree operations (list, add, select to remove)
- **src/tasks/** - Task implementations run on the concurrent task pool (clone/sync an item, submit item tasks)
- **dist/** - Compiled JavaScript output (generated by TypeScript compiler)

Layering is one-directional: `bin → tasks → lib` and `bin → lib`. `lib` never imports from `tasks`.

### CLI Commands

Each binary is registered in `package.json` `bin` and invoked as a git subcommand (e.g. `git recent`).

**Branch commands** (operate on the current repo, no workspace config required):

- `git recent` - list recently checked-out branches, select one to check out
- `git merged` / `git squashed` - list merged/squashed branches, multi-select to delete
- `git stale` / `git tidy` - list branches, multi-select to delete
- `git list-remote` - list remote branches, select one to check out

**Worktree command** - `git work` with subcommands (operates on the current repo):

- `git work add <name>` - create a worktree under the `<item>-worktrees` sibling folder
- `git work remove` - list worktrees, multi-select to remove

**Workspace command** - `git workspace` with subcommands (backed by git config + registry):

- `git workspace` / `git workspace list` - list registered repos (default subcommand)
- `git workspace add <urlConnection>` - register a repo
- `git workspace remove` - remove matching repos
- `git workspace clone` / `git workspace sync` - clone/sync matching repos via the task pool
- `git workspace config` - interactively write the git global workspace config

`git work` and `git workspace` use [commander](https://github.com/tj/commander.js) subcommands in a single binary. The filter options (`-w/--workspace`, `--ns/--namespace`, `-h/--host`, `-s/--slug`, `-k/--keyword`) are shared via `withFilterOptions`. Workspace config is loaded lazily inside each action (not at startup) so `git workspace config` works before any config exists.

### Key Components

**Workspace Management**: The system uses git global config to store workspace configuration:
Expand Down
25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,27 @@ git squashed origin/master
git tidy
```

#### working with worktrees

- add a worktree for the current repo, created under the `<item>-worktrees`
sibling folder

```bash
git work add <name>
```

- list the current repo worktrees, multi select the ones you want to remove

```bash
git work remove
```

#### setup git workspaces

- start by some configuration

```bash
git workspace-config
git workspace config
```

it will add the following git global config entries
Expand All @@ -82,8 +97,8 @@ git.workspaces.default # your default workspace, i.e open-sourc
- add a repo to your workspace

```bash
git workspace-add <urlConnection> # adds to default workspace
git workspace-add <urlConnect> -w <name> # adds to workspace with provided name
git workspace add <urlConnection> # adds to default workspace
git workspace add <urlConnect> -w <name> # adds to workspace with provided name
```

- view your workspace
Expand All @@ -96,8 +111,8 @@ git workspace -w <name> # describes workspace with provided name
- clone your entire workspace

```bash
git workspace-clone
git workspace-clone -w <name>
git workspace clone
git workspace clone -w <name>
```

> [!NOTE]
Expand Down
8 changes: 2 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,8 @@
"git-squashed": "dist/bin/git-squashed.js",
"git-stale": "dist/bin/git-stale.js",
"git-tidy": "dist/bin/git-tidy.js",
"git-workspace": "dist/bin/git-workspace.js",
"git-workspace-add": "dist/bin/git-workspace-add.js",
"git-workspace-clone": "dist/bin/git-workspace-clone.js",
"git-workspace-config": "dist/bin/git-workspace-config.js",
"git-workspace-remove": "dist/bin/git-workspace-remove.js",
"git-workspace-sync": "dist/bin/git-workspace-sync.js"
"git-work": "dist/bin/git-work.js",
"git-workspace": "dist/bin/git-workspace.js"
},
"scripts": {
"build": "tsc",
Expand Down
2 changes: 1 addition & 1 deletion src/bin/git-list-remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/
import { simpleGit } from 'simple-git';

import { selectAndCheckoutBranch } from '../lib/select-and-checkout-branch';
import { selectAndCheckoutBranch } from '../lib/branches';

const listRemoteBranches = async ({ pattern }: { pattern?: string }) => {
const params = ['--remote'];
Expand Down
6 changes: 4 additions & 2 deletions src/bin/git-merged.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
*/
import { simpleGit } from 'simple-git';

import { excludeOperationalBranches } from '../lib/exclude-operational-branches';
import { selectAndDeleteBranches } from '../lib/select-and-delete-branches';
import {
excludeOperationalBranches,
selectAndDeleteBranches
} from '../lib/branches';

const listMergedBranches = async ({ target }: { target?: string }) => {
const params = ['--merged'];
Expand Down
3 changes: 1 addition & 2 deletions src/bin/git-recent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { listRecentBranches } from '../lib/list-recent-branches';
import { selectAndCheckoutBranch } from '../lib/select-and-checkout-branch';
import { listRecentBranches, selectAndCheckoutBranch } from '../lib/branches';

listRecentBranches()
.then(selectAndCheckoutBranch(/\d{4}-\d{2}-\d{2} (?<branchName>.*)/))
Expand Down
6 changes: 4 additions & 2 deletions src/bin/git-squashed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
*/
import { simpleGit } from 'simple-git';

import { excludeOperationalBranches } from '../lib/exclude-operational-branches';
import { selectAndDeleteBranches } from '../lib/select-and-delete-branches';
import {
excludeOperationalBranches,
selectAndDeleteBranches
} from '../lib/branches';

const listSquashedBranches = async ({ target }: { target?: string }) => {
const params = ['--no-contains'];
Expand Down
3 changes: 1 addition & 2 deletions src/bin/git-stale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { listRecentBranches } from '../lib/list-recent-branches';
import { selectAndDeleteBranches } from '../lib/select-and-delete-branches';
import { listRecentBranches, selectAndDeleteBranches } from '../lib/branches';

const mainBranches =
/^\d{4}-\d{2}-\d{2} (main|master|production|demo|website|development)$/;
Expand Down
8 changes: 5 additions & 3 deletions src/bin/git-tidy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
* limitations under the License.
*/

import { excludeOperationalBranches } from '../lib/exclude-operational-branches';
import { listRecentBranches } from '../lib/list-recent-branches';
import { selectAndDeleteBranches } from '../lib/select-and-delete-branches';
import {
excludeOperationalBranches,
listRecentBranches,
selectAndDeleteBranches
} from '../lib/branches';

listRecentBranches()
.then(excludeOperationalBranches)
Expand Down
40 changes: 40 additions & 0 deletions src/bin/git-work.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#! /usr/bin/env node

/**
* Copyright 2023 Hasnae Rehioui
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Command } from 'commander';

import { addWorktree, listWorktrees, selectAndRemoveWorktrees } from '../lib';

const program = new Command();
program.description('manage git worktrees for the current repo');

program
.command('add')
.description('add a worktree under the <item>-worktrees sibling folder')
.argument('<name>', 'worktree name')
.action(async (name) => {
await addWorktree(name);
});

program
.command('remove')
.description('list worktrees and select the ones to remove')
.action(async () => {
await listWorktrees().then(selectAndRemoveWorktrees);
});

program.parseAsync(process.argv).catch(console.error);
37 changes: 0 additions & 37 deletions src/bin/git-workspace-add.ts

This file was deleted.

54 changes: 0 additions & 54 deletions src/bin/git-workspace-clone.ts

This file was deleted.

54 changes: 0 additions & 54 deletions src/bin/git-workspace-sync.ts

This file was deleted.

Loading
Loading