@gw-tools/gw 0.2.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +677 -47
- package/install.js +33 -0
- package/package.json +4 -2
- package/uninstall.js +50 -0
package/README.md
CHANGED
|
@@ -2,25 +2,99 @@
|
|
|
2
2
|
|
|
3
3
|
A command-line tool for managing git worktrees, built with Deno.
|
|
4
4
|
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [gw - Git Worktree Tools](#gw---git-worktree-tools)
|
|
8
|
+
- [Table of Contents](#table-of-contents)
|
|
9
|
+
- [Quick Start](#quick-start)
|
|
10
|
+
- [Features](#features)
|
|
11
|
+
- [Installation](#installation)
|
|
12
|
+
- [Via npm (Recommended)](#via-npm-recommended)
|
|
13
|
+
- [Build from source](#build-from-source)
|
|
14
|
+
- [Configuration](#configuration)
|
|
15
|
+
- [Auto-Detection](#auto-detection)
|
|
16
|
+
- [Example Configuration](#example-configuration)
|
|
17
|
+
- [Configuration Options](#configuration-options)
|
|
18
|
+
- [Commands](#commands)
|
|
19
|
+
- [add](#add)
|
|
20
|
+
- [Arguments](#arguments)
|
|
21
|
+
- [Options](#options)
|
|
22
|
+
- [Examples](#examples)
|
|
23
|
+
- [Auto-Copy Configuration](#auto-copy-configuration)
|
|
24
|
+
- [cd](#cd)
|
|
25
|
+
- [Arguments](#arguments-1)
|
|
26
|
+
- [Examples](#examples-1)
|
|
27
|
+
- [How It Works](#how-it-works)
|
|
28
|
+
- [install-shell](#install-shell)
|
|
29
|
+
- [Options](#options-1)
|
|
30
|
+
- [Examples](#examples-2)
|
|
31
|
+
- [root](#root)
|
|
32
|
+
- [Examples](#examples-3)
|
|
33
|
+
- [How It Works](#how-it-works-1)
|
|
34
|
+
- [init](#init)
|
|
35
|
+
- [Options](#options-2)
|
|
36
|
+
- [Examples](#examples-4)
|
|
37
|
+
- [When to Use](#when-to-use)
|
|
38
|
+
- [sync](#sync)
|
|
39
|
+
- [Arguments](#arguments-2)
|
|
40
|
+
- [Options](#options-3)
|
|
41
|
+
- [Examples](#examples-5)
|
|
42
|
+
- [Git Worktree Proxy Commands](#git-worktree-proxy-commands)
|
|
43
|
+
- [list (ls)](#list-ls)
|
|
44
|
+
- [remove (rm)](#remove-rm)
|
|
45
|
+
- [move (mv)](#move-mv)
|
|
46
|
+
- [prune](#prune)
|
|
47
|
+
- [lock](#lock)
|
|
48
|
+
- [unlock](#unlock)
|
|
49
|
+
- [repair](#repair)
|
|
50
|
+
- [Use Case](#use-case)
|
|
51
|
+
- [Typical Workflow](#typical-workflow)
|
|
52
|
+
- [Development](#development)
|
|
53
|
+
- [Local Development \& Testing](#local-development--testing)
|
|
54
|
+
- [Method 1: Shell Alias (Recommended for Active Development)](#method-1-shell-alias-recommended-for-active-development)
|
|
55
|
+
- [Method 2: Symlink to Compiled Binary (Faster Execution)](#method-2-symlink-to-compiled-binary-faster-execution)
|
|
56
|
+
- [Method 3: Development Wrapper Script (Best of Both Worlds)](#method-3-development-wrapper-script-best-of-both-worlds)
|
|
57
|
+
- [Method 4: npm link (For Testing Installation)](#method-4-npm-link-for-testing-installation)
|
|
58
|
+
- [Watch Mode for Active Development](#watch-mode-for-active-development)
|
|
59
|
+
- [Available Scripts](#available-scripts)
|
|
60
|
+
- [Publishing](#publishing)
|
|
61
|
+
- [Automated Release (Recommended)](#automated-release-recommended)
|
|
62
|
+
- [Manual Publishing](#manual-publishing)
|
|
63
|
+
- [Publishing to JSR (Optional)](#publishing-to-jsr-optional)
|
|
64
|
+
- [Version Management](#version-management)
|
|
65
|
+
- [Project Structure](#project-structure)
|
|
66
|
+
- [Adding New Commands](#adding-new-commands)
|
|
67
|
+
- [License](#license)
|
|
68
|
+
|
|
5
69
|
## Quick Start
|
|
6
70
|
|
|
7
71
|
```bash
|
|
8
72
|
# Install
|
|
9
73
|
npm install -g @gw-tools/gw
|
|
10
74
|
|
|
11
|
-
# Create a new worktree
|
|
12
|
-
|
|
75
|
+
# Create a new worktree and copy files
|
|
76
|
+
gw add feat-new-feature .env secrets/
|
|
77
|
+
|
|
78
|
+
# Navigate to your new worktree
|
|
79
|
+
gw cd feat-new-feature
|
|
80
|
+
```
|
|
13
81
|
|
|
14
|
-
|
|
15
|
-
|
|
82
|
+
**Or with auto-copy (one-time setup):**
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Configure auto-copy files once per repository
|
|
86
|
+
gw init --root $(gw root) --auto-copy-files .env,secrets/
|
|
16
87
|
|
|
17
|
-
#
|
|
18
|
-
|
|
88
|
+
# Now just create worktrees - files are copied automatically
|
|
89
|
+
gw add feat-another-feature
|
|
90
|
+
gw cd feat-another-feature
|
|
19
91
|
```
|
|
20
92
|
|
|
21
93
|
## Features
|
|
22
94
|
|
|
95
|
+
- **Quick navigation**: Navigate to worktrees instantly with smart partial matching (`gw cd feat` finds `feat-branch`)
|
|
23
96
|
- **Copy files between worktrees**: Easily copy secrets, environment files, and configurations from one worktree to another
|
|
97
|
+
- **Automatic shell integration**: Shell function installs automatically on npm install for seamless `gw cd` navigation
|
|
24
98
|
- **Multi-command architecture**: Extensible framework for adding new worktree management commands
|
|
25
99
|
- **Auto-configured per repository**: Each repository gets its own local config file, automatically created on first use
|
|
26
100
|
- **Dry-run mode**: Preview what would be copied without making changes
|
|
@@ -63,65 +137,551 @@ cp dist/packages/gw-tool/gw /usr/local/bin/gw
|
|
|
63
137
|
|
|
64
138
|
## Configuration
|
|
65
139
|
|
|
66
|
-
On first run, `gw` will automatically create a configuration file at
|
|
140
|
+
On first run, `gw` will automatically detect your git repository root and create a configuration file at `.gw/config.json`. The tool finds the config by walking up the directory tree from your current location, so you can run `gw` commands from anywhere within your repository.
|
|
141
|
+
|
|
142
|
+
### Auto-Detection
|
|
143
|
+
|
|
144
|
+
The tool automatically:
|
|
145
|
+
|
|
146
|
+
1. **Searches for existing config**: Walks up from your current directory looking for `.gw/config.json`
|
|
147
|
+
2. **Auto-detects git root**: If no config is found, detects the repository root automatically
|
|
148
|
+
3. **Creates config**: Saves the detected root and default settings to `.gw/config.json`
|
|
149
|
+
|
|
150
|
+
If auto-detection fails (rare edge cases), you can manually initialize:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
gw init --root /path/to/your/repo.git
|
|
154
|
+
```
|
|
67
155
|
|
|
68
156
|
### Example Configuration
|
|
69
157
|
|
|
70
158
|
```json
|
|
71
159
|
{
|
|
72
|
-
"
|
|
160
|
+
"root": "/Users/username/Workspace/my-project.git",
|
|
161
|
+
"defaultBranch": "main",
|
|
162
|
+
"autoCopyFiles": [
|
|
163
|
+
".env",
|
|
164
|
+
"components/agents/.env",
|
|
165
|
+
"components/ui/.vercel/"
|
|
166
|
+
]
|
|
73
167
|
}
|
|
74
168
|
```
|
|
75
169
|
|
|
76
170
|
### Configuration Options
|
|
77
171
|
|
|
78
|
-
- **
|
|
172
|
+
- **root**: Absolute path to the git repository root (automatically detected or manually set with `gw init`)
|
|
173
|
+
- **defaultBranch**: Default source worktree name (optional, defaults to "main")
|
|
174
|
+
- **autoCopyFiles**: Array of file/directory paths to automatically copy when creating worktrees with `gw add` (optional, only set via `gw init --auto-copy-files`)
|
|
79
175
|
|
|
80
176
|
## Commands
|
|
81
177
|
|
|
82
|
-
###
|
|
178
|
+
### add
|
|
179
|
+
|
|
180
|
+
Create a new git worktree with optional automatic file copying.
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
gw add <worktree-name> [files...]
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
This command wraps `git worktree add` and optionally copies files to the new worktree. If `autoCopyFiles` is configured, those files are automatically copied. You can override this by specifying files as arguments.
|
|
187
|
+
|
|
188
|
+
#### Arguments
|
|
189
|
+
|
|
190
|
+
- `<worktree-name>`: Name or path for the new worktree
|
|
191
|
+
- `[files...]`: Optional files to copy (overrides `autoCopyFiles` config)
|
|
192
|
+
|
|
193
|
+
#### Options
|
|
194
|
+
|
|
195
|
+
All `git worktree add` options are supported:
|
|
196
|
+
- `-b <branch>`: Create a new branch
|
|
197
|
+
- `-B <branch>`: Create or reset a branch
|
|
198
|
+
- `--detach`: Detach HEAD in new worktree
|
|
199
|
+
- `--force, -f`: Force checkout even if already checked out
|
|
200
|
+
- `--track`: Track branch from remote
|
|
201
|
+
- `-h, --help`: Show help message
|
|
202
|
+
|
|
203
|
+
#### Examples
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
# Create worktree (auto-copies files if autoCopyFiles is configured)
|
|
207
|
+
gw add feat/new-feature
|
|
208
|
+
|
|
209
|
+
# Create worktree with new branch
|
|
210
|
+
gw add feat/new-feature -b my-branch
|
|
211
|
+
|
|
212
|
+
# Create worktree and copy specific files (overrides config)
|
|
213
|
+
gw add feat/new-feature .env secrets/
|
|
214
|
+
|
|
215
|
+
# Force create even if branch exists elsewhere
|
|
216
|
+
gw add feat/bugfix -f
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
#### Auto-Copy Configuration
|
|
220
|
+
|
|
221
|
+
To enable automatic file copying, configure `autoCopyFiles` using `gw init`:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
gw init --root /path/to/repo.git --auto-copy-files .env,secrets/,components/ui/.vercel/
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
This creates:
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"root": "/path/to/repo.git",
|
|
231
|
+
"defaultBranch": "main",
|
|
232
|
+
"autoCopyFiles": [".env", "secrets/", "components/ui/.vercel/"]
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Now every time you run `gw add`, these files will be automatically copied from your default source worktree (usually `main`) to the new worktree.
|
|
237
|
+
|
|
238
|
+
### cd
|
|
239
|
+
|
|
240
|
+
Navigate directly to a worktree by name or partial match. The command uses smart matching to find worktrees, searching both branch names and worktree paths.
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
gw cd <worktree>
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
#### Arguments
|
|
247
|
+
|
|
248
|
+
- `<worktree>`: Name or partial name of the worktree (matches branch name or path)
|
|
249
|
+
|
|
250
|
+
#### Examples
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
# Navigate to a worktree by exact name
|
|
254
|
+
gw cd feat-branch
|
|
255
|
+
|
|
256
|
+
# Navigate using partial match (finds "feat-new-feature")
|
|
257
|
+
gw cd feat
|
|
258
|
+
|
|
259
|
+
# If multiple matches found, shows list with helpful error:
|
|
260
|
+
gw cd api
|
|
261
|
+
# Output: Multiple worktrees match "api":
|
|
262
|
+
# api-refactor -> /path/to/repo/api-refactor
|
|
263
|
+
# graphql-api -> /path/to/repo/graphql-api
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
#### How It Works
|
|
267
|
+
|
|
268
|
+
The `cd` command integrates with your shell through an automatically installed function (see [install-shell](#install-shell)). When you run `gw cd <worktree>`:
|
|
269
|
+
|
|
270
|
+
1. The command finds the matching worktree path
|
|
271
|
+
2. The shell function intercepts the call and navigates you there
|
|
272
|
+
3. All other `gw` commands pass through normally
|
|
273
|
+
|
|
274
|
+
**Note**: Shell integration is automatically installed when you install via npm. If needed, you can manually install or remove it using `gw install-shell`.
|
|
275
|
+
|
|
276
|
+
### install-shell
|
|
277
|
+
|
|
278
|
+
Install or remove shell integration for the `gw cd` command. This is automatically run during `npm install`, but can be run manually if needed.
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
gw install-shell [options]
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
#### Options
|
|
285
|
+
|
|
286
|
+
- `--remove`: Remove shell integration
|
|
287
|
+
- `--quiet, -q`: Suppress output messages
|
|
288
|
+
- `-h, --help`: Show help message
|
|
289
|
+
|
|
290
|
+
#### Examples
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
# Install shell integration (usually not needed - auto-installed)
|
|
294
|
+
gw install-shell
|
|
295
|
+
|
|
296
|
+
# Remove shell integration
|
|
297
|
+
gw install-shell --remove
|
|
298
|
+
|
|
299
|
+
# Install quietly (for automation)
|
|
300
|
+
gw install-shell --quiet
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**Supported Shells:**
|
|
304
|
+
- **Zsh** (~/.zshrc)
|
|
305
|
+
- **Bash** (~/.bashrc)
|
|
306
|
+
- **Fish** (~/.config/fish/functions/gw.fish)
|
|
307
|
+
|
|
308
|
+
The command is idempotent - running it multiple times won't create duplicate entries.
|
|
309
|
+
|
|
310
|
+
### root
|
|
311
|
+
|
|
312
|
+
Get the root directory of the current git repository. For git worktrees, returns the parent directory containing all worktrees.
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
gw root
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
This command is useful when working with git worktrees to find the main repository directory that contains all worktrees, regardless of how deeply nested you are in the directory structure.
|
|
83
319
|
|
|
84
|
-
|
|
320
|
+
#### Examples
|
|
85
321
|
|
|
86
322
|
```bash
|
|
87
|
-
|
|
323
|
+
# Get repository root path
|
|
324
|
+
gw root
|
|
325
|
+
# Output: /Users/username/Workspace/my-project.git
|
|
326
|
+
|
|
327
|
+
# Navigate to repository root
|
|
328
|
+
cd "$(gw root)"
|
|
329
|
+
|
|
330
|
+
# List all worktrees
|
|
331
|
+
ls "$(gw root)"
|
|
332
|
+
|
|
333
|
+
# Use in scripts
|
|
334
|
+
REPO_ROOT=$(gw root)
|
|
335
|
+
echo "Repository is at: $REPO_ROOT"
|
|
336
|
+
|
|
337
|
+
# Works from any depth
|
|
338
|
+
cd /Users/username/Workspace/my-project.git/feat/deeply/nested/folder
|
|
339
|
+
gw root
|
|
340
|
+
# Output: /Users/username/Workspace/my-project.git
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
#### How It Works
|
|
344
|
+
|
|
345
|
+
- **In a worktree**: Returns the parent directory containing all worktrees (e.g., `/path/to/repo.git`)
|
|
346
|
+
- **In a regular repo**: Returns the directory containing the `.git` directory
|
|
347
|
+
- **From nested directories**: Walks up the directory tree to find the repository root
|
|
348
|
+
|
|
349
|
+
### init
|
|
350
|
+
|
|
351
|
+
Initialize gw configuration for a git repository. This command is only needed if auto-detection fails or if you want to manually specify the repository root.
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
gw init --root <path> [options]
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
#### Options
|
|
358
|
+
|
|
359
|
+
- `--root <path>`: Specify the git repository root path (required)
|
|
360
|
+
- `--default-source <name>`: Set the default source worktree (default: "main")
|
|
361
|
+
- `--auto-copy-files <files>`: Comma-separated list of files to auto-copy when creating worktrees with `gw add`
|
|
362
|
+
- `-h, --help`: Show help message
|
|
363
|
+
|
|
364
|
+
#### Examples
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
# Initialize with repository root
|
|
368
|
+
gw init --root /Users/username/Workspace/my-project.git
|
|
369
|
+
|
|
370
|
+
# Initialize with custom default source
|
|
371
|
+
gw init --root /Users/username/Workspace/my-project.git --default-source master
|
|
372
|
+
|
|
373
|
+
# Initialize with auto-copy files
|
|
374
|
+
gw init --root /Users/username/Workspace/my-project.git --auto-copy-files .env,secrets/,components/ui/.vercel/
|
|
375
|
+
|
|
376
|
+
# Show help
|
|
377
|
+
gw init --help
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
#### When to Use
|
|
381
|
+
|
|
382
|
+
In most cases, you won't need to run `gw init` manually because the tool auto-detects your repository root on first run. However, you may need it when:
|
|
383
|
+
|
|
384
|
+
- Auto-detection fails (rare edge cases with non-standard repository structures)
|
|
385
|
+
- You want to override the auto-detected root
|
|
386
|
+
- You're setting up configuration before the repository has standard git structures
|
|
387
|
+
|
|
388
|
+
The config file is created at `.gw/config.json` in your current directory, so you can run this command from wherever makes sense for your workflow (typically the repository root).
|
|
389
|
+
|
|
390
|
+
### sync
|
|
391
|
+
|
|
392
|
+
Sync files and directories between worktrees, preserving directory structure.
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
gw sync [options] <target-worktree> <files...>
|
|
88
396
|
```
|
|
89
397
|
|
|
90
398
|
#### Arguments
|
|
91
399
|
|
|
92
400
|
- `<target-worktree>`: Name or full path of the target worktree
|
|
93
|
-
- `<files...>`: One or more files or directories to
|
|
401
|
+
- `<files...>`: One or more files or directories to sync (paths relative to worktree root)
|
|
94
402
|
|
|
95
403
|
#### Options
|
|
96
404
|
|
|
97
405
|
- `--from <source>`: Source worktree name (default: from config or "main")
|
|
98
|
-
- `-n, --dry-run`: Show what would be
|
|
406
|
+
- `-n, --dry-run`: Show what would be synced without actually syncing
|
|
99
407
|
- `-h, --help`: Show help message
|
|
100
408
|
|
|
101
409
|
#### Examples
|
|
102
410
|
|
|
103
411
|
```bash
|
|
104
|
-
#
|
|
105
|
-
gw
|
|
412
|
+
# Sync .env file from main to feat-branch
|
|
413
|
+
gw sync feat-branch .env
|
|
106
414
|
|
|
107
|
-
#
|
|
108
|
-
gw
|
|
415
|
+
# Sync multiple files
|
|
416
|
+
gw sync feat-branch .env components/agents/.env components/agents/agents.yaml
|
|
109
417
|
|
|
110
|
-
#
|
|
111
|
-
gw
|
|
418
|
+
# Sync entire directory
|
|
419
|
+
gw sync feat-branch components/ui/.vercel
|
|
112
420
|
|
|
113
421
|
# Use custom source worktree
|
|
114
|
-
gw
|
|
422
|
+
gw sync --from develop feat-branch .env
|
|
115
423
|
|
|
116
424
|
# Dry run to preview changes
|
|
117
|
-
gw
|
|
425
|
+
gw sync --dry-run feat-branch .env
|
|
118
426
|
|
|
119
427
|
# Use absolute path as target
|
|
120
|
-
gw
|
|
428
|
+
gw sync /full/path/to/repo/feat-branch .env
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Git Worktree Proxy Commands
|
|
432
|
+
|
|
433
|
+
These commands wrap native `git worktree` operations, providing consistent colored output and help messages. All git flags and options are passed through transparently.
|
|
434
|
+
|
|
435
|
+
#### list (ls)
|
|
436
|
+
|
|
437
|
+
List all worktrees in the repository.
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
gw list
|
|
441
|
+
# or
|
|
442
|
+
gw ls
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
**Examples:**
|
|
446
|
+
```bash
|
|
447
|
+
gw list # List all worktrees
|
|
448
|
+
gw list --porcelain # Machine-readable output
|
|
449
|
+
gw list -v # Verbose output
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
#### remove (rm)
|
|
453
|
+
|
|
454
|
+
Remove a worktree from the repository.
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
gw remove <worktree>
|
|
458
|
+
# or
|
|
459
|
+
gw rm <worktree>
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**Examples:**
|
|
463
|
+
```bash
|
|
464
|
+
gw remove feat-branch # Remove a worktree
|
|
465
|
+
gw remove --force feat-branch # Force remove even if dirty
|
|
466
|
+
gw rm feat-branch # Using alias
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
#### move (mv)
|
|
470
|
+
|
|
471
|
+
Move a worktree to a new location.
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
gw move <worktree> <new-path>
|
|
475
|
+
# or
|
|
476
|
+
gw mv <worktree> <new-path>
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
**Examples:**
|
|
480
|
+
```bash
|
|
481
|
+
gw move feat-branch ../new-location
|
|
482
|
+
gw mv feat-branch ../new-location
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
#### prune
|
|
486
|
+
|
|
487
|
+
Clean up worktree information for deleted worktrees.
|
|
488
|
+
|
|
489
|
+
```bash
|
|
490
|
+
gw prune
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
**Examples:**
|
|
494
|
+
```bash
|
|
495
|
+
gw prune # Clean up stale worktree information
|
|
496
|
+
gw prune --dry-run # Preview what would be pruned
|
|
497
|
+
gw prune --verbose # Show detailed output
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
#### lock
|
|
501
|
+
|
|
502
|
+
Lock a worktree to prevent removal.
|
|
503
|
+
|
|
504
|
+
```bash
|
|
505
|
+
gw lock <worktree>
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
**Examples:**
|
|
509
|
+
```bash
|
|
510
|
+
gw lock feat-branch
|
|
511
|
+
gw lock --reason "Work in progress" feat-branch
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
#### unlock
|
|
515
|
+
|
|
516
|
+
Unlock a worktree to allow removal.
|
|
517
|
+
|
|
518
|
+
```bash
|
|
519
|
+
gw unlock <worktree>
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
**Examples:**
|
|
523
|
+
```bash
|
|
524
|
+
gw unlock feat-branch
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
#### repair
|
|
528
|
+
|
|
529
|
+
Repair worktree administrative files.
|
|
530
|
+
|
|
531
|
+
```bash
|
|
532
|
+
gw repair [<path>]
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
**Examples:**
|
|
536
|
+
```bash
|
|
537
|
+
gw repair # Repair all worktrees
|
|
538
|
+
gw repair /path/to/worktree # Repair specific worktree
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
## Use Case
|
|
542
|
+
|
|
543
|
+
This tool was originally created to simplify the workflow of copying secrets and environment files when creating new git worktrees. When you create a new worktree for a feature branch, you often need to copy `.env` files, credentials, and other configuration files from your main worktree to the new one. This tool automates that process.
|
|
544
|
+
|
|
545
|
+
The tool automatically detects which git repository you're working in and creates a local config file (`.gw/config.json`) on first use. The config stores the repository root and other settings, so subsequent runs are fast and don't need to re-detect the repository structure. Each repository has its own configuration, and you can customize the default source worktree per repository.
|
|
546
|
+
|
|
547
|
+
### Typical Workflow
|
|
548
|
+
|
|
549
|
+
```bash
|
|
550
|
+
# One-time setup: Configure auto-copy files
|
|
551
|
+
gw init --root $(gw root) --auto-copy-files .env,components/agents/.env,components/ui/.vercel/
|
|
552
|
+
|
|
553
|
+
# From within any worktree of your repository
|
|
554
|
+
# Create a new worktree with auto-copy
|
|
555
|
+
gw add feat-new-feature
|
|
556
|
+
|
|
557
|
+
# Navigate to your new worktree
|
|
558
|
+
gw cd feat-new-feature
|
|
559
|
+
|
|
560
|
+
# Alternative: Create worktree and copy specific files
|
|
561
|
+
gw add feat-bugfix .env custom-config.json
|
|
562
|
+
|
|
563
|
+
# Alternative: Use the manual sync command
|
|
564
|
+
git worktree add feat-manual
|
|
565
|
+
gw sync feat-manual .env
|
|
566
|
+
gw cd feat-manual
|
|
121
567
|
```
|
|
122
568
|
|
|
123
569
|
## Development
|
|
124
570
|
|
|
571
|
+
### Local Development & Testing
|
|
572
|
+
|
|
573
|
+
When developing the tool, you can test changes locally without publishing by creating a global symlink. This allows you to use the `gw` command with live code updates.
|
|
574
|
+
|
|
575
|
+
#### Method 1: Shell Alias (Recommended for Active Development)
|
|
576
|
+
|
|
577
|
+
Create a shell alias that runs the Deno version directly with watch mode:
|
|
578
|
+
|
|
579
|
+
```bash
|
|
580
|
+
# Add to your ~/.zshrc or ~/.bashrc
|
|
581
|
+
alias gw-dev='deno run --allow-all ~/path/to/gw-tools/packages/gw-tool/src/main.ts'
|
|
582
|
+
|
|
583
|
+
# Reload your shell
|
|
584
|
+
source ~/.zshrc # or ~/.bashrc
|
|
585
|
+
|
|
586
|
+
# Now you can use it anywhere
|
|
587
|
+
cd ~/some-project
|
|
588
|
+
gw-dev copy feat-branch .env
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
This gives you instant feedback - just edit the TypeScript files and run the command again.
|
|
592
|
+
|
|
593
|
+
#### Method 2: Symlink to Compiled Binary (Faster Execution)
|
|
594
|
+
|
|
595
|
+
Create a symlink to the compiled binary and recompile when needed:
|
|
596
|
+
|
|
597
|
+
```bash
|
|
598
|
+
# From the workspace root
|
|
599
|
+
nx run gw-tool:compile
|
|
600
|
+
|
|
601
|
+
# Create global symlink (one-time setup)
|
|
602
|
+
sudo ln -sf ~/path/to/gw-tools/dist/packages/gw-tool/gw /usr/local/bin/gw
|
|
603
|
+
|
|
604
|
+
# Now you can use `gw` globally
|
|
605
|
+
cd ~/some-project
|
|
606
|
+
gw sync feat-branch .env
|
|
607
|
+
|
|
608
|
+
# When you make changes, recompile
|
|
609
|
+
nx run gw-tool:compile
|
|
610
|
+
# The symlink automatically points to the new binary
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
#### Method 3: Development Wrapper Script (Best of Both Worlds)
|
|
614
|
+
|
|
615
|
+
Create a wrapper script that provides both speed and live updates:
|
|
616
|
+
|
|
617
|
+
```bash
|
|
618
|
+
# Create ~/bin/gw (make sure ~/bin is in your PATH)
|
|
619
|
+
cat > ~/bin/gw << 'EOF'
|
|
620
|
+
#!/bin/bash
|
|
621
|
+
# Check if we're in development mode (set GW_DEV=1 to use source)
|
|
622
|
+
if [ "$GW_DEV" = "1" ]; then
|
|
623
|
+
exec deno run --allow-all ~/path/to/gw-tools/packages/gw-tool/src/main.ts "$@"
|
|
624
|
+
else
|
|
625
|
+
exec ~/path/to/gw-tools/dist/packages/gw-tool/gw "$@"
|
|
626
|
+
fi
|
|
627
|
+
EOF
|
|
628
|
+
|
|
629
|
+
chmod +x ~/bin/gw
|
|
630
|
+
|
|
631
|
+
# Use compiled version (fast)
|
|
632
|
+
gw sync feat-branch .env
|
|
633
|
+
|
|
634
|
+
# Use development version with live updates
|
|
635
|
+
GW_DEV=1 gw sync feat-branch .env
|
|
636
|
+
|
|
637
|
+
# Or set it for your entire session
|
|
638
|
+
export GW_DEV=1
|
|
639
|
+
gw sync feat-branch .env
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
#### Method 4: npm link (For Testing Installation)
|
|
643
|
+
|
|
644
|
+
Test the npm package installation flow locally:
|
|
645
|
+
|
|
646
|
+
```bash
|
|
647
|
+
# Compile binaries
|
|
648
|
+
nx run gw-tool:compile-all
|
|
649
|
+
|
|
650
|
+
# Prepare npm package
|
|
651
|
+
nx run gw-tool:npm-pack
|
|
652
|
+
|
|
653
|
+
# Link the package globally
|
|
654
|
+
cd dist/packages/gw-tool/npm
|
|
655
|
+
npm link
|
|
656
|
+
|
|
657
|
+
# Now `gw` is available globally via npm
|
|
658
|
+
gw sync feat-branch .env
|
|
659
|
+
|
|
660
|
+
# When you make changes
|
|
661
|
+
cd ~/path/to/gw-tools
|
|
662
|
+
nx run gw-tool:compile-all
|
|
663
|
+
nx run gw-tool:npm-pack
|
|
664
|
+
# The link automatically uses the updated binaries
|
|
665
|
+
|
|
666
|
+
# To unlink when done
|
|
667
|
+
npm unlink -g @gw-tools/gw
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
#### Watch Mode for Active Development
|
|
671
|
+
|
|
672
|
+
Use the watch mode to automatically restart when files change:
|
|
673
|
+
|
|
674
|
+
```bash
|
|
675
|
+
# Terminal 1: Run in watch mode
|
|
676
|
+
nx run gw-tool:dev
|
|
677
|
+
|
|
678
|
+
# Terminal 2: Test in another project
|
|
679
|
+
cd ~/some-project
|
|
680
|
+
~/path/to/gw-tools/dist/packages/gw-tool/gw sync feat-branch .env
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
**Pro tip**: Combine Method 3 (wrapper script) with watch mode by setting `GW_DEV=1` in your development shell.
|
|
684
|
+
|
|
125
685
|
### Available Scripts
|
|
126
686
|
|
|
127
687
|
```bash
|
|
@@ -287,13 +847,32 @@ packages/gw-tool/
|
|
|
287
847
|
│ ├── main.ts # CLI entry point and command dispatcher
|
|
288
848
|
│ ├── index.ts # Public API exports
|
|
289
849
|
│ ├── commands/ # Command implementations
|
|
290
|
-
│ │
|
|
850
|
+
│ │ ├── add.ts # Add command (create worktree with auto-copy)
|
|
851
|
+
│ │ ├── copy.ts # Sync command (sync files between worktrees)
|
|
852
|
+
│ │ ├── init.ts # Init command
|
|
853
|
+
│ │ ├── root.ts # Root command
|
|
854
|
+
│ │ ├── list.ts # List command (proxy)
|
|
855
|
+
│ │ ├── remove.ts # Remove command (proxy)
|
|
856
|
+
│ │ ├── move.ts # Move command (proxy)
|
|
857
|
+
│ │ ├── prune.ts # Prune command (proxy)
|
|
858
|
+
│ │ ├── lock.ts # Lock command (proxy)
|
|
859
|
+
│ │ ├── unlock.ts # Unlock command (proxy)
|
|
860
|
+
│ │ └── repair.ts # Repair command (proxy)
|
|
291
861
|
│ └── lib/ # Shared utilities
|
|
292
862
|
│ ├── types.ts # TypeScript type definitions
|
|
293
863
|
│ ├── config.ts # Configuration management
|
|
294
|
-
│ ├── cli.ts # CLI argument parsing
|
|
864
|
+
│ ├── cli.ts # CLI argument parsing & help
|
|
295
865
|
│ ├── file-ops.ts # File/directory operations
|
|
296
|
-
│
|
|
866
|
+
│ ├── path-resolver.ts # Path resolution utilities
|
|
867
|
+
│ ├── output.ts # Colored output formatting
|
|
868
|
+
│ └── git-proxy.ts # Git command proxy utilities
|
|
869
|
+
├── npm/ # npm package files
|
|
870
|
+
│ ├── package.json # npm package metadata
|
|
871
|
+
│ ├── install.js # Binary installation script
|
|
872
|
+
│ └── bin/
|
|
873
|
+
│ └── gw.js # Binary wrapper
|
|
874
|
+
├── scripts/
|
|
875
|
+
│ └── release.sh # Automated release script
|
|
297
876
|
├── deno.json # Deno configuration
|
|
298
877
|
├── project.json # Nx project configuration
|
|
299
878
|
└── README.md # This file
|
|
@@ -301,43 +880,94 @@ packages/gw-tool/
|
|
|
301
880
|
|
|
302
881
|
### Adding New Commands
|
|
303
882
|
|
|
304
|
-
|
|
883
|
+
There are two types of commands you can add:
|
|
305
884
|
|
|
306
|
-
|
|
307
|
-
|
|
885
|
+
#### Custom Commands (like `add`, `copy`)
|
|
886
|
+
|
|
887
|
+
For commands with custom logic, follow the pattern used by existing commands:
|
|
888
|
+
|
|
889
|
+
1. **Create a new file** in `src/commands/` (e.g., `list.ts`):
|
|
308
890
|
```typescript
|
|
309
|
-
|
|
891
|
+
// src/commands/list.ts
|
|
892
|
+
export async function executeList(args: string[]): Promise<void> {
|
|
893
|
+
// Check for help flag
|
|
894
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
895
|
+
console.log(`Usage: gw list
|
|
896
|
+
|
|
897
|
+
List all git worktrees in the current repository.
|
|
898
|
+
|
|
899
|
+
Options:
|
|
900
|
+
-h, --help Show this help message
|
|
901
|
+
`);
|
|
902
|
+
Deno.exit(0);
|
|
903
|
+
}
|
|
904
|
+
|
|
310
905
|
// Command implementation
|
|
906
|
+
// ...
|
|
311
907
|
}
|
|
312
908
|
```
|
|
313
|
-
|
|
909
|
+
|
|
910
|
+
2. **Import and register** the command in `src/main.ts`:
|
|
314
911
|
```typescript
|
|
912
|
+
import { executeList } from "./commands/list.ts";
|
|
913
|
+
|
|
315
914
|
const COMMANDS = {
|
|
316
|
-
|
|
317
|
-
|
|
915
|
+
add: executeAdd,
|
|
916
|
+
sync: executeCopy,
|
|
917
|
+
init: executeInit,
|
|
918
|
+
root: executeRoot,
|
|
919
|
+
list: executeList, // Add your new command
|
|
318
920
|
};
|
|
319
921
|
```
|
|
320
922
|
|
|
321
|
-
|
|
923
|
+
3. **Update global help** in `src/lib/cli.ts`:
|
|
924
|
+
```typescript
|
|
925
|
+
export function showGlobalHelp(): void {
|
|
926
|
+
console.log(`
|
|
927
|
+
Commands:
|
|
928
|
+
add Create a new worktree with optional auto-copy
|
|
929
|
+
sync Sync files/directories between worktrees
|
|
930
|
+
init Initialize gw configuration for a repository
|
|
931
|
+
root Get the root directory of the current git repository
|
|
932
|
+
list List all git worktrees in the repository
|
|
933
|
+
`);
|
|
934
|
+
}
|
|
935
|
+
```
|
|
322
936
|
|
|
323
|
-
|
|
937
|
+
#### Git Proxy Commands (like `list`, `remove`)
|
|
324
938
|
|
|
325
|
-
|
|
939
|
+
For simple pass-through commands that wrap git worktree operations, use the `git-proxy` utility:
|
|
326
940
|
|
|
327
|
-
|
|
941
|
+
1. **Create a new file** in `src/commands/` (e.g., `list.ts`):
|
|
942
|
+
```typescript
|
|
943
|
+
// src/commands/list.ts
|
|
944
|
+
import { executeGitWorktree, showProxyHelp } from '../lib/git-proxy.ts';
|
|
945
|
+
|
|
946
|
+
export async function executeList(args: string[]): Promise<void> {
|
|
947
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
948
|
+
showProxyHelp(
|
|
949
|
+
'list',
|
|
950
|
+
'list',
|
|
951
|
+
'List all worktrees in the repository',
|
|
952
|
+
['gw list', 'gw list --porcelain', 'gw list -v'],
|
|
953
|
+
);
|
|
954
|
+
Deno.exit(0);
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
await executeGitWorktree('list', args);
|
|
958
|
+
}
|
|
959
|
+
```
|
|
328
960
|
|
|
329
|
-
|
|
330
|
-
# From within any worktree of your repository
|
|
331
|
-
# Create a new worktree
|
|
332
|
-
git worktree add feat-new-feature
|
|
961
|
+
2. **Register** in `src/main.ts` (same as above)
|
|
333
962
|
|
|
334
|
-
|
|
335
|
-
# gw automatically detects your repository and uses its config
|
|
336
|
-
gw copy feat-new-feature .env components/agents/.env components/ui/.vercel
|
|
963
|
+
3. **Update global help** in `src/lib/cli.ts` (same as above)
|
|
337
964
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
965
|
+
This approach requires minimal maintenance as it simply forwards all arguments to git.
|
|
966
|
+
|
|
967
|
+
**Tips**:
|
|
968
|
+
- Look at [src/commands/root.ts](src/commands/root.ts) for a simple custom command
|
|
969
|
+
- Look at [src/commands/copy.ts](src/commands/copy.ts) for a complex command with argument parsing
|
|
970
|
+
- Look at [src/commands/list.ts](src/commands/list.ts) for a simple proxy command
|
|
341
971
|
|
|
342
972
|
## License
|
|
343
973
|
|
package/install.js
CHANGED
|
@@ -112,7 +112,13 @@ async function install() {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
console.log('✓ Installation complete!');
|
|
115
|
+
|
|
116
|
+
// Install shell integration
|
|
117
|
+
console.log('\n⚙️ Setting up shell integration...');
|
|
118
|
+
await installShellIntegration(binaryPath);
|
|
119
|
+
|
|
115
120
|
console.log('\nRun "gw --help" to get started.');
|
|
121
|
+
console.log('Tip: Restart your terminal or run "source ~/.zshrc" (or ~/.bashrc) to use "gw cd"');
|
|
116
122
|
} catch (error) {
|
|
117
123
|
console.error('\n✗ Installation failed:', error.message);
|
|
118
124
|
console.error('\nYou can manually download the binary from:');
|
|
@@ -125,5 +131,32 @@ async function install() {
|
|
|
125
131
|
}
|
|
126
132
|
}
|
|
127
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Install shell integration
|
|
136
|
+
*/
|
|
137
|
+
async function installShellIntegration(binaryPath) {
|
|
138
|
+
const { spawn } = require('child_process');
|
|
139
|
+
|
|
140
|
+
return new Promise((resolve) => {
|
|
141
|
+
const child = spawn(binaryPath, ['install-shell', '--quiet'], {
|
|
142
|
+
stdio: 'inherit'
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
child.on('close', (code) => {
|
|
146
|
+
if (code === 0) {
|
|
147
|
+
console.log('✓ Shell integration installed!');
|
|
148
|
+
} else {
|
|
149
|
+
console.log(' (Shell integration can be installed later with: gw install-shell)');
|
|
150
|
+
}
|
|
151
|
+
resolve();
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
child.on('error', (err) => {
|
|
155
|
+
console.log(' (Shell integration can be installed later with: gw install-shell)');
|
|
156
|
+
resolve();
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
128
161
|
// Run installation
|
|
129
162
|
install();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gw-tools/gw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "A command-line tool for managing git worktrees - copy files between worktrees with ease",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"git",
|
|
@@ -25,11 +25,13 @@
|
|
|
25
25
|
"gw": "./bin/gw.js"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
|
-
"postinstall": "node install.js"
|
|
28
|
+
"postinstall": "node install.js",
|
|
29
|
+
"preuninstall": "node uninstall.js"
|
|
29
30
|
},
|
|
30
31
|
"files": [
|
|
31
32
|
"bin/",
|
|
32
33
|
"install.js",
|
|
34
|
+
"uninstall.js",
|
|
33
35
|
"README.md"
|
|
34
36
|
],
|
|
35
37
|
"engines": {
|
package/uninstall.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Preuninstall script for @gw-tools/gw
|
|
5
|
+
* Removes shell integration before uninstalling the package
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { existsSync } = require('fs');
|
|
9
|
+
const { join } = require('path');
|
|
10
|
+
const { platform } = require('os');
|
|
11
|
+
const { spawnSync } = require('child_process');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Remove shell integration
|
|
15
|
+
*/
|
|
16
|
+
function uninstall() {
|
|
17
|
+
const binDir = join(__dirname, 'bin');
|
|
18
|
+
const binaryPath = join(binDir, platform() === 'win32' ? 'gw.exe' : 'gw');
|
|
19
|
+
|
|
20
|
+
// Check if binary exists
|
|
21
|
+
if (!existsSync(binaryPath)) {
|
|
22
|
+
console.log('Binary not found, skipping shell integration removal.');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log('🧹 Removing shell integration...');
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const result = spawnSync(binaryPath, ['install-shell', '--remove', '--quiet'], {
|
|
30
|
+
stdio: 'inherit',
|
|
31
|
+
timeout: 5000
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (result.error) {
|
|
35
|
+
console.log(' (Could not remove shell integration automatically)');
|
|
36
|
+
console.log(' You can manually remove it with: gw install-shell --remove');
|
|
37
|
+
} else if (result.status === 0) {
|
|
38
|
+
console.log('✓ Shell integration removed!');
|
|
39
|
+
} else {
|
|
40
|
+
console.log(' (Shell integration may not have been installed)');
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.log(' (Could not remove shell integration)');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log('\nTip: Restart your terminal to complete the removal.');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Run uninstall
|
|
50
|
+
uninstall();
|