Skip to content
/ jgit2 Public

Java Foreign Function & Memory bindings for libgit2

License

Notifications You must be signed in to change notification settings

sernamar/jgit2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jgit2

Java Foreign Function & Memory bindings for libgit2.

Warning: This library is still in early development and subject to change. I’m still exploring Java FFM and learning how to interop with C libraries.

Only the main branch should be considered stable. Other branches are used for development or are experimental, and may have breaking changes.

Table of contents

Requirements

To use this library, you must have libgit2 installed on your system.

Make sure that the shared library (libgit2.so on Linux, libgit2.dylib on macOS, or git2.dll on Windows) is available in your library search path.

Installation

Since this library has not yet been published for distribution, you need to build and install it locally.

First, clone this repository or download the source code:

git clone https://github.com/sernamar/jgit2
cd jgit2

Then, build and install it using Maven:

mvn clean install

This will compile the project and install it into your local Maven repository, making it available for use in other projects.

Java bindings

Java bindings were generated using jextract by running the following command from the project root:

export LIBGIT2_HOME=/tmp/libgit2-1.9.0
export JEXTRACT_HOME=/tmp/jextract-22

${JEXTRACT_HOME}/bin/jextract \
      --output src/main/java \
      --target-package com.sernamar.jgit2.bindings \
      --header-class-name git2 \
      --library :${LIBGIT2_HOME}/build/libgit2.so \
      --include-dir ${LIBGIT2_HOME}/include \
      ${LIBGIT2_HOME}/include/git2.h

Usage examples

Initialize a repo, create commits, and log commit information

package com.sernamar.examples;

import com.sernamar.jgit2.*;
import com.sernamar.jgit2.utils.GitException;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

import static com.sernamar.jgit2.Commit.*;
import static com.sernamar.jgit2.Global.gitLibgit2Init;
import static com.sernamar.jgit2.Global.gitLibgit2Shutdown;
import static com.sernamar.jgit2.Index.*;
import static com.sernamar.jgit2.Message.gitMessagePrettify;
import static com.sernamar.jgit2.Oid.gitOidToString;
import static com.sernamar.jgit2.Refs.gitReferenceNameToId;
import static com.sernamar.jgit2.Repository.gitRepositoryIndex;
import static com.sernamar.jgit2.Repository.gitRepositoryInit;
import static com.sernamar.jgit2.Revwalk.gitRevwalkNext;
import static com.sernamar.jgit2.Revwalk.gitRevwalkPushHead;
import static com.sernamar.jgit2.Signature.*;
import static com.sernamar.jgit2.TestUtils.deleteRepoDirectory;
import static com.sernamar.jgit2.Tree.gitTreeLookup;

public class CreateCommits {
    private static final String PATH = "/tmp/repo";
    private static final String NAME = "sernamar";
    private static final String EMAIL = "[email protected]";

    private static void createCommit(GitRepository repo, GitTree tree, GitCommit parentCommit, String message) throws GitException {
        try (GitSignature signature = gitSignatureNow(NAME, EMAIL)) {
            if ((parentCommit == null)) {
                gitCommitCreateV(repo, "HEAD", signature, signature, null, gitMessagePrettify(message), tree);
            } else {
                gitCommitCreateV(repo, "HEAD", signature, signature, null, gitMessagePrettify(message), tree, parentCommit);
            }
        }
    }

