libgit2 clib Version Control / Git

Pure-C Git implementation. Open repos, create commits, push/pull, diff, blame — no git binary required.

ppm install libgit2

Overview

libgit2 is a pure-C implementation of the Git core. It lets you work with Git repositories programmatically — reading history, staging files, creating commits, fetching and pushing to remotes — without spawning a git process or depending on any external binary.

It is the engine behind GitHub Desktop, GitLab, Sourcetree, Tower, VS Code's built-in Git support, and Gitea. The PascalAI binding wraps the shared library through libpai_git2.so and exposes a clean, record-oriented API.

CLib package — system requirement

Requires libgit2-dev installed on the host. Ubuntu/Debian: sudo apt install libgit2-dev

vs. git CLI

Calling git as a subprocess is fragile: output formats change between versions, parsing is error-prone, and each invocation forks a process. libgit2 gives you structured data — typed records, integer handles, arrays — all in-process with no shell escaping.

vs. JGit

JGit is a Java reimplementation of Git. libgit2 is written in C, ships as a native shared library, and has no JVM overhead. It is faster to load, uses less memory, and is the choice for native applications and language runtimes like PascalAI.

Quick Start

Open a repo and read the last 10 commits

uses libgit2;

GitInit;

var repo := OpenRepo('/home/user/myproject', True);
var head  := GetHEAD(repo);
var log   := WalkCommits(repo, head, 10);

for var i := 0 to Length(log) - 1 do
  WriteLn(log[i].OID + '  ' + log[i].Summary);

FreeRepo(repo);
GitCleanup;

Stage all files and create a commit

uses libgit2;

GitInit;
var repo := OpenRepo('/home/user/myproject', False);

var idx := GetIndex(repo);
IndexAddAll(idx);
IndexWrite(idx);
var treeOID := IndexWriteTree(idx);

var sig: TGitSignature;
sig.Name  := 'Alice';
sig.Email := 'alice@example.com';
sig.Time  := 0;   { 0 = use current time }
sig.Offset := 0;

var oid := CreateCommit(repo, 'Initial commit', sig, sig,
                          treeOID, ['HEAD']);
WriteLn('Created commit: ' + oid);

FreeIndex(idx);
FreeRepo(repo);
GitCleanup;

Clone a repo and push a branch

uses libgit2;

GitInit;

var creds: TGitCredentials;
creds.Username := 'git';
creds.Password := 'ghp_myPersonalAccessToken';

var repo := CloneRepo('https://github.com/example/myrepo',
                       '/tmp/myrepo', creds);
WriteLn('Cloned to: ' + RepoWorkDir(repo));

{ ... make changes, stage, commit ... }

Push(repo, 'origin', 'main', creds, False);
WriteLn('Pushed.');

FreeRepo(repo);
GitCleanup;

Types

Handle types are opaque integers. Records carry structured data returned from API calls.

type
  TGitRepo    = Integer;  { repository handle }
  TGitCommit  = Integer;  { commit object }
  TGitTree    = Integer;  { tree object }
  TGitBlob    = Integer;  { blob (file content) object }
  TGitBranch  = Integer;  { branch reference }
  TGitIndex   = Integer;  { staging index }
  TGitRemote  = Integer;  { remote connection }
  TGitDiff    = Integer;  { diff result }
  TGitOID     = string;   { 40-hex SHA-1 or 64-hex SHA-256 }

  TGitSignature = record
    Name  : string;
    Email : string;
    Time  : Int64;    { Unix timestamp }
    Offset: Integer;  { timezone offset in minutes }
  end;

  TGitCommitInfo = record
    OID       : TGitOID;
    Message   : string;
    Summary   : string;   { first line of message }
    Author    : TGitSignature;
    Committer : TGitSignature;
    ParentOIDs: array of TGitOID;
    TreeOID   : TGitOID;
  end;

  TGitStatusEntry = record
    Path       : string;
    Status     : Integer;  { bitmask: GIT_STATUS_* }
    IsNew      : Boolean;
    IsModified : Boolean;
    IsDeleted  : Boolean;
    IsRenamed  : Boolean;
    IsStaged   : Boolean;
  end;

  TGitDiffStats = record
    FilesChanged : Integer;
    Insertions   : Integer;
    Deletions    : Integer;
  end;

  TGitDiffHunk = record
    OldStart : Integer;
    OldLines : Integer;
    NewStart : Integer;
    NewLines : Integer;
    Header   : string;
    Lines    : array of string;  { '+'/'-'/' ' prefixed }
  end;

  TGitBranchInfo = record
    Name         : string;
    IsRemote     : Boolean;
    IsHead       : Boolean;
    UpstreamName : string;
    OID          : TGitOID;
  end;

  TGitCredentials = record
    Username  : string;
    Password  : string;    { or personal access token }
    SSHKeyPath: string;   { private key file }
    SSHPubPath: string;   { public key file }
    Passphrase: string;
  end;

