CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pygit2

Python bindings for libgit2 providing comprehensive Git repository operations and version control functionality.

Pending
Overview
Eval results
Files

staging.mddocs/

Index and Staging

Git index (staging area) operations for preparing commits. The index serves as a staging area between the working directory and repository, allowing fine-grained control over what changes are included in commits and providing conflict resolution capabilities.

Capabilities

Index Management

The Index class provides access to the Git staging area with methods for adding, removing, and querying staged content.

class Index:
    def read(self, force: bool = True):
        """Read index from disk"""
    
    def write(self):
        """Write index to disk"""
    
    def write_tree(self, repo: Repository = None) -> Oid:
        """
        Write index as tree object.
        
        Parameters:
        - repo: Repository (uses index's repo if None)
        
        Returns:
        Tree object ID
        """
    
    def read_tree(self, tree: Tree):
        """Read tree into index"""
    
    def clear(self):
        """Remove all entries from index"""
    
    def __len__(self) -> int:
        """Number of entries in index"""
    
    def __getitem__(self, key: str | int) -> IndexEntry:
        """Get entry by path or index"""
    
    def __contains__(self, path: str) -> bool:
        """Check if path is in index"""
    
    def __iter__(self):
        """Iterate over index entries"""
    
    # File Operations
    def add(self, path: str):
        """Add file to index"""
    
    def add_all(self, pathspecs: list[str] = None, flags: int = 0):
        """
        Add all matching files to index.
        
        Parameters:
        - pathspecs: Path patterns (None = all files)
        - flags: Add flags
        """
    
    def remove(self, path: str, stage: int = 0):
        """Remove file from index"""
    
    def remove_all(self, pathspecs: list[str], flags: int = 0):
        """Remove all matching files from index"""
    
    def update_all(self, pathspecs: list[str] = None, flags: int = 0):
        """Update all matching files in index"""
    
    # Entry Management
    def get_entry_stage(self, path: str, stage: int) -> IndexEntry | None:
        """Get entry at specific stage"""
    
    def add_frombuffer(self, path: str, buffer: bytes, mode: int):
        """Add file from buffer"""
    
    # Conflict Resolution
    @property  
    def conflicts(self) -> ConflictCollection:
        """Access merge conflicts"""
    
    def conflict_cleanup(self):
        """Remove conflict markers from index"""
    
    # Status
    def diff_to_workdir(self, **kwargs) -> Diff:
        """Compare index to working directory"""
    
    def diff_to_tree(self, tree: Tree, **kwargs) -> Diff:
        """Compare index to tree"""

# Index Add Flags
GIT_INDEX_ADD_DEFAULT: int          # Default behavior
GIT_INDEX_ADD_FORCE: int            # Add ignored files
GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH: int  # Disable pathspec matching
GIT_INDEX_ADD_CHECK_PATHSPEC: int   # Check pathspec validity

Index Entries

IndexEntry represents a single file in the Git index with metadata.

class IndexEntry:
    @property
    def path(self) -> str:
        """File path relative to repository root"""
    
    @property
    def oid(self) -> Oid:
        """Object ID of file content"""
    
    @property
    def mode(self) -> int:
        """File mode (permissions)"""
    
    @property
    def flags(self) -> int:
        """Entry flags"""
    
    @property
    def flags_extended(self) -> int:
        """Extended entry flags"""
    
    @property
    def stage(self) -> int:
        """Merge stage (0=normal, 1=base, 2=ours, 3=theirs)"""
    
    @property
    def ctime(self) -> int:
        """Creation time"""
    
    @property
    def mtime(self) -> int:
        """Modification time"""
    
    @property
    def dev(self) -> int:
        """Device ID"""
    
    @property
    def ino(self) -> int:
        """Inode number"""
    
    @property
    def uid(self) -> int:
        """User ID"""
    
    @property
    def gid(self) -> int:
        """Group ID"""
    
    @property
    def file_size(self) -> int:
        """File size in bytes"""

Conflict Resolution

Handle merge conflicts in the index with three-way merge information.

class ConflictCollection:
    def __getitem__(self, path: str) -> tuple[IndexEntry, IndexEntry, IndexEntry]:
        """Get conflict entries (ancestor, ours, theirs)"""
    
    def __contains__(self, path: str) -> bool:
        """Check if path has conflicts"""
    
    def __iter__(self):
        """Iterate over conflicted paths"""
    
    def __len__(self) -> int:
        """Number of conflicted files"""
    
    def get(self, path: str) -> tuple[IndexEntry, IndexEntry, IndexEntry] | None:
        """Get conflict entries with None fallback"""

class MergeFileResult:
    @property
    def automergeable(self) -> bool:
        """True if merge was automatic"""
    
    @property
    def path(self) -> str:
        """Merged file path"""
    
    @property
    def mode(self) -> int:
        """Merged file mode"""
    
    @property
    def contents(self) -> bytes:
        """Merged file contents"""

Status Constants

File status flags used throughout pygit2 for working directory and index state.

