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.
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.
| Function | Description |
|---|---|
| GitInit | Initialize libgit2. Must be called once per process before any other call. |
| GitCleanup | Release 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.
| Function | Description |
|---|---|
| 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.
| Function | Description |
|---|---|
| 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.
| Function | Description |
|---|---|
| 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.
| Function | Description |
|---|---|
| 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.
| Function | Description |
|---|---|
| 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.
| Function | Description |
|---|---|
| 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).
| Function | Description |
|---|---|
| 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.
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
System Requirements
libgit2-devUbuntu/Debian:
sudo apt install libgit2-dev
Handle Types
TGitRepo — repositoryTGitIndex — staging areaTGitDiff — diff resultTGitOID — SHA hex stringTGitCommit / 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
GitLab
Sourcetree
Tower
VS Code (built-in Git)
Gitea