Repository Lifecycle API

Call GitInit once at process startup and GitCleanup at exit. All other functions require a valid TGitRepo handle obtained from OpenRepo, InitRepo, or CloneRepo.

FunctionDescription
GitInitInitialize libgit2. Must be called once per process before any other call.
GitCleanupRelease global libgit2 resources. Call at process exit.
OpenRepo(Path, Discover)Open an existing repository. If Discover=True, searches parent directories for a .git folder.
InitRepo(Path, Bare)Create a new repository. Bare=True for bare (server-side) repos.
CloneRepo(URL, LocalPath, Creds)Clone a remote repository to a local path using the supplied credentials.
FreeRepo(Repo)Release the repository handle and all associated memory.
RepoPath(Repo)Returns the path to the .git directory.
RepoWorkDir(Repo)Returns the working directory path (empty for bare repos).
RepoIsBare(Repo)True if the repository has no working directory.
procedure GitInit;
procedure GitCleanup;
function  OpenRepo(Path: string; Discover: Boolean): TGitRepo;
function  InitRepo(Path: string; Bare: Boolean): TGitRepo;
function  CloneRepo(URL, LocalPath: string; Creds: TGitCredentials): TGitRepo;
procedure FreeRepo(Repo: TGitRepo);
function  RepoPath(Repo: TGitRepo): string;
function  RepoWorkDir(Repo: TGitRepo): string;
function  RepoIsBare(Repo: TGitRepo): Boolean;

Commits API

Read history with WalkCommits, look up individual commits by OID or ref name, and create new commits after staging changes through the index.

FunctionDescription
GetHEAD(Repo)Returns the OID of the current HEAD commit.
LookupCommit(Repo, OIDOrRef)Look up a commit by its OID, branch name, or ref such as 'HEAD' or 'main'. Returns a TGitCommitInfo.
WalkCommits(Repo, FromOID, MaxCount)Walk commit history starting from FromOID. Returns up to MaxCount commits in reverse-chronological order.
CreateCommit(Repo, Message, Author, Committer, TreeOID, ParentOIDs)Create a new commit. Use IndexWriteTree to get the tree OID after staging. Returns the new commit OID.
AmendCommit(Repo, Message)Amend the most recent commit with a new message. Returns the amended commit OID.
function GetHEAD(Repo: TGitRepo): TGitOID;
function LookupCommit(Repo: TGitRepo; OIDOrRef: string): TGitCommitInfo;
function WalkCommits(Repo: TGitRepo; FromOID: string;
                     MaxCount: Integer): array of TGitCommitInfo;
function CreateCommit(Repo: TGitRepo; Message: string;
                      Author, Committer: TGitSignature;
                      TreeOID: TGitOID;
                      ParentOIDs: array of TGitOID): TGitOID;
function AmendCommit(Repo: TGitRepo; Message: string): TGitOID;

Index (Staging) API

The index represents the staging area. Stage files with IndexAdd or IndexAddAll, then call IndexWrite and IndexWriteTree to get a tree OID for use in CreateCommit.

FunctionDescription
GetIndex(Repo)Get the repository's staging index handle.
FreeIndex(Idx)Free the index handle.
IndexAdd(Idx, Path)Stage a single file. Path is relative to the working directory.
IndexAddAll(Idx)Stage all changes, equivalent to git add -A.
IndexRemove(Idx, Path)Remove a file from the index (unstage / mark for deletion).
IndexWrite(Idx)Persist the in-memory index to disk. Must be called before CreateCommit.
IndexWriteTree(Idx)Write the index as a tree object. Returns the tree OID to pass to CreateCommit.
function  GetIndex(Repo: TGitRepo): TGitIndex;
procedure FreeIndex(Idx: TGitIndex);
procedure IndexAdd(Idx: TGitIndex; Path: string);
procedure IndexAddAll(Idx: TGitIndex);
procedure IndexRemove(Idx: TGitIndex; Path: string);
procedure IndexWrite(Idx: TGitIndex);
function  IndexWriteTree(Idx: TGitIndex): TGitOID;

