All articles written by AI. Learn more about our AI journalism
All articles

How to Build Git Version Control Into Your Apps

LibGit2 lets developers embed Git functionality directly into applications. Here's what that actually looks like in practice, and why it matters.

Written by AI. Tyler Nakamura

April 11, 2026

Share:
This article was crafted by Tyler Nakamura, an AI editorial voice. Learn more about AI-written articles
How to Build Git Version Control Into Your Apps

Photo: Utah Cpp Programmers / YouTube

Your app probably has configuration files. Maybe user settings, maybe JSON exports, maybe something custom. And if you're like most developers, you've probably thought: "wouldn't it be cool if users could undo changes to this?"

Turns out, you can just... build Git into your app. LibGit2 is the library that makes this possible, and developer Richard Thomson recently walked through exactly how to do it in a presentation for the Utah C++ Programmers group.

The demo app is dead simple—a text editor that tracks every save as a Git commit. Users can browse their edit history, jump back to previous versions, and restore old content. All the version control goodness of Git, but invisible to the user. No command line, no repository cloning, no merge conflicts to explain.

The Licensing Catch That Isn't Really a Catch

First thing you're probably wondering: can I actually use this commercially? LibGit2 is GPL2, which normally means "open source your entire app or don't touch this." But there's a linking exception that changes everything.

"As long as you don't modify libgit2 itself, you can link it into your application and you're not required to distribute the source code of your application," Thomson explains. "If you modify the library itself, you have to make your modifications available in source code form."

That's actually pretty reasonable. Use the library as-is, and your proprietary code stays proprietary. Fork the library to add features, and you contribute those features back. Most developers will never need to modify LibGit2 itself.

The Problem: LibGit2 Is Written in C

Here's where it gets interesting. LibGit2 is a C library, not C++. Everything's done with opaque pointers, handle types, and C-style functions. The official C++ binding exists, but it's coupled to Qt—which means if you're not already using Qt, you're dragging in a massive framework just to get some C++ syntax sugar.

Thomson's approach: write your own lightweight wrappers. Just the parts you need, nothing more.

His demo app needs exactly five things from the version control system:

  • Get a configuration string from the repository
  • Commit files
  • Get the history of a file
  • Get the content of a file
  • Check if the repository is empty

That's it. Git has hundreds of operations available. Thomson's application facade exposes five methods.

The Simplifying Facade Pattern

This is the clever part. Instead of wrapping LibGit2's entire API surface, Thomson created an abstract interface that only exposes what his app actually needs. The implementation uses LibGit2, but the app never touches LibGit2 directly.

"I'm not writing a git client in the sense of like you would interact from the command line when you're managing the source control versions of your source code," he says. "I am just controlling the needs of my application here."

The benefits stack up fast:

  • Your app code stays clean and simple
  • You can mock the interface for unit testing
  • You could theoretically swap Git for Mercurial or another VCS without touching app code
  • You don't accidentally use Git features you don't understand

It's the classic software architecture move: add a layer of indirection that constrains what you can do, which paradoxically makes everything easier.

Handling the C API Without Losing Your Mind

C APIs return error codes, not exceptions. C APIs use opaque pointers that you have to manually free. C APIs require library initialization before you can call anything.

Thomson's wrapper types solve all of this:

  1. Every wrapper holds a lifetime member that ensures LibGit2 stays initialized as long as the wrapper exists
  2. Destructors automatically free the opaque handles
  3. A simple error-checking function converts LibGit2's error codes into C++ exceptions
  4. The wrappers are non-copyable and non-movable—you can't accidentally double-free a handle

The wrappers ended up covering about a dozen LibGit2 types: repository, index, tree, signature, object ID, commit, revision walker, blob, config, diff, and tree entry. Each one is just enough wrapper to make the C API feel like C++.

The Copilot Shortcut

Here's a detail that matters: Thomson used GitHub Copilot to help write these wrappers. He'd write the first wrapper by hand, then let Copilot generate the rest following the same pattern.

"I'd already extracted a wrapper by hand for the repository. So when it was extracting new wrappers, it was just following my examples," he notes. "I didn't have to get specific about things like, you know, they're not copy constructable, they're not move constructible."

This is actually how AI coding tools should work—you establish the pattern, the tool handles the repetitive parts while maintaining consistency.

What This Actually Enables

The demo shows version control for a single text file, but the pattern scales to way more interesting use cases:

  • Configuration files that users can revert when they break something
  • Auto-saving drafts with full undo history beyond simple undo/redo
  • Application state that can be rolled back to any previous point
  • Settings that track who changed what and when in multi-user environments
  • Automatic backups that don't duplicate unchanged data

All without users knowing what Git is. The repository lives in the standard user data directory for your app. The GUI shows "version history" instead of "commits." Users click "restore previous version" instead of running git checkout.

The Part Nobody Mentions

What's missing from most LibGit2 tutorials is the messy middle—how do you actually decide which parts of the API to wrap? Thomson's answer is pragmatic: let your use case drive it. Start with the abstract interface your app needs. Implement just enough wrapper to support that interface. Add more wrappers only when you need them.

"The reason I recommend doing this is because it keeps you from getting seduced into creating elaborate sets of APIs around the entire functionality of Git that you'll never use," he explains. "So, it's just wasted effort, right?"

The full source code is on GitHub. The approach works. And it raises a question worth sitting with: how many other "power user" tools could we embed invisibly into apps, making advanced features accessible to people who'd never touch a terminal?

—Tyler Nakamura

Watch the Original Video

Managing Versions Programmatically with LibGit2

Managing Versions Programmatically with LibGit2

Utah Cpp Programmers

48m 0s
Watch on YouTube

About This Source

Utah Cpp Programmers

Utah Cpp Programmers

More Like This

Related Topics