References API Reference
This document provides a complete API reference for the gitpy.refs module, which implements Git's reference management: branches, tags, HEAD, reflogs, and revision expressions.
Module Overview
from gitpy.refs import (
RefManager, # Low-level ref read/write/resolve
Head, # HEAD state data class
HeadState, # Enum: ATTACHED or DETACHED
HeadManager, # Read/write HEAD
Branch, # Branch data class
BranchManager, # High-level branch operations
LightweightTag, # Lightweight tag data class
AnnotatedTag, # Annotated tag data class
TagType, # Type alias: LightweightTag | AnnotatedTag
TagManager, # High-level tag operations
Reflog, # Reflog file manager
ReflogEntry, # Single reflog entry
ZERO_SHA, # "0" * 40 — sentinel for new refs
RevisionParser, # Parse revision expressions to SHAs
)
RefManager
Module: gitpy.refs.manager
Manages Git references. Handles reading, writing, resolving, and listing references including support for packed-refs. All write and delete operations invalidate the packed-refs cache so subsequent reads remain consistent.
class RefManager:
git_dir: Path # Path to the .git directory
refs_dir: Path # Path to .git/refs/
packed_refs_path: Path # Path to .git/packed-refs
Constructor
def __init__(self, git_dir: Path) -> None
Parameters:
- git_dir - Path to the .git directory.
Example:
from pathlib import Path
from gitpy.refs import RefManager
ref_mgr = RefManager(Path("/path/to/repo/.git"))
Methods
read()
def read(self, name: str) -> str | None
Read a reference value (SHA or "ref: <target>" for symbolic refs). Loose refs take priority over packed refs.
Parameters:
- name - Reference name (e.g. "refs/heads/main").
Returns: SHA string, "ref: <target>" for symbolic refs, or None if the reference does not exist or the name is unsafe.
Example:
value = ref_mgr.read("refs/heads/main")
# Returns: "abc123def456..." or None
resolve()
def resolve(self, name: str, max_depth: int = 10) -> str | None
Resolve a reference name or SHA to a final commit SHA. Resolution order for non-SHA names: direct name, refs/<name>, refs/tags/<name>, refs/heads/<name>, refs/remotes/<name>. Symbolic refs are followed recursively up to max_depth times.
Parameters:
- name - Reference name or 40-char hex SHA.
- max_depth - Maximum symbolic-ref chain depth (prevents loops).
Returns: 40-character hex SHA, or None if not found.
Raises:
- ValueError - A symbolic reference loop was detected.
Example:
sha = ref_mgr.resolve("main") # short name
sha = ref_mgr.resolve("refs/heads/main") # full ref
sha = ref_mgr.resolve("HEAD") # follows symref chain
write()
def write(self, name: str, sha: str) -> None
Update a reference to point to a SHA. Creates parent directories as needed. Uses an atomic lock-file write. Invalidates the packed-refs cache.
Parameters:
- name - Reference name (e.g. "refs/heads/main").
- sha - 40-character hex SHA.
Raises:
- ValueError - sha is not a valid 40-char hex SHA, or name is unsafe.
write_symbolic()
def write_symbolic(self, name: str, target: str) -> None
Create a symbolic reference pointing to target.
Parameters:
- name - Reference name (e.g. "HEAD").
- target - Target reference name (e.g. "refs/heads/main").
Raises:
- ValueError - name is unsafe.
delete()
def delete(self, name: str) -> bool
Delete a loose reference. Also cleans up empty parent directories up to refs/. Invalidates the packed-refs cache.
Parameters:
- name - Reference name.
Returns: True if the reference existed and was deleted, False otherwise.
Raises:
- ValueError - name is unsafe.
list_refs()
def list_refs(self, pattern: str = "refs/**") -> Iterator[tuple[str, str]]
List references matching a glob pattern. Loose refs are yielded first; packed refs are yielded for any names not already seen.
Parameters:
- pattern - Glob pattern relative to git_dir (e.g. "refs/heads/*").
Yields: (name, sha) tuples.
Example:
for name, sha in ref_mgr.list_refs("refs/heads/*"):
print(name, sha)
list_branches()
def list_branches(self) -> Iterator[tuple[str, str]]
List local branches.
Yields: (short_name, sha) tuples where short_name has "refs/heads/" stripped.
list_tags()
def list_tags(self) -> Iterator[tuple[str, str]]
List tags.
Yields: (short_name, sha) tuples where short_name has "refs/tags/" stripped.
pack_refs()
def pack_refs(self) -> None
Pack all loose refs into the packed-refs file. After packing, loose ref files are removed (except HEAD) and the cache is invalidated.
HeadState
Module: gitpy.refs.head
Enum describing the two possible states of the HEAD reference.
class HeadState(Enum):
ATTACHED = "attached" # HEAD points to a branch ref
DETACHED = "detached" # HEAD points directly to a commit SHA
Head
Module: gitpy.refs.head
Data class representing the HEAD reference state.
@dataclass(slots=True)
class Head:
state: HeadState # Whether HEAD is attached or detached
target: str # Branch ref (attached) or SHA (detached)
Attributes
| Name | Type | Description |
|---|---|---|
state |
HeadState |
Whether HEAD is attached to a branch or detached at a SHA |
target |
str |
Full ref (e.g. "refs/heads/main") when attached, or 40-char SHA when detached |
Properties
is_detached
@property
def is_detached(self) -> bool
True when HEAD points directly to a commit SHA.
branch
@property
def branch(self) -> str | None
Current branch short name, or None if detached. Strips the "refs/heads/" prefix from target.
Returns: Short branch name (e.g. "main") or None.
sha
@property
def sha(self) -> str | None
Direct SHA if detached, None if attached.
Returns: 40-char hex SHA or None.
HeadManager
Module: gitpy.refs.head
Manages the HEAD reference. Provides atomic read/write operations for HEAD, supporting both attached (branch-tracking) and detached (SHA-pinned) states.
class HeadManager:
git_dir: Path # Path to the .git directory
head_path: Path # Path to .git/HEAD
Constructor
def __init__(self, git_dir: Path) -> None
Parameters:
- git_dir - Path to the .git directory.
Methods
read()
def read(self) -> Head
Read the current HEAD state.
Returns: Head instance reflecting current HEAD content.
Example:
head = head_mgr.read()
if head.is_detached:
print(f"Detached at {head.sha}")
else:
print(f"On branch {head.branch}")
set_branch()
def set_branch(self, branch: str) -> None
Point HEAD at a branch. Short names are automatically prefixed with "refs/heads/".
Parameters:
- branch - Branch short name or full ref (e.g. "main" or "refs/heads/main").
set_detached()
def set_detached(self, sha: str) -> None
Point HEAD at a specific commit (detached mode).
Parameters:
- sha - 40-character hex commit SHA.
Raises:
- ValueError - If sha is not exactly 40 characters.
resolve()
def resolve(self, ref_manager: RefManager) -> str
Resolve HEAD to a commit SHA.
Parameters:
- ref_manager - Reference manager used to resolve branch names.
Returns: 40-character hex SHA of the current commit.
Raises:
- ValueError - HEAD points to a branch that does not exist yet.
Branch
Module: gitpy.refs.branch
Data class representing a Git branch.
@dataclass(slots=True)
class Branch:
name: str # Short branch name (e.g. "main")
sha: str # Full 40-char SHA the branch currently points to
Attributes
| Name | Type | Description |
|---|---|---|
name |
str |
Short branch name (e.g. "main") |
sha |
str |
Full 40-char SHA the branch currently points to |
Properties
full_name
@property
def full_name(self) -> str
Full ref name including namespace (e.g. "refs/heads/main").
short_sha
@property
def short_sha(self) -> str
First 7 characters of the SHA.
BranchManager
Module: gitpy.refs.branch
High-level branch operations. Delegates low-level ref I/O to RefManager and HEAD mutations to HeadManager.
class BranchManager:
refs: RefManager # Ref manager for this repository
head: HeadManager # HEAD manager for this repository
Constructor
def __init__(self, ref_manager: RefManager, head_manager: HeadManager) -> None
Parameters:
- ref_manager - RefManager instance for this repository.
- head_manager - HeadManager instance for this repository.
Methods
current()
def current(self) -> str | None
Return the current branch short name, or None if detached.
Returns: Branch name string, or None when HEAD is detached.
exists()
def exists(self, name: str) -> bool
Check whether a branch exists.
Parameters:
- name - Short branch name.
Returns: True if the branch ref can be resolved.
get()
def get(self, name: str) -> Branch | None
Retrieve a branch by name.
Parameters:
- name - Short branch name.
Returns: Branch instance, or None if the branch does not exist.
create()
def create(self, name: str, sha: str, force: bool = False) -> Branch
Create a new branch.
Parameters:
- name - Short branch name to create.
- sha - Commit SHA the branch should point to.
- force - If True, overwrite an existing branch.
Returns: Newly created Branch.
Raises:
- ValueError - Branch already exists and force is False, or the name is invalid.
Example:
branch = branch_mgr.create("feature/my-work", commit_sha)
print(branch.full_name) # "refs/heads/feature/my-work"
delete()
def delete(self, name: str, *, force: bool = False) -> bool
Delete a branch.
Parameters:
- name - Short branch name.
- force - Reserved for future merge-safety checks; currently unused.
Returns: True if the branch was deleted, False if it did not exist.
Raises:
- ValueError - Attempting to delete the currently checked-out branch.
rename()
def rename(self, old_name: str, new_name: str, force: bool = False) -> Branch
Rename a branch. If the renamed branch is currently checked out, HEAD is updated to track the new name.
Parameters:
- old_name - Current short branch name.
- new_name - New short branch name.
- force - If True, overwrite new_name if it already exists.
Returns: Updated Branch with the new name.
Raises:
- ValueError - old_name does not exist, new_name already exists and force is False, or new_name is invalid.
list()
def list(self) -> list[Branch]
List all local branches.
Returns: List of Branch instances.
LightweightTag
Module: gitpy.refs.tag
A lightweight tag — just a reference, no tag object.
@dataclass(slots=True)
class LightweightTag:
name: str # Short tag name
sha: str # SHA the tag points to (usually a commit)
Attributes
| Name | Type | Description |
|---|---|---|
name |
str |
Short tag name |
sha |
str |
SHA the tag points to (usually a commit) |
Properties
is_annotated
@property
def is_annotated(self) -> bool
Always False for lightweight tags.
AnnotatedTag
Module: gitpy.refs.tag
An annotated tag — a tag object stored in the object database plus a reference.
@dataclass(slots=True)
class AnnotatedTag:
name: str # Short tag name
sha: str # SHA of the tag object
target: str # SHA of the tagged commit (or other object)
message: str # Tag message
tagger: Identity | None # Identity who created the tag, or None
Attributes
| Name | Type | Description |
|---|---|---|
name |
str |
Short tag name |
sha |
str |
SHA of the tag object |
target |
str |
SHA of the tagged commit (or other object) |
message |
str |
Tag message |
tagger |
Identity \| None |
Identity who created the tag, or None |
Properties
is_annotated
@property
def is_annotated(self) -> bool
Always True for annotated tags.
TagType
Module: gitpy.refs.tag
Type alias for tag union.
type TagType = LightweightTag | AnnotatedTag
TagManager
Module: gitpy.refs.tag
High-level tag operations.
class TagManager:
refs: RefManager # Ref manager for this repository
objects: ObjectDatabase # Object database for this repository
Constructor
def __init__(self, ref_manager: RefManager, object_db: ObjectDatabase) -> None
Parameters:
- ref_manager - RefManager instance for this repository.
- object_db - ObjectDatabase instance for this repository.
Methods
get()
def get(self, name: str) -> TagType | None
Get a tag by name, distinguishing lightweight from annotated.
Parameters:
- name - Short tag name.
Returns: LightweightTag or AnnotatedTag, or None if not found.
create_lightweight()
def create_lightweight(self, name: str, sha: str, force: bool = False) -> LightweightTag
Create a lightweight tag.
Parameters:
- name - Short tag name.
- sha - SHA to tag (usually a commit).
- force - If True, overwrite existing tag.
Returns: Created LightweightTag.
Raises:
- ValueError - Tag already exists and force is False.
Example:
tag = tag_mgr.create_lightweight("v1.0.0", commit_sha)
create_annotated()
def create_annotated(
self,
name: str,
sha: str,
message: str,
tagger: Identity,
force: bool = False,
) -> AnnotatedTag
Create an annotated tag. Writes a tag object to the object database and creates a ref pointing to that object.
Parameters:
- name - Short tag name.
- sha - SHA of the object being tagged (usually a commit).
- message - Tag message.
- tagger - Identity of the person creating the tag.
- force - If True, overwrite existing tag.
Returns: Created AnnotatedTag.
Raises:
- ValueError - Tag already exists and force is False.
Example:
from gitpy.objects import Identity
tagger = Identity.now("Alice", "alice@example.com")
tag = tag_mgr.create_annotated("v1.0.0", commit_sha, "Release 1.0", tagger)
exists()
def exists(self, name: str) -> bool
Check whether a tag exists.
Parameters:
- name - Short tag name.
Returns: True if the tag ref resolves.
delete()
def delete(self, name: str) -> bool
Delete a tag.
Parameters:
- name - Short tag name.
Returns: True if deleted, False if it did not exist.
list()
def list(self) -> list[TagType]
List all tags.
Returns: List of LightweightTag or AnnotatedTag instances.
peel()
def peel(self, name: str) -> str | None
Resolve a tag to the underlying commit SHA. For lightweight tags this is the sha itself; for annotated tags this follows the tag object to its target.
Parameters:
- name - Short tag name.
Returns: Commit SHA, or None if the tag does not exist.
ZERO_SHA
Module: gitpy.refs.reflog
ZERO_SHA: str = "0" * 40
Sentinel SHA used to represent the absence of a previous value when a ref is newly created.
ReflogEntry
Module: gitpy.refs.reflog
A single reflog entry.
@dataclass(slots=True)
class ReflogEntry:
old_sha: str # Previous SHA (ZERO_SHA for a newly created ref)
new_sha: str # SHA after the change
identity: Identity # Who made the change
message: str # Short description of what happened
Attributes
| Name | Type | Description |
|---|---|---|
old_sha |
str |
Previous SHA (ZERO_SHA for a newly created ref) |
new_sha |
str |
SHA after the change |
identity |
Identity |
Who made the change |
message |
str |
Short description of what happened |
Methods
format()
def format(self) -> str
Serialise as a reflog line (including trailing newline).
Returns: String in the form "<old> <new> <identity>\t<message>\n".
parse()
@classmethod
def parse(cls, line: str) -> Self
Parse a single reflog line.
Parameters:
- line - Raw line from a reflog file (trailing newline stripped).
Returns: A new ReflogEntry instance.
Reflog
Module: gitpy.refs.reflog
Manages reflog files for references. Reflog files are stored under .git/logs/<ref-name>.
class Reflog:
git_dir: Path # Path to the .git directory
logs_dir: Path # Path to .git/logs/
Constructor
def __init__(self, git_dir: Path) -> None
Parameters:
- git_dir - Path to the .git directory.
Methods
append()
def append(
self,
ref: str,
old_sha: str,
new_sha: str,
identity: Identity,
message: str,
) -> None
Append an entry to the reflog. Creates parent directories if they do not exist.
Parameters:
- ref - Reference name (e.g. "HEAD").
- old_sha - Previous SHA (use ZERO_SHA for new refs).
- new_sha - New SHA after the change.
- identity - Who made the change.
- message - Human-readable description (e.g. "commit: initial commit").
Example:
from gitpy.refs import ZERO_SHA
from gitpy.objects import Identity
reflog.append("HEAD", ZERO_SHA, commit_sha, author, "commit: initial commit")
read()
def read(self, ref: str, limit: int | None = None) -> list[ReflogEntry]
Read reflog entries, newest first.
Parameters:
- ref - Reference name.
- limit - Maximum number of entries to return.
Returns: List of ReflogEntry instances, most recent first.
get()
def get(self, ref: str, index: int) -> ReflogEntry | None
Retrieve a specific reflog entry by index.
Parameters:
- ref - Reference name.
- index - 0-based index where 0 is the most recent entry.
Returns: ReflogEntry at index, or None if not enough entries.
clear()
def clear(self, ref: str) -> None
Delete the reflog for ref.
Parameters:
- ref - Reference name.
RevisionParser
Module: gitpy.refs.revision
Parses Git revision expressions into commit SHAs.
Supported expressions:
- Plain names: HEAD, main, v1.0, abc123 (abbreviated SHA)
- Parent: HEAD^, HEAD^2 (second parent of a merge commit)
- Ancestor: HEAD~3 (three steps up the first-parent chain)
- Reflog: HEAD@{1} (previous value of HEAD)
class RevisionParser:
ref_manager: RefManager # Ref manager for resolving branch/tag names
object_db: ObjectDatabase # Object database for reading commit objects
Constructor
def __init__(self, ref_manager: RefManager, object_db: ObjectDatabase) -> None
Parameters:
- ref_manager - RefManager for resolving branch/tag names.
- object_db - ObjectDatabase for reading commit objects.
Methods
parse()
def parse(self, rev: str) -> str | None
Parse a revision expression and return a 40-char SHA.
Parameters:
- rev - Revision expression string.
Returns: 40-character hex SHA, or None if the expression cannot be resolved.
Example:
parser = RevisionParser(ref_mgr, db)
sha = parser.parse("HEAD") # current commit
sha = parser.parse("HEAD^") # parent of HEAD
sha = parser.parse("HEAD~3") # three ancestors back
sha = parser.parse("HEAD^2") # second parent (merge commit)
sha = parser.parse("HEAD@{1}") # previous HEAD value
sha = parser.parse("main") # branch tip
sha = parser.parse("v1.0") # tag
sha = parser.parse("abc1234") # abbreviated SHA
Complete Example
from pathlib import Path
from gitpy.refs import (
RefManager, HeadManager, BranchManager, TagManager,
Reflog, RevisionParser, ZERO_SHA,
)
from gitpy.storage import ObjectDatabase
from gitpy.objects import Identity
git_dir = Path("/path/to/repo/.git")
ref_mgr = RefManager(git_dir)
head_mgr = HeadManager(git_dir)
db = ObjectDatabase(git_dir)
branch_mgr = BranchManager(ref_mgr, head_mgr)
tag_mgr = TagManager(ref_mgr, db)
reflog = Reflog(git_dir)
parser = RevisionParser(ref_mgr, db)
# Create a branch and check HEAD
branch = branch_mgr.create("feature/my-work", some_sha)
head_mgr.set_branch("feature/my-work")
head = head_mgr.read()
print(head.branch) # "feature/my-work"
# Create a lightweight tag
tag_mgr.create_lightweight("v0.1", some_sha)
# Record in reflog
identity = Identity.now("Alice", "alice@example.com")
reflog.append("HEAD", ZERO_SHA, some_sha, identity, "commit: initial")
# Parse revision expressions
sha = parser.parse("HEAD~1")
sha = parser.parse("HEAD@{0}")
# Pack all loose refs
ref_mgr.pack_refs()
Git Compatibility
| Feature | Notes |
|---|---|
| Loose refs | Stored as refs/heads/<name>, refs/tags/<name> |
| Packed refs | Parsed and written in Git's packed-refs format |
| Symbolic refs | "ref: <target>" format, followed recursively |
| Reflog | Appended to .git/logs/<ref> in Git wire format |
| Revision expressions | HEAD, HEAD^, HEAD~N, HEAD^2, HEAD@{N} |
See Also
- Storage API: Object database used by
TagManagerandRevisionParser - Object Model API:
Identityclass used in tag and reflog entries - Staging API: Index operations that consume ref resolution