Traversing Git Repos¶
libvcs provides Managers and Commands for intuitively traversing and
navigating entities in a git repository—branches, tags, remotes, stashes, and
more—with ORM-like convenience via QueryList.
Overview¶
The pattern consists of two types of classes:
Managers (
git.branches,git.tags, etc.) let you traverse repository entities intuitively, listing, filtering, and retrieving them with ORM-like convenienceCommands are contextual ways to run git commands against a specific target entity (e.g., delete a branch, rename a tag, set a remote’s URL)
Git instance
├── branches: GitBranchManager
│ ├── ls() -> QueryList[GitBranchCmd]
│ ├── get() -> GitBranchCmd
│ └── create()
├── tags: GitTagManager
├── remotes: GitRemoteManager
├── stashes: GitStashManager
├── worktrees: GitWorktreeManager
├── notes: GitNotesManager
├── submodules: GitSubmoduleManager
└── reflog: GitReflogManager
Basic Usage¶
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
Listing Items¶
All Manager classes have an ls() method that returns a
QueryList:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> branches = git.branches.ls()
>>> isinstance(branches, list)
True
>>> tags = git.tags.ls()
>>> remotes = git.remotes.ls()
Getting a Single Item¶
Use get() with filter criteria to retrieve a single item:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.tags.create(name='v1.0.0', message='Release 1.0')
''
>>> tag = git.tags.get(tag_name='v1.0.0')
>>> tag.tag_name
'v1.0.0'
Creating Items¶
Manager classes provide create() or add() methods:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.tags.create(name='v2.0.0', message='Release 2.0')
''
>>> git.branches.create(branch='feature-branch')
''
Per-Entity Operations¶
Cmd objects have methods for mutating or inspecting that entity:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.tags.create(name='v3.0.0', message='Release 3.0')
''
>>> tag = git.tags.get(tag_name='v3.0.0')
>>> tag.delete()
"Deleted tag 'v3.0.0' ..."
>>> git.branches.create(branch='temp-branch')
''
>>> branch = git.branches.get(branch_name='temp-branch')
>>> branch.delete()
'Deleted branch temp-branch ...'
Comparison to Raw Commands¶
Before: Parsing Strings¶
Without Managers and Commands, you’d parse raw output:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> # Raw output requires parsing
>>> raw_output = git.run(['tag', '-l'])
>>> tag_names = [t for t in raw_output.strip().split('\\n') if t]
After: Typed Objects¶
With Managers and Commands, you get typed objects:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> tags = git.tags.ls()
>>> for tag in tags:
... print(f"{tag.tag_name}")
Working with Remotes¶
Add and configure remote repositories:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.remotes.add(name='upstream', url='https://github.com/vcs-python/libvcs.git')
''
>>> remotes = git.remotes.ls()
>>> len(remotes) >= 1
True
Get a remote and update its URL:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.remotes.add(name='backup', url='https://example.com/old.git')
''
>>> remote = git.remotes.get(remote_name='backup')
>>> remote.remote_name
'backup'
>>> remote.set_url(url='https://example.com/new.git')
''
Branch Operations¶
Beyond creating and deleting, branches support rename and upstream tracking:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.branches.create(branch='old-name')
''
>>> branch = git.branches.get(branch_name='old-name')
>>> branch.rename('new-name')
''
Copy a branch:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.branches.create(branch='source-branch')
''
>>> branch = git.branches.get(branch_name='source-branch')
>>> branch.copy('copied-branch')
''
Stash Workflow¶
Save work in progress and restore it later:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> # Push returns message (or "No local changes to save")
>>> git.stashes.push(message='WIP: feature work')
'...'
List and inspect stashes:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> stashes = git.stashes.ls()
>>> isinstance(stashes, list)
True
Worktree Management¶
Create additional working directories for parallel development:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> worktrees = git.worktrees.ls()
>>> len(worktrees) >= 1 # Main worktree always exists
True
Notes¶
Attach metadata to commits:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> git.notes.add(message='Reviewed by Alice')
''
Filtering with ls() Parameters¶
Manager ls() methods accept parameters to narrow results:
>>> from libvcs.cmd.git import Git
>>> git = Git(path=example_git_repo.path)
>>> # All local branches
>>> local = git.branches.ls()
>>> isinstance(local, list)
True
>>> # Branches merged into HEAD
>>> merged = git.branches.ls(merged='HEAD')
>>> isinstance(merged, list)
True
Error Handling¶
When get() finds no match, it raises ObjectDoesNotExist:
>>> from libvcs.cmd.git import Git
>>> from libvcs._internal.query_list import ObjectDoesNotExist
>>> git = Git(path=example_git_repo.path)
>>> try:
... git.branches.get(branch_name='nonexistent-branch-xyz')
... except ObjectDoesNotExist:
... print('Branch not found')
Branch not found
When to Use¶
Use Case |
Approach |
|---|---|
List/filter/get entities |
Manager class ( |
Mutate a specific entity |
Cmd class ( |
Run arbitrary git commands |
Direct methods ( |
Complex pipelines |
Mix of both |
Available Managers¶
Manager |
Access |
Operations |
|---|---|---|
|
List, create, checkout branches |
|
|
List, create tags |
|
|
List, add, configure remotes |
|
|
List, push, clear stashes |
|
|
List, add, prune worktrees |
|
|
List, add, prune notes |
|
|
List, add, sync submodules |
|
|
List, expire reflog entries |
See libvcs.cmd.git for the complete API reference.