# Index Status Flags
GIT_STATUS_CURRENT: int            # No changes
GIT_STATUS_INDEX_NEW: int          # New file in index
GIT_STATUS_INDEX_MODIFIED: int     # Modified file in index  
GIT_STATUS_INDEX_DELETED: int      # Deleted file in index
GIT_STATUS_INDEX_RENAMED: int      # Renamed file in index
GIT_STATUS_INDEX_TYPECHANGE: int   # Type change in index

# Working Tree Status Flags
GIT_STATUS_WT_NEW: int             # New file in workdir
GIT_STATUS_WT_MODIFIED: int        # Modified file in workdir
GIT_STATUS_WT_DELETED: int         # Deleted file in workdir
GIT_STATUS_WT_TYPECHANGE: int      # Type change in workdir  
GIT_STATUS_WT_RENAMED: int         # Renamed file in workdir
GIT_STATUS_WT_UNREADABLE: int      # Unreadable file

# Combined Status Flags
GIT_STATUS_IGNORED: int            # Ignored file
GIT_STATUS_CONFLICTED: int         # Conflicted file

Usage Examples

Basic Index Operations

import pygit2

repo = pygit2.Repository('/path/to/repo')
index = repo.index

# Check index status
print(f"Index has {len(index)} entries")

# Add single file
index.add('README.md')
index.write()

# Add all files
index.add_all()
index.write()

# Remove file
index.remove('unwanted.txt')
index.write()

# Stage specific files
pathspecs = ['*.py', 'docs/*.md']
index.add_all(pathspecs)
index.write()

Working with Index Entries

# Examine index contents
for entry in index:
    print(f"{entry.path}: {entry.oid} (mode: {oct(entry.mode)})")
    if entry.stage > 0:
        print(f"  Merge stage: {entry.stage}")

# Get specific entry
if 'main.py' in index:
    entry = index['main.py']
    print(f"main.py: {entry.oid}")
    print(f"Size: {entry.file_size} bytes")
    print(f"Modified: {entry.mtime}")

# Check for conflicts
if index.conflicts:
    print(f"Index has {len(index.conflicts)} conflicted files")

Creating Commits from Index

# Create tree from index
tree_oid = index.write_tree()

# Create commit
signature = pygit2.Signature('Author', 'author@example.com')
commit_oid = repo.create_commit(
    'HEAD',
    signature,
    signature,
    'Commit message',
    tree_oid,
    [repo.head.target]  # Parent commits
)

print(f"Created commit: {commit_oid}")

Conflict Resolution

# Check for merge conflicts
if repo.index.conflicts:
    print("Resolving conflicts:")
    
    for path in repo.index.conflicts:
        ancestor, ours, theirs = repo.index.conflicts[path]
        
        print(f"\nConflict in {path}:")
        if ancestor:
            print(f"  Ancestor: {ancestor.oid}")
        if ours:
            print(f"  Ours: {ours.oid}")
        if theirs:
            print(f"  Theirs: {theirs.oid}")
        
        # Get file contents for manual merge
        if ours and theirs:
            our_content = repo[ours.oid].data
            their_content = repo[theirs.oid].data
            
            # Perform manual merge (simplified)
            merged_content = merge_files(our_content, their_content)
            
            # Stage resolved content
            repo.index.add_frombuffer(
                path,
                merged_content,
                pygit2.GIT_FILEMODE_BLOB
            )
    
    # Write resolved index
    repo.index.write()
    
    # Clean up conflict markers
    repo.index.conflict_cleanup()

Index Diffs

# Compare index to working directory
diff = repo.index.diff_to_workdir()
print(f"Working directory has {len(diff.deltas)} changes:")

for delta in diff.deltas:
    status_map = {
        pygit2.GIT_DELTA_ADDED: "Added",
        pygit2.GIT_DELTA_DELETED: "Deleted", 
        pygit2.GIT_DELTA_MODIFIED: "Modified",
        pygit2.GIT_DELTA_RENAMED: "Renamed"
    }
    status = status_map.get(delta.status, "Unknown")
    print(f"  {status}: {delta.new_file.path}")

# Compare index to HEAD
head_tree = repo[repo.head.target].tree
diff = repo.index.diff_to_tree(head_tree)
print(f"Index has {len(diff.deltas)} staged changes")

Advanced Index Operations

# Add files from buffer
file_content = b"print('Hello, World!')"
index.add_frombuffer('hello.py', file_content, pygit2.GIT_FILEMODE_BLOB)

# Update only modified files (don't add new files)
index.update_all()

# Force add ignored files
index.add_all(flags=pygit2.GIT_INDEX_ADD_FORCE)

# Read specific tree into index
tree = repo.revparse_single('v1.0.0').tree
index.read_tree(tree)
index.write()

# Clear index completely
index.clear()
index.write()

Install with Tessl CLI

npx tessl i tessl/pypi-pygit2

docs

advanced.md

auth.md

config.md

diff.md

index.md

objects.md

references.md

remotes.md

repository.md

staging.md

tile.json