Cppgit2
Git for Modern C++ (A libgit2 Wrapper Library)
Install / Use
/learn @p-ranav/Cppgit2README
cppgit2 is a libgit2 wrapper library for use in modern C++ ( >= C++11). See the Build and Integration section for details on how to build and integrate cppgit2 in your projects.
// Create new repo
std::string repo_name = "my_project";
auto repo = repository::init(repo_name, false);
// Write README file
std::string file_name = "README.md";
auto readme = std::ofstream(repo_name + "/" + file_name);
readme << "Hello, World!\n";
readme.close();
// Stage README.md
auto index = repo.index();
index.add_entry_by_path(file_name);
index.write();
// Prepare signatures
auto author = signature("foobar", "foo.bar@baz.com");
auto committer = author;
// Create commit
auto tree_oid = index.write_tree();
repo.create_commit("HEAD", author, committer, "utf-8", "Update README",
repo.lookup_tree(tree_oid), {});
Table of Contents
- Build and Integration
- Sample Programs
- Initialize a new repository (
git init) - Clone a repository and checkout specific branch (
git clone --branch) - Open an existing repository
- Add and Commit a File (
git add,git commit) - Walk Repository Tree (
git ls-tree) - Print Branches (
git branch) - Print Commits (
git log) - Print Repository Tags (
git tag) - Inspect Repository Objects (
git cat-file (-s|-p|-t))
- Initialize a new repository (
- Design Notes
- Version Compatibility
- API Coverage
- Contributing
- License
Build and Integration
Run the following commands to build cppgit2.
NOTE: This also builds libgit2 from source. libgit2 is a submodule in the ext/ directory that points to a stable release commit, e.g., v1.0.0.
git clone --recurse-submodules -j8 https://github.com/p-ranav/cppgit2
cd cppgit2
mkdir build && cd build
cmake .. && make
The build output is in four directories: include, lib, samples and test:
include/
├── cppgit2/
├── git2/
└── git2.h
lib/
├── libcppgit2.so -> libcppgit2.so.1
├── libcppgit2.so.0.1.0
├── libcppgit2.so.1 -> libcppgit2.so.0.1.0
├── libcppgit2.static.a
├── libgit2_clar
├── libgit2.pc
├── libgit2.so -> libgit2.so.0
├── libgit2.so.1.0.0
├── libgit2.so.0 -> libgit2.so.1.0.0
└── ...
samples/
test/
For integration in your projects,
- Add
build/includeto yourinclude_directories - Add
build/libto yourlink_directories - Build your application, linking with
cppgit2 - Add
build/libto yourLD_LIBRARY_PATHto load the shared libraries at runtime.
Here's an example using g++:
g++ -std=c++11 -Ibuild/include -Lbuild/lib -o my_sample my_sample.cpp -lcppgit2
export LD_LIBRARY_PATH=build/lib:$LD_LIBRARY_PATH
./my_sample
and the same example with CMake:
PROJECT(my_sample)
CMAKE_MINIMUM_REQUIRED(VERSION 3.8)
INCLUDE_DIRECTORIES("build/include")
ADD_EXECUTABLE(my_sample my_sample.cpp)
find_library(CPPGIT2_LIBRARY cppgit2 HINTS ./build/lib)
TARGET_LINK_LIBRARIES(my_sample ${CPPGIT2_LIBRARY})
SET_PROPERTY(TARGET my_sample PROPERTY CXX_STANDARD 11)
Sample Programs
This section presents some simple examples illustrating various cppgit2 features. You can find the full set of available examples in the /samples directory. Samples are still a work-in-progress. Pull requests are welcome here.
Initialize a new repository (git init)
To initialize a new repository, simply call repository::init.
#include <cppgit2/repository.hpp>
using namespace cppgit2;
int main() {
auto repo = repository::init("hello_world", false);
}
If you want to create a bare repository, set the second argument to true.
Clone a repository and checkout specific branch (git clone --branch)
Let's say you want to clone a repository and checkout a specific branch. Construct an options object using clone::options, set the checkout branch name, and then use repository::clone to clone the repository.
#include <cppgit2/repository.hpp>
using namespace cppgit2;
int main() {
auto url = "https://github.com/fffaraz/awesome-cpp";
auto branch_name = "gh-pages";
auto path = "awesome_cpp";
// Prepare clone options
clone::options options;
options.set_checkout_branch_name(branch_name);
// Clone repository
auto repo = repository::clone(url, path, options);
}
Open an existing repository
You can open an existing repository with repository::open.
#include <cppgit2/repository.hpp>
using namespace cppgit2;
int main() {
auto path = "~/dev/foo/bar"; // bar must contain a .git directory
auto repo = repository::open(path);
}
Use repository::open_bare to open a bare repository.
Create Remote (git remote)
#include <cppgit2/repository.hpp>
#include <iostream>
using namespace cppgit2;
int main(int argc, char **argv) {
if (argc == 2) {
// Create new repo
auto repo = repository::init(argv[1], false);
// Create remote
auto remote = repo.create_remote("origin", "https://github.com/p-ranav/test");
} else {
std::cout << "Usage: ./executable <new_repo_path>\n";
}
}
$ ./create_remote foo
$ cd foo
$ git remote -v
origin https://github.com/p-ranav/test (fetch)
origin https://github.com/p-ranav/test (push)
Add and Commit a File (git add, git commit)
#include <cppgit2/repository.hpp>
#include <fstream>
#include <iostream>
using namespace cppgit2;
int main(int argc, char **argv) {
if (argc == 2) {
// Create new repo
auto repo = repository::init(argv[1], false);
// Write README file
std::ofstream readme;
readme.open(std::string{argv[1]} + "/README.md");
readme << "Hello, World!";
readme.close();
// Stage README.md
auto index = repo.index();
index.add_entry_by_path("README.md");
index.write();
auto tree_oid = index.write_tree();
// Prepare signatures
auto author = signature("foobar", "foo.bar@baz.com");
auto committer = signature("foobar", "foo.bar@baz.com");
// Create commit
auto commit_oid =
repo.create_commit("HEAD", author, committer, "utf-8", "Update README",
repo.lookup_tree(tree_oid), {});
std::cout << "Created commit with ID: " << commit_oid.to_hex_string()
<< std::endl;
} else {
std::cout << "Usage: ./executable <new_repo_path>\n";
}
}
$ ./commit_file foo
Created commit with ID: 34614c460ee9dd6a6e56c1a90c5533b7e284b197
$ cd foo
$ cat README.md
Hello, World!
$ git log --stat
commit 34614c460ee9dd6a6e56c1a90c5533b7e284b197 (HEAD -> master)
Author: foobar <foo.bar@baz.com>
Date: Thu Mar 19 20:48:07 2020 -0500
Update README
README.md | 1 +
1 file changed, 1 insertion(+)
Walk Repository Tree (git ls-tree)
#include <cppgit2/repository.hpp>
#include <iostream>
using namespace cppgit2;
int main(int argc, char **argv) {
if (argc == 2) {
auto repo = repository::open(argv[1]);
auto head = repo.head();
auto head_commit = repo.lookup_commit(head.target());
auto tree = head_commit.tree();
tree.walk(tree::traversal_mode::preorder,
[](const std::string &root, const tree::entry &entry) {
auto type = entry.type();
std::string type_string{""};
switch (type) {
case object::object_type::blob:
type_string = " - blob";
break;
case object::object_type::tree:
type_string = "tree";
break;
case object::object_type::commit:
type_string = " - commit";
break;
default:
type_string = "other";
break;
}
std::cout << type_string << " [" << entry.id().to_hex_string(8)
<< "] " << entry.filename() << std::endl;
});
} else {
std::cout << "Usage: ./executable <repo_path>\n";
}
}
Running this program on the cppgit2 repository yields the following:
$ cd cppgit2
$ ./build/samples/walk_tree .
- blob [ae28a6af] .clang-format
- blob [e4bbfcd3] .gitignore
- blob [7f2703f2] .gitmodules
- blob [3ed1714f] CMakeLists.txt
- blob [f6857659] README.md
- blob [9f435d50] clang-format.bash
tree [4352ee62] ext
- commit [17223902] libgit2
tree [7eed768f] img
- blob [d0fa9dbe] init_add_commit.png
- blob [dc19ed13] logo.png
tree [4d47c532] include
tree [c9adc194] cppgit2
- blob [ca1b6723] annotated_commit.hpp
- blob [4f168526] apply.hpp
- blob [79ac5ed9] attribute.hpp
- blob [5bf06b5a] bitmask_operators.hpp
- blob [10546242] blame.hpp
- blob [1a9107ab] blob.hpp
- blob [2bce809e] branch.hpp
- blob [a56ff9
