# Git Module

A comprehensive Rust library for managing git repositories with support for advanced cloning, branch management, and specific directory extraction from URLs.

## Features

- **URL Parsing**: Parse git URLs from GitHub, GitLab, Gitea/Forgejo with branch and subpath information
- **Repository Management**: Clone, list, and find git repositories under a base directory
- **Git Operations**: Pull, push, commit, reset, and check repository status
- **Advanced Cloning**: Clone with specific branches, extract subdirectories, and automatic reset of local changes
- **Branch Management**: Checkout specific branches and fetch latest changes
- **Flexible Path Handling**: Support for tilde expansion and relative paths

## Quick Start

### Basic Repository Setup

```rust
use herolib_os::git::GitTree;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a repository collection with a base directory
    let repos = GitTree::new("~/.git_repos")?;

    // Clone a repository
    let repo_path = repos
        .clone_repo("https://github.com/user/repo")
        .execute()?;

    println!("Repository cloned to: {}", repo_path);
    Ok(())
}
```

## Key Types

### `GitTree`

Manages a collection of git repositories under a base directory.

```rust
let repos = GitTree::new("/tmp/repos")?;

// List all repositories
let all_repos = repos.list()?;

// Find repositories by pattern
let matches = repos.find("herolib")?;
```

### `GitCloneBuilder`

Builder for cloning repositories with advanced options. Use this to handle branches, subpaths, and more.

```rust
let repos = GitTree::new("/tmp/repos")?;

let path = repos
    .clone_repo("https://github.com/user/repo")
    .branch("develop")
    .pull()
    .execute()?;
```

### `GitRepo`

Represents a single git repository for performing operations.

```rust
let repo = GitRepo::new("/path/to/repo");

// Check repository status
let status = repo.status()?;

// Reset all changes
repo.reset()?;

// Pull latest changes
repo.pull()?;
```

### `ParsedGitUrl`

Parsed git URL information including optional branch and subpath.

```rust
use herolib_os::git::parse_git_url_extended;

let parsed = parse_git_url_extended(
    "https://github.com/user/repo/tree/main/docs"
);

println!("Server: {}", parsed.server);        // "github.com"
println!("Account: {}", parsed.account);      // "user"
println!("Repo: {}", parsed.repo);            // "repo"
println!("Branch: {:?}", parsed.branch);      // Some("main")
println!("Subpath: {:?}", parsed.subpath);    // Some("docs")
```

## Usage Examples

### Clone and Get Path to Specific Directory from URL

The most common use case: clone a repository and extract a specific subdirectory.

#### Simple Example

Get path to a repository:

```rust
use herolib_os::git::GitTree;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let tree = GitTree::new("/tmp/repos")?;
    let path = tree.get_path("https://github.com/user/project.git")?;
    println!("Repo at: {}", path);
    Ok(())
}
```

#### With Branch and Subpath

Clone a repository and extract a specific subdirectory from a specific branch:

```rust
use herolib_os::git::GitTree;
use std::fs;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let tree = GitTree::new("/tmp/repos")?;
    
    // The URL includes branch and subpath information
    // Format: https://github.com/org/repo/src/branch/main/docs
    let docs = tree.get_path("https://github.com/org/repo/src/branch/main/docs")?;
    
    // Now you can work with the directory
    let readme_path = format!("{}/README.md", docs);
    let content = fs::read_to_string(&readme_path)?;
    println!("README content: {}", content);
    
    Ok(())
}
```

The URL format supports branch and subpath information from multiple platforms:
- **GitHub**: `https://github.com/account/repo/tree/branch/path`
- **GitLab**: `https://gitlab.com/account/repo/-/tree/branch/path`
- **Gitea/Forgejo**: `https://server/account/repo/src/branch/branch_name/path`

### Clone with Branch Override

Override the branch specified in the URL or clone a different branch:

```rust
let repos = GitTree::new("/tmp/repos")?;

let path = repos
    .clone_repo("https://github.com/user/repo")
    .branch("development")  // Override default branch
    .execute()?;
```

### Clone and Extract Subdirectory

Get the path to a specific subdirectory within the repository:

```rust
let repos = GitTree::new("/tmp/repos")?;

let src_path = repos
    .clone_repo("https://github.com/user/repo")
    .subpath("src/components")
    .execute()?;

println!("Source code at: {}", src_path);
```

### Combine Branch and Subpath

Clone a specific branch and extract a subdirectory:

```rust
let repos = GitTree::new("/tmp/repos")?;

let path = repos
    .clone_repo("https://github.com/user/repo")
    .branch("feature-branch")
    .subpath("src/lib")
    .execute()?;
```

### Clone with Reset (Discard Local Changes)

Reset all local changes before operations:

```rust
let repos = GitTree::new("/tmp/repos")?;

let path = repos
    .clone_repo("https://github.com/user/repo")
    .reset()  // Performs git reset --hard HEAD and git clean -fd
    .execute()?;
```

