easy-worktree 0.1.1__py3-none-any.whl → 0.1.2__py3-none-any.whl

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.
easy_worktree/__init__.py CHANGED
@@ -131,14 +131,14 @@ MESSAGES = {
131
131
  "en": "Completed sync of {} files",
132
132
  "ja": "{} 個のファイルを同期しました",
133
133
  },
134
- "usage_sync": {
135
- "en": "Usage: wt sync (sy) [files...] [--from <name>] [--to <name>]",
136
- "ja": "使用方法: wt sync (sy) [files...] [--from <name>] [--to <name>]",
137
- },
138
134
  "usage_pr": {
139
135
  "en": "Usage: wt pr add <number>",
140
136
  "ja": "使用方法: wt pr add <number>",
141
137
  },
138
+ "usage_setup": {
139
+ "en": "Usage: wt setup (su)",
140
+ "ja": "使用方法: wt setup (su)",
141
+ },
142
142
  "usage_stash": {
143
143
  "en": "Usage: wt stash (st) <work_name> [<base_branch>]",
144
144
  "ja": "使用方法: wt stash (st) <work_name> [<base_branch>]",
@@ -155,6 +155,35 @@ MESSAGES = {
155
155
  "en": "No local changes to stash.",
156
156
  "ja": "スタッシュする変更がありません",
157
157
  },
158
+ "select_switched": {
159
+ "en": "Switched worktree to: {}",
160
+ "ja": "作業ディレクトリを切り替えました: {}",
161
+ },
162
+ "select_not_found": {
163
+ "en": "Worktree not found: {}",
164
+ "ja": "worktree が見つかりません: {}",
165
+ },
166
+ "select_no_last": {
167
+ "en": "No previous selection found",
168
+ "ja": "以前の選択が見つかりません",
169
+ },
170
+ "setting_up": {"en": "Setting up: {} -> {}", "ja": "セットアップ中: {} -> {}"},
171
+ "completed_setup": {
172
+ "en": "Completed setup of {} files",
173
+ "ja": "{} 個のファイルをセットアップしました",
174
+ },
175
+ "suggest_setup": {
176
+ "en": "Some setup files are missing. Run 'wt setup' to initialize this worktree.",
177
+ "ja": "一部のセットアップファイルが不足しています。'wt setup' を実行して初期化してください。",
178
+ },
179
+ "nesting_error": {
180
+ "en": "Error: Already in a wt subshell ({}). Please 'exit' before switching.",
181
+ "ja": "エラー: すでに wt のサブシェル ({}) 内にいます。切り替える前に 'exit' してください。",
182
+ },
183
+ "jump_instruction": {
184
+ "en": "Jumping to '{}' ({}). Type 'exit' or Ctrl-D to return.",
185
+ "ja": "'{}' ({}) にジャンプします。戻るには 'exit' または Ctrl-D を入力してください。",
186
+ },
158
187
  }
159
188
 
160
189
 
@@ -199,7 +228,7 @@ def load_config(base_dir: Path) -> dict:
199
228
  config_file = base_dir / ".wt" / "config.toml"
200
229
  default_config = {
201
230
  "worktrees_dir": ".worktrees",
202
- "sync_files": [".env"],
231
+ "setup_files": [".env"],
203
232
  "auto_copy_on_add": True,
204
233
  }
205
234
 
@@ -238,7 +267,7 @@ def create_hook_template(base_dir: Path):
238
267
  base_dir,
239
268
  {
240
269
  "worktrees_dir": ".worktrees",
241
- "sync_files": [".env"],
270
+ "setup_files": [".env"],
242
271
  "auto_copy_on_add": True,
243
272
  },
244
273
  )
@@ -330,9 +359,6 @@ wt add <作業名>
330
359
  # 既存ブランチから worktree を作成
331
360
  wt add <作業名> <既存ブランチ名>
332
361
 
333
- # エイリアスを作成(current エイリアスで現在の作業を切り替え)
334
- wt add <作業名> --alias current
335
-
336
362
  # worktree 一覧を表示
337
363
  wt list
338
364
 
@@ -342,31 +368,6 @@ wt rm <作業名>
342
368
 
343
369
  詳細は https://github.com/igtm/easy-worktree を参照してください。
344
370
 