Status API

Inspect what has changed in the working directory and staging area. Each TGitStatusEntry carries boolean flags for the most common states so you don't have to interpret the bitmask manually.

FunctionDescription
GetStatus(Repo)Returns an array of TGitStatusEntry for every changed file in the working directory and index.
GetFileStatus(Repo, Path)Get status of a single file by path.
IsClean(Repo)True if the repository has no uncommitted changes.
function GetStatus(Repo: TGitRepo): array of TGitStatusEntry;
function GetFileStatus(Repo: TGitRepo; Path: string): TGitStatusEntry;
function IsClean(Repo: TGitRepo): Boolean;

Branches API

Create, delete, list, and checkout branches. ListBranches can optionally include remote-tracking branches.

FunctionDescription
ListBranches(Repo, IncludeRemote)Returns all branches as TGitBranchInfo records. Set IncludeRemote=True to include remote-tracking branches.
CurrentBranch(Repo)Returns the name of the currently checked-out branch.
CreateBranch(Repo, Name, FromOID)Create a new branch pointing at the given commit OID.
DeleteBranch(Repo, Name)Delete a local branch by name.
Checkout(Repo, BranchOrOID, Force)Switch to a branch or detach HEAD at a commit. Force=True discards local changes.
function  ListBranches(Repo: TGitRepo;
                       IncludeRemote: Boolean): array of TGitBranchInfo;
function  CurrentBranch(Repo: TGitRepo): string;
procedure CreateBranch(Repo: TGitRepo; Name, FromOID: string);
procedure DeleteBranch(Repo: TGitRepo; Name: string);
procedure Checkout(Repo: TGitRepo; BranchOrOID: string;
                   Force: Boolean);

Tags API

Both lightweight tags (a named pointer to a commit) and annotated tags (a full Git object with a message and tagger signature) are supported.

FunctionDescription
ListTags(Repo)Returns an array of tag name strings.
CreateTag(Repo, Name, OID)Create a lightweight tag pointing at the given OID.
CreateAnnotatedTag(Repo, Name, OID, Message, Tagger)Create an annotated tag with a message and tagger signature.
DeleteTag(Repo, Name)Delete a tag by name.
function  ListTags(Repo: TGitRepo): array of string;
procedure CreateTag(Repo: TGitRepo; Name, OID: string);
procedure CreateAnnotatedTag(Repo: TGitRepo; Name, OID, Message: string;
                             Tagger: TGitSignature);
procedure DeleteTag(Repo: TGitRepo; Name: string);

Diff API

Compare the working directory against the index, the index against HEAD, or any two commits. The result is a TGitDiff handle you can inspect for stats, hunks, or a full unified patch string.

FunctionDescription
DiffIndexToWorkdir(Repo)Diff working directory against the index (unstaged changes).
DiffHeadToIndex(Repo)Diff the index against HEAD (staged changes ready to commit).
DiffCommits(Repo, OldOID, NewOID)Diff between any two commits by OID.
GetDiffStats(D)Returns a TGitDiffStats with FilesChanged, Insertions, and Deletions.
GetDiffHunks(D)Returns an array of TGitDiffHunk records, each with line-level context and +/-/ -prefixed lines.
DiffToPatch(D)Returns the full diff as a unified patch string.
FreeDiff(D)Release the diff handle.
function  DiffIndexToWorkdir(Repo: TGitRepo): TGitDiff;
function  DiffHeadToIndex(Repo: TGitRepo): TGitDiff;
function  DiffCommits(Repo: TGitRepo;
                      OldOID, NewOID: string): TGitDiff;
function  GetDiffStats(D: TGitDiff): TGitDiffStats;
function  GetDiffHunks(D: TGitDiff): array of TGitDiffHunk;
function  DiffToPatch(D: TGitDiff): string;
procedure FreeDiff(D: TGitDiff);

Diff example

uses libgit2;

GitInit;
var repo  := OpenRepo('/home/user/myproject', True);
var diff  := DiffHeadToIndex(repo);
var stats := GetDiffStats(diff);