### Clone and Pull Latest Changes

Clone and automatically pull the latest changes:

```rust
let repos = GitTree::new("/tmp/repos")?;

let path = repos
    .clone_repo("https://github.com/user/repo")
    .pull()
    .execute()?;
```

### Get Repository Path from URL

Retrieve the path to a repository that matches the URL without performing operations:

```rust
let repos = GitTree::new("/tmp/repos")?;

// This will clone if needed and return the path
let repo_path = repos.get_path("https://github.com/user/repo")?;
```

### List All Repositories

List all cloned repositories:

```rust
let repos = GitTree::new("/tmp/repos")?;

let all_repos = repos.list()?;
for repo_path in all_repos {
    println!("Repository: {}", repo_path);
}
```

### Find Repositories by Pattern

Find repositories matching a name or partial path:

```rust
let repos = GitTree::new("/tmp/repos")?;

// Find repositories matching "herolib" (must be unique)
let matches = repos.find("herolib")?;

// Find all repositories matching a pattern (use '*' suffix)
let all_matches = repos.find("user/*")?;
```

### Perform Operations on a Repository

Work with a specific repository:

```rust
use herolib_os::git::GitRepo;

let repo = GitRepo::new("/path/to/repo");

// Check if repository has local changes
if repo.has_changes()? {
    println!("Repository has uncommitted changes");
}

// Reset repository to HEAD
repo.reset()?;

// Pull latest changes
repo.pull()?;

// Get repository status
let status = repo.status()?;
println!("Status: {}", status);
```

## Supported URL Formats

The module supports multiple git URL formats with branch and subpath information:

### GitHub

- Basic: `https://github.com/user/repo`
- With branch: `https://github.com/user/repo/tree/main`
- With branch and path: `https://github.com/user/repo/tree/main/src/lib`

### GitLab

- Basic: `https://gitlab.com/user/repo`
- With branch: `https://gitlab.com/user/repo/-/tree/main`
- With branch and path: `https://gitlab.com/user/repo/-/tree/main/src`

### Gitea/Forgejo

- Basic: `https://server/user/repo`
- With branch: `https://server/user/repo/src/branch/main`
- With branch and path: `https://server/user/repo/src/branch/main/docs`

### SSH Formats

- Basic: `git@github.com:user/repo.git`
- With prefix: `ssh://git@github.com/user/repo.git`

## Common Workflows

### Workflow 1: Clone from URL with Specific Directory

```rust
use herolib_os::git::GitTree;

fn get_docs_directory(url: &str) -> Result<String, Box<dyn std::error::Error>> {
    let repos = GitTree::new("~/.git_repos")?;
    
    // This URL includes branch and subpath information
    let docs_path = repos.get_path(url)?;
    
    Ok(docs_path)
}

// Usage
let path = get_docs_directory(
    "https://github.com/user/repo/tree/main/documentation"
)?;
```

### Workflow 2: Clone Multiple Repositories

```rust
use herolib_os::git::GitTree;

fn clone_all_repos(urls: &[&str]) -> Result<Vec<String>, Box<dyn std::error::Error>> {
    let repos = GitTree::new("~/.git_repos")?;
    let mut paths = Vec::new();

    for url in urls {
        let path = repos.clone_repo(url).execute()?;
        paths.push(path);
    }

    Ok(paths)
}
```

### Workflow 3: Keep Repository Updated

```rust
use herolib_os::git::GitTree;

fn keep_repo_updated(url: &str) -> Result<String, Box<dyn std::error::Error>> {
    let repos = GitTree::new("~/.git_repos")?;

    let path = repos
        .clone_repo(url)
        .reset()  // Discard any local changes
        .pull()   // Get latest changes
        .execute()?;

    Ok(path)
}
```

## Error Handling

The module provides detailed error types for different failure scenarios:

```rust
use herolib_os::git::{GitTree, GitError};

match GitTree::new("/invalid/path") {
    Ok(repos) => {
        // Success
    }
    Err(GitError::InvalidBasePath(path)) => {
        eprintln!("Invalid base path: {}", path);
    }
    Err(GitError::GitNotInstalled(e)) => {
        eprintln!("Git is not installed: {}", e);
    }
    Err(GitError::InvalidUrl(url)) => {
        eprintln!("Could not parse URL: {}", url);
    }
    Err(e) => {
        eprintln!("Error: {}", e);
    }
}
```

## Requirements

- Git must be installed and available in PATH
- Rust 1.92.0 or later
- Edition 2024

## Notes

- The module uses shallow cloning (`--depth 1`) by default for faster operations
- Repository paths are structured as: `{base_path}/{server}/{account}/{repo}`
- Paths support tilde (`~`) expansion for home directory
- The module is designed for parallel access to independent repositories