345
- ## エイリアスとは
346
-
347
- エイリアスは、worktree へのシンボリックリンク(symbolic link)です。同じエイリアス名で異なる worktree を指すことで、固定されたパスで複数のブランチを切り替えられます。
348
-
349
- ### エイリアスの便利な使い方
350
-
351
- **VSCode ワークスペースでの活用**
352
-
353
- `current` などの固定エイリアスを VSCode のワークスペースとして開くことで、worktree を切り替えても VSCode を開き直す必要がなくなります。
354
-
355
- ```bash
356
- # 最初の作業
357
- wt add feature-a --alias current
358
- code current # VSCode で current を開く
359
-
360
- # 別の作業に切り替え(VSCode は開いたまま)
361
- wt add feature-b --alias current
362
- # current エイリアスが feature-b を指すようになる
363
- ```
364
-
365
- このように、エイリアスを使うことで:
366
- - VSCode のワークスペース設定が維持される
367
- - 拡張機能の設定やウィンドウレイアウトが保持される
368
- - ブランチ切り替えのたびにエディタを開き直す手間が不要
369
-
370
371
  ## post-add フック
371
372
 
372
373
  `post-add` フックは、worktree 作成後に自動実行されるスクリプトです。
@@ -413,43 +414,15 @@ wt add <work_name>
413
414
  # Create a worktree from an existing branch
414
415
  wt add <work_name> <existing_branch_name>
415
416
 
416
- # Create an alias (use "current" alias to switch between tasks)
417
- wt add <work_name> --alias current
418
-
419
417
  # List worktrees
420
418
  wt list
421
419
 
422
420
  # Remove a worktree
