diff --git a/CHANGES b/CHANGES index 197b5c989..c322f2eb7 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,59 @@ $ uv add libvcs --prerelease allow _Upcoming changes will be written here._ +### New features + +#### cmd: Manager/Cmd pattern for git subcommands (#465) + +New architecture for git subcommands that returns typed objects instead of raw strings: + +- **Manager classes** handle collection-level operations (`ls()`, `get()`, `filter()`, `add()`/`create()`) +- **Cmd classes** handle per-entity operations (`show()`, `remove()`, `rename()`) +- All `ls()` methods return `QueryList` for chainable filtering + +New subcommand managers accessible via `Git` instance: + +- {attr}`Git.branches ` -> {class}`~libvcs.cmd.git.GitBranchManager` +- {attr}`Git.remotes ` -> {class}`~libvcs.cmd.git.GitRemoteManager` +- {attr}`Git.stashes ` -> {class}`~libvcs.cmd.git.GitStashManager` +- {attr}`Git.tags ` -> {class}`~libvcs.cmd.git.GitTagManager` +- {attr}`Git.worktrees ` -> {class}`~libvcs.cmd.git.GitWorktreeManager` +- {attr}`Git.notes ` -> {class}`~libvcs.cmd.git.GitNotesManager` +- {attr}`Git.submodules ` -> {class}`~libvcs.cmd.git.GitSubmoduleManager` +- {attr}`Git.reflog ` -> {class}`~libvcs.cmd.git.GitReflogManager` + +Example usage: + +```python +git = Git(path="/path/to/repo") + +# List all branches, filter remote ones +remote_branches = git.branches.ls(remotes=True) + +# Get a specific tag +tag = git.tags.get(tag_name="v1.0.0") +tag.delete() + +# Create a new branch and switch to it +git.branches.create("feature-branch") +``` + +#### cmd: Enhanced Git.init() (#465) + +- Added `ref_format` parameter for `--ref-format` (files/reftable) +- Added `make_parents` parameter to auto-create parent directories +- Improved parameter validation with clear error messages +- Extended `shared` parameter to support octal permissions (e.g., "0660") + +### Documentation + +- Add API documentation for all new Manager/Cmd classes (#465) +- Split git subcommand documentation into separate pages: branch, tag, worktree, notes, reflog + +### Tests + +- Comprehensive test coverage for all new Manager/Cmd classes (#465) + ## libvcs 0.37.0 (2025-11-01) ### Breaking changes diff --git a/README.md b/README.md index 7256a0e7d..1af4ca67a 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,31 @@ git.clone(url='https://github.com/vcs-python/libvcs.git') Above: [`libvcs.cmd.git.Git`](https://libvcs.git-pull.com/cmd/git.html#libvcs.cmd.git.Git) using [`Git.clone()`](http://libvcs.git-pull.com/cmd/git.html#libvcs.cmd.git.Git.clone). +### Manage Branches, Tags, and More + +Work with git subcommands using typed Python objects: + +```python +from libvcs.cmd.git import Git + +git = Git(path='/path/to/repo') + +# Branches +branches = git.branches.ls() # List all branches +git.branches.create('feature-branch') # Create a branch + +# Tags +git.tags.create(name='v1.0.0', message='Release') +tags = git.tags.ls() + +# Remotes +remotes = git.remotes.ls() +remote = git.remotes.get(remote_name='origin') +remote.prune() +``` + +See the [Manager/Cmd pattern documentation](https://libvcs.git-pull.com/cmd/git/index.html) for more. + ## Repository Synchronization Synchronize your repositories using the diff --git a/docs/cmd/git/branch.md b/docs/cmd/git/branch.md new file mode 100644 index 000000000..cc71877dc --- /dev/null +++ b/docs/cmd/git/branch.md @@ -0,0 +1,44 @@ +# `branch` + +For `git-branch(1)`. + +## Overview + +Manage git branches using {class}`~libvcs.cmd.git.GitBranchManager` (collection-level) +and {class}`~libvcs.cmd.git.GitBranchCmd` (per-branch operations). + +### Example + +```python +from libvcs.cmd.git import Git + +git = Git(path='/path/to/repo') + +# List all branches +branches = git.branches.ls() + +# List remote branches only +remote_branches = git.branches.ls(remotes=True) + +# Create a new branch +git.branches.create('feature-branch') + +# Get a specific branch and operate on it +branch = git.branches.get(branch_name='feature-branch') +branch.rename('new-feature') +branch.delete() +``` + +## API Reference + +```{eval-rst} +.. autoclass:: libvcs.cmd.git.GitBranchManager + :members: + :show-inheritance: + :undoc-members: + +.. autoclass:: libvcs.cmd.git.GitBranchCmd + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/cmd/git/index.md b/docs/cmd/git/index.md index 18d200661..a11eb6156 100644 --- a/docs/cmd/git/index.md +++ b/docs/cmd/git/index.md @@ -6,6 +6,51 @@ _Compare to: [`fabtools.git`](https://fabtools.readthedocs.io/en/0.19.0/api/git. [`salt.modules.git`](https://docs.saltproject.io/en/latest/ref/modules/all/salt.modules.git.html), [`ansible.builtin.git`](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/git_module.html)_ +## Manager/Cmd Pattern + +libvcs provides a **Manager/Cmd pattern** for git subcommands: + +- **Manager** classes (`git.branches`, `git.tags`, etc.) handle collection-level operations +- **Cmd** classes represent individual entities with mutation methods + +``` +Git instance +├── branches: GitBranchManager +│ ├── ls() -> QueryList[GitBranchCmd] +│ ├── get() -> GitBranchCmd +│ └── create() +├── tags: GitTagManager +├── remotes: GitRemoteManager +├── stashes: GitStashManager +├── worktrees: GitWorktreeManager +├── notes: GitNotesManager +├── submodules: GitSubmoduleManager +└── reflog: GitReflogManager +``` + +### Quick Example + +```python +from libvcs.cmd.git import Git + +git = Git(path='/path/to/repo') + +# List all branches +branches = git.branches.ls() + +# Filter to remote branches only +remote_branches = git.branches.ls(remotes=True) + +# Get a specific branch and rename it +branch = git.branches.get(branch_name='old-name') +branch.rename('new-name') + +# Create and manage tags +git.tags.create(name='v1.0.0', message='Release 1.0') +tag = git.tags.get(tag_name='v1.0.0') +tag.delete() +``` + ```{toctree} :caption: Subcommands :maxdepth: 1 @@ -13,6 +58,11 @@ _Compare to: [`fabtools.git`](https://fabtools.readthedocs.io/en/0.19.0/api/git. submodule remote stash +branch +tag +worktree +notes +reflog ``` ```{eval-rst} @@ -21,6 +71,23 @@ stash :show-inheritance: :undoc-members: :exclude-members: GitSubmoduleCmd, + GitSubmoduleManager, + GitSubmodule, + GitSubmoduleEntryCmd, GitRemoteCmd, - GitStashCmd + GitRemoteManager, + GitStashCmd, + GitStashManager, + GitStashEntryCmd, + GitBranchCmd, + GitBranchManager, + GitTagCmd, + GitTagManager, + GitWorktreeCmd, + GitWorktreeManager, + GitNoteCmd, + GitNotesManager, + GitReflogEntry, + GitReflogEntryCmd, + GitReflogManager ``` diff --git a/docs/cmd/git/notes.md b/docs/cmd/git/notes.md new file mode 100644 index 000000000..01b545563 --- /dev/null +++ b/docs/cmd/git/notes.md @@ -0,0 +1,45 @@ +# `notes` + +For `git-notes(1)`. + +## Overview + +Manage git notes using {class}`~libvcs.cmd.git.GitNotesManager` (collection-level) +and {class}`~libvcs.cmd.git.GitNoteCmd` (per-note operations). + +### Example + +```python +from libvcs.cmd.git import Git + +git = Git(path='/path/to/repo') + +# Add a note to a commit +git.notes.add(object='HEAD', message='This is a note') + +# List all notes +notes = git.notes.ls() + +# Get a specific note and operate on it +note = git.notes.get(object='HEAD') +note.show() +note.append(message='Additional info') +note.remove() + +# Prune notes for non-existent objects +git.notes.prune() +``` + +## API Reference + +```{eval-rst} +.. autoclass:: libvcs.cmd.git.GitNotesManager + :members: + :show-inheritance: + :undoc-members: + +.. autoclass:: libvcs.cmd.git.GitNoteCmd + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/cmd/git/reflog.md b/docs/cmd/git/reflog.md new file mode 100644 index 000000000..59f52162c --- /dev/null +++ b/docs/cmd/git/reflog.md @@ -0,0 +1,47 @@ +# `reflog` + +For `git-reflog(1)`. + +## Overview + +Manage git reflog using {class}`~libvcs.cmd.git.GitReflogManager` (collection-level) +and {class}`~libvcs.cmd.git.GitReflogEntryCmd` (per-entry operations). + +### Example + +```python +from libvcs.cmd.git import Git + +git = Git(path='/path/to/repo') + +# List reflog entries +entries = git.reflog.ls() + +# List entries for a specific ref +head_entries = git.reflog.ls(ref='HEAD') + +# Check if reflog exists for a ref +git.reflog.exists(ref='main') + +# Expire old reflog entries +git.reflog.expire(ref='HEAD', expire='90.days.ago') +``` + +## API Reference + +```{eval-rst} +.. autoclass:: libvcs.cmd.git.GitReflogManager + :members: + :show-inheritance: + :undoc-members: + +.. autoclass:: libvcs.cmd.git.GitReflogEntryCmd + :members: + :show-inheritance: + :undoc-members: + +.. autoclass:: libvcs.cmd.git.GitReflogEntry + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/cmd/git/remote.md b/docs/cmd/git/remote.md index 13891e37b..be42a9b1c 100644 --- a/docs/cmd/git/remote.md +++ b/docs/cmd/git/remote.md @@ -2,7 +2,39 @@ For `git-remote(1)`. +## Overview + +Manage git remotes using {class}`~libvcs.cmd.git.GitRemoteManager` (collection-level) +and {class}`~libvcs.cmd.git.GitRemoteCmd` (per-remote operations). + +### Example + +```python +from libvcs.cmd.git import Git + +git = Git(path='/path/to/repo') + +# List all remotes +remotes = git.remotes.ls() + +# Add a new remote +git.remotes.add(name='upstream', url='https://github.com/org/repo.git') + +# Get a specific remote and operate on it +origin = git.remotes.get(remote_name='origin') +origin.show() +origin.prune() +origin.set_url('https://new-url.git') +``` + +## API Reference + ```{eval-rst} +.. autoclass:: libvcs.cmd.git.GitRemoteManager + :members: + :show-inheritance: + :undoc-members: + .. autoclass:: libvcs.cmd.git.GitRemoteCmd :members: :show-inheritance: diff --git a/docs/cmd/git/stash.md b/docs/cmd/git/stash.md index 15e138a2b..82b471fb0 100644 --- a/docs/cmd/git/stash.md +++ b/docs/cmd/git/stash.md @@ -2,7 +2,52 @@ For `git-stash(1)`. +## Overview + +Manage git stashes using {class}`~libvcs.cmd.git.GitStashManager` (collection-level) +and {class}`~libvcs.cmd.git.GitStashEntryCmd` (per-stash operations). + +:::{note} +{class}`~libvcs.cmd.git.GitStashCmd` is the legacy interface. Use `git.stashes` +({class}`~libvcs.cmd.git.GitStashManager`) for the new Manager/Cmd pattern. +::: + +### Example + +```python +from libvcs.cmd.git import Git + +git = Git(path='/path/to/repo') + +# Push changes to stash +git.stashes.push(message='Work in progress') + +# List all stashes +stashes = git.stashes.ls() + +# Get a specific stash and operate on it +stash = git.stashes.get(index=0) +stash.show() +stash.apply() +stash.drop() + +# Clear all stashes +git.stashes.clear() +``` + +## API Reference + ```{eval-rst} +.. autoclass:: libvcs.cmd.git.GitStashManager + :members: + :show-inheritance: + :undoc-members: + +.. autoclass:: libvcs.cmd.git.GitStashEntryCmd + :members: + :show-inheritance: + :undoc-members: + .. autoclass:: libvcs.cmd.git.GitStashCmd :members: :show-inheritance: diff --git a/docs/cmd/git/submodule.md b/docs/cmd/git/submodule.md index d799ddd09..83110ca6a 100644 --- a/docs/cmd/git/submodule.md +++ b/docs/cmd/git/submodule.md @@ -2,7 +2,57 @@ For `git-submodule(1)`. +## Overview + +Manage git submodules using {class}`~libvcs.cmd.git.GitSubmoduleManager` (collection-level) +and {class}`~libvcs.cmd.git.GitSubmoduleEntryCmd` (per-submodule operations). + +:::{note} +{class}`~libvcs.cmd.git.GitSubmoduleCmd` is the legacy interface. Use `git.submodules` +({class}`~libvcs.cmd.git.GitSubmoduleManager`) for the new Manager/Cmd pattern. +::: + +### Example + +```python +from libvcs.cmd.git import Git + +git = Git(path='/path/to/repo') + +# Add a submodule +git.submodules.add(url='https://github.com/org/lib.git', path='vendor/lib') + +# List all submodules +submodules = git.submodules.ls() + +# Get a specific submodule and operate on it +submodule = git.submodules.get(path='vendor/lib') +submodule.init() +submodule.update() +submodule.deinit() + +# Sync submodule URLs +git.submodules.sync() +``` + +## API Reference + ```{eval-rst} +.. autoclass:: libvcs.cmd.git.GitSubmoduleManager + :members: + :show-inheritance: + :undoc-members: + +.. autoclass:: libvcs.cmd.git.GitSubmodule + :members: + :show-inheritance: + :undoc-members: + +.. autoclass:: libvcs.cmd.git.GitSubmoduleEntryCmd + :members: + :show-inheritance: + :undoc-members: + .. autoclass:: libvcs.cmd.git.GitSubmoduleCmd :members: :show-inheritance: diff --git a/docs/cmd/git/tag.md b/docs/cmd/git/tag.md new file mode 100644 index 000000000..d18324ddd --- /dev/null +++ b/docs/cmd/git/tag.md @@ -0,0 +1,44 @@ +# `tag` + +For `git-tag(1)`. + +## Overview + +Manage git tags using {class}`~libvcs.cmd.git.GitTagManager` (collection-level) +and {class}`~libvcs.cmd.git.GitTagCmd` (per-tag operations). + +### Example + +```python +from libvcs.cmd.git import Git + +git = Git(path='/path/to/repo') + +# Create a tag +git.tags.create(name='v1.0.0', message='Release 1.0.0') + +# List all tags +tags = git.tags.ls() + +# Filter tags +release_tags = git.tags.ls(pattern='v*') + +# Get a specific tag and operate on it +tag = git.tags.get(tag_name='v1.0.0') +tag.show() +tag.delete() +``` + +## API Reference + +```{eval-rst} +.. autoclass:: libvcs.cmd.git.GitTagManager + :members: + :show-inheritance: + :undoc-members: + +.. autoclass:: libvcs.cmd.git.GitTagCmd + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/cmd/git/worktree.md b/docs/cmd/git/worktree.md new file mode 100644 index 000000000..2c42c24ab --- /dev/null +++ b/docs/cmd/git/worktree.md @@ -0,0 +1,45 @@ +# `worktree` + +For `git-worktree(1)`. + +## Overview + +Manage git worktrees using {class}`~libvcs.cmd.git.GitWorktreeManager` (collection-level) +and {class}`~libvcs.cmd.git.GitWorktreeCmd` (per-worktree operations). + +### Example + +```python +from libvcs.cmd.git import Git + +git = Git(path='/path/to/repo') + +# List all worktrees +worktrees = git.worktrees.ls() + +# Add a new worktree +git.worktrees.add(path='/path/to/worktree', branch='feature-branch') + +# Get a specific worktree and operate on it +wt = git.worktrees.get(worktree_path='/path/to/worktree') +wt.lock(reason='Do not delete') +wt.unlock() +wt.remove() + +# Prune stale worktrees +git.worktrees.prune() +``` + +## API Reference + +```{eval-rst} +.. autoclass:: libvcs.cmd.git.GitWorktreeManager + :members: + :show-inheritance: + :undoc-members: + +.. autoclass:: libvcs.cmd.git.GitWorktreeCmd + :members: + :show-inheritance: + :undoc-members: +``` diff --git a/docs/cmd/index.md b/docs/cmd/index.md index 23726bc08..05ba7ff0f 100644 --- a/docs/cmd/index.md +++ b/docs/cmd/index.md @@ -13,6 +13,21 @@ versions. ::: +## Overview + +The `libvcs.cmd` module provides Python wrappers for VCS command-line tools: + +- {mod}`libvcs.cmd.git` - Git commands with Manager/Cmd pattern for branches, tags, remotes, etc. +- {mod}`libvcs.cmd.hg` - Mercurial commands +- {mod}`libvcs.cmd.svn` - Subversion commands + +### When to use `cmd` vs `sync` + +| Module | Use Case | +|--------|----------| +| `libvcs.cmd` | Fine-grained control over individual VCS commands | +| `libvcs.sync` | High-level repository cloning and updating | + ```{toctree} :caption: API diff --git a/docs/internals/query_list.md b/docs/internals/query_list.md index 5cb6a38c1..d68bc59a8 100644 --- a/docs/internals/query_list.md +++ b/docs/internals/query_list.md @@ -1,5 +1,74 @@ # List querying - `libvcs._internal.query_list` +`QueryList` is the backbone of the Manager/Cmd pattern. Every `ls()` method in +libvcs returns a `QueryList`, enabling chainable filtering on the results. + +## How It's Used + +All Manager classes return `QueryList` from their `ls()` methods: + +```python +from libvcs.cmd.git import Git + +git = Git(path='/path/to/repo') + +# Each ls() returns a QueryList +branches = git.branches.ls() # QueryList[GitBranchCmd] +tags = git.tags.ls() # QueryList[GitTagCmd] +remotes = git.remotes.ls() # QueryList[GitRemoteCmd] +stashes = git.stashes.ls() # QueryList[GitStashEntryCmd] +worktrees = git.worktrees.ls() # QueryList[GitWorktreeCmd] +``` + +## Filtering + +`QueryList` extends Python's built-in `list` with Django-style lookups: + +```python +# Exact match +branches.filter(name='main') + +# Case-insensitive contains +branches.filter(name__icontains='feature') + +# Nested attribute access +branches.filter(commit__sha__startswith='abc123') +``` + +### Available Lookups + +| Lookup | Description | +|--------|-------------| +| `exact` | Exact match (default) | +| `iexact` | Case-insensitive exact match | +| `contains` | Substring match | +| `icontains` | Case-insensitive substring | +| `startswith` | Prefix match | +| `istartswith` | Case-insensitive prefix | +| `endswith` | Suffix match | +| `iendswith` | Case-insensitive suffix | +| `in` | Value in list | +| `nin` | Value not in list | +| `regex` | Regular expression match | +| `iregex` | Case-insensitive regex | + +### Chaining + +Filters can be chained and combined: + +```python +# Multiple conditions (AND) +branches.filter(name__startswith='feature', is_remote=False) + +# Get single result +branches.get(name='main') + +# Chain filters +branches.filter(is_remote=True).filter(name__contains='release') +``` + +## API Reference + ```{eval-rst} .. automodule:: libvcs._internal.query_list :members: diff --git a/docs/quickstart.md b/docs/quickstart.md index a0d68a079..109beb5df 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -52,3 +52,49 @@ via trunk (can break easily): [pip]: https://pip.pypa.io/en/stable/ [uv]: https://docs.astral.sh/uv/ + +## Basic Usage + +### Commands + +Run git commands directly using {class}`~libvcs.cmd.git.Git`: + +```python +from libvcs.cmd.git import Git + +git = Git(path='/path/to/repo') + +# Initialize a new repository +git.init() + +# Clone a repository +git.clone(url='https://github.com/vcs-python/libvcs.git') + +# Check status +git.status() +``` + +### Subcommand Managers + +Work with branches, tags, remotes, and more using the Manager/Cmd pattern: + +```python +from libvcs.cmd.git import Git + +git = Git(path='/path/to/repo') + +# List and filter branches +branches = git.branches.ls() +remote_branches = git.branches.ls(remotes=True) + +# Create and manage tags +git.tags.create(name='v1.0.0', message='Release 1.0') +tag = git.tags.get(tag_name='v1.0.0') + +# Work with remotes +remotes = git.remotes.ls() +origin = git.remotes.get(remote_name='origin') +origin.prune() +``` + +See {doc}`/cmd/git/index` for the full API reference. diff --git a/notes/2025-11-26-command-support.md b/notes/2025-11-26-command-support.md new file mode 100644 index 000000000..32bc4be6b --- /dev/null +++ b/notes/2025-11-26-command-support.md @@ -0,0 +1,612 @@ +# Git Command Support Audit - 2025-11-26 + +This document provides a comprehensive audit of git command support in libvcs, documenting existing implementations and planned additions following the Manager/Cmd pattern. + +## Pattern Overview + +``` +Manager (collection-level) Cmd (per-entity) +├── ls() -> QueryList[Cmd] ├── run() +├── get(**kwargs) -> Cmd | None ├── show() +├── filter(**kwargs) -> list[Cmd] ├── remove()/delete() +├── add() / create() ├── rename() +└── run() └── entity-specific operations +``` + +--- + +## 1. GitBranchManager / GitBranchCmd + +**Pattern Status**: Implemented + +### GitBranchManager (Collection-level) + +| Method | Status | Description | +|--------|--------|-------------| +| `__init__(path, cmd)` | Implemented | Constructor | +| `__repr__()` | Implemented | String representation | +| `run(command, local_flags, quiet, cached)` | Implemented | Run git branch command | +| `checkout(branch)` | Implemented | Checkout a branch | +| `create(branch)` | Implemented | Create new branch via `checkout -b` | +| `_ls()` | Implemented | Internal raw listing | +| `ls()` | Implemented | Returns `QueryList[GitBranchCmd]` | +| `get(**kwargs)` | Implemented | Get single branch by filter | +| `filter(**kwargs)` | Implemented | Filter branches | + +#### CLI Flag → Python Parameter Mapping: `ls()` Enhancements + +| Git CLI Flag | Python Parameter | Description | +|--------------|------------------|-------------| +| `-a, --all` | `_all: bool` | List all branches (local + remote) | +| `-r, --remotes` | `remotes: bool` | List remote branches only | +| `--merged ` | `merged: str \| None` | Filter merged branches | +| `--no-merged ` | `no_merged: str \| None` | Filter unmerged branches | +| `-v, --verbose` | `verbose: bool` | Show tracking info | +| `--contains ` | `contains: str \| None` | Branches containing commit | +| `--sort=` | `sort: str \| None` | Sort key | + +### GitBranchCmd (Per-entity) + +| Method | Status | Description | +|--------|--------|-------------| +| `__init__(path, branch_name, cmd)` | Implemented | Constructor with `branch_name` | +| `__repr__()` | Implemented | String representation | +| `run(command, local_flags, quiet, cached)` | Implemented | Run git branch command | +| `checkout()` | Implemented | Checkout this branch | +| `create()` | Implemented | Create this branch | +| `delete(force)` | Implemented | `-d` / `-D` | +| `rename(new_name, force)` | Implemented | `-m` / `-M` | +| `copy(new_name, force)` | Implemented | `-c` / `-C` | +| `set_upstream(upstream)` | Implemented | `--set-upstream-to` | +| `unset_upstream()` | Implemented | `--unset-upstream` | +| `track(remote_branch)` | Implemented | `-t` / `--track` | + +#### CLI Flag → Python Parameter Mapping: GitBranchCmd Methods + +| Method | Git CLI | Parameters → Flags | +|--------|---------|-------------------| +| `delete()` | `git branch -d/-D` | `force=True` → `-D`, else `-d` | +| `rename(new_name)` | `git branch -m/-M` | `force=True` → `-M`, else `-m` | +| `copy(new_name)` | `git branch -c/-C` | `force=True` → `-C`, else `-c` | +| `set_upstream(upstream)` | `git branch --set-upstream-to=` | `upstream` → `--set-upstream-to={upstream}` | +| `unset_upstream()` | `git branch --unset-upstream` | None | +| `track(remote_branch)` | `git branch -t` | `remote_branch` → `-t {remote_branch}` | + +### GitBranchManager Enhancements + +| Feature | Status | Description | +|---------|--------|-------------| +| `--all` support | Implemented | List all branches (local + remote) | +| `--remotes` support | Implemented | List remote branches only | +| `--merged` filter | Implemented | Filter merged branches | +| `--no-merged` filter | Implemented | Filter unmerged branches | +| `--verbose` support | Implemented | Show tracking info | + +--- + +## 2. GitRemoteManager / GitRemoteCmd + +**Pattern Status**: Implemented + +### GitRemoteManager (Collection-level) + +| Method | Status | Description | +|--------|--------|-------------| +| `__init__(path, cmd)` | Implemented | Constructor | +| `__repr__()` | Implemented | String representation | +| `run(command, local_flags)` | Implemented | Run git remote command | +| `add(name, url, fetch, track, master, mirror)` | Implemented | Add new remote | +| `show(name, verbose, no_query_remotes)` | Implemented | Show remotes | +| `_ls()` | Implemented | Internal raw listing | +| `ls()` | Implemented | Returns `QueryList[GitRemoteCmd]` | +| `get(**kwargs)` | Implemented | Get single remote by filter | +| `filter(**kwargs)` | Implemented | Filter remotes | + +### GitRemoteCmd (Per-entity) + +Properties: `remote_name`, `fetch_url`, `push_url` + +| Method | Status | Description | +|--------|--------|-------------| +| `__init__(path, remote_name, fetch_url, push_url, cmd)` | Implemented | Constructor | +| `__repr__()` | Implemented | String representation | +| `run(command, local_flags, verbose)` | Implemented | Run git remote command | +| `rename(old, new, progress)` | Implemented | Rename remote | +| `remove()` | Implemented | Delete remote | +| `show(verbose, no_query_remotes)` | Implemented | Show remote details | +| `prune(dry_run)` | Implemented | Prune stale branches | +| `get_url(push, _all)` | Implemented | Get remote URL | +| `set_url(url, old_url, push, add, delete)` | Implemented | Set remote URL | +| `set_branches(*branches, add)` | Implemented | `set-branches` | +| `set_head(branch, auto, delete)` | Implemented | `set-head` | +| `update(prune)` | Implemented | `update` | + +#### CLI Flag → Python Parameter Mapping: Existing Methods + +| Method | Parameters → Flags | +|--------|-------------------| +| `rename()` | `progress=True` → `--progress`, `progress=False` → `--no-progress` | +| `show()` | `verbose=True` → `--verbose`, `no_query_remotes=True` → `-n` | +| `prune()` | `dry_run=True` → `--dry-run` | +| `get_url()` | `push=True` → `--push`, `_all=True` → `--all` | +| `set_url()` | `push=True` → `--push`, `add=True` → `--add`, `delete=True` → `--delete` | + +#### CLI Flag → Python Parameter Mapping: Additional Methods + +| Method | Git CLI | Parameters → Flags | +|--------|---------|-------------------| +| `set_branches(*branches)` | `git remote set-branches` | `add=True` → `--add`, `branches` → positional | +| `set_head(branch)` | `git remote set-head` | `auto=True` → `-a`, `delete=True` → `-d`, `branch` → positional | +| `update()` | `git remote update` | `prune=True` → `-p` | + +--- + +## 3. GitStashManager / GitStashEntryCmd + +**Pattern Status**: Implemented + +### Current GitStashCmd + +| Method | Status | Description | +|--------|--------|-------------| +| `__init__(path, cmd)` | Implemented | Constructor | +| `__repr__()` | Implemented | String representation | +| `run(command, local_flags, quiet, cached)` | Implemented | Run git stash command | +| `ls()` | Implemented | List stashes (returns string) | +| `push(path, patch, staged)` | Implemented | Push to stash | +| `pop(stash, index, quiet)` | Implemented | Pop from stash | +| `save(message, staged, keep_index, patch, include_untracked, _all, quiet)` | Implemented | Save to stash | + +### Planned GitStashManager (Collection-level) + +| Method | Status | Description | +|--------|--------|-------------| +| `__init__(path, cmd)` | Implemented | Constructor | +| `run(command, local_flags)` | Implemented | Run git stash command | +| `ls()` | Implemented | Returns `QueryList[GitStashEntryCmd]` | +| `get(**kwargs)` | Implemented | Get single stash by filter | +| `filter(**kwargs)` | Implemented | Filter stashes | +| `push(message, path, patch, staged, keep_index, include_untracked)` | Implemented | Push to stash | +| `clear()` | Implemented | Clear all stashes | + +#### CLI Flag → Python Parameter Mapping: `push()` + +| Git CLI Flag | Python Parameter | Description | +|--------------|------------------|-------------| +| `-p, --patch` | `patch: bool` | Interactive patch selection | +| `-S, --staged` | `staged: bool` | Stash only staged changes | +| `-k, --keep-index` | `keep_index: bool` | Keep index intact | +| `-u, --include-untracked` | `include_untracked: bool` | Include untracked files | +| `-a, --all` | `_all: bool` | Include ignored files | +| `-q, --quiet` | `quiet: bool` | Suppress output | +| `-m, --message ` | `message: str \| None` | Stash message | +| `-- ` | `path: list[str] \| None` | Limit to paths | + +### Planned GitStashEntryCmd (Per-entity) + +Properties: `index: int`, `branch: str`, `message: str` + +Parse from: `stash@{0}: On master: my message` + +**Parsing pattern**: +```python +stash_pattern = r"stash@\{(?P\d+)\}: On (?P[^:]+): (?P.+)" +``` + +| Method | Status | Description | +|--------|--------|-------------| +| `__init__(path, index, branch, message, cmd)` | Implemented | Constructor | +| `show(stat, patch)` | Implemented | Show stash diff | +| `apply(index)` | Implemented | Apply without removing | +| `pop(index)` | Implemented | Apply and remove | +| `drop()` | Implemented | Delete this stash | +| `branch(branch_name)` | Implemented | Create branch from stash | + +#### CLI Flag → Python Parameter Mapping: GitStashEntryCmd Methods + +| Method | Git CLI | Parameters → Flags | +|--------|---------|-------------------| +| `show()` | `git stash show` | `stat=True` → `--stat`, `patch=True` → `-p`, `include_untracked=True` → `-u` | +| `apply()` | `git stash apply` | `index=True` → `--index`, `quiet=True` → `-q` | +| `pop()` | `git stash pop` | `index=True` → `--index`, `quiet=True` → `-q` | +| `drop()` | `git stash drop` | `quiet=True` → `-q` | +| `branch(name)` | `git stash branch` | `name` → positional | + +--- + +## 4. GitSubmoduleCmd (Current) → GitSubmoduleManager / GitSubmoduleCmd (Planned) + +**Pattern Status**: Implemented + +### Current GitSubmoduleCmd + +| Method | Status | Description | +|--------|--------|-------------| +| `__init__(path, cmd)` | Implemented | Constructor | +| `__repr__()` | Implemented | String representation | +| `run(command, local_flags, quiet, cached)` | Implemented | Run git submodule command | +| `init(path)` | Implemented | Initialize submodules | +| `update(path, init, force, checkout, rebase, merge, recursive, _filter)` | Implemented | Update submodules | + +### Planned GitSubmoduleManager (Collection-level) + +| Method | Status | Description | +|--------|--------|-------------| +| `__init__(path, cmd)` | Implemented | Constructor | +| `run(command, local_flags)` | Implemented | Run git submodule command | +| `ls()` | Implemented | Returns `QueryList[GitSubmoduleCmd]` | +| `get(**kwargs)` | Implemented | Get single submodule by filter | +| `filter(**kwargs)` | Implemented | Filter submodules | +| `add(url, path, branch, name, force)` | Implemented | Add submodule | +| `foreach(command, recursive)` | Implemented | Execute in each submodule | +| `sync(recursive)` | Implemented | Sync submodule URLs | +| `summary(commit, files, cached)` | Implemented | Summarize changes | + +#### CLI Flag → Python Parameter Mapping: GitSubmoduleManager Methods + +| Method | Git CLI | Parameters → Flags | +|--------|---------|-------------------| +| `add()` | `git submodule add` | `branch` → `-b`, `force=True` → `-f`, `name` → `--name`, `depth` → `--depth` | +| `foreach()` | `git submodule foreach` | `recursive=True` → `--recursive` | +| `sync()` | `git submodule sync` | `recursive=True` → `--recursive` | +| `summary()` | `git submodule summary` | `cached=True` → `--cached`, `files=True` → `--files`, `summary_limit` → `-n` | + +### Planned GitSubmoduleCmd (Per-entity) + +Properties: `name`, `path`, `url`, `branch`, `sha` + +| Method | Status | Description | +|--------|--------|-------------| +| `__init__(path, name, submodule_path, url, branch, cmd)` | Implemented | Constructor | +| `init()` | Implemented | Initialize this submodule | +| `update(init, force, checkout, rebase, merge, recursive)` | Implemented | Update this submodule | +| `deinit(force)` | Implemented | Unregister submodule | +| `set_branch(branch)` | Implemented | Set branch | +| `set_url(url)` | Implemented | Set URL | +| `status()` | Implemented | Show status | +| `absorbgitdirs()` | Implemented | Absorb gitdir | + +#### CLI Flag → Python Parameter Mapping: GitSubmoduleCmd Methods + +| Method | Git CLI | Parameters → Flags | +|--------|---------|-------------------| +| `init()` | `git submodule init` | None | +| `update()` | `git submodule update` | `init=True` → `--init`, `force=True` → `-f`, `recursive=True` → `--recursive`, `checkout/rebase/merge` → mode flags | +| `deinit()` | `git submodule deinit` | `force=True` → `-f`, `_all=True` → `--all` | +| `set_branch(branch)` | `git submodule set-branch` | `branch` → `-b`, `default=True` → `-d` | +| `set_url(url)` | `git submodule set-url` | `url` → positional | +| `status()` | `git submodule status` | `recursive=True` → `--recursive` | +| `absorbgitdirs()` | `git submodule absorbgitdirs` | None | + +--- + +## 5. GitTagManager / GitTagCmd (New) + +**Pattern Status**: Implemented + +### Planned GitTagManager (Collection-level) + +| Method | Status | Description | +|--------|--------|-------------| +| `__init__(path, cmd)` | Implemented | Constructor | +| `run(command, local_flags)` | Implemented | Run git tag command | +| `ls(pattern, sort, contains, no_contains, merged, no_merged)` | Implemented | Returns `QueryList[GitTagCmd]` | +| `get(**kwargs)` | Implemented | Get single tag by filter | +| `filter(**kwargs)` | Implemented | Filter tags | +| `create(name, ref, message, annotate, sign, force)` | Implemented | Create tag | + +#### CLI Flag → Python Parameter Mapping: `create()` + +| Git CLI Flag | Python Parameter | Description | +|--------------|------------------|-------------| +| `-a, --annotate` | `annotate: bool` | Create annotated tag | +| `-s, --sign` | `sign: bool` | Create GPG-signed tag | +| `-u ` | `local_user: str \| None` | Use specific GPG key | +| `-f, --force` | `force: bool` | Replace existing tag | +| `-m ` | `message: str \| None` | Tag message | +| `-F ` | `file: str \| None` | Read message from file | + +#### CLI Flag → Python Parameter Mapping: `ls()` + +| Git CLI Flag | Python Parameter | Description | +|--------------|------------------|-------------| +| `-l ` | `pattern: str \| None` | List tags matching pattern | +| `--sort=` | `sort: str \| None` | Sort by key | +| `--contains ` | `contains: str \| None` | Tags containing commit | +| `--no-contains ` | `no_contains: str \| None` | Tags not containing commit | +| `--merged ` | `merged: str \| None` | Tags merged into commit | +| `--no-merged ` | `no_merged: str \| None` | Tags not merged | +| `-n` | `lines: int \| None` | Print annotation lines | + +### Planned GitTagCmd (Per-entity) + +Properties: `tag_name`, `ref`, `message` (for annotated) + +| Method | Status | Description | +|--------|--------|-------------| +| `__init__(path, tag_name, ref, message, cmd)` | Implemented | Constructor | +| `show()` | Implemented | Show tag details | +| `delete()` | Implemented | Delete tag (`-d`) | +| `verify()` | Implemented | Verify signed tag (`-v`) | + +#### CLI Flag → Python Parameter Mapping: GitTagCmd Methods + +| Method | Git CLI | Parameters → Flags | +|--------|---------|-------------------| +| `delete()` | `git tag -d` | None | +| `verify()` | `git tag -v` | None | +| `show()` | `git show` | (uses git show, not git tag) | + +--- + +## 6. GitWorktreeManager / GitWorktreeCmd (New) + +**Pattern Status**: Implemented + +### Planned GitWorktreeManager (Collection-level) + +| Method | Status | Description | +|--------|--------|-------------| +| `__init__(path, cmd)` | Implemented | Constructor | +| `run(command, local_flags)` | Implemented | Run git worktree command | +| `ls()` | Implemented | Returns `QueryList[GitWorktreeCmd]` | +| `get(**kwargs)` | Implemented | Get single worktree by filter | +| `filter(**kwargs)` | Implemented | Filter worktrees | +| `add(path, branch, detach, checkout, lock, force)` | Implemented | Add worktree | +| `prune(dry_run, verbose, expire)` | Implemented | Prune worktrees | + +#### CLI Flag → Python Parameter Mapping: `add()` + +| Git CLI Flag | Python Parameter | Description | +|--------------|------------------|-------------| +| `-f, --force` | `force: bool` | Force creation | +| `--detach` | `detach: bool` | Detach HEAD | +| `--checkout` | `checkout: bool` | Checkout after add | +| `--lock` | `lock: bool` | Lock worktree | +| `--reason ` | `reason: str \| None` | Lock reason | +| `-b ` | `new_branch: str \| None` | Create new branch | +| `-B ` | `new_branch_force: str \| None` | Force create branch | +| `--orphan` | `orphan: bool` | Create orphan branch | +| `--track` | `track: bool` | Track remote | + +#### CLI Flag → Python Parameter Mapping: `prune()` + +| Git CLI Flag | Python Parameter | Description | +|--------------|------------------|-------------| +| `-n, --dry-run` | `dry_run: bool` | Dry run | +| `-v, --verbose` | `verbose: bool` | Verbose output | +| `--expire