> ## Documentation Index
> Fetch the complete documentation index at: https://lunr-f6858e75.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Git client

> Interface with Git repositories and commit history

## GitRepository

The `GitRepository` class provides access to Git repository data.

### Constructor

```python theme={}
GitRepository(path: Path)
```

<ParamField path="path" type="Path" required>
  Path to the Git repository directory.

  ```python theme={}
  from pathlib import Path
  from helixcommit.git_client import GitRepository

  # Current directory
  repo = GitRepository(Path("."))

  # Specific path
  repo = GitRepository(Path("/path/to/repository"))

  # Using string (converted to Path internally)
  repo = GitRepository(Path("~/projects/myrepo").expanduser())
  ```
</ParamField>

***

## Methods

### iter\_commits()

Iterate over commits in a specified range.

```python theme={}
iter_commits(commit_range: CommitRange) -> Iterator[CommitInfo]
```

<ParamField path="commit_range" type="CommitRange" required>
  Range specification for commits to fetch.
</ParamField>

**Returns:** Iterator of `CommitInfo` objects

**Example:**

```python theme={}
from helixcommit.git_client import CommitRange

commit_range = CommitRange(since="v1.0.0", until="v2.0.0")
commits = list(repo.iter_commits(commit_range))

for commit in commits:
    print(f"{commit.sha[:7]}: {commit.subject}")
```

### list\_tags()

List all tags in the repository, sorted by date (newest first).

```python theme={}
list_tags() -> List[TagInfo]
```

**Returns:** List of `TagInfo` objects

**Example:**

```python theme={}
tags = repo.list_tags()

# Get latest tag
latest = tags[0] if tags else None
if latest:
    print(f"Latest tag: {latest.name} on {latest.date}")

# Get all version tags
version_tags = [tag for tag in tags if tag.name.startswith("v")]
```

### get\_github\_slug()

Extract GitHub owner/repo from remote URL.

```python theme={}
get_github_slug() -> Optional[Tuple[str, str]]
```

**Returns:** Tuple of `(owner, repo)` or `None` if not a GitHub repository

**Example:**

```python theme={}
slug = repo.get_github_slug()

if slug:
    owner, repo_name = slug
    print(f"GitHub repository: {owner}/{repo_name}")
else:
    print("Not a GitHub repository")
```

***

## CommitRange

Specification for a range of commits to process.

### Constructor

```python theme={}
CommitRange(
    since: Optional[str] = None,
    until: Optional[str] = None,
    include_merges: bool = True,
    max_count: Optional[int] = None
)
```

### Parameters

<ParamField path="since" type="str">
  Starting ref (tag, commit SHA, branch, or date expression).

  Commits **after** this ref are included.

  ```python theme={}
  # From a tag
  CommitRange(since="v1.0.0")

  # From a commit SHA
  CommitRange(since="abc123def")

  # From a branch
  CommitRange(since="main")

  # From a date
  CommitRange(since="2024-01-01")
  CommitRange(since="7 days ago")
  ```
</ParamField>

<ParamField path="until" type="str" default="HEAD">
  Ending ref. Commits up to and including this ref are included.

  ```python theme={}
  # To HEAD (default)
  CommitRange(since="v1.0.0")

  # To a specific tag
  CommitRange(since="v1.0.0", until="v2.0.0")

  # To a branch
  CommitRange(since="main", until="develop")
  ```
</ParamField>

<ParamField path="include_merges" type="bool" default="true">
  Whether to include merge commits.

  ```python theme={}
  # Include merge commits (default)
  CommitRange(since="v1.0.0", include_merges=True)

  # Exclude merge commits
  CommitRange(since="v1.0.0", include_merges=False)
  ```

  <Tip>
    Excluding merge commits often produces cleaner release notes.
  </Tip>
</ParamField>

<ParamField path="max_count" type="int">
  Maximum number of commits to process.

  ```python theme={}
  # Limit to 100 commits
  CommitRange(since="v1.0.0", max_count=100)

  # Process all commits (default)
  CommitRange(since="v1.0.0")
  ```

  Useful for testing or when you only need recent changes.
</ParamField>

***

## Data models

### CommitInfo

```python theme={}
@dataclass
class CommitInfo:
    sha: str
    subject: str
    body: str
    message: str
    author_name: str
    author_email: str
    date: datetime
    commit_type: Optional[str] = None
    scope: Optional[str] = None
    breaking: bool = False
    pr_number: Optional[int] = None
```

**Example:**