423
- wt rm <work_name>
421
+ wt remove <work_name>
424
422
  ```
425
423
 
426
424
  For more details, see https://github.com/igtm/easy-worktree
427
425
 
428
- ## What are Aliases?
429
-
430
- Aliases are symbolic links to worktrees. By pointing the same alias name to different worktrees, you can switch between multiple branches using a fixed path.
431
-
432
- ### Smart Use of Aliases
433
-
434
- **Using with VSCode Workspace**
435
-
436
- By opening a fixed alias like `current` as a VSCode workspace, you can switch worktrees without needing to reopen VSCode.
437
-
438
- ```bash
439
- # First task
440
- wt add feature-a --alias current
441
- code current # Open current in VSCode
442
-
443
- # Switch to another task (VSCode stays open)
444
- wt add feature-b --alias current
445
- # The current alias now points to feature-b
446
- ```
447
-
448
- Benefits of using aliases:
449
- - VSCode workspace settings are preserved
450
- - Extension settings and window layouts are maintained
451
- - No need to reopen the editor when switching branches
452
-
453
426
  ## post-add Hook
454
427
 
455
428
  The `post-add` hook is a script that runs automatically after creating a worktree.
@@ -480,6 +453,25 @@ The `post-add` hook is a script that runs automatically after creating a worktre
480
453
 
481
454
  def find_base_dir() -> Path | None:
482
455
  """現在のディレクトリまたは親ディレクトリから git root を探す"""
456
+ # ワークツリーでもメインリポジトリのルートを見つけられるように
457
+ try:
458
+ # --git-common-dir はメインリポジトリの .git ディレクトリを返す
459
+ result = run_command(["git", "rev-parse", "--git-common-dir"], check=False)
460
+ if result.returncode == 0:
461
+ git_common_dir = Path(result.stdout.strip())
462
+ if not git_common_dir.is_absolute():
463
+ # 相対パスの場合は CWD からのパス
464
+ git_common_dir = (Path.cwd() / git_common_dir).resolve()
465
+
466
+ # .git ディレクトリの親がベースディレクトリ
467
+ if git_common_dir.name == ".git":
468
+ return git_common_dir.parent
469
+ else:
470
+ # ベアリポジトリなどの場合はそのディレクトリ自体
471
+ return git_common_dir
472
+ except Exception:
473
+ pass
474
+
483
475
  try:
484
476
  result = run_command(["git", "rev-parse", "--show-toplevel"], check=False)
485
477
  if result.returncode == 0:
@@ -487,7 +479,7 @@ def find_base_dir() -> Path | None:
487
479
  except Exception:
488
480
  pass
489
481
 
490
- # git コマンドが失敗した場合、.git ディレクトリを探す
482
+ # fallback
491
483
  current = Path.cwd()
492
484
  for parent in [current] + list(current.parents):
493
485
  if (parent / ".git").exists():
@@ -786,12 +778,12 @@ def add_worktree(
786
778
  if result.returncode == 0:
787
779
  # 自動同期
788
780
  if config.get("auto_copy_on_add"):
789
- sync_files = config.get("sync_files", [])
790
- for file_name in sync_files:
781
+ setup_files = config.get("setup_files", [])
782
+ for file_name in setup_files:
791
783
  src = base_dir / file_name
792
784
  dst = worktree_path / file_name
793
785
  if src.exists():
794
- print(msg("syncing", src, dst), file=sys.stderr)
786
+ print(msg("setting_up", src, dst), file=sys.stderr)
795
787
  import shutil
796
788
 
797
789
  shutil.copy2(src, dst)
@@ -1322,6 +1314,242 @@ def cmd_remove(args: list[str]):
1322
1314
  sys.exit(1)
1323
1315
 
1324
1316
 
1317
+ def cmd_checkout(args: list[str]):
1318
+ """wt co/checkout <work_name> - Get path to a worktree (for cd)"""
1319
+ if len(args) < 1:
1320
+ return
1321
+
1322
+ work_name = args[0]
1323
+ base_dir = find_base_dir()
1324
+ if not base_dir:
1325
+ print(msg("error", msg("base_not_found")), file=sys.stderr)
1326
+ sys.exit(1)
1327
+
1328
+ worktrees = get_worktree_info(base_dir)
1329
+ for wt in worktrees:
1330
+ p = Path(wt["path"])
1331
+ if p.name == work_name or (p == base_dir and work_name == "main"):
1332
+ print(str(p))
1333
+ return
1334
+
1335
+ print(msg("error", msg("select_not_found", work_name)), file=sys.stderr)
1336
+ sys.exit(1)
1337
+
1338
+
1339
+ def cmd_select(args: list[str]):
1340
+ """wt sl/select [<name>|-] - Manage/Switch worktree selection"""
1341
+ base_dir = find_base_dir()
1342
+ if not base_dir:
1343
+ print(msg("error", msg("base_not_found")), file=sys.stderr)
1344
+ sys.exit(1)
1345
+
1346
+ wt_dir = base_dir / ".wt"
1347
+ wt_dir.mkdir(exist_ok=True)
1348
+ last_sel_file = wt_dir / "last_selection"
1349
+
1350
+ # Get current selection name based on CWD or environment
1351
+ current_sel = os.environ.get("WT_SESSION_NAME")
1352
+ if not current_sel:
1353
+ cwd = Path.cwd().resolve()
1354
+ worktrees = get_worktree_info(base_dir)
1355
+ resolved_base = base_dir.resolve()
1356
+ for wt in worktrees:
1357
+ wt_path = Path(wt["path"]).resolve()
1358
+ if cwd == wt_path or cwd.is_relative_to(wt_path):
1359
+ current_sel = "main" if wt_path == resolved_base else wt_path.name
1360
+ break
1361
+
1362
+ worktrees = get_worktree_info(base_dir)
1363
+ names = []
1364
+ for wt in worktrees:
1365
+ p = Path(wt["path"])
1366
+ name = "main" if p == base_dir else p.name
1367
+ names.append(name)
1368
+
1369
+ if not args:
1370
+ # Interactive mode or list with highlight
1371
+ if shutil.which("fzf") and sys.stdin.isatty():
1372
+ # Run fzf
1373
+ try:
1374
+ # Prepare input for fzf with current highlighted
1375
+ fzf_input = ""
1376
+ for name in names:
1377
+ if name == current_sel:
1378
+ fzf_input += f"{name} (*)\n"
1379
+ else:
1380
+ fzf_input += f"{name}\n"
1381
+
1382
+ process = subprocess.Popen(
1383
+ ["fzf", "--height", "40%", "--reverse", "--header", "Select Worktree"],
1384
+ stdin=subprocess.PIPE,
1385
+ stdout=subprocess.PIPE,
1386
+ text=True,
1387
+ )
1388
+ stdout, _ = process.communicate(input=fzf_input)
1389
+
1390
+ if process.returncode == 0 and stdout.strip():
1391
+ selected = stdout.strip().split(" ")[0]
1392
+ switch_selection(selected, base_dir, current_sel, last_sel_file)
1393
+ return
1394
+ except Exception as e:
1395
+ print(f"fzf error: {e}", file=sys.stderr)
1396
+ # Fallback to listing
1397
+
1398
+ # List with highlight
1399
+ YELLOW = "\033[33m"
1400
+ RESET = "\033[0m"
1401
+ BOLD = "\033[1m"
1402
+
1403
+ for name in names:
1404
+ if name == current_sel:
1405
+ print(f"{YELLOW}{BOLD}{name}{RESET}")
1406
+ else:
1407
+ print(name)
1408
+ return
1409
+
1410
+ target = args[0]
1411
+
1412
+ if target == "-":
1413
+ if not last_sel_file.exists():
1414
+ print(msg("error", msg("select_no_last")), file=sys.stderr)
1415
+ sys.exit(1)
1416
+ target = last_sel_file.read_text().strip()
1417
+ if not target:
1418
+ print(msg("error", msg("select_no_last")), file=sys.stderr)
1419
+ sys.exit(1)
1420
+
1421
+ if target not in names:
1422
+ print(msg("error", msg("select_not_found", target)), file=sys.stderr)
1423
+ sys.exit(1)
1424
+
1425
+ switch_selection(target, base_dir, current_sel, last_sel_file)
1426
+
1427
+
1428
+ def cmd_current(args: list[str]):
1429
+ """wt current (cur) - Show name of the current worktree"""
1430
+ name = os.environ.get("WT_SESSION_NAME")
1431
+ if not name:
1432
+ base_dir = find_base_dir()
1433
+ if not base_dir:
1434
+ return
1435
+ cwd = Path.cwd().resolve()
1436
+ worktrees = get_worktree_info(base_dir)
1437
+ resolved_base = base_dir.resolve()
1438
+ for wt in worktrees:
1439
+ wt_path = Path(wt["path"]).resolve()
1440
+ if cwd == wt_path:
1441
+ name = "main" if wt_path == resolved_base else wt_path.name
1442
+ break
1443
+ if name:
1444
+ print(name)
1445
+
1446
+
1447
+ def switch_selection(target, base_dir, current_sel, last_sel_file):
1448
+ """Switch selection and update last_selection"""
1449
+ # Calculate target path
1450
+ target_path = base_dir
1451
+ if target != "main":
1452
+ config = load_config(base_dir)
1453
+ worktrees_dir_name = config.get("worktrees_dir", ".worktrees")
1454
+ target_path = base_dir / worktrees_dir_name / target
1455
+
1456
+ if not target_path.exists():
1457
+ print(msg("error", msg("select_not_found", target)), file=sys.stderr)
1458
+ sys.exit(1)
1459
+
1460
+ if target != current_sel:
1461
+ # Save last selection
1462
+ if current_sel:
1463
+ last_sel_file.write_text(current_sel)
1464
+
1465
+ print(msg("select_switched", target), file=sys.stderr)
1466
+
1467
+ # Check for setup files
1468
+ config = load_config(base_dir)
1469
+ setup_files = config.get("setup_files", [])
1470
+ missing = False
1471
+ for f in setup_files:
1472
+ if not (target_path / f).exists():
1473
+ missing = True
1474
+ break
1475
+ if missing:
1476
+ print(f"\033[33m{msg('suggest_setup')}\033[0m", file=sys.stderr)
1477
+
1478
+ if sys.stdout.isatty():
1479
+ # Check for nesting
1480
+ current_session = os.environ.get("WT_SESSION_NAME")
1481
+ if current_session:
1482
+ print(
1483
+ f"\033[31m{msg('nesting_error', current_session)}\033[0m", file=sys.stderr
1484
+ )
1485
+ sys.exit(1)
1486
+
1487
+ # Subshell jump
1488
+ shell = os.environ.get("SHELL", "/bin/sh")
1489
+ print(msg("jump_instruction", target, target_path), file=sys.stderr)
1490
+
1491
+ os.chdir(target_path)
1492
+ os.environ["WT_SESSION_NAME"] = target
1493
+ # Prepend to PS1 for visibility (if supported by shell)
1494
+ ps1 = os.environ.get("PS1", "$ ")
1495
+ if not ps1.startswith("(wt:"):
1496
+ os.environ["PS1"] = f"(wt:{target}) {ps1}"
1497
+
1498
+ # Set terminal title
1499
+ sys.stderr.write(f"\033]0;wt:{target}\007")
1500
+ sys.stderr.flush()
1501
+
1502
+ # Update tmux window name if inside tmux
1503
+ if os.environ.get("TMUX"):
1504
+ subprocess.run(["tmux", "rename-window", f"wt:{target}"], check=False)
1505
+
1506
+ os.execl(shell, shell)
1507
+ else:
1508
+ # Output path for script/backtick use
1509
+ print(str(target_path.absolute()))
1510
+
1511
+
1512
+ def cmd_setup(args: list[str]):
1513
+ """wt setup - Initialize current worktree (copy setup_files and run hooks)"""
1514
+ base_dir = find_base_dir()
1515
+ if not base_dir:
1516
+ print(msg("error", msg("base_not_found")), file=sys.stderr)
1517
+ sys.exit(1)
1518
+
1519
+ current_dir = Path.cwd()
1520
+ target_path = current_dir
1521
+
1522
+ config = load_config(base_dir)
1523
+ setup_files = config.get("setup_files", [])
1524
+
1525
+ import shutil
1526
+ count = 0
1527
+ for f in setup_files:
1528
+ src = base_dir / f
1529
+ dst = target_path / f
1530
+ if src.exists() and src != dst:
1531
+ print(msg("setting_up", src, dst), file=sys.stderr)
1532
+ dst.parent.mkdir(parents=True, exist_ok=True)
1533
+ shutil.copy2(src, dst)
1534
+ count += 1
1535
+
1536
+ if count > 0:
1537
+ print(msg("completed_setup", count), file=sys.stderr)
1538
+
1539
+ # Run post-add hook
1540
+ work_name = target_path.name
1541
+ if target_path == base_dir:
1542
+ work_name = "main"
1543
+
1544
+ # Get branch name for the current worktree
1545
+ branch = None
1546
+ result = run_command(["git", "branch", "--show-current"], cwd=target_path, check=False)
1547
+ if result.returncode == 0:
1548
+ branch = result.stdout.strip()
1549
+
1550
+ run_post_add_hook(target_path, work_name, base_dir, branch)
1551
+
1552
+
1325
1553
  def cmd_clean(args: list[str]):
1326
1554
  """wt clean - Remove old/unused/merged worktrees"""
1327
1555
  base_dir = find_base_dir()
@@ -1532,96 +1760,6 @@ def cmd_clean(args: list[str]):
1532
1760
  if result.stderr:
1533
1761
  print(result.stderr, file=sys.stderr)
1534
1762
 
1535
-
1536
- def cmd_sync(args: list[str]):
1537
- """wt sync [files...] [--from <name>] [--to <name>] - Sync files between worktrees"""
1538
- base_dir = find_base_dir()
1539
- if not base_dir:
1540
- print(msg("error", msg("base_not_found")), file=sys.stderr)
1541
- sys.exit(1)
1542
-
1543
- config = load_config(base_dir)
1544
- files_to_sync = []
1545
- from_name = None
1546
- to_name = None
1547
-
1548
- # 引数解析
1549
- i = 0
1550
- while i < len(args):
1551
- if args[i] == "--from" and i + 1 < len(args):
1552
- from_name = args[i + 1]
1553
- i += 2
1554
- elif args[i] == "--to" and i + 1 < len(args):
1555
- to_name = args[i + 1]
1556
- i += 2
1557
- else:
1558
- files_to_sync.append(args[i])
1559
- i += 1
1560
-
1561
- if not files_to_sync:
1562
- files_to_sync = config.get("sync_files", [])
1563
-
1564
- if not files_to_sync:
1565
- return
1566
-
1567
- worktrees = get_worktree_info(base_dir)
1568
-
1569
- # 送信元と送信先のパスを決定
1570
- from_path = base_dir
1571
- if from_name:
1572
- found = False
1573
- for wt in worktrees:
1574
- if Path(wt["path"]).name == from_name:
1575
- from_path = Path(wt["path"])
1576
- found = True
1577
- break
1578
- if not found:
1579
- print(msg("error", f"Worktree not found: {from_name}"), file=sys.stderr)
1580
- sys.exit(1)
1581
-
1582
- dest_paths = []
1583
- if to_name:
1584
- if to_name == "main":
1585
- dest_paths = [base_dir]
1586
- else:
1587
- found = False
1588
- for wt in worktrees:
1589
- if Path(wt["path"]).name == to_name:
1590
- dest_paths = [Path(wt["path"])]
1591
- found = True
1592
- break
1593
- if not found:
1594
- print(msg("error", f"Worktree not found: {to_name}"), file=sys.stderr)
1595
- sys.exit(1)
1596
- else:
1597
- # 指定がない場合は現在のディレクトリが worktree ならそこへ、そうでなければ全自動(通常は base -> current)
1598
- current_dir = Path.cwd()
1599
- if current_dir != base_dir and any(
1600
- current_dir.is_relative_to(Path(wt["path"])) for wt in worktrees
1601
- ):
1602
- dest_paths = [current_dir]
1603
- else:
1604
- # base から全 worktree へ(安全のため、ユーザーが現在の worktree にいる場合はそこだけにするのが一般的だが、ここでは全 worktree とした)
1605
- dest_paths = [
1606
- Path(wt["path"]) for wt in worktrees if Path(wt["path"]) != base_dir
1607
- ]
1608
-
1609
- import shutil
1610
-
1611
- count = 0
1612
- for dst_root in dest_paths:
1613
- if dst_root == from_path:
1614
- continue
1615
- for f in files_to_sync:
1616
- src = from_path / f
1617
- dst = dst_root / f
1618
- if src.exists():
1619
- print(msg("syncing", src, dst), file=sys.stderr)
1620
- dst.parent.mkdir(parents=True, exist_ok=True)
1621
- shutil.copy2(src, dst)
1622
- count += 1
1623
-
1624
-
1625
1763
  def cmd_passthrough(args: list[str]):
1626
1764
  """Passthrough other git worktree commands"""
1627
1765
  base_dir = find_base_dir()
@@ -1650,7 +1788,12 @@ def show_help():
1650
1788
  print(
1651
1789
  f" {'add (ad) <作業名> [<base_branch>]':<55} - worktree を追加(デフォルト: 新規ブランチ作成)"
1652
1790
  )
1791
+ print(
1792
+ f" {'select (sl) [<作業名>|-]':<55} - 作業ディレクトリを切り替え(fzf対応)"
1793
+ )
1653
1794
  print(f" {'list (ls) [--pr]':<55} - worktree 一覧を表示")
1795
+ print(f" {'co/checkout <作業名>':<55} - worktree のパスを表示")
1796
+ print(f" {'current (cur)':<55} - 現在の worktree 名を表示")
1654
1797
  print(
1655
1798
  f" {'stash (st) <作業名> [<base_branch>]':<55} - 現在の変更をスタッシュして新規 worktree に移動"
1656
1799
  )
@@ -1662,7 +1805,7 @@ def show_help():
1662
1805
  f" {'clean (cl) [--days N] [--merged] [--closed]':<55} - 不要な worktree を削除"
1663
1806
  )
1664
1807
  print(
1665
- f" {'sync (sy) [files...] [--from <名>] [--to <名>]':<55} - ファイル(.env等)を同期"
1808
+ f" {'setup (su)':<55} - 作業ディレクトリを初期化(ファイルコピー・フック実行)"
1666
1809
  )
1667
1810
  print()
1668
1811
  print("オプション:")
@@ -1680,7 +1823,12 @@ def show_help():
1680
1823
  print(
1681
1824
  f" {'add (ad) <work_name> [<base_branch>]':<55} - Add a worktree (default: create new branch)"
1682
1825
  )
1826
+ print(
1827
+ f" {'select (sl) [<name>|-]':<55} - Switch worktree selection (fzf support)"
1828
+ )
1683
1829
  print(f" {'list (ls) [--pr]':<55} - List worktrees")
1830
+ print(f" {'co/checkout <work_name>':<55} - Show path to a worktree")
1831
+ print(f" {'current (cur)':<55} - Show current worktree name")
1684
1832
  print(
1685
1833
  f" {'stash (st) <work_name> [<base_branch>]':<55} - Stash current changes and move to new worktree"
1686
1834
  )
@@ -1690,7 +1838,7 @@ def show_help():
1690
1838
  f" {'clean (cl) [--days N] [--merged] [--closed]':<55} - Remove unused/merged worktrees"
1691
1839
  )
1692
1840
  print(
1693
- f" {'sync (sy) [files...] [--from <name>] [--to <name>]':<55} - Sync files (.env, etc.)"
1841
+ f" {'setup (su)':<55} - Setup worktree (copy files and run hooks)"
1694
1842
  )
1695
1843
  print()
1696
1844
  print("Options:")
@@ -1735,12 +1883,18 @@ def main():
1735
1883
  cmd_remove(args)
1736
1884
  elif command in ["clean", "cl"]:
1737
1885
  cmd_clean(args)
1738
- elif command in ["sync", "sy"]:
1739
- cmd_sync(args)
1886
+ elif command in ["setup", "su"]:
1887
+ cmd_setup(args)
1740
1888
  elif command in ["stash", "st"]:
1741
1889
  cmd_stash(args)
1742
1890
  elif command == "pr":
1743
1891
  cmd_pr(args)
1892
+ elif command == "select" or command == "sl":
1893
+ cmd_select(args)
1894
+ elif command in ["current", "cur"]:
1895
+ cmd_current(args)
1896
+ elif command in ["co", "checkout"]:
1897
+ cmd_checkout(args)
1744
1898
  else:
1745
1899
  # その他のコマンドは git worktree にパススルー
1746
1900
  cmd_passthrough([command] + args)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easy-worktree
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Git worktree を簡単に管理するための CLI ツール
5
5
  Project-URL: Homepage, https://github.com/igtm/easy-worktree
6
6
  Project-URL: Repository, https://github.com/igtm/easy-worktree
@@ -35,11 +35,11 @@ It keeps the root of your git repository as your primary working area (main), wh
35
35
 
36
36
  ### Key Features
37
37
 
38
- - **Standardized directory structure**: Worktrees are created in a `.worktrees/` subdirectory (configurable). Keeps your root directory clean.
39
- - **Auto Sync**: Automatically sync files ignored by git (like `.env`) from the root to each worktree.
38
+ - **Smart Selection**: Quickly switch between worktrees with `wt select`. "Jump" into a new shell instantly without any special setup.
39
+ - **Auto Setup**: Automatically copy files (like `.env`) and run hooks to prepare each worktree.
40
40
  - **Clear Status**: `wt list` shows worktree branches, their status (clean/dirty), and associated GitHub PRs in a beautiful table.
41
41
  - **Smart Cleanup**: Easily batch remove merged branches or old unused worktrees.
42
- - **Two-letter shortcuts**: Fast execution with shortcuts like `ad`, `ls`, `st`, `sy`, `cl`.
42
+ - **Two-letter shortcuts**: Fast execution with shortcuts like `ad`, `ls`, `sl`, `su`, `st`, `cl`.
43
43
 
44
44
  ## Prerequisites
45
45
 
@@ -123,6 +123,23 @@ Quickly stash your current changes and move them to a new worktree.
123
123
  wt stash feature-2
124
124
  ```
