Go to file
2026-01-03 23:36:22 -05:00
Archivers LibControl 2026-01-03 12:31:38 -05:00
Blame LibControl 2026-01-03 12:31:38 -05:00
Certificates LibControl 2026-01-03 12:31:38 -05:00
Commands LibControl 2026-01-03 12:31:38 -05:00
Core LibControl 2026-01-03 12:31:38 -05:00
Credentials LibControl 2026-01-03 12:31:38 -05:00
Diff LibControl 2026-01-03 12:31:38 -05:00
Exceptions LibControl 2026-01-03 12:31:38 -05:00
Filters LibControl 2026-01-03 12:31:38 -05:00
Handlers LibControl 2026-01-03 12:31:38 -05:00
Index LibControl 2026-01-03 12:31:38 -05:00
Logging LibControl 2026-01-03 12:31:38 -05:00
Notes LibControl 2026-01-03 12:31:38 -05:00
Options LibControl 2026-01-03 12:31:38 -05:00
Push LibControl 2026-01-03 12:31:38 -05:00
Rebase LibControl 2026-01-03 12:31:38 -05:00
References LibControl 2026-01-03 12:31:38 -05:00
Remotes LibControl 2026-01-03 12:31:38 -05:00
Results LibControl 2026-01-03 12:31:38 -05:00
Stash LibControl 2026-01-03 12:31:38 -05:00
Submodules LibControl 2026-01-03 12:31:38 -05:00
Tags LibControl 2026-01-03 12:31:38 -05:00
Targets Add Targets folder with build targets 2025-12-28 10:10:45 +00:00
Transports LibControl 2026-01-03 12:31:38 -05:00
Worktrees LibControl 2026-01-03 12:31:38 -05:00
.gitignore Initial commit - MarketAlly.LibGit2Sharp library 2025-12-28 09:58:52 +00:00
Blob.cs LibControl 2026-01-03 12:31:38 -05:00
Branch.cs LibControl 2026-01-03 12:31:38 -05:00
BranchCollection.cs LibControl 2026-01-03 12:31:38 -05:00
BranchTrackingDetails.cs LibControl 2026-01-03 12:31:38 -05:00
BranchUpdater.cs LibControl 2026-01-03 12:31:38 -05:00
BuiltInFeatures.cs LibControl 2026-01-03 12:31:38 -05:00
ChangeKind.cs LibControl 2026-01-03 12:31:38 -05:00
Commit.cs LibControl 2026-01-03 12:31:38 -05:00
CommitFilter.cs LibControl 2026-01-03 12:31:38 -05:00
CommitLog.cs LibControl 2026-01-03 12:31:38 -05:00
CommitRewriteInfo.cs LibControl 2026-01-03 12:31:38 -05:00
CommitSortStrategies.cs LibControl 2026-01-03 12:31:38 -05:00
Configuration.cs LibControl 2026-01-03 12:31:38 -05:00
ConfigurationEntry.cs LibControl 2026-01-03 12:31:38 -05:00
ConfigurationLevel.cs LibControl 2026-01-03 12:31:38 -05:00
Conflict.cs LibControl 2026-01-03 12:31:38 -05:00
ConflictCollection.cs LibControl 2026-01-03 12:31:38 -05:00
CurrentOperation.cs LibControl 2026-01-03 12:31:38 -05:00
DetachedHead.cs LibControl 2026-01-03 12:31:38 -05:00
FileStatus.cs LibControl 2026-01-03 12:31:38 -05:00
GitLink.cs LibControl 2026-01-03 12:31:38 -05:00
GitObject.cs LibControl 2026-01-03 12:31:38 -05:00
GitObjectMetadata.cs LibControl 2026-01-03 12:31:38 -05:00
GlobalSettings.cs LibControl 2026-01-03 12:31:38 -05:00
IBelongToARepository.cs LibControl 2026-01-03 12:31:38 -05:00
ICommitLog.cs LibControl 2026-01-03 12:31:38 -05:00
icon_lib.png LibControl 2026-01-03 12:31:38 -05:00
Identity.cs LibControl 2026-01-03 12:31:38 -05:00
IDiffResult.cs LibControl 2026-01-03 12:31:38 -05:00
Ignore.cs LibControl 2026-01-03 12:31:38 -05:00
IQueryableCommitLog.cs LibControl 2026-01-03 12:31:38 -05:00
IRepository.cs LibControl 2026-01-03 12:31:38 -05:00
MarketAlly.LibGit2Sharp.csproj version bump 2026-01-03 23:36:22 -05:00
MatchedPathsAggregator.cs LibControl 2026-01-03 12:31:38 -05:00
MergeHead.cs LibControl 2026-01-03 12:31:38 -05:00
Mode.cs LibControl 2026-01-03 12:31:38 -05:00
Network.cs LibControl 2026-01-03 12:31:38 -05:00
ObjectDatabase.cs LibControl 2026-01-03 12:31:38 -05:00
ObjectId.cs LibControl 2026-01-03 12:31:38 -05:00
ObjectType.cs LibControl 2026-01-03 12:31:38 -05:00
OdbBackend.cs LibControl 2026-01-03 12:31:38 -05:00
OdbBackendStream.cs LibControl 2026-01-03 12:31:38 -05:00
PackBuilder.cs LibControl 2026-01-03 12:31:38 -05:00
README.md Initial commit - MarketAlly.LibGit2Sharp library 2025-12-28 09:58:52 +00:00
Repository.cs LibControl 2026-01-03 12:31:38 -05:00
RepositoryExtensions.cs LibControl 2026-01-03 12:31:38 -05:00
RepositoryInformation.cs LibControl 2026-01-03 12:31:38 -05:00
RepositoryOperationContext.cs LibControl 2026-01-03 12:31:38 -05:00
RepositoryStatus.cs LibControl 2026-01-03 12:31:38 -05:00
ResetMode.cs LibControl 2026-01-03 12:31:38 -05:00
Signature.cs LibControl 2026-01-03 12:31:38 -05:00
SignatureInfo.cs LibControl 2026-01-03 12:31:38 -05:00
StatusEntry.cs LibControl 2026-01-03 12:31:38 -05:00
TransferProgress.cs LibControl 2026-01-03 12:31:38 -05:00
Tree.cs LibControl 2026-01-03 12:31:38 -05:00
TreeDefinition.cs LibControl 2026-01-03 12:31:38 -05:00
TreeEntry.cs LibControl 2026-01-03 12:31:38 -05:00
TreeEntryDefinition.cs LibControl 2026-01-03 12:31:38 -05:00
TreeEntryTargetType.cs LibControl 2026-01-03 12:31:38 -05:00
Version.cs LibControl 2026-01-03 12:31:38 -05:00