```python theme={}
commit: CommitInfo = commits[0]

print(f"SHA: {commit.sha}")
print(f"Subject: {commit.subject}")
print(f"Author: {commit.author_name} <{commit.author_email}>")
print(f"Date: {commit.date}")
print(f"Type: {commit.commit_type}")
print(f"Scope: {commit.scope}")
print(f"Breaking: {commit.breaking}")
print(f"PR: #{commit.pr_number}" if commit.pr_number else "No PR")
```

### TagInfo

```python theme={}
@dataclass
class TagInfo:
    name: str
    sha: str
    date: datetime
```

**Example:**

```python theme={}
tag: TagInfo = tags[0]

print(f"Tag: {tag.name}")
print(f"Commit: {tag.sha}")
print(f"Date: {tag.date.strftime('%Y-%m-%d')}")
```

***

## Complete examples

### Basic commit fetching

```python theme={}
from pathlib import Path
from helixcommit.git_client import GitRepository, CommitRange

# Initialize repository
repo = GitRepository(Path("."))

# Get commits between tags
commit_range = CommitRange(
    since="v1.0.0",
    until="v2.0.0",
    include_merges=False
)

commits = list(repo.iter_commits(commit_range))

# Display commits
for commit in commits:
    prefix = "⚠️ " if commit.breaking else ""
    print(f"{prefix}{commit.commit_type}: {commit.subject}")
```

### Working with tags

```python theme={}
from pathlib import Path
from helixcommit.git_client import GitRepository, CommitRange

repo = GitRepository(Path("."))

# Get all tags
tags = repo.list_tags()

if len(tags) >= 2:
    # Get commits between last two tags
    latest = tags[0]
    previous = tags[1]
    
    print(f"Changes from {previous.name} to {latest.name}")
    
    commit_range = CommitRange(since=previous.name, until=latest.name)
    commits = list(repo.iter_commits(commit_range))
    
    print(f"Found {len(commits)} commits")
```

### Unreleased changes

```python theme={}
from pathlib import Path
from helixcommit.git_client import GitRepository, CommitRange

repo = GitRepository(Path("."))

# Get latest tag
tags = repo.list_tags()
latest_tag = tags[0] if tags else None

if latest_tag:
    # Get unreleased commits
    commit_range = CommitRange(since=latest_tag.name, until="HEAD")
    unreleased = list(repo.iter_commits(commit_range))
    
    print(f"Unreleased changes since {latest_tag.name}:")
    for commit in unreleased:
        print(f"  - {commit.subject}")
else:
    print("No tags found")
```

### Filtering by commit type

```python theme={}
from pathlib import Path
from helixcommit.git_client import GitRepository, CommitRange

repo = GitRepository(Path("."))
commits = list(repo.iter_commits(CommitRange(since="v1.0.0")))

# Filter by type
features = [c for c in commits if c.commit_type == "feat"]
fixes = [c for c in commits if c.commit_type == "fix"]
breaking = [c for c in commits if c.breaking]

print(f"Features: {len(features)}")
print(f"Fixes: {len(fixes)}")
print(f"Breaking changes: {len(breaking)}")
```

### GitHub integration

```python theme={}
from pathlib import Path
from helixcommit.git_client import GitRepository

repo = GitRepository(Path("."))

# Get GitHub info
slug = repo.get_github_slug()

if slug:
    owner, repo_name = slug
    print(f"Repository: https://github.com/{owner}/{repo_name}")
    
    # Use with GitHub client
    from helixcommit.github_client import GitHubClient, GitHubSettings
    
    settings = GitHubSettings(owner=owner, repo=repo_name, token="ghp_...")
    client = GitHubClient(settings)
    # ... use client
else:
    print("Not a GitHub repository")
```

***

## Error handling

```python theme={}
from pathlib import Path
from helixcommit.git_client import GitRepository, CommitRange

try:
    repo = GitRepository(Path("/nonexistent/path"))
except FileNotFoundError:
    print("Repository not found")
except Exception as e:
    print(f"Error: {e}")

try:
    commits = list(repo.iter_commits(CommitRange(since="invalid-ref")))
except Exception as e:
    print(f"Invalid ref: {e}")
```

***

## Next steps

<CardGroup cols={2}>
  <Card title="Changelog builder" icon="list" href="/api-reference/changelog" color="#0A6ACB">
    Build changelogs from commits
  </Card>

  <Card title="Python API" icon="python" href="/api-reference/python-api" color="#F26419">
    Complete API reference
  </Card>

  <Card title="Examples" icon="code" href="/examples" color="#F6A02D">
    See practical examples
  </Card>

  <Card title="Conventional Commits" icon="code-branch" href="/conventional-commits" color="#0A6ACB">
    Learn about commit parsing
  </Card>
</CardGroup>