125
125
 
126
+ #### Switch Worktree (shortcut: `sl`)
127
+
128
+ ```bash
129
+ wt select feature-1
130
+ ```
131
+
132
+ Running `wt select` will **automatically "jump"** you into the worktree directory by starting a new subshell.
133
+
134
+ - **Prompt**: Shows a `(wt:feature-1)` indicator.
135
+ - **Terminal Title**: Updates the window title to `wt:feature-1`.
136
+ - **Tmux**: Updates the tmux window name to `wt:feature-1` if running inside tmux.
137
+
138
+ To return to your original directory, simply type `exit` or press `Ctrl-D`.
139
+
140
+ * **Interactive Mode**: Running `wt select` without arguments opens an interactive picker using `fzf`.
141
+ * **Nesting Control**: If you are already in a `wt` subshell, it will warn you to avoid confusing nesting.
142
+
126
143
  #### PR Management
127
144
 
128
145
  Fetch a PR and create a worktree for it. (Requires `gh` CLI)
@@ -141,12 +158,63 @@ Removes the worktree and its directory.
141
158
 
142
159
  ### Useful Features
143
160
 
144
- #### Sync configuration files (shortcut: `sy`)
161
+ #### Setup Worktree (shortcut: `su`)
162
+
163
+ Initialize the current worktree by copying required files and running the `post-add` hook.
164
+
165
+ ```bash
166
+ wt setup
167
+ ```
168
+
169
+ #### Visualization and External Tools
170
+
171
+ When you switch to a worktree using `wt select`, the following features are automatically enabled:
172
+ - **Terminal Title**: The window or tab title is updated to `wt:worktree-name`.
173
+ - **Tmux**: If you are inside tmux, the window name is automatically renamed to `wt:worktree-name`.
174
+
175
+ You can also use the `wt current` (or `cur`) command to display the current worktree name in external tools.
176
+
177
+ ##### Tmux Status Bar
178
+ Add the following to your `.tmux.conf` to show the worktree name in your status line:
179
+ ```tmux
180
+ set -g status-right "#(wt current) | %Y-%m-%d %H:%M"
181
+ ```
182
+
183
+ ##### Zsh / Bash Prompt
184
+ You can customize your prompt using the `$WT_SESSION_NAME` environment variable.
145
185
 
146
- Sync files like `.env` that are not in git from the root to your worktrees.
186
+ **Zsh (.zshrc)**:
187
+ ```zsh
188
+ RPROMPT='${WT_SESSION_NAME:+"(wt:$WT_SESSION_NAME)"} '"$RPROMPT"
189
+ ```
147
190
 
191
+ **Bash (.bashrc)**:
148
192
  ```bash
149
- wt sync .env
193
+ PS1='$(if [ -n "$WT_SESSION_NAME" ]; then echo "($WT_SESSION_NAME) "; fi)'$PS1
194
+ ```
195
+
196
+ ##### Starship
197
+ Add a custom module to your `starship.toml`:
198
+ ```toml
199
+ [custom.easy_worktree]
200
+ command = "wt current"
201
+ when = 'test -n "$WT_SESSION_NAME"'
202
+ format = "via [$symbol$output]($style) "
203
+ symbol = "🌳 "
204
+ style = "bold green"
205
+ ```
206
+
207
+ ##### Powerlevel10k
208
+ Integrate beautiful worktree indicators by adding a custom segment to `.p10k.zsh`:
209
+
210
+ 1. Add `easy_worktree` to `POWERLEVEL9K_LEFT_PROMPT_ELEMENTS`.
211
+ 2. Define the following function:
212
+ ```zsh
213
+ function prompt_easy_worktree() {
214
+ if [[ -n $WT_SESSION_NAME ]]; then
215
+ p10k segment -f 255 -b 28 -i '🌳' -t "wt:$WT_SESSION_NAME"
216
+ fi
217
+ }
150
218
  ```
151
219
 
152
220
 
@@ -166,8 +234,8 @@ Customize behavior in `.wt/config.toml`:
166
234
 
167
235
  ```toml
168
236
  worktrees_dir = ".worktrees" # Directory where worktrees are created
169
- sync_files = [".env"] # Files to auto-sync
170
- auto_copy_on_add = true # Enable auto-sync on add
237
+ setup_files = [".env"] # Files to auto-copy during setup
238
+ auto_copy_on_add = true # Enable auto-copy on worktree creation
171
239
  ```
172
240
 
173
241
  ## Hooks
@@ -0,0 +1,6 @@
1
+ easy_worktree/__init__.py,sha256=yHHZKyeljLEChIumw8ybGUmnvoCpoCJRBO_JE-l1Vmc,62539
2
+ easy_worktree-0.1.2.dist-info/METADATA,sha256=v4sCzlh43PBw0YPoNepc-orui3iobogZ_hKwyKep0Ug,6249
3
+ easy_worktree-0.1.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
4
+ easy_worktree-0.1.2.dist-info/entry_points.txt,sha256=Mf6MYDS2obZLvIJJFl-BbU8-SL0QGu5UWcC0FWnqtbg,42
5
+ easy_worktree-0.1.2.dist-info/licenses/LICENSE,sha256=7MGvWFDxXPqW2nrr9D7KHT0vWFiGwIUL5SQCj0IiAPc,1061
6
+ easy_worktree-0.1.2.dist-info/RECORD,,
@@ -1,6 +0,0 @@
1
- easy_worktree/__init__.py,sha256=PxMDS1lRU_47fwM5dGMiMg-DPjweHxlDlRsuecO6_cs,56917
2
- easy_worktree-0.1.1.dist-info/METADATA,sha256=6Y_woHPXhCKTC8DjHKGIem2cwX83enGD6jRhZLZWAMA,4047
3
- easy_worktree-0.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
4
- easy_worktree-0.1.1.dist-info/entry_points.txt,sha256=Mf6MYDS2obZLvIJJFl-BbU8-SL0QGu5UWcC0FWnqtbg,42
5
- easy_worktree-0.1.1.dist-info/licenses/LICENSE,sha256=7MGvWFDxXPqW2nrr9D7KHT0vWFiGwIUL5SQCj0IiAPc,1061
6
- easy_worktree-0.1.1.dist-info/RECORD,,