// libgit2 バインディング
// 不透明な外部型(C FFI用)
///|
#external
type GitRepository
///|
#external
priv type GitReference
///|
#external
priv type GitRevwalk
///|
#external
priv type GitCommit
///|
#external
priv type GitOidHolder
// ===== UTF-8エンコーディングヘルパー =====
// Using @encoding from moonbitlang/x/encoding
///|
fn to_c_string(s : String) -> Bytes {
@encoding.encode(UTF8, s)
}
///|
fn from_c_bytes(bytes : Bytes) -> String {
@encoding.decoder(UTF8).decode_lossy(bytes[:])
}
// ===== Low-level extern declarations =====
///|
extern "C" fn git2_init() -> Int = "git2_init"
///|
extern "C" fn git2_shutdown() -> Int = "git2_shutdown"
///|
#borrow(path)
extern "C" fn git2_repository_open(path : Bytes) -> GitRepository = "git2_repository_open"
///|
#borrow(repo)
extern "C" fn git2_repository_free(repo : GitRepository) -> Unit = "git2_repository_free"
///|
#borrow(repo)
extern "C" fn git2_repository_is_null(repo : GitRepository) -> Int = "git2_repository_is_null"
///|
#borrow(repo)
extern "C" fn git2_repository_path(repo : GitRepository) -> Bytes = "git2_repository_path"
///|
#borrow(repo)
extern "C" fn git2_repository_head(repo : GitRepository) -> GitReference = "git2_repository_head"
///|
#borrow(reference)
extern "C" fn git2_reference_free(reference : GitReference) -> Unit = "git2_reference_free"
///|
#borrow(reference)
extern "C" fn git2_reference_is_null(reference : GitReference) -> Int = "git2_reference_is_null"
///|
#borrow(reference)
extern "C" fn git2_reference_name(reference : GitReference) -> Bytes = "git2_reference_name"
///|
#borrow(reference)
extern "C" fn git2_reference_target_str(reference : GitReference) -> Bytes = "git2_reference_target_str"
///|
#borrow(repo)
extern "C" fn git2_revwalk_new(repo : GitRepository) -> GitRevwalk = "git2_revwalk_new"
///|
#borrow(walk)
extern "C" fn git2_revwalk_free(walk : GitRevwalk) -> Unit = "git2_revwalk_free"
///|
#borrow(walk)
extern "C" fn git2_revwalk_is_null(walk : GitRevwalk) -> Int = "git2_revwalk_is_null"
///|
#borrow(walk)
extern "C" fn git2_revwalk_push_head(walk : GitRevwalk) -> Int = "git2_revwalk_push_head"
///|
#borrow(walk)
extern "C" fn git2_revwalk_sorting(walk : GitRevwalk, sort_mode : Int) -> Int = "git2_revwalk_sorting"
///|
extern "C" fn git2_oid_holder_new() -> GitOidHolder = "git2_oid_holder_new"
///|
#borrow(holder)
extern "C" fn git2_oid_holder_free(holder : GitOidHolder) -> Unit = "git2_oid_holder_free"
///|
#borrow(walk, holder)
extern "C" fn git2_revwalk_next(
walk : GitRevwalk,
holder : GitOidHolder,
) -> Int = "git2_revwalk_next"
///|
#borrow(repo, holder)
extern "C" fn git2_commit_lookup(
repo : GitRepository,
holder : GitOidHolder,
) -> GitCommit = "git2_commit_lookup"
///|
#borrow(commit)
extern "C" fn git2_commit_free(commit : GitCommit) -> Unit = "git2_commit_free"
///|
#borrow(commit)
extern "C" fn git2_commit_is_null(commit : GitCommit) -> Int = "git2_commit_is_null"
///|
#borrow(commit)
extern "C" fn git2_commit_summary(commit : GitCommit) -> Bytes = "git2_commit_summary"
///|
#borrow(commit)
extern "C" fn git2_commit_id_str(commit : GitCommit) -> Bytes = "git2_commit_id_str"
///|
#borrow(commit)
extern "C" fn git2_commit_time(commit : GitCommit) -> Int64 = "git2_commit_time"
///|
#borrow(commit)
extern "C" fn git2_commit_author_name(commit : GitCommit) -> Bytes = "git2_commit_author_name"
///|
#borrow(commit)
extern "C" fn git2_commit_author_email(commit : GitCommit) -> Bytes = "git2_commit_author_email"
///|
#borrow(commit)
extern "C" fn git2_commit_committer_name(commit : GitCommit) -> Bytes = "git2_commit_committer_name"
///|
#borrow(commit)
extern "C" fn git2_commit_committer_email(commit : GitCommit) -> Bytes = "git2_commit_committer_email"
///|
#borrow(commit)
extern "C" fn git2_commit_message(commit : GitCommit) -> Bytes = "git2_commit_message"
///|
extern "C" fn git2_error_last_message() -> Bytes = "git2_error_last_message"
///|
extern "C" fn git2_is_loaded() -> Int = "git2_is_loaded"
// ===== Branch iterator =====
///|
#external
priv type GitBranchIterator
///|
#borrow(repo)
extern "C" fn git2_branch_iterator_new(
repo : GitRepository,
flags : Int,
) -> GitBranchIterator = "git2_branch_iterator_new"
///|
#borrow(iter)
extern "C" fn git2_branch_iterator_free(iter : GitBranchIterator) -> Unit = "git2_branch_iterator_free"
///|
#borrow(iter)
extern "C" fn git2_branch_iterator_is_null(iter : GitBranchIterator) -> Int = "git2_branch_iterator_is_null"
///|
#borrow(iter)
extern "C" fn git2_branch_next(iter : GitBranchIterator) -> Int = "git2_branch_next"
///|
#borrow(iter)
extern "C" fn git2_branch_name(iter : GitBranchIterator) -> Bytes = "git2_branch_name"
///|
#borrow(iter)
extern "C" fn git2_branch_type(iter : GitBranchIterator) -> Int = "git2_branch_type"
// ===== Diff =====
///|
#external
priv type GitDiff
///|
#external
priv type GitDiffStats
///|
#borrow(repo)
extern "C" fn git2_diff_index_to_workdir(repo : GitRepository) -> GitDiff = "git2_diff_index_to_workdir"
///|
#borrow(diff)
extern "C" fn git2_diff_free(diff : GitDiff) -> Unit = "git2_diff_free"
///|
#borrow(diff)
extern "C" fn git2_diff_is_null(diff : GitDiff) -> Int = "git2_diff_is_null"
///|
#borrow(diff)
extern "C" fn git2_diff_num_deltas(diff : GitDiff) -> Int = "git2_diff_num_deltas"
///|
#borrow(diff)
extern "C" fn git2_diff_stats(diff : GitDiff) -> GitDiffStats = "git2_diff_stats"
///|
#borrow(stats)
extern "C" fn git2_diff_stats_free(stats : GitDiffStats) -> Unit = "git2_diff_stats_free"
///|
#borrow(stats)
extern "C" fn git2_diff_stats_is_null(stats : GitDiffStats) -> Int = "git2_diff_stats_is_null"
///|
#borrow(stats)
extern "C" fn git2_diff_stats_files_changed(stats : GitDiffStats) -> Int = "git2_diff_stats_files_changed"
///|
#borrow(stats)
extern "C" fn git2_diff_stats_insertions(stats : GitDiffStats) -> Int = "git2_diff_stats_insertions"
///|
#borrow(stats)
extern "C" fn git2_diff_stats_deletions(stats : GitDiffStats) -> Int = "git2_diff_stats_deletions"
// ===== Status =====
///|
#external
priv type GitStatusIterator
///|
#borrow(repo)
extern "C" fn git2_status_new(repo : GitRepository) -> GitStatusIterator = "git2_status_new"
///|
#borrow(iter)
extern "C" fn git2_status_free(iter : GitStatusIterator) -> Unit = "git2_status_free"
///|
#borrow(iter)
extern "C" fn git2_status_is_null(iter : GitStatusIterator) -> Int = "git2_status_is_null"
///|
#borrow(iter)
extern "C" fn git2_status_count(iter : GitStatusIterator) -> Int = "git2_status_count"
///|
#borrow(iter)
extern "C" fn git2_status_next(iter : GitStatusIterator) -> Int = "git2_status_next"
///|
#borrow(iter)
extern "C" fn git2_status_advance(iter : GitStatusIterator) -> Unit = "git2_status_advance"
///|
#borrow(iter)
extern "C" fn git2_status_entry_flags(iter : GitStatusIterator) -> Int = "git2_status_entry_flags"
// ===== High-level API =====
// Sort modes for revwalk
///|
pub const GIT_SORT_NONE : Int = 0
///|
pub const GIT_SORT_TOPOLOGICAL : Int = 1
///|
pub const GIT_SORT_TIME : Int = 2
///|
pub const GIT_SORT_REVERSE : Int = 4
///|
/// ライブラリがロードされているかチェック
pub fn is_loaded() -> Bool {
git2_is_loaded() == 1
}
///|
/// ライブラリの初期化(ライブラリのロードも行う)
/// 戻り値: 成功時はtrue、失敗時はfalse
pub fn git_init() -> Bool {
let rc = git2_init()
rc >= 0
}
///|
/// ライブラリのシャットダウン
pub fn git_shutdown() -> Unit {
let _ = git2_shutdown()
}
///|
/// 最後のエラーメッセージを取得
pub fn last_error() -> String {
from_c_bytes(git2_error_last_message())
}
///|
/// リポジトリ
pub struct Repository {
priv handle : GitRepository
}
///|
#borrow(path)
extern "C" fn git2_repository_init(
path : Bytes,
is_bare : Int,
) -> GitRepository = "git2_repository_init"
///|
/// リポジトリを開く
pub fn Repository::open(path : String) -> Repository? {
let repo = git2_repository_open(to_c_string(path))
if git2_repository_is_null(repo) == 1 {
None
} else {
Some({ handle: repo })
}
}
///|
/// 新しいリポジトリを初期化
pub fn Repository::init(path : String) -> Repository? {
let repo = git2_repository_init(to_c_string(path), 0)
if git2_repository_is_null(repo) == 1 {
None
} else {
Some({ handle: repo })
}
}
///|
/// リポジトリを閉じる
pub fn Repository::close(self : Repository) -> Unit {
git2_repository_free(self.handle)
}
///|
/// リポジトリのパスを取得
pub fn Repository::path(self : Repository) -> String {
from_c_bytes(git2_repository_path(self.handle))
}
///|
/// HEADの参照名を取得
pub fn Repository::head_name(self : Repository) -> String? {
let head_ref = git2_repository_head(self.handle)
if git2_reference_is_null(head_ref) == 1 {
None
} else {
let name = from_c_bytes(git2_reference_name(head_ref))
git2_reference_free(head_ref)
Some(name)
}
}
///|
/// HEADのコミットIDを取得
pub fn Repository::head_id(self : Repository) -> String? {
let head_ref = git2_repository_head(self.handle)
if git2_reference_is_null(head_ref) == 1 {
None
} else {
let id = from_c_bytes(git2_reference_target_str(head_ref))
git2_reference_free(head_ref)
Some(id)
}
}
///|
/// コミット情報
pub struct CommitInfo {
id : String
message : String
summary : String
author_name : String
author_email : String
committer_name : String
committer_email : String
time : Int64
}
///|
pub fn CommitInfo::short_id(self : CommitInfo) -> String {
let buf = StringBuilder::new()
let len = if self.id.length() < 7 { self.id.length() } else { 7 }
for i = 0; i < len; i = i + 1 {
buf.write_char(self.id.code_unit_at(i).to_int().unsafe_to_char())
}
buf.to_string()
}
///|
pub fn CommitInfo::to_string(self : CommitInfo) -> String {
let short_id = self.short_id()
"\{short_id} \{self.summary} <\{self.author_name}>"
}
// ===== Branch API =====
///|
/// ブランチタイプ
pub enum BranchType {
Local
Remote
}
///|
/// ブランチ情報
pub struct BranchInfo {
name : String
branch_type : BranchType
}
///|
pub fn BranchInfo::to_string(self : BranchInfo) -> String {
let type_str = match self.branch_type {
Local => "local"
Remote => "remote"
}
"\{self.name} (\{type_str})"
}
///|
/// ブランチ一覧を取得
pub fn Repository::branches(
self : Repository,
include_remote : Bool,
) -> Array[BranchInfo] {
let branches : Array[BranchInfo] = []
let flags = if include_remote { 3 } else { 1 } // GIT_BRANCH_ALL or GIT_BRANCH_LOCAL
let iter = git2_branch_iterator_new(self.handle, flags)
if git2_branch_iterator_is_null(iter) == 1 {
return branches
}
while git2_branch_next(iter) == 0 {
let name = from_c_bytes(git2_branch_name(iter))
let branch_type : BranchType = if git2_branch_type(iter) == 1 {
Local
} else {
Remote
}
branches.push({ name, branch_type })
}
git2_branch_iterator_free(iter)
branches
}
// ===== Diff API =====
///|
/// 差分統計
pub struct DiffStats {
files_changed : Int
insertions : Int
deletions : Int
}
///|
pub fn DiffStats::to_string(self : DiffStats) -> String {
"\{self.files_changed} files changed, \{self.insertions} insertions(+), \{self.deletions} deletions(-)"
}
///|
/// ワーキングツリーの差分統計を取得
pub fn Repository::diff_stats(self : Repository) -> DiffStats? {
let diff = git2_diff_index_to_workdir(self.handle)
if git2_diff_is_null(diff) == 1 {
return None
}
let stats = git2_diff_stats(diff)
if git2_diff_stats_is_null(stats) == 1 {
git2_diff_free(diff)
return None
}
let result : DiffStats = {
files_changed: git2_diff_stats_files_changed(stats),
insertions: git2_diff_stats_insertions(stats),
deletions: git2_diff_stats_deletions(stats),
}
git2_diff_stats_free(stats)
git2_diff_free(diff)
Some(result)
}
///|
/// ワーキングツリーの変更ファイル数を取得
pub fn Repository::diff_count(self : Repository) -> Int {
let diff = git2_diff_index_to_workdir(self.handle)
if git2_diff_is_null(diff) == 1 {
return 0
}
let count = git2_diff_num_deltas(diff)
git2_diff_free(diff)
count
}
// ===== Status API =====
// Status flags (from libgit2)
///|
pub const GIT_STATUS_CURRENT : Int = 0
///|
pub const GIT_STATUS_INDEX_NEW : Int = 1
///|
pub const GIT_STATUS_INDEX_MODIFIED : Int = 2
///|
pub const GIT_STATUS_INDEX_DELETED : Int = 4
///|
pub const GIT_STATUS_WT_NEW : Int = 128
///|
pub const GIT_STATUS_WT_MODIFIED : Int = 256
///|
pub const GIT_STATUS_WT_DELETED : Int = 512
///|
/// ステータスフラグ
pub struct StatusFlags {
flags : Int
}
///|
pub fn StatusFlags::new(flags : Int) -> StatusFlags {
{ flags, }
}
///|
pub fn StatusFlags::is_new(self : StatusFlags) -> Bool {
(self.flags & GIT_STATUS_WT_NEW) != 0
}
///|
pub fn StatusFlags::is_modified(self : StatusFlags) -> Bool {
(self.flags & GIT_STATUS_WT_MODIFIED) != 0
}
///|
pub fn StatusFlags::is_deleted(self : StatusFlags) -> Bool {
(self.flags & GIT_STATUS_WT_DELETED) != 0
}
///|
pub fn StatusFlags::is_staged(self : StatusFlags) -> Bool {
(self.flags & 0x1F) != 0 // Any INDEX_* flag
}
///|
pub fn StatusFlags::to_string(self : StatusFlags) -> String {
let parts : Array[String] = []
if self.is_new() {
parts.push("new")
}
if self.is_modified() {
parts.push("modified")
}
if self.is_deleted() {
parts.push("deleted")
}
if self.is_staged() {
parts.push("staged")
}
if parts.length() == 0 {
"clean"
} else {
parts
.iter()
.fold(init="", fn(acc, s) { if acc == "" { s } else { acc + ", " + s } })
}
}
///|
/// ワーキングツリーのステータス(変更ファイル数)
pub fn Repository::status_count(self : Repository) -> Int {
let iter = git2_status_new(self.handle)
if git2_status_is_null(iter) == 1 {
return 0
}
let count = git2_status_count(iter)
git2_status_free(iter)
count
}
///|
/// ワーキングツリーのステータスフラグ一覧を取得
pub fn Repository::status_flags(self : Repository) -> Array[StatusFlags] {
let result : Array[StatusFlags] = []
let iter = git2_status_new(self.handle)
if git2_status_is_null(iter) == 1 {
return result
}
while git2_status_next(iter) == 0 {
let flags = git2_status_entry_flags(iter)
result.push(StatusFlags::new(flags))
git2_status_advance(iter)
}
git2_status_free(iter)
result
}
// ===== Tree API =====
///|
#external
type GitTree
///|
#borrow(commit)
extern "C" fn git2_commit_tree(commit : GitCommit) -> GitTree = "git2_commit_tree"
///|
#borrow(tree)
extern "C" fn git2_tree_free(tree : GitTree) -> Unit = "git2_tree_free"
///|
#borrow(tree)
extern "C" fn git2_tree_is_null(tree : GitTree) -> Int = "git2_tree_is_null"
///|
#borrow(tree)
extern "C" fn git2_tree_entrycount(tree : GitTree) -> Int = "git2_tree_entrycount"
///|
#borrow(tree)
extern "C" fn git2_tree_entry_name_at(tree : GitTree, idx : Int) -> Bytes = "git2_tree_entry_name_at"
///|
#borrow(tree)
extern "C" fn git2_tree_entry_type_at(tree : GitTree, idx : Int) -> Int = "git2_tree_entry_type_at"
///|
#borrow(tree)
extern "C" fn git2_tree_entry_id_at(tree : GitTree, idx : Int) -> Bytes = "git2_tree_entry_id_at"
///|
/// オブジェクトタイプ
pub enum ObjectType {
Blob
Tree
Commit
Tag
Unknown
}
///|
/// ツリーエントリ情報
pub struct TreeEntry {
name : String
id : String
object_type : ObjectType
}
///|
pub fn TreeEntry::to_string(self : TreeEntry) -> String {
let type_str = match self.object_type {
Blob => "blob"
Tree => "tree"
Commit => "commit"
Tag => "tag"
Unknown => "unknown"
}
"\{type_str} \{self.id} \{self.name}"
}
///|
/// ツリー
pub struct Tree {
priv handle : GitTree
}
///|
pub fn Tree::entry_count(self : Tree) -> Int {
git2_tree_entrycount(self.handle)
}
///|
pub fn Tree::entries(self : Tree) -> Array[TreeEntry] {
let result : Array[TreeEntry] = []
let count = git2_tree_entrycount(self.handle)
for i = 0; i < count; i = i + 1 {
let name = from_c_bytes(git2_tree_entry_name_at(self.handle, i))
let id = from_c_bytes(git2_tree_entry_id_at(self.handle, i))
let type_int = git2_tree_entry_type_at(self.handle, i)
// libgit2 git_object_t values: COMMIT=1, TREE=2, BLOB=3, TAG=4
let object_type : ObjectType = match type_int {
1 => Commit
2 => Tree
3 => Blob
4 => Tag
_ => Unknown
}
result.push({ name, id, object_type })
}
result
}
///|
pub fn Tree::close(self : Tree) -> Unit {
git2_tree_free(self.handle)
}
///|
/// コミットからツリーを取得する内部関数
fn get_tree_from_commit(commit : GitCommit) -> Tree? {
let tree = git2_commit_tree(commit)
if git2_tree_is_null(tree) == 1 {
None
} else {
Some({ handle: tree })
}
}
// ===== Tag API =====
///|
#external
priv type GitTagList
///|
#borrow(repo)
extern "C" fn git2_tag_list(repo : GitRepository) -> GitTagList = "git2_tag_list"
///|
#borrow(list)
extern "C" fn git2_tag_list_free(list : GitTagList) -> Unit = "git2_tag_list_free"
///|
#borrow(list)
extern "C" fn git2_tag_list_is_null(list : GitTagList) -> Int = "git2_tag_list_is_null"
///|
#borrow(list)
extern "C" fn git2_tag_list_count(list : GitTagList) -> Int = "git2_tag_list_count"
///|
#borrow(list)
extern "C" fn git2_tag_list_name_at(list : GitTagList, idx : Int) -> Bytes = "git2_tag_list_name_at"
///|
#borrow(repo)
extern "C" fn git2_tag_count(repo : GitRepository) -> Int = "git2_tag_count"
///|
/// タグ一覧を取得
pub fn Repository::tags(self : Repository) -> Array[String] {
let result : Array[String] = []
let list = git2_tag_list(self.handle)
if git2_tag_list_is_null(list) == 1 {
return result
}
let count = git2_tag_list_count(list)
for i = 0; i < count; i = i + 1 {
let name = from_c_bytes(git2_tag_list_name_at(list, i))
result.push(name)
}
git2_tag_list_free(list)
result
}
///|
/// タグ数を取得
pub fn Repository::tag_count(self : Repository) -> Int {
git2_tag_count(self.handle)
}
// ===== Remote API =====
///|
#external
priv type GitRemoteList
///|
#external
priv type GitRemote
///|
#borrow(repo)
extern "C" fn git2_remote_list(repo : GitRepository) -> GitRemoteList = "git2_remote_list"
///|
#borrow(list)
extern "C" fn git2_remote_list_free(list : GitRemoteList) -> Unit = "git2_remote_list_free"
///|
#borrow(list)
extern "C" fn git2_remote_list_is_null(list : GitRemoteList) -> Int = "git2_remote_list_is_null"
///|
#borrow(list)
extern "C" fn git2_remote_list_count(list : GitRemoteList) -> Int = "git2_remote_list_count"
///|
#borrow(list)
extern "C" fn git2_remote_list_name_at(
list : GitRemoteList,
idx : Int,
) -> Bytes = "git2_remote_list_name_at"
///|
#borrow(repo, name)
extern "C" fn git2_remote_lookup(
repo : GitRepository,
name : Bytes,
) -> GitRemote = "git2_remote_lookup"
///|
#borrow(remote)
extern "C" fn git2_remote_free(remote : GitRemote) -> Unit = "git2_remote_free"
///|
#borrow(remote)
extern "C" fn git2_remote_is_null(remote : GitRemote) -> Int = "git2_remote_is_null"
///|
#borrow(remote)
extern "C" fn git2_remote_name(remote : GitRemote) -> Bytes = "git2_remote_name"
///|
#borrow(remote)
extern "C" fn git2_remote_url(remote : GitRemote) -> Bytes = "git2_remote_url"
///|
#borrow(remote)
extern "C" fn git2_remote_pushurl(remote : GitRemote) -> Bytes = "git2_remote_pushurl"
///|
/// リモート情報
pub struct RemoteInfo {
name : String
url : String
push_url : String
}
///|
pub fn RemoteInfo::to_string(self : RemoteInfo) -> String {
"\{self.name} \{self.url}"
}
///|
/// リモート名一覧を取得
pub fn Repository::remote_names(self : Repository) -> Array[String] {
let result : Array[String] = []
let list = git2_remote_list(self.handle)
if git2_remote_list_is_null(list) == 1 {
return result
}
let count = git2_remote_list_count(list)
for i = 0; i < count; i = i + 1 {
let name = from_c_bytes(git2_remote_list_name_at(list, i))
result.push(name)
}
git2_remote_list_free(list)
result
}
///|
/// リモート情報を取得
pub fn Repository::remote(self : Repository, name : String) -> RemoteInfo? {
let remote = git2_remote_lookup(self.handle, to_c_string(name))
if git2_remote_is_null(remote) == 1 {
return None
}
let result : RemoteInfo = {
name: from_c_bytes(git2_remote_name(remote)),
url: from_c_bytes(git2_remote_url(remote)),
push_url: from_c_bytes(git2_remote_pushurl(remote)),
}
git2_remote_free(remote)
Some(result)
}
///|
/// すべてのリモート情報を取得
pub fn Repository::remotes(self : Repository) -> Array[RemoteInfo] {
let result : Array[RemoteInfo] = []
let names = self.remote_names()
for name in names {
match self.remote(name) {
Some(info) => result.push(info)
None => ()
}
}
result
}
// ===== Config API =====
///|
#borrow(repo, name)
extern "C" fn git2_config_get_string(
repo : GitRepository,
name : Bytes,
) -> Bytes = "git2_config_get_string"
///|
/// git config の値を取得
pub fn Repository::config_get(self : Repository, name : String) -> String? {
let value = git2_config_get_string(self.handle, to_c_string(name))
let str = from_c_bytes(value)
if str.length() == 0 {
None
} else {
Some(str)
}
}
///|
/// HEAD のツリーを取得
pub fn Repository::head_tree(self : Repository) -> Tree? {
// Get HEAD commit
let walk = git2_revwalk_new(self.handle)
if git2_revwalk_is_null(walk) == 1 {
return None
}
let _ = git2_revwalk_push_head(walk)
let holder = git2_oid_holder_new()
let err = git2_revwalk_next(walk, holder)
if err != 0 {
git2_oid_holder_free(holder)
git2_revwalk_free(walk)
return None
}
let commit = git2_commit_lookup(self.handle, holder)
git2_oid_holder_free(holder)
git2_revwalk_free(walk)
if git2_commit_is_null(commit) == 1 {
return None
}
let result = get_tree_from_commit(commit)
git2_commit_free(commit)
result
}
///|
/// コミット履歴を取得(最新からn件)
pub fn Repository::log(self : Repository, max_count : Int) -> Array[CommitInfo] {
let commits : Array[CommitInfo] = []
let walk = git2_revwalk_new(self.handle)
if git2_revwalk_is_null(walk) == 1 {
return commits
}
let _ = git2_revwalk_sorting(walk, GIT_SORT_TIME)
let _ = git2_revwalk_push_head(walk)
let holder = git2_oid_holder_new()
let mut count = 0
while count < max_count {
let err = git2_revwalk_next(walk, holder)
if err != 0 {
break
}
let commit = git2_commit_lookup(self.handle, holder)
if git2_commit_is_null(commit) == 1 {
continue
}
let info : CommitInfo = {
id: from_c_bytes(git2_commit_id_str(commit)),
message: from_c_bytes(git2_commit_message(commit)),
summary: from_c_bytes(git2_commit_summary(commit)),
author_name: from_c_bytes(git2_commit_author_name(commit)),
author_email: from_c_bytes(git2_commit_author_email(commit)),
committer_name: from_c_bytes(git2_commit_committer_name(commit)),
committer_email: from_c_bytes(git2_commit_committer_email(commit)),
time: git2_commit_time(commit),
}
commits.push(info)
git2_commit_free(commit)
count += 1
}
git2_oid_holder_free(holder)
git2_revwalk_free(walk)
commits
}
// ===== Blob API =====
///|
#external
type GitBlob
///|
#borrow(repo, oid_str)
extern "C" fn git2_blob_lookup(
repo : GitRepository,
oid_str : Bytes,
) -> GitBlob = "git2_blob_lookup"
///|
#borrow(blob)
extern "C" fn git2_blob_free(blob : GitBlob) -> Unit = "git2_blob_free"
///|
#borrow(blob)
extern "C" fn git2_blob_is_null(blob : GitBlob) -> Int = "git2_blob_is_null"
///|
#borrow(blob)
extern "C" fn git2_blob_rawsize(blob : GitBlob) -> Int64 = "git2_blob_rawsize"
///|
#borrow(blob)
extern "C" fn git2_blob_is_binary(blob : GitBlob) -> Int = "git2_blob_is_binary"
///|
#borrow(blob)
extern "C" fn git2_blob_content(blob : GitBlob) -> Bytes = "git2_blob_content"
///|
/// ブロブ(ファイル内容)
pub struct Blob {
priv handle : GitBlob
}
///|
/// OID からブロブを取得
pub fn Repository::blob(self : Repository, oid : String) -> Blob? {
let blob = git2_blob_lookup(self.handle, to_c_string(oid))
if git2_blob_is_null(blob) == 1 {
None
} else {
Some({ handle: blob })
}
}
///|
/// ブロブのサイズを取得
pub fn Blob::size(self : Blob) -> Int64 {
git2_blob_rawsize(self.handle)
}
///|
/// バイナリファイルかどうか
pub fn Blob::is_binary(self : Blob) -> Bool {
git2_blob_is_binary(self.handle) == 1
}
///|
/// ブロブの内容を取得(バイト列)
pub fn Blob::content(self : Blob) -> Bytes {
git2_blob_content(self.handle)
}
///|
/// ブロブの内容を文字列として取得
pub fn Blob::content_string(self : Blob) -> String {
from_c_bytes(git2_blob_content(self.handle))
}
///|
/// ブロブを閉じる
pub fn Blob::close(self : Blob) -> Unit {
git2_blob_free(self.handle)
}
// ===== Index API =====
///|
#external
type GitIndex
///|
#borrow(repo)
extern "C" fn git2_index_open(repo : GitRepository) -> GitIndex = "git2_index_open"
///|
#borrow(index)
extern "C" fn git2_index_free(index : GitIndex) -> Unit = "git2_index_free"
///|
#borrow(index)
extern "C" fn git2_index_is_null(index : GitIndex) -> Int = "git2_index_is_null"
///|
#borrow(index, path)
extern "C" fn git2_index_add_bypath(index : GitIndex, path : Bytes) -> Int = "git2_index_add_bypath"
///|
#borrow(index, path)
extern "C" fn git2_index_remove_bypath(index : GitIndex, path : Bytes) -> Int = "git2_index_remove_bypath"
///|
#borrow(index)
extern "C" fn git2_index_write(index : GitIndex) -> Int = "git2_index_write"
///|
#borrow(index)
extern "C" fn git2_index_write_tree(index : GitIndex) -> Bytes = "git2_index_write_tree"
///|
#borrow(index)
extern "C" fn git2_index_entrycount(index : GitIndex) -> Int = "git2_index_entrycount"
///|
#borrow(index)
extern "C" fn git2_index_read(index : GitIndex) -> Int = "git2_index_read"
///|
/// インデックス(ステージングエリア)
pub struct Index {
priv handle : GitIndex
}
///|
/// インデックスを開く
pub fn Repository::index(self : Repository) -> Index? {
let idx = git2_index_open(self.handle)
if git2_index_is_null(idx) == 1 {
None
} else {
Some({ handle: idx })
}
}
///|
/// ファイルをステージに追加
pub fn Index::add(self : Index, path : String) -> Bool {
git2_index_add_bypath(self.handle, to_c_string(path)) == 0
}
///|
/// ファイルをステージから削除
pub fn Index::remove(self : Index, path : String) -> Bool {
git2_index_remove_bypath(self.handle, to_c_string(path)) == 0
}
///|
/// インデックスを書き込み
pub fn Index::write(self : Index) -> Bool {
git2_index_write(self.handle) == 0
}
///|
/// インデックスからツリーを作成し、ツリー OID を返す
pub fn Index::write_tree(self : Index) -> String? {
let oid = from_c_bytes(git2_index_write_tree(self.handle))
if oid.length() == 40 {
Some(oid)
} else {
None
}
}
///|
/// ステージされたエントリ数
pub fn Index::entry_count(self : Index) -> Int {
git2_index_entrycount(self.handle)
}
///|
/// インデックスを再読み込み
pub fn Index::reload(self : Index) -> Bool {
git2_index_read(self.handle) == 0
}
///|
/// インデックスを閉じる
pub fn Index::close(self : Index) -> Unit {
git2_index_free(self.handle)
}
// ===== Signature API =====
///|
#external
type GitSignature
///|
#borrow(name, email)
extern "C" fn git2_signature_now(name : Bytes, email : Bytes) -> GitSignature = "git2_signature_now"
///|
#borrow(repo)
extern "C" fn git2_signature_default(repo : GitRepository) -> GitSignature = "git2_signature_default"
///|
#borrow(sig)
extern "C" fn git2_signature_free(sig : GitSignature) -> Unit = "git2_signature_free"
///|
#borrow(sig)
extern "C" fn git2_signature_is_null(sig : GitSignature) -> Int = "git2_signature_is_null"
///|
#borrow(sig)
extern "C" fn git2_signature_name(sig : GitSignature) -> Bytes = "git2_signature_name"
///|
#borrow(sig)
extern "C" fn git2_signature_email(sig : GitSignature) -> Bytes = "git2_signature_email"
///|
/// 署名(author/committer)
pub struct Signature {
priv handle : GitSignature
}
///|
/// 現在時刻で署名を作成
pub fn Signature::now(name : String, email : String) -> Signature? {
let sig = git2_signature_now(to_c_string(name), to_c_string(email))
if git2_signature_is_null(sig) == 1 {
None
} else {
Some({ handle: sig })
}
}
///|
/// リポジトリのデフォルト署名(user.name, user.email から)
pub fn Repository::default_signature(self : Repository) -> Signature? {
let sig = git2_signature_default(self.handle)
if git2_signature_is_null(sig) == 1 {
None
} else {
Some({ handle: sig })
}
}
///|
/// 署名の名前
pub fn Signature::name(self : Signature) -> String {
from_c_bytes(git2_signature_name(self.handle))
}
///|
/// 署名のメールアドレス
pub fn Signature::email(self : Signature) -> String {
from_c_bytes(git2_signature_email(self.handle))
}
///|
/// 署名を解放
pub fn Signature::close(self : Signature) -> Unit {
git2_signature_free(self.handle)
}
// ===== Commit 作成 API =====
///|
#borrow(repo, message, author, committer, tree_oid)
extern "C" fn git2_commit_create(
repo : GitRepository,
message : Bytes,
author : GitSignature,
committer : GitSignature,
tree_oid : Bytes,
) -> Bytes = "git2_commit_create"
///|
/// コミットを作成(HEAD を親として)
/// 戻り値: 新しいコミットの OID
pub fn Repository::commit(
self : Repository,
message : String,
author : Signature,
committer : Signature,
tree_oid : String,
) -> String? {
let oid = from_c_bytes(
git2_commit_create(
self.handle,
to_c_string(message),
author.handle,
committer.handle,
to_c_string(tree_oid),
),
)
if oid.length() == 40 {
Some(oid)
} else {
None
}
}
///|
/// 簡易コミット(デフォルト署名を使用)
pub fn Repository::commit_simple(
self : Repository,
message : String,
) -> String? {
// インデックスを開く
let idx = match self.index() {
Some(i) => i
None => return None
}
// ツリーを書き込み
let tree_oid = match idx.write_tree() {
Some(oid) => oid
None => {
idx.close()
return None
}
}
idx.close()
// デフォルト署名を取得
let sig = match self.default_signature() {
Some(s) => s
None => return None
}
// コミット作成
let result = self.commit(message, sig, sig, tree_oid)
sig.close()
result
}
// ===== Checkout API =====
///|
#borrow(repo)
extern "C" fn git2_checkout_head(repo : GitRepository, force : Int) -> Int = "git2_checkout_head"
///|
/// HEAD にチェックアウト
pub fn Repository::checkout_head(self : Repository) -> Bool {
git2_checkout_head(self.handle, 0) == 0
}
// ===== Reset API =====
///|
/// リセットタイプ
pub(all) enum ResetType {
Soft // HEAD のみ移動
Mixed // HEAD とインデックスを更新(デフォルト)
Hard // HEAD、インデックス、ワーキングツリーを更新
}
///|
#borrow(repo, commit_oid)
extern "C" fn git2_reset(
repo : GitRepository,
commit_oid : Bytes,
reset_type : Int,
) -> Int = "git2_reset"
///|
/// 指定したコミットにリセット
pub fn Repository::reset(
self : Repository,
commit_oid : String,
reset_type : ResetType,
) -> Bool {
let type_int = match reset_type {
Soft => 1
Mixed => 2
Hard => 3
}
git2_reset(self.handle, to_c_string(commit_oid), type_int) == 0
}