MarketAlly.LibGit2Sharp

MarketAlly fork of LibGit2Sharp - a managed wrapper around the native libgit2 library providing full Git functionality in .NET.

Overview

This library provides programmatic access to Git repositories without requiring Git to be installed. It wraps the native libgit2 C library and exposes a clean, idiomatic .NET API.

Target Framework: .NET 9.0 License: MIT (original LibGit2Sharp license preserved)

Installation

Reference the project directly or via NuGet package dependency. Requires LibGit2Sharp.NativeBinaries for the native git binaries.

Quick Start

using MarketAlly.LibGit2Sharp;

// Open a repository (implements IDisposable)
using var repo = new Repository(@"C:\Projects\MyRepo");

// Read repository info
Console.WriteLine($"Current branch: {repo.Head.FriendlyName}");
Console.WriteLine($"Is bare: {repo.Info.IsBare}");

// Iterate recent commits
foreach (var commit in repo.Commits.Take(10))
{
    Console.WriteLine($"{commit.Sha[..7]} - {commit.MessageShort}");
}

Core Classes

Repository

The primary entry point. Implements IRepository and IDisposable.

// Open existing repository
using var repo = new Repository(path);

// Check if path is a valid repo
bool isValid = Repository.IsValid(path);

// Discover repository from nested path (walks up to find .git)
string? repoPath = Repository.Discover(startingPath);

// Initialize new repository
string createdPath = Repository.Init(path);
string bareRepoPath = Repository.Init(path, isBare: true);

// Clone from remote
string clonedPath = Repository.Clone(sourceUrl, workdirPath, new CloneOptions
{
    BranchName = "main",
    RecurseSubmodules = true,
    OnCheckoutProgress = (path, completed, total) => { }
});

Key Repository Properties

Property Type Description
Head Branch Current branch (or detached HEAD)
Commits IQueryableCommitLog Queryable commit history
Branches BranchCollection All local and remote branches
Tags TagCollection All tags
Refs ReferenceCollection All references
Index Index Staging area
Stashes StashCollection Stash entries
Config Configuration Repository configuration
Diff Diff Diff operations
Network Network Remote operations
ObjectDatabase ObjectDatabase Low-level object access
Info RepositoryInformation Repo metadata

Common Operations

Querying Commits

// Get HEAD commit
Commit head = repo.Head.Tip;

// Query with filters
var filter = new CommitFilter
{
    IncludeReachableFrom = repo.Head,
    ExcludeReachableFrom = repo.Tags["v1.0"],
    SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Time
};

foreach (var commit in repo.Commits.QueryBy(filter))
{
    Console.WriteLine($"{commit.Author.Name}: {commit.MessageShort}");
}

// Lookup by SHA
var commit = repo.Lookup<Commit>("abc1234");

Commit Properties

Commit commit = repo.Head.Tip;

string sha = commit.Sha;              // Full SHA
string message = commit.Message;       // Full message
string subject = commit.MessageShort;  // First line only
Signature author = commit.Author;      // Author name, email, timestamp
Signature committer = commit.Committer;
Tree tree = commit.Tree;              // File tree at this commit
IEnumerable<Commit> parents = commit.Parents;

Branches

// List branches
foreach (var branch in repo.Branches)
{
    Console.WriteLine($"{branch.FriendlyName} (Remote: {branch.IsRemote})");
}

// Get specific branch
Branch main = repo.Branches["main"];
Branch remote = repo.Branches["origin/main"];

// Create branch
Branch newBranch = repo.Branches.Add("feature/new", repo.Head.Tip);

// Delete branch
repo.Branches.Remove("feature/old");

// Tracking info
if (branch.IsTracking)
{
    var tracking = branch.TrackingDetails;
    Console.WriteLine($"Ahead: {tracking.AheadBy}, Behind: {tracking.BehindBy}");
}

Staging and Committing

// Stage files (using Commands helper)
Commands.Stage(repo, "file.txt");
Commands.Stage(repo, new[] { "src/*.cs" });

// Unstage
Commands.Unstage(repo, "file.txt");

// Check status
RepositoryStatus status = repo.RetrieveStatus(new StatusOptions());
foreach (var entry in status)
{
    Console.WriteLine($"{entry.State}: {entry.FilePath}");
}

// Create commit
Signature author = new Signature("Name", "email@example.com", DateTimeOffset.Now);
Commit commit = repo.Commit("Commit message", author, author, new CommitOptions());

// Amend previous commit
Commit amended = repo.Commit("New message", author, author,
    new CommitOptions { AmendPreviousCommit = true });

Diff Operations

// Compare trees (e.g., parent to commit)
Commit commit = repo.Head.Tip;
Commit parent = commit.Parents.FirstOrDefault();

// Get list of changed files
using var changes = repo.Diff.Compare<TreeChanges>(parent?.Tree, commit.Tree);
foreach (var change in changes)
{
    Console.WriteLine($"{change.Status}: {change.Path}");
}

// Get full patch with line changes
using var patch = repo.Diff.Compare<Patch>(parent?.Tree, commit.Tree);
Console.WriteLine($"Lines added: {patch.LinesAdded}, deleted: {patch.LinesDeleted}");

foreach (var entry in patch)
{
    Console.WriteLine($"--- {entry.Path} ---");
    Console.WriteLine(entry.Patch);
}

// Compare working directory to index
using var workdirChanges = repo.Diff.Compare<TreeChanges>();

Network Operations

// List remotes
foreach (var remote in repo.Network.Remotes)
{
    Console.WriteLine($"{remote.Name}: {remote.Url}");
}

// Fetch
Commands.Fetch(repo, "origin", new string[0], new FetchOptions
{
    CredentialsProvider = (url, user, types) =>
        new UsernamePasswordCredentials { Username = "user", Password = "token" }
}, "fetch log");

// Pull
Commands.Pull(repo, signature, new PullOptions());

// Push
repo.Network.Push(repo.Branches["main"], new PushOptions
{
    CredentialsProvider = (url, user, types) => credentials
});

Reset and Checkout

// Reset HEAD (soft/mixed/hard)
repo.Reset(ResetMode.Hard, repo.Head.Tip);

// Checkout branch
Commands.Checkout(repo, repo.Branches["feature"]);

// Checkout specific commit (detached HEAD)
Commands.Checkout(repo, repo.Lookup<Commit>("abc1234"));

// Checkout specific files
repo.CheckoutPaths("HEAD~1", new[] { "file.txt" }, new CheckoutOptions());

History Rewriting

Rewrite commit messages or tree contents for multiple commits:

// Rewrite commit messages
var commitsToRewrite = repo.Commits.Take(5).ToArray();

repo.Refs.RewriteHistory(new RewriteHistoryOptions
{
    BackupRefsNamespace = "refs/original/",
    CommitHeaderRewriter = commit =>
    {
        if (commit.Message.StartsWith("WIP"))
        {
            return new CommitRewriteInfo
            {
                Message = commit.Message.Replace("WIP: ", ""),
                Author = commit.Author,
                Committer = commit.Committer
            };
        }
        return null; // Keep original
    },
    PruneEmptyCommits = false
}, commitsToRewrite);

Low-Level Object Database

// Create commits directly (without updating refs)
var newCommit = repo.ObjectDatabase.CreateCommit(
    author,
    committer,
    "Message",
    tree,
    parents,
    prettifyMessage: false);

// Create tree from definition
var treeDefinition = TreeDefinition.From(repo.Head.Tip.Tree);
treeDefinition.Remove("old-file.txt");
var newTree = repo.ObjectDatabase.CreateTree(treeDefinition);

// Check if object exists
bool exists = repo.ObjectDatabase.Contains(objectId);

References

// Update reference target
repo.Refs.UpdateTarget("HEAD", newCommit.Sha);
repo.Refs.UpdateTarget(repo.Head, newCommit.Id, "reflog message");

// Create reference
repo.Refs.Add("refs/heads/new-branch", commit.Id, "created branch");

// Get references pointing to a commit
var refs = repo.Refs.ReachableFrom(new[] { commit });

Important Types

Signature

var sig = new Signature("Name", "email@example.com", DateTimeOffset.Now);

ObjectId

var id = new ObjectId("abc123def456...");
string sha = id.Sha;

ChangeKind

  • Added, Deleted, Modified, Renamed, Copied, Unmodified, TypeChanged, Untracked, Conflicted, Ignored

FileStatus

  • NewInIndex, ModifiedInIndex, DeletedFromIndex, NewInWorkdir, ModifiedInWorkdir, DeletedFromWorkdir, Conflicted, etc.

ResetMode

  • Soft - Move HEAD only
  • Mixed - Move HEAD and reset index
  • Hard - Move HEAD, reset index and working directory

Thread Safety

  • Repository instances are not thread-safe. Use separate instances per thread.
  • For concurrent access, consider implementing a repository cache with TTL (see GitOperationsService in parent project for example).

Resource Management

Always dispose Repository instances:

// Using statement (recommended)
using var repo = new Repository(path);

// Or explicit disposal
var repo = new Repository(path);
try { /* work */ }
finally { repo.Dispose(); }

Global Settings

// Enable/disable object caching (useful during history rewriting)
GlobalSettings.SetEnableCaching(false);

// Get native library version
string version = GlobalSettings.Version.InformationalVersion;

Exceptions

Exception Cause
RepositoryNotFoundException Path is not a git repository
BareRepositoryException Operation not supported on bare repo
LibGit2SharpException General git operation failure
EmptyCommitException Commit would be empty
MergeConflictException Unresolved conflicts
NonFastForwardException Push rejected (not fast-forward)
CheckoutConflictException Checkout blocked by local changes

Differences from Original LibGit2Sharp

  • Targets .NET 9.0 only (dropped older framework support)
  • Namespace changed to MarketAlly.LibGit2Sharp
  • Trimming support enabled for AOT scenarios
  • Maintained by MarketAlly for internal projects

Dependencies

  • LibGit2Sharp.NativeBinaries (2.0.323) - Native libgit2 binaries