import 'dart:ffi'; import 'package:ffi/ffi.dart'; import '../error.dart'; import 'libgit2_bindings.dart'; import '../util.dart'; import 'oid.dart' as oid_bindings; /// Lookup a commit object from a repository. /// /// The returned object should be released with `free()` when no longer needed. /// /// Throws a [LibGit2Error] if error occured. Pointer lookup({ required Pointer repoPointer, required Pointer oidPointer, }) { final out = calloc>(); final error = libgit2.git_commit_lookup(out, repoPointer, oidPointer); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } else { return out.value; } } /// Lookup a commit object from a repository, given a prefix of its identifier (short id). /// /// Throws a [LibGit2Error] if error occured. Pointer lookupPrefix({ required Pointer repoPointer, required Pointer oidPointer, required int len, }) { final out = calloc>(); final error = libgit2.git_commit_lookup_prefix( out, repoPointer, oidPointer, len, ); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } else { return out.value; } } /// Creates a git_annotated_commit from the given commit id. The resulting git_annotated_commit /// must be freed with git_annotated_commit_free. /// /// An annotated commit contains information about how it was looked up, which may be useful /// for functions like merge or rebase to provide context to the operation. For example, conflict /// files will include the name of the source or target branches being merged. It is therefore /// preferable to use the most specific function (eg git_annotated_commit_from_ref) instead of /// this one when that data is known. /// /// Throws a [LibGit2Error] if error occured. Pointer> annotatedLookup({ required Pointer repoPointer, required Pointer oidPointer, }) { final out = calloc>(); final error = libgit2.git_annotated_commit_lookup( out, repoPointer, oidPointer, ); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } else { return out; } } /// Frees a git_annotated_commit. void annotatedFree(Pointer commit) { libgit2.git_annotated_commit_free(commit); } /// Create new commit in the repository. /// /// Throws a [LibGit2Error] if error occured. Pointer create({ required Pointer repoPointer, String? updateRef, required Pointer authorPointer, required Pointer committerPointer, String? messageEncoding, required String message, required Pointer treePointer, required int parentCount, required List parents, }) { final out = calloc(); final updateRefC = updateRef?.toNativeUtf8().cast() ?? nullptr; final messageEncodingC = messageEncoding?.toNativeUtf8().cast() ?? nullptr; final messageC = message.toNativeUtf8().cast(); Pointer> parentsC = calloc.call>(parentCount); if (parents.isNotEmpty) { for (var i = 0; i < parentCount; i++) { final oid = oid_bindings.fromSHA(parents[i]); var commit = calloc(); commit = lookup(repoPointer: repoPointer, oidPointer: oid).cast(); parentsC[i] = commit.cast(); } } else { final commit = calloc(); parentsC[0] = commit.cast(); } final error = libgit2.git_commit_create( out, repoPointer, updateRefC, authorPointer, committerPointer, messageEncodingC, messageC, treePointer, parentCount, parentsC, ); calloc.free(updateRefC); calloc.free(messageEncodingC); calloc.free(messageC); for (var i = 0; i < parentCount; i++) { free(parentsC[i]); } calloc.free(parentsC); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } else { return out; } } /// Get the encoding for the message of a commit, as a string representing a standard encoding name. /// /// The encoding may be NULL if the encoding header in the commit is missing; /// in that case UTF-8 is assumed. String messageEncoding(Pointer commit) { final result = libgit2.git_commit_message_encoding(commit); if (result == nullptr) { return 'utf-8'; } else { return result.cast().toDartString(); } } /// Get the full message of a commit. /// /// The returned message will be slightly prettified by removing any potential leading newlines. String message(Pointer commit) { final out = libgit2.git_commit_message(commit); return out.cast().toDartString(); } /// Get the id of a commit. Pointer id(Pointer commit) => libgit2.git_commit_id(commit); /// Get the number of parents of this commit. int parentCount(Pointer commit) => libgit2.git_commit_parentcount(commit); /// Get the oid of a specified parent for a commit. /// /// Throws a [LibGit2Error] if error occured. Pointer parentId({ required Pointer commitPointer, required int position, }) { final parentOid = libgit2.git_commit_parent_id(commitPointer, position); if (parentOid == nullptr) { throw LibGit2Error(libgit2.git_error_last()); } else { return parentOid; } } /// Get the commit time (i.e. committer time) of a commit. int time(Pointer commit) => libgit2.git_commit_time(commit); /// Get the committer of a commit. Pointer committer(Pointer commit) { return libgit2.git_commit_committer(commit); } /// Get the author of a commit. Pointer author(Pointer commit) { return libgit2.git_commit_author(commit); } /// Get the id of the tree pointed to by a commit. Pointer tree(Pointer commit) { return libgit2.git_commit_tree_id(commit); } /// Reverts the given commit against the given "our" commit, producing an index that /// reflects the result of the revert. /// /// The returned index must be freed explicitly with `free()`. /// /// Throws a [LibGit2Error] if error occured. Pointer revertCommit({ required Pointer repoPointer, required Pointer revertCommitPointer, required Pointer ourCommitPointer, required int mainline, }) { final out = calloc>(); final opts = calloc(); final optsError = libgit2.git_merge_options_init(opts, GIT_MERGE_OPTIONS_VERSION); if (optsError < 0) { throw LibGit2Error(libgit2.git_error_last()); } final error = libgit2.git_revert_commit( out, repoPointer, revertCommitPointer, ourCommitPointer, mainline, opts, ); calloc.free(opts); if (error < 0) { throw LibGit2Error(libgit2.git_error_last()); } else { return out.value; } } /// Get the repository that contains the commit. Pointer owner(Pointer commit) => libgit2.git_commit_owner(commit); /// Close an open commit to release memory. void free(Pointer commit) => libgit2.git_commit_free(commit);