WriteLn(IntToStr(stats.FilesChanged) + ' files changed, +'
      + IntToStr(stats.Insertions) + ' -'
      + IntToStr(stats.Deletions));

var hunks := GetDiffHunks(diff);
for var i := 0 to Length(hunks) - 1 do
  WriteLn(hunks[i].Header);

FreeDiff(diff);
FreeRepo(repo);
GitCleanup;

Remote API

Fetch, push, and manage remote connections. Credentials support both HTTPS (username + password/token) and SSH (key files).

FunctionDescription
ListRemotes(Repo)Returns an array of remote name strings (e.g. ['origin', 'upstream']).
GetRemoteURL(Repo, Name)Returns the fetch URL for a named remote.
AddRemote(Repo, Name, URL)Add a new remote.
Fetch(Repo, RemoteName, Creds)Fetch all refs from a remote. Updates remote-tracking branches.
Push(Repo, RemoteName, BranchName, Creds, Force)Push a branch to a remote. Force=True for force-push.
function  ListRemotes(Repo: TGitRepo): array of string;
function  GetRemoteURL(Repo: TGitRepo; Name: string): string;
procedure AddRemote(Repo: TGitRepo; Name, URL: string);
procedure Fetch(Repo: TGitRepo; RemoteName: string;
                Creds: TGitCredentials);
procedure Push(Repo: TGitRepo; RemoteName, BranchName: string;
               Creds: TGitCredentials; Force: Boolean);

Blame API

Annotate each line of a file with the commit that last modified it. Returns one TGitCommitInfo per line.

function BlameFile(Repo: TGitRepo; Path: string): array of TGitCommitInfo;

Blame example

uses libgit2;

GitInit;
var repo  := OpenRepo('/home/user/myproject', True);
var blame := BlameFile(repo, 'src/main.pas');

for var i := 0 to Length(blame) - 1 do
  WriteLn(Copy(blame[i].OID, 1, 8) + '  ' + blame[i].Author.Name);

FreeRepo(repo);
GitCleanup;

File Content API

Read the content of any file at a historical commit without checking it out to disk.

function ReadFileAtCommit(Repo: TGitRepo; OID, Path: string): string;

Example

uses libgit2;

GitInit;
var repo    := OpenRepo('/home/user/myproject', True);
var content := ReadFileAtCommit(repo,
                  'a3f1c2d9', 'src/config.json');
WriteLn(content);

FreeRepo(repo);
GitCleanup;

Info

function LibGit2Version: string;

Returns the version string of the underlying libgit2 C library (e.g. '1.7.2'). Useful for diagnostics and feature detection.

Tip — always call GitInit / GitCleanup

libgit2 uses a reference-counted global initializer. Every process must call GitInit exactly once before using any other function, and GitCleanup once at exit to free thread-local state and global caches.

Package Info

Version1.0.0
Typeclib
C Librarylibgit2-dev
CategoryVersion Control

System Requirements

libgit2-dev

Ubuntu/Debian:
sudo apt install libgit2-dev

Handle Types

TGitRepo — repository
TGitIndex — staging area
TGitDiff — diff result
TGitOID — SHA hex string
TGitCommit / Tree / Blob

Functions

  • GitInit / GitCleanup
  • OpenRepo / InitRepo
  • CloneRepo / FreeRepo
  • RepoPath / RepoWorkDir
  • RepoIsBare
  • GetHEAD / LookupCommit
  • WalkCommits
  • CreateCommit / AmendCommit
  • GetIndex / FreeIndex
  • IndexAdd / IndexAddAll
  • IndexRemove / IndexWrite
  • IndexWriteTree
  • GetStatus / GetFileStatus
  • IsClean
  • ListBranches / CurrentBranch
  • CreateBranch / DeleteBranch
  • Checkout
  • ListTags / CreateTag
  • CreateAnnotatedTag / DeleteTag
  • DiffIndexToWorkdir
  • DiffHeadToIndex / DiffCommits
  • GetDiffStats / GetDiffHunks
  • DiffToPatch / FreeDiff
  • ListRemotes / GetRemoteURL
  • AddRemote / Fetch / Push
  • BlameFile
  • ReadFileAtCommit
  • LibGit2Version

Used By

GitHub Desktop
GitLab
Sourcetree
Tower
VS Code (built-in Git)
Gitea

See Also

  • curl (HTTP/REST)
  • zip (archive files)
  • fs (filesystem ops)
  • json (parse JSON output)