|
| 1 | +# Git Command Support Audit - 2025-11-26 |
| 2 | + |
| 3 | +This document provides a comprehensive audit of git command support in libvcs, documenting existing implementations and planned additions following the Manager/Cmd pattern. |
| 4 | + |
| 5 | +## Pattern Overview |
| 6 | + |
| 7 | +``` |
| 8 | +Manager (collection-level) Cmd (per-entity) |
| 9 | +├── ls() -> QueryList[Cmd] ├── run() |
| 10 | +├── get(**kwargs) -> Cmd | None ├── show() |
| 11 | +├── filter(**kwargs) -> list[Cmd] ├── remove()/delete() |
| 12 | +├── add() / create() ├── rename() |
| 13 | +└── run() └── entity-specific operations |
| 14 | +``` |
| 15 | + |
| 16 | +--- |
| 17 | + |
| 18 | +## 1. GitBranchManager / GitBranchCmd |
| 19 | + |
| 20 | +**Pattern Status**: Implemented |
| 21 | + |
| 22 | +### GitBranchManager (Collection-level) |
| 23 | + |
| 24 | +| Method | Status | Description | |
| 25 | +|--------|--------|-------------| |
| 26 | +| `__init__(path, cmd)` | Implemented | Constructor | |
| 27 | +| `__repr__()` | Implemented | String representation | |
| 28 | +| `run(command, local_flags, quiet, cached)` | Implemented | Run git branch command | |
| 29 | +| `checkout(branch)` | Implemented | Checkout a branch | |
| 30 | +| `create(branch)` | Implemented | Create new branch via `checkout -b` | |
| 31 | +| `_ls()` | Implemented | Internal raw listing | |
| 32 | +| `ls()` | Implemented | Returns `QueryList[GitBranchCmd]` | |
| 33 | +| `get(**kwargs)` | Implemented | Get single branch by filter | |
| 34 | +| `filter(**kwargs)` | Implemented | Filter branches | |
| 35 | + |
| 36 | +### GitBranchCmd (Per-entity) |
| 37 | + |
| 38 | +| Method | Status | Description | |
| 39 | +|--------|--------|-------------| |
| 40 | +| `__init__(path, branch_name, cmd)` | Implemented | Constructor with `branch_name` | |
| 41 | +| `__repr__()` | Implemented | String representation | |
| 42 | +| `run(command, local_flags, quiet, cached)` | Implemented | Run git branch command | |
| 43 | +| `checkout()` | Implemented | Checkout this branch | |
| 44 | +| `create()` | Implemented | Create this branch | |
| 45 | +| `delete(force)` | **Missing** | `-d` / `-D` | |
| 46 | +| `rename(new_name, force)` | **Missing** | `-m` / `-M` | |
| 47 | +| `copy(new_name, force)` | **Missing** | `-c` / `-C` | |
| 48 | +| `set_upstream(upstream)` | **Missing** | `--set-upstream-to` | |
| 49 | +| `unset_upstream()` | **Missing** | `--unset-upstream` | |
| 50 | +| `track(remote_branch)` | **Missing** | `-t` / `--track` | |
| 51 | + |
| 52 | +### GitBranchManager Enhancements Needed |
| 53 | + |
| 54 | +| Feature | Status | Description | |
| 55 | +|---------|--------|-------------| |
| 56 | +| `--all` support | **Missing** | List all branches (local + remote) | |
| 57 | +| `--remotes` support | **Missing** | List remote branches only | |
| 58 | +| `--merged` filter | **Missing** | Filter merged branches | |
| 59 | +| `--no-merged` filter | **Missing** | Filter unmerged branches | |
| 60 | +| `--verbose` support | **Missing** | Show tracking info | |
| 61 | + |
| 62 | +--- |
| 63 | + |
| 64 | +## 2. GitRemoteManager / GitRemoteCmd |
| 65 | + |
| 66 | +**Pattern Status**: Implemented |
| 67 | + |
| 68 | +### GitRemoteManager (Collection-level) |
| 69 | + |
| 70 | +| Method | Status | Description | |
| 71 | +|--------|--------|-------------| |
| 72 | +| `__init__(path, cmd)` | Implemented | Constructor | |
| 73 | +| `__repr__()` | Implemented | String representation | |
| 74 | +| `run(command, local_flags)` | Implemented | Run git remote command | |
| 75 | +| `add(name, url, fetch, track, master, mirror)` | Implemented | Add new remote | |
| 76 | +| `show(name, verbose, no_query_remotes)` | Implemented | Show remotes | |
| 77 | +| `_ls()` | Implemented | Internal raw listing | |
| 78 | +| `ls()` | Implemented | Returns `QueryList[GitRemoteCmd]` | |
| 79 | +| `get(**kwargs)` | Implemented | Get single remote by filter | |
| 80 | +| `filter(**kwargs)` | Implemented | Filter remotes | |
| 81 | + |
| 82 | +### GitRemoteCmd (Per-entity) |
| 83 | + |
| 84 | +Properties: `remote_name`, `fetch_url`, `push_url` |
| 85 | + |
| 86 | +| Method | Status | Description | |
| 87 | +|--------|--------|-------------| |
| 88 | +| `__init__(path, remote_name, fetch_url, push_url, cmd)` | Implemented | Constructor | |
| 89 | +| `__repr__()` | Implemented | String representation | |
| 90 | +| `run(command, local_flags, verbose)` | Implemented | Run git remote command | |
| 91 | +| `rename(old, new, progress)` | Implemented | Rename remote | |
| 92 | +| `remove()` | Implemented | Delete remote | |
| 93 | +| `show(verbose, no_query_remotes)` | Implemented | Show remote details | |
| 94 | +| `prune(dry_run)` | Implemented | Prune stale branches | |
| 95 | +| `get_url(push, _all)` | Implemented | Get remote URL | |
| 96 | +| `set_url(url, old_url, push, add, delete)` | Implemented | Set remote URL | |
| 97 | +| `set_branches(*branches, add)` | **Missing** | `set-branches` | |
| 98 | +| `set_head(branch, auto, delete)` | **Missing** | `set-head` | |
| 99 | +| `update(prune)` | **Missing** | `update` | |
| 100 | + |
| 101 | +--- |
| 102 | + |
| 103 | +## 3. GitStashCmd (Current) → GitStashManager / GitStashEntryCmd (Planned) |
| 104 | + |
| 105 | +**Pattern Status**: Not implemented - needs refactoring |
| 106 | + |
| 107 | +### Current GitStashCmd |
| 108 | + |
| 109 | +| Method | Status | Description | |
| 110 | +|--------|--------|-------------| |
| 111 | +| `__init__(path, cmd)` | Implemented | Constructor | |
| 112 | +| `__repr__()` | Implemented | String representation | |
| 113 | +| `run(command, local_flags, quiet, cached)` | Implemented | Run git stash command | |
| 114 | +| `ls()` | Implemented | List stashes (returns string) | |
| 115 | +| `push(path, patch, staged)` | Implemented | Push to stash | |
| 116 | +| `pop(stash, index, quiet)` | Implemented | Pop from stash | |
| 117 | +| `save(message, staged, keep_index, patch, include_untracked, _all, quiet)` | Implemented | Save to stash | |
| 118 | + |
| 119 | +### Planned GitStashManager (Collection-level) |
| 120 | + |
| 121 | +| Method | Status | Description | |
| 122 | +|--------|--------|-------------| |
| 123 | +| `__init__(path, cmd)` | **Planned** | Constructor | |
| 124 | +| `run(command, local_flags)` | **Planned** | Run git stash command | |
| 125 | +| `ls()` | **Planned** | Returns `QueryList[GitStashEntryCmd]` | |
| 126 | +| `get(**kwargs)` | **Planned** | Get single stash by filter | |
| 127 | +| `filter(**kwargs)` | **Planned** | Filter stashes | |
| 128 | +| `push(message, path, patch, staged, keep_index, include_untracked)` | **Planned** | Push to stash | |
| 129 | +| `clear()` | **Planned** | Clear all stashes | |
| 130 | + |
| 131 | +### Planned GitStashEntryCmd (Per-entity) |
| 132 | + |
| 133 | +Properties: `index: int`, `branch: str`, `message: str` |
| 134 | + |
| 135 | +Parse from: `stash@{0}: On master: my message` |
| 136 | + |
| 137 | +| Method | Status | Description | |
| 138 | +|--------|--------|-------------| |
| 139 | +| `__init__(path, index, branch, message, cmd)` | **Planned** | Constructor | |
| 140 | +| `show(stat, patch)` | **Planned** | Show stash diff | |
| 141 | +| `apply(index)` | **Planned** | Apply without removing | |
| 142 | +| `pop(index)` | **Planned** | Apply and remove | |
| 143 | +| `drop()` | **Planned** | Delete this stash | |
| 144 | +| `branch(branch_name)` | **Planned** | Create branch from stash | |
| 145 | + |
| 146 | +--- |
| 147 | + |
| 148 | +## 4. GitSubmoduleCmd (Current) → GitSubmoduleManager / GitSubmoduleCmd (Planned) |
| 149 | + |
| 150 | +**Pattern Status**: Not implemented - needs refactoring |
| 151 | + |
| 152 | +### Current GitSubmoduleCmd |
| 153 | + |
| 154 | +| Method | Status | Description | |
| 155 | +|--------|--------|-------------| |
| 156 | +| `__init__(path, cmd)` | Implemented | Constructor | |
| 157 | +| `__repr__()` | Implemented | String representation | |
| 158 | +| `run(command, local_flags, quiet, cached)` | Implemented | Run git submodule command | |
| 159 | +| `init(path)` | Implemented | Initialize submodules | |
| 160 | +| `update(path, init, force, checkout, rebase, merge, recursive, _filter)` | Implemented | Update submodules | |
| 161 | + |
| 162 | +### Planned GitSubmoduleManager (Collection-level) |
| 163 | + |
| 164 | +| Method | Status | Description | |
| 165 | +|--------|--------|-------------| |
| 166 | +| `__init__(path, cmd)` | **Planned** | Constructor | |
| 167 | +| `run(command, local_flags)` | **Planned** | Run git submodule command | |
| 168 | +| `ls()` | **Planned** | Returns `QueryList[GitSubmoduleCmd]` | |
| 169 | +| `get(**kwargs)` | **Planned** | Get single submodule by filter | |
| 170 | +| `filter(**kwargs)` | **Planned** | Filter submodules | |
| 171 | +| `add(url, path, branch, name, force)` | **Planned** | Add submodule | |
| 172 | +| `foreach(command, recursive)` | **Planned** | Execute in each submodule | |
| 173 | +| `sync(recursive)` | **Planned** | Sync submodule URLs | |
| 174 | +| `summary(commit, files, cached)` | **Planned** | Summarize changes | |
| 175 | + |
| 176 | +### Planned GitSubmoduleCmd (Per-entity) |
| 177 | + |
| 178 | +Properties: `name`, `path`, `url`, `branch` |
| 179 | + |
| 180 | +| Method | Status | Description | |
| 181 | +|--------|--------|-------------| |
| 182 | +| `__init__(path, name, submodule_path, url, branch, cmd)` | **Planned** | Constructor | |
| 183 | +| `init()` | **Planned** | Initialize this submodule | |
| 184 | +| `update(init, force, checkout, rebase, merge, recursive)` | **Planned** | Update this submodule | |
| 185 | +| `deinit(force)` | **Planned** | Unregister submodule | |
| 186 | +| `set_branch(branch)` | **Planned** | Set branch | |
| 187 | +| `set_url(url)` | **Planned** | Set URL | |
| 188 | +| `status()` | **Planned** | Show status | |
| 189 | +| `absorbgitdirs()` | **Planned** | Absorb gitdir | |
| 190 | + |
| 191 | +--- |
| 192 | + |
| 193 | +## 5. GitTagManager / GitTagCmd (New) |
| 194 | + |
| 195 | +**Pattern Status**: Not implemented |
| 196 | + |
| 197 | +### Planned GitTagManager (Collection-level) |
| 198 | + |
| 199 | +| Method | Status | Description | |
| 200 | +|--------|--------|-------------| |
| 201 | +| `__init__(path, cmd)` | **Planned** | Constructor | |
| 202 | +| `run(command, local_flags)` | **Planned** | Run git tag command | |
| 203 | +| `ls(pattern, sort, contains, no_contains, merged, no_merged)` | **Planned** | Returns `QueryList[GitTagCmd]` | |
| 204 | +| `get(**kwargs)` | **Planned** | Get single tag by filter | |
| 205 | +| `filter(**kwargs)` | **Planned** | Filter tags | |
| 206 | +| `create(name, ref, message, annotate, sign, force)` | **Planned** | Create tag | |
| 207 | + |
| 208 | +### Planned GitTagCmd (Per-entity) |
| 209 | + |
| 210 | +Properties: `tag_name`, `ref`, `message` (for annotated) |
| 211 | + |
| 212 | +| Method | Status | Description | |
| 213 | +|--------|--------|-------------| |
| 214 | +| `__init__(path, tag_name, ref, message, cmd)` | **Planned** | Constructor | |
| 215 | +| `show()` | **Planned** | Show tag details | |
| 216 | +| `delete()` | **Planned** | Delete tag (`-d`) | |
| 217 | +| `verify()` | **Planned** | Verify signed tag (`-v`) | |
| 218 | + |
| 219 | +--- |
| 220 | + |
| 221 | +## 6. GitWorktreeManager / GitWorktreeCmd (New) |
| 222 | + |
| 223 | +**Pattern Status**: Not implemented |
| 224 | + |
| 225 | +### Planned GitWorktreeManager (Collection-level) |
| 226 | + |
| 227 | +| Method | Status | Description | |
| 228 | +|--------|--------|-------------| |
| 229 | +| `__init__(path, cmd)` | **Planned** | Constructor | |
| 230 | +| `run(command, local_flags)` | **Planned** | Run git worktree command | |
| 231 | +| `ls()` | **Planned** | Returns `QueryList[GitWorktreeCmd]` | |
| 232 | +| `get(**kwargs)` | **Planned** | Get single worktree by filter | |
| 233 | +| `filter(**kwargs)` | **Planned** | Filter worktrees | |
| 234 | +| `add(path, branch, detach, checkout, lock, force)` | **Planned** | Add worktree | |
| 235 | +| `prune(dry_run, verbose, expire)` | **Planned** | Prune worktrees | |
| 236 | + |
| 237 | +### Planned GitWorktreeCmd (Per-entity) |
| 238 | + |
| 239 | +Properties: `worktree_path`, `branch`, `head`, `locked`, `prunable` |
| 240 | + |
| 241 | +| Method | Status | Description | |
| 242 | +|--------|--------|-------------| |
| 243 | +| `__init__(path, worktree_path, branch, head, locked, prunable, cmd)` | **Planned** | Constructor | |
| 244 | +| `remove(force)` | **Planned** | Remove worktree | |
| 245 | +| `lock(reason)` | **Planned** | Lock worktree | |
| 246 | +| `unlock()` | **Planned** | Unlock worktree | |
| 247 | +| `move(new_path)` | **Planned** | Move worktree | |
| 248 | +| `repair()` | **Planned** | Repair worktree | |
| 249 | + |
| 250 | +--- |
| 251 | + |
| 252 | +## 7. GitNotesManager / GitNoteCmd (New) |
| 253 | + |
| 254 | +**Pattern Status**: Not implemented |
| 255 | + |
| 256 | +### Planned GitNotesManager (Collection-level) |
| 257 | + |
| 258 | +| Method | Status | Description | |
| 259 | +|--------|--------|-------------| |
| 260 | +| `__init__(path, cmd)` | **Planned** | Constructor | |
| 261 | +| `run(command, local_flags)` | **Planned** | Run git notes command | |
| 262 | +| `ls(ref)` | **Planned** | Returns `QueryList[GitNoteCmd]` | |
| 263 | +| `get(**kwargs)` | **Planned** | Get single note by filter | |
| 264 | +| `filter(**kwargs)` | **Planned** | Filter notes | |
| 265 | +| `add(object, message, file, force, allow_empty)` | **Planned** | Add note | |
| 266 | +| `prune(dry_run, verbose)` | **Planned** | Prune notes | |
| 267 | +| `merge(notes_ref, strategy, commit, abort, quiet)` | **Planned** | Merge notes | |
| 268 | +| `get_ref()` | **Planned** | Get notes ref | |
| 269 | + |
| 270 | +### Planned GitNoteCmd (Per-entity) |
| 271 | + |
| 272 | +Properties: `object`, `note_ref` |
| 273 | + |
| 274 | +| Method | Status | Description | |
| 275 | +|--------|--------|-------------| |
| 276 | +| `__init__(path, object, note_ref, cmd)` | **Planned** | Constructor | |
| 277 | +| `show()` | **Planned** | Show note | |
| 278 | +| `edit()` | **Planned** | Edit note (non-interactive) | |
| 279 | +| `append(message)` | **Planned** | Append to note | |
| 280 | +| `copy(from_object)` | **Planned** | Copy note | |
| 281 | +| `remove()` | **Planned** | Remove note | |
| 282 | + |
| 283 | +--- |
| 284 | + |
| 285 | +## 8. GitReflogManager / GitReflogCmd (New) |
| 286 | + |
| 287 | +**Pattern Status**: Not implemented |
| 288 | + |
| 289 | +### Planned GitReflogManager (Collection-level) |
| 290 | + |
| 291 | +| Method | Status | Description | |
| 292 | +|--------|--------|-------------| |
| 293 | +| `__init__(path, cmd)` | **Planned** | Constructor | |
| 294 | +| `run(command, local_flags)` | **Planned** | Run git reflog command | |
| 295 | +| `ls(ref)` | **Planned** | Returns `QueryList[GitReflogCmd]` | |
| 296 | +| `get(**kwargs)` | **Planned** | Get single entry by filter | |
| 297 | +| `filter(**kwargs)` | **Planned** | Filter entries | |
| 298 | +| `expire(ref, _all, dry_run, rewrite, updateref, stale_fix, verbose)` | **Planned** | Expire entries | |
| 299 | +| `exists(ref)` | **Planned** | Check if reflog exists | |
| 300 | + |
| 301 | +### Planned GitReflogCmd (Per-entity) |
| 302 | + |
| 303 | +Properties: `ref`, `index`, `action`, `message`, `sha` |
| 304 | + |
| 305 | +Parse from: `abc1234 HEAD@{0}: commit: message` |
| 306 | + |
| 307 | +| Method | Status | Description | |
| 308 | +|--------|--------|-------------| |
| 309 | +| `__init__(path, ref, index, action, message, sha, cmd)` | **Planned** | Constructor | |
| 310 | +| `show()` | **Planned** | Show entry details | |
| 311 | +| `delete()` | **Planned** | Delete entry | |
| 312 | + |
| 313 | +--- |
| 314 | + |
| 315 | +## Git Class Exposure |
| 316 | + |
| 317 | +### Current |
| 318 | + |
| 319 | +```python |
| 320 | +class Git: |
| 321 | + submodule: GitSubmoduleCmd |
| 322 | + remotes: GitRemoteManager # ✓ Manager pattern |
| 323 | + stash: GitStashCmd |
| 324 | + branches: GitBranchManager # ✓ Manager pattern |
| 325 | +``` |
| 326 | + |
| 327 | +### Planned |
| 328 | + |
| 329 | +```python |
| 330 | +class Git: |
| 331 | + submodules: GitSubmoduleManager # Renamed + Manager pattern |
| 332 | + remotes: GitRemoteManager # ✓ Already done |
| 333 | + stash: GitStashManager # Refactored to Manager pattern |
| 334 | + branches: GitBranchManager # ✓ Already done |
| 335 | + tags: GitTagManager # New |
| 336 | + worktrees: GitWorktreeManager # New |
| 337 | + notes: GitNotesManager # New |
| 338 | + reflog: GitReflogManager # New |
| 339 | +``` |
| 340 | + |
| 341 | +--- |
| 342 | + |
| 343 | +## Implementation Order |
| 344 | + |
| 345 | +1. Complete GitBranchCmd (add missing methods) |
| 346 | +2. Complete GitRemoteCmd (add missing methods) |
| 347 | +3. Implement GitTagManager/GitTagCmd |
| 348 | +4. Refactor GitStashCmd → GitStashManager/GitStashEntryCmd |
| 349 | +5. Implement GitWorktreeManager/GitWorktreeCmd |
| 350 | +6. Implement GitNotesManager/GitNoteCmd |
| 351 | +7. Refactor GitSubmoduleCmd → GitSubmoduleManager/GitSubmoduleCmd |
| 352 | +8. Implement GitReflogManager/GitReflogCmd |
| 353 | +9. Update Git class to expose all managers |
| 354 | + |
| 355 | +--- |
| 356 | + |
| 357 | +## Test Coverage |
| 358 | + |
| 359 | +Currently **no direct unit tests** exist for: |
| 360 | +- GitBranchManager / GitBranchCmd |
| 361 | +- GitRemoteManager / GitRemoteCmd |
| 362 | +- GitStashCmd |
| 363 | +- GitSubmoduleCmd |
| 364 | + |
| 365 | +Tests use pytest fixtures from `libvcs.pytest_plugin`: |
| 366 | +- `create_git_remote_repo` - Creates temporary git repo |
| 367 | +- `git_repo` - Pre-made GitSync instance |
| 368 | +- `example_git_repo` - Example repo for doctests |
| 369 | + |
| 370 | +All new tests will: |
| 371 | +- Use real git commands (no mocks) |
| 372 | +- Use pytest fixtures for setup/teardown |
| 373 | +- Follow TDD approach |
0 commit comments