    public static void main(String[] args) {
        gitLibgit2Init();
        try (GitRepository repo = gitRepositoryInit(PATH);
             GitIndex index = gitRepositoryIndex(repo)) {
            // Create initial commit
            GitOid treeId = gitIndexWriteTree(index);
            try (GitTree tree = gitTreeLookup(repo, treeId)) {
                createCommit(repo, tree, null, "Initial commit");
            }

            // Create hello.txt file and add it to the index
            String filename = "hello.txt";
            String content = "Hello, World!";
            try {
                Files.write(Paths.get(PATH, filename), content.getBytes());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            gitIndexAddByPath(index, filename);
            gitIndexWrite(index);

            // Create another commit
            treeId = gitIndexWriteTree(index);
            try (GitTree tree = gitTreeLookup(repo, treeId)) {
                GitOid referenceId = gitReferenceNameToId(repo, "HEAD");
                assert referenceId != null;
                try (GitCommit parentCommit = gitCommitLookup(repo, referenceId)) {
                    createCommit(repo, tree, parentCommit, "Add hello.txt");
                }
            }
	    
            // Iterate through the commits, printing their information
            try (GitRevwalk walk = Revwalk.gitRevwalkNew(repo)) {
                gitRevwalkPushHead(walk);
                GitOid commitId;
                while ((commitId = gitRevwalkNext(walk)) != null) {
                    try (GitCommit commit = gitCommitLookup(repo, commitId)) {
                        GitSignature author = gitCommitAuthor(commit);
                        GitSignature committer = gitCommitCommitter(commit);
                        long length = 40 + 1; // SHA1 hex size + 1 for null terminator
                        System.out.println("Commit ID: " + gitOidToString(commitId, length));
                        System.out.println("Author: " + gitSignatureName(author) + " <" + gitSignatureEmail(author) + ">");
                        System.out.println("AuthorDate: " + gitSignatureTime(author));
                        System.out.println("Committer: " + gitSignatureName(committer) + " <" + gitSignatureEmail(committer) + ">");
                        System.out.println("CommitDate: " + gitSignatureTime(committer));
                        System.out.println("Message: " + gitCommitMessage(commit));
                    }
                }
            }
        } catch (GitException e) {
            System.err.println(e.getMessage());
        } finally {
            deleteRepoDirectory(PATH);
        }
        gitLibgit2Shutdown();
    }
}

Create new branch and list existing branches

package com.sernamar.examples;

import com.sernamar.jgit2.*;
import com.sernamar.jgit2.utils.GitException;

import java.util.ArrayList;
import java.util.List;

import static com.sernamar.jgit2.Branch.gitBranchName;
import static com.sernamar.jgit2.Branch.gitBranchNext;
import static com.sernamar.jgit2.Commit.gitCommitCreateV;
import static com.sernamar.jgit2.Global.gitLibgit2Init;
import static com.sernamar.jgit2.Global.gitLibgit2Shutdown;
import static com.sernamar.jgit2.Index.gitIndexWriteTree;
import static com.sernamar.jgit2.Message.gitMessagePrettify;
import static com.sernamar.jgit2.Refs.gitReferenceNameToId;
import static com.sernamar.jgit2.Repository.gitRepositoryIndex;
import static com.sernamar.jgit2.Repository.gitRepositoryInit;
import static com.sernamar.jgit2.Signature.gitSignatureNow;
import static com.sernamar.jgit2.TestUtils.deleteRepoDirectory;
import static com.sernamar.jgit2.Tree.gitTreeLookup;

public class ListBranches {
    private static final String PATH = "/tmp/repo";
    private static final String NAME = "sernamar";
    private static final String EMAIL = "[email protected]";

    private static void createCommit(GitRepository repo, GitTree tree, GitCommit parentCommit, String message) throws GitException {
        try (GitSignature signature = gitSignatureNow(NAME, EMAIL)) {
            if ((parentCommit == null)) {
                gitCommitCreateV(repo, "HEAD", signature, signature, null, gitMessagePrettify(message), tree);
            } else {
                gitCommitCreateV(repo, "HEAD", signature, signature, null, gitMessagePrettify(message), tree, parentCommit);
            }
        }
    }

    public static void main(String[] args) {
        gitLibgit2Init();

        try (GitRepository repo = gitRepositoryInit(PATH);
             GitIndex index = gitRepositoryIndex(repo)) {
            // Create initial commit
            GitOid treeId = gitIndexWriteTree(index);
            try (GitTree tree = gitTreeLookup(repo, treeId)) {
                createCommit(repo, tree, null, "Initial commit");
            }

            // Create new branch
            String branchName = "new_branch";
            GitOid commitId = gitReferenceNameToId(repo, "HEAD");
            assert commitId != null;
            try (GitCommit commit = Commit.gitCommitLookup(repo, commitId);
                 GitReference branchRef = Branch.gitBranchCreate(repo, branchName, commit)) {
                System.out.println("Branch created: " + branchName);
            }

            // List branches
            List<String> branches = new ArrayList<>();
            try (GitBranchIterator branchIterator = Branch.gitBranchIteratorNew(repo, GitBranchT.LOCAL)) {
                GitReference branchRef;
                while ((branchRef = gitBranchNext(GitBranchT.LOCAL, branchIterator)) != null) {
                    branches.add(gitBranchName(branchRef));
                }
                System.out.println("Branches: " + branches);

            }
        } catch (GitException e) {
            System.err.println(e.getMessage());
        } finally {
            deleteRepoDirectory(PATH);
        }

        gitLibgit2Shutdown();
    }
}

Implementation progress

This section tracks the implementation of Java bindings for libgit2 functions, as outlined in the official libgit2 documentation.

The first level represents the major functional groups from libgit2, and the second level lists the specific functions within each group.

Legend:

  • [X] Implemented.
  • [ ] Pending.
  • [-] Partially implemented.
  • Functions marked with (adapted) are implemented and functional, but do not exactly follow the native libgit2 signature. Full signature support may be added in the future.

Below is the current progress on the implementation:

  • [ ] annotated_commit
  • [ ] apply
  • [ ] attr
  • [ ] blame
  • [ ] blob
  • [-] branch
    • [X] git_branch_create
    • [ ] git_branch_create_from_annotated
    • [X] git_branch_delete
    • [X] git_branch_iterator_new
    • [X] git_branch_next
    • [ ] git_branch_iterator_free
    • [ ] git_branch_move
    • [ ] git_branch_lookup
    • [X] git_branch_name
    • [ ] git_branch_upstream
    • [ ] git_branch_set_upstream
    • [ ] git_branch_upstream_name
    • [ ] git_branch_is_head
    • [ ] git_branch_is_checked_out
    • [ ] git_branch_remote_name
    • [ ] git_branch_upstream_remote
    • [ ] git_branch_upstream_merge
    • [ ] git_branch_name_is_valid
  • [ ] buffer
  • [ ] cert
  • [ ] checkout
  • [ ] cherrypick
  • [-] clone
    • [ ] git_clone_options_init
    • [X] git_clone (adapted)
  • [-] commit
    • [X] git_commit_lookup
    • [ ] git_commit_lookup_prefix
    • [ ] git_commit_free
    • [ ] git_commit_id
    • [ ] git_commit_owner
    • [ ] git_commit_message_encoding
    • [X] git_commit_message
    • [ ] git_commit_message_raw
    • [ ] git_commit_summary
    • [ ] git_commit_body
    • [ ] git_commit_time
    • [ ] git_commit_time_offset
    • [ ] git_commit_committer
    • [X] git_commit_author
    • [ ] git_commit_committer_with_mailmap
    • [ ] git_commit_author_with_mailmap
    • [ ] git_commit_raw_header
    • [ ] git_commit_tree
    • [ ] git_commit_tree_id
    • [ ] git_commit_parentcount
    • [ ] git_commit_parent
    • [ ] git_commit_parent_id
    • [ ] git_commit_nth_gen_ancestor
    • [ ] git_commit_header_field
    • [ ] git_commit_extract_signature
    • [ ] git_commit_create
    • [X] git_commit_create_v
    • [ ] git_commit_create_from_stage
    • [ ] git_commit_amend
    • [ ] git_commit_create_buffer
    • [ ] git_commit_create_with_signature
    • [ ] git_commit_dup
    • [ ] git_commitarray_dispose
  • [-] common
    • [X] git_libgit2_version
    • [ ] git_libgit2_prerelease
    • [ ] git_libgit2_features
    • [ ] git_libgit2_feature_backend
    • [ ] git_libgit2_opts
    • [ ] git_libgit2_buildinfo
  • [ ] config
  • [ ] credential
  • [ ] credential_helpers
  • [ ] deprecated
  • [ ] describe
  • [ ] diff
  • [ ] email
  • [ ] errors
  • [ ] filter
  • [X] global
    • [X] git_libgit2_init
    • [X] git_libgit2_shutdown
  • [ ] graph
  • [ ] ignore
  • [-] index
    • [ ] git_index_options_init
    • [ ] git_index_open
    • [ ] git_index_new
    • [ ] git_index_free
    • [ ] git_index_owner
    • [ ] git_index_caps
    • [ ] git_index_set_caps
    • [ ] git_index_version
    • [ ] git_index_set_version
    • [ ] git_index_read
    • [X] git_index_write
    • [ ] git_index_path
    • [ ] git_index_checksum
    • [ ] git_index_read_tree
    • [X] git_index_write_tree
    • [ ] git_index_write_tree_to
    • [ ] git_index_entrycount
    • [ ] git_index_clear
    • [ ] git_index_get_byindex
    • [ ] git_index_get_bypath
    • [ ] git_index_remove
    • [ ] git_index_remove_directory
    • [ ] git_index_add
    • [ ] git_index_entry_stage
    • [ ] git_index_entry_is_conflict
    • [ ] git_index_iterator_new
    • [ ] git_index_iterator_next
    • [ ] git_index_iterator_free
    • [X] git_index_add_bypath
    • [ ] git_index_add_from_buffer
    • [ ] git_index_remove_bypath
    • [ ] git_index_add_all
    • [ ] git_index_remove_all
    • [ ] git_index_update_all
    • [ ] git_index_find
    • [ ] git_index_find_prefix
    • [ ] git_index_conflict_add
    • [ ] git_index_conflict_get
    • [ ] git_index_conflict_remove
    • [ ] git_index_conflict_cleanup
    • [ ] git_index_has_conflicts
    • [ ] git_index_conflict_iterator_new
    • [ ] git_index_conflict_next
    • [ ] git_index_conflict_iterator_free
  • [ ] indexer
  • [ ] mailmap
  • [ ] merge
  • [-] message
    • [X] git_message_prettify
    • [ ] git_message_trailers
    • [ ] git_message_trailer_array_free
  • [ ] net
  • [ ] notes
  • [ ] object
  • [ ] odb
  • [ ] odb_backend
  • [-] oid
    • [X] git_oid_fromstr
    • [ ] git_oid_fromstrp
    • [ ] git_oid_fromstrn
    • [ ] git_oid_fromraw
    • [ ] git_oid_fmt
    • [ ] git_oid_nfmt
    • [ ] git_oid_pathfmt
    • [ ] git_oid_tostr_s
    • [X] git_oid_tostr
    • [ ] git_oid_cpy
    • [ ] git_oid_cmp
    • [ ] git_oid_equal
    • [ ] git_oid_ncmp
    • [ ] git_oid_streq
    • [ ] git_oid_strcmp
    • [ ] git_oid_is_zero
    • [X] git_oid_shorten_new
    • [X] git_oid_shorten_add
    • [ ] git_oid_shorten_free
  • [ ] oidarray
  • [ ] pack
  • [ ] patch
  • [ ] pathspec
  • [ ] proxy
  • [ ] rebase
  • [ ] refdb
  • [ ] reflog
  • [-] refs
    • [ ] git_reference_lookup
    • [X] git_reference_name_to_id
    • [ ] git_reference_dwim
    • [ ] git_reference_symbolic_create_matching
    • [ ] git_reference_symbolic_create
    • [ ] git_reference_create
    • [ ] git_reference_create_matching
    • [ ] git_reference_target
    • [ ] git_reference_target_peel
    • [ ] git_reference_symbolic_target
    • [ ] git_reference_type
    • [ ] git_reference_name
    • [ ] git_reference_resolve
    • [ ] git_reference_owner
    • [ ] git_reference_symbolic_set_target
    • [ ] git_reference_set_target
    • [ ] git_reference_rename
    • [ ] git_reference_delete
    • [ ] git_reference_remove
    • [ ] git_reference_list
    • [ ] git_reference_foreach
    • [ ] git_reference_foreach_name
    • [ ] git_reference_dup
    • [ ] git_reference_free
    • [ ] git_reference_cmp
    • [ ] git_reference_iterator_new
    • [ ] git_reference_iterator_glob_new
    • [ ] git_reference_next
    • [ ] git_reference_next_name
    • [ ] git_reference_iterator_free
    • [ ] git_reference_foreach_glob
    • [ ] git_reference_has_log
    • [ ] git_reference_ensure_log
    • [ ] git_reference_is_branch
    • [ ] git_reference_is_remote
    • [ ] git_reference_is_tag
    • [ ] git_reference_is_note
    • [ ] git_reference_normalize_name
    • [ ] git_reference_peel
    • [ ] git_reference_name_is_valid
    • [ ] git_reference_shorthand
  • [ ] refspec
  • [ ] remote
  • [-] repository
    • [X] git_repository_open
    • [ ] git_repository_open_from_worktree
    • [ ] git_repository_wrap_odb
    • [ ] git_repository_discover
    • [ ] git_repository_open_ext
    • [ ] git_repository_open_bare
    • [ ] git_repository_free
    • [X] git_repository_init
    • [ ] git_repository_init_options_init
    • [ ] git_repository_init_ext
    • [ ] git_repository_head
    • [ ] git_repository_head_for_worktree
    • [ ] git_repository_head_detached
    • [ ] git_repository_head_detached_for_worktree
    • [ ] git_repository_head_unborn
    • [ ] git_repository_is_empty
    • [ ] git_repository_item_path
    • [ ] git_repository_path
    • [ ] git_repository_workdir
    • [ ] git_repository_commondir
    • [ ] git_repository_set_workdir
    • [ ] git_repository_is_bare
    • [ ] git_repository_is_worktree
    • [ ] git_repository_config
    • [ ] git_repository_config_snapshot
    • [ ] git_repository_odb
    • [ ] git_repository_refdb
    • [X] git_repository_index
    • [ ] git_repository_message
    • [ ] git_repository_message_remove
    • [ ] git_repository_state_cleanup
    • [ ] git_repository_fetchhead_foreach
    • [ ] git_repository_mergehead_foreach
    • [ ] git_repository_hashfile
    • [ ] git_repository_set_head
    • [ ] git_repository_set_head_detached
    • [ ] git_repository_set_head_detached_from_annotated
    • [ ] git_repository_detach_head
    • [ ] git_repository_state
    • [ ] git_repository_set_namespace
    • [ ] git_repository_get_namespace
    • [ ] git_repository_is_shallow
    • [ ] git_repository_ident
    • [ ] git_repository_set_ident
    • [ ] git_repository_oid_type
    • [ ] git_repository_commit_parents
  • [ ] reset
  • [ ] revert
  • [ ] revparse
  • [-] revwalk
    • [X] git_revwalk_new
    • [ ] git_revwalk_reset
    • [ ] git_revwalk_push
    • [ ] git_revwalk_push_glob
    • [X] git_revwalk_push_head
    • [ ] git_revwalk_hide
    • [ ] git_revwalk_hide_glob
    • [ ] git_revwalk_hide_head
    • [ ] git_revwalk_push_ref
    • [ ] git_revwalk_hide_ref
    • [X] git_revwalk_next
    • [X] git_revwalk_sorting
    • [ ] git_revwalk_push_range
    • [ ] git_revwalk_simplify_first_parent
    • [ ] git_revwalk_free
    • [ ] git_revwalk_repository
    • [ ] git_revwalk_add_hide_cb
  • [ ] revert
  • [-] signature
    • [ ] git_signature_new
    • [X] git_signature_now
    • [ ] git_signature_default_from_env
    • [ ] git_signature_default
    • [ ] git_signature_from_buffer
    • [ ] git_signature_dup
    • [ ] git_signature_free
  • [ ] status
  • [ ] strarray
  • [ ] submodule
  • [ ] tag
  • [ ] trace
  • [ ] transaction
  • [ ] transport
  • [-] tree
    • [X] git_tree_lookup
    • [ ] git_tree_lookup_prefix
    • [ ] git_tree_free
    • [ ] git_tree_id
    • [ ] git_tree_owner
    • [ ] git_tree_entrycount
    • [ ] git_tree_entry_byname
    • [ ] git_tree_entry_byindex
    • [ ] git_tree_entry_byid
    • [ ] git_tree_entry_bypath
    • [ ] git_tree_entry_dup
    • [ ] git_tree_entry_free
    • [ ] git_tree_entry_name
    • [ ] git_tree_entry_id
    • [ ] git_tree_entry_type
    • [ ] git_tree_entry_filemode
    • [ ] git_tree_entry_filemode_raw
    • [ ] git_tree_entry_cmp
    • [ ] git_tree_entry_to_object
    • [ ] git_treebuilder_new
    • [ ] git_treebuilder_clear
    • [ ] git_treebuilder_entrycount
    • [ ] git_treebuilder_free
    • [ ] git_treebuilder_get
    • [ ] git_treebuilder_insert
    • [ ] git_treebuilder_remove
    • [ ] git_treebuilder_filter
    • [ ] git_treebuilder_write
    • [ ] git_tree_walk
    • [ ] git_tree_dup
    • [ ] git_tree_create_updated
  • [ ] types
  • [ ] version
  • [ ] worktree

License

Copyright © 2025 Sergio Navarro

Distributed under the MIT License.

About

Java Foreign Function & Memory bindings for libgit2

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages