diff --git a/lib/libgit2dart.dart b/lib/libgit2dart.dart index b73111d..58095a2 100644 --- a/lib/libgit2dart.dart +++ b/lib/libgit2dart.dart @@ -20,5 +20,7 @@ export 'src/stash.dart'; export 'src/remote.dart'; export 'src/refspec.dart'; export 'src/callbacks.dart'; +export 'src/credentials.dart'; +export 'src/features.dart'; export 'src/error.dart'; export 'src/git_types.dart'; diff --git a/lib/src/bindings/credentials.dart b/lib/src/bindings/credentials.dart new file mode 100644 index 0000000..b4d1781 --- /dev/null +++ b/lib/src/bindings/credentials.dart @@ -0,0 +1,135 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import '../error.dart'; +import '../util.dart'; +import 'libgit2_bindings.dart'; + +/// Create a credential to specify a username. +/// +/// This is used with ssh authentication to query for the username if none is +/// specified in the url. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer username(String username) { + final out = calloc>(); + final usernameC = username.toNativeUtf8().cast(); + + final error = libgit2.git_credential_username_new(out, usernameC); + + calloc.free(usernameC); + + if (error < 0) { + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } +} + +/// Create a new plain-text username and password credential object. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer userPass(String username, String password) { + final out = calloc>(); + final usernameC = username.toNativeUtf8().cast(); + final passwordC = password.toNativeUtf8().cast(); + + final error = + libgit2.git_credential_userpass_plaintext_new(out, usernameC, passwordC); + + calloc.free(usernameC); + calloc.free(passwordC); + + if (error < 0) { + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } +} + +/// Create a new passphrase-protected ssh key credential object. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer sshKey( + String username, + String publicKey, + String privateKey, + String passPhrase, +) { + final out = calloc>(); + final usernameC = username.toNativeUtf8().cast(); + final publicKeyC = publicKey.toNativeUtf8().cast(); + final privateKeyC = privateKey.toNativeUtf8().cast(); + final passPhraseC = passPhrase.toNativeUtf8().cast(); + + final error = libgit2.git_credential_ssh_key_new( + out, + usernameC, + publicKeyC, + privateKeyC, + passPhraseC, + ); + + calloc.free(usernameC); + calloc.free(publicKeyC); + calloc.free(privateKeyC); + calloc.free(passPhraseC); + + if (error < 0) { + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } +} + +/// Create a new ssh key credential object used for querying an ssh-agent. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer sshKeyFromAgent(String username) { + final out = calloc>(); + final usernameC = username.toNativeUtf8().cast(); + + final error = libgit2.git_credential_ssh_key_from_agent(out, usernameC); + + calloc.free(usernameC); + + if (error < 0) { + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } +} + +/// Create a new ssh key credential object reading the keys from memory. +/// +/// Throws a [LibGit2Error] if error occured. +Pointer sshKeyFromMemory( + String username, + String publicKey, + String privateKey, + String passPhrase, +) { + final out = calloc>(); + final usernameC = username.toNativeUtf8().cast(); + final publicKeyC = publicKey.toNativeUtf8().cast(); + final privateKeyC = privateKey.toNativeUtf8().cast(); + final passPhraseC = passPhrase.toNativeUtf8().cast(); + + final error = libgit2.git_credential_ssh_key_memory_new( + out, + usernameC, + publicKeyC, + privateKeyC, + passPhraseC, + ); + + calloc.free(usernameC); + calloc.free(publicKeyC); + calloc.free(privateKeyC); + calloc.free(passPhraseC); + + if (error < 0) { + throw LibGit2Error(libgit2.git_error_last()); + } else { + return out.value; + } +} diff --git a/lib/src/bindings/libgit2_bindings.dart b/lib/src/bindings/libgit2_bindings.dart index dddfcd0..cf0493f 100644 --- a/lib/src/bindings/libgit2_bindings.dart +++ b/lib/src/bindings/libgit2_bindings.dart @@ -2447,6 +2447,22 @@ class Libgit2 { /// > Override the default priority of the loose ODB backend which /// > is added when default backends are assigned to a repository /// + /// opts(GIT_OPT_GET_EXTENSIONS, git_strarray *out) + /// > Returns the list of git extensions that are supported. This + /// > is the list of built-in extensions supported by libgit2 and + /// > custom extensions that have been added with + /// > `GIT_OPT_SET_EXTENSIONS`. Extensions that have been negated + /// > will not be returned. The returned list should be released + /// > with `git_strarray_dispose`. + /// + /// opts(GIT_OPT_SET_EXTENSIONS, const char **extensions, size_t len) + /// > Set that the given git extensions are supported by the caller. + /// > Extensions supported by libgit2 may be negated by prefixing + /// > them with a `!`. For example: setting extensions to + /// > { "!noop", "newext" } indicates that the caller does not want + /// > to support repositories with the `noop` extension but does want + /// > to support repositories with the `newext` extension. + /// /// @param option Option key /// @param ... value to set the option /// @return 0 on success, <0 on failure @@ -3990,13 +4006,15 @@ class Libgit2 { /// /// @param out Output value of calculated SHA /// @param repo Repository pointer - /// @param path Path to file on disk whose contents should be hashed. If the - /// repository is not NULL, this can be a relative path. + /// @param path Path to file on disk whose contents should be hashed. This + /// may be an absolute path or a relative path, in which case it + /// will be treated as a path within the working directory. /// @param type The object type to hash as (e.g. GIT_OBJECT_BLOB) /// @param as_path The path to use to look up filtering rules. If this is - /// NULL, then the `path` parameter will be used instead. If - /// this is passed as the empty string, then no filters will be - /// applied when calculating the hash. + /// an empty string then no filters will be applied when + /// calculating the hash. If this is `NULL` and the `path` + /// parameter is a file within the repository's working + /// directory, then the `path` will be used. /// @return 0 on success, or an error code int git_repository_hashfile( ffi.Pointer out, @@ -8083,112 +8101,6 @@ class Libgit2 { late final _git_diff_stats_free = _git_diff_stats_freePtr .asFunction)>(); - /// Create an e-mail ready patch from a diff. - /// - /// @param out buffer to store the e-mail patch in - /// @param diff containing the commit - /// @param opts structure with options to influence content and formatting. - /// @return 0 or an error code - int git_diff_format_email( - ffi.Pointer out, - ffi.Pointer diff, - ffi.Pointer opts, - ) { - return _git_diff_format_email( - out, - diff, - opts, - ); - } - - late final _git_diff_format_emailPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>>( - 'git_diff_format_email'); - late final _git_diff_format_email = _git_diff_format_emailPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>(); - - /// Create an e-mail ready patch for a commit. - /// - /// Does not support creating patches for merge commits (yet). - /// - /// @param out buffer to store the e-mail patch in - /// @param repo containing the commit - /// @param commit pointer to up commit - /// @param patch_no patch number of the commit - /// @param total_patches total number of patches in the patch set - /// @param flags determines the formatting of the e-mail - /// @param diff_opts structure with options to influence diff or NULL for defaults. - /// @return 0 or an error code - int git_diff_commit_as_email( - ffi.Pointer out, - ffi.Pointer repo, - ffi.Pointer commit, - int patch_no, - int total_patches, - int flags, - ffi.Pointer diff_opts, - ) { - return _git_diff_commit_as_email( - out, - repo, - commit, - patch_no, - total_patches, - flags, - diff_opts, - ); - } - - late final _git_diff_commit_as_emailPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - size_t, - size_t, - ffi.Uint32, - ffi.Pointer)>>('git_diff_commit_as_email'); - late final _git_diff_commit_as_email = - _git_diff_commit_as_emailPtr.asFunction< - int Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - int, - int, - int, - ffi.Pointer)>(); - - /// Initialize git_diff_format_email_options structure - /// - /// Initializes a `git_diff_format_email_options` with default values. Equivalent - /// to creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT. - /// - /// @param opts The `git_blame_options` struct to initialize. - /// @param version The struct version; pass `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION`. - /// @return Zero on success; -1 on failure. - int git_diff_format_email_options_init( - ffi.Pointer opts, - int version, - ) { - return _git_diff_format_email_options_init( - opts, - version, - ); - } - - late final _git_diff_format_email_options_initPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, - ffi.Uint32)>>('git_diff_format_email_options_init'); - late final _git_diff_format_email_options_init = - _git_diff_format_email_options_initPtr.asFunction< - int Function(ffi.Pointer, int)>(); - /// Initialize git_diff_patchid_options structure /// /// Initializes a `git_diff_patchid_options` with default values. Equivalent to @@ -10006,29 +9918,26 @@ class Libgit2 { int Function(ffi.Pointer, ffi.Pointer, ffi.Pointer)>(); - /// Free the OID array - /// - /// This method must (and must only) be called on `git_oidarray` - /// objects where the array is allocated by the library. Not doing so, - /// will result in a memory leak. + /// Free the object IDs contained in an oid_array. This method should + /// be called on `git_oidarray` objects that were provided by the + /// library. Not doing so will result in a memory leak. /// /// This does not free the `git_oidarray` itself, since the library will - /// never allocate that object directly itself (it is more commonly embedded - /// inside another struct or created on the stack). + /// never allocate that object directly itself. /// /// @param array git_oidarray from which to free oid data - void git_oidarray_free( + void git_oidarray_dispose( ffi.Pointer array, ) { - return _git_oidarray_free( + return _git_oidarray_dispose( array, ); } - late final _git_oidarray_freePtr = + late final _git_oidarray_disposePtr = _lookup)>>( - 'git_oidarray_free'); - late final _git_oidarray_free = _git_oidarray_freePtr + 'git_oidarray_dispose'); + late final _git_oidarray_dispose = _git_oidarray_disposePtr .asFunction)>(); /// Initializes a `git_indexer_options` with default values. Equivalent to @@ -19948,6 +19857,102 @@ class Libgit2 { late final _git_buf_free = _git_buf_freePtr.asFunction)>(); + /// Create an e-mail ready patch from a diff. + /// + /// @deprecated git_email_create_from_diff + /// @see git_email_create_from_diff + int git_diff_format_email( + ffi.Pointer out, + ffi.Pointer diff, + ffi.Pointer opts, + ) { + return _git_diff_format_email( + out, + diff, + opts, + ); + } + + late final _git_diff_format_emailPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>( + 'git_diff_format_email'); + late final _git_diff_format_email = _git_diff_format_emailPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + + /// Create an e-mail ready patch for a commit. + /// + /// @deprecated git_email_create_from_commit + /// @see git_email_create_from_commit + int git_diff_commit_as_email( + ffi.Pointer out, + ffi.Pointer repo, + ffi.Pointer commit, + int patch_no, + int total_patches, + int flags, + ffi.Pointer diff_opts, + ) { + return _git_diff_commit_as_email( + out, + repo, + commit, + patch_no, + total_patches, + flags, + diff_opts, + ); + } + + late final _git_diff_commit_as_emailPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + size_t, + size_t, + ffi.Uint32, + ffi.Pointer)>>('git_diff_commit_as_email'); + late final _git_diff_commit_as_email = + _git_diff_commit_as_emailPtr.asFunction< + int Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + int, + int, + int, + ffi.Pointer)>(); + + /// Initialize git_diff_format_email_options structure + /// + /// Initializes a `git_diff_format_email_options` with default values. Equivalent + /// to creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT. + /// + /// @param opts The `git_blame_options` struct to initialize. + /// @param version The struct version; pass `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION`. + /// @return Zero on success; -1 on failure. + int git_diff_format_email_options_init( + ffi.Pointer opts, + int version, + ) { + return _git_diff_format_email_options_init( + opts, + version, + ); + } + + late final _git_diff_format_email_options_initPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, + ffi.Uint32)>>('git_diff_format_email_options_init'); + late final _git_diff_format_email_options_init = + _git_diff_format_email_options_initPtr.asFunction< + int Function(ffi.Pointer, int)>(); + /// Return the last `git_error` object that was generated for the /// current thread. This is an alias of `git_error_last` and is /// preserved for backward compatibility. @@ -20446,6 +20451,28 @@ class Libgit2 { late final _git_oid_iszero = _git_oid_iszeroPtr.asFunction)>(); + /// Free the memory referred to by the git_oidarray. This is an alias of + /// `git_oidarray_dispose` and is preserved for backward compatibility. + /// + /// This function is deprecated, but there is no plan to remove this + /// function at this time. + /// + /// @deprecated Use git_oidarray_dispose + /// @see git_oidarray_dispose + void git_oidarray_free( + ffi.Pointer array, + ) { + return _git_oidarray_free( + array, + ); + } + + late final _git_oidarray_freePtr = + _lookup)>>( + 'git_oidarray_free'); + late final _git_oidarray_free = _git_oidarray_freePtr + .asFunction)>(); + /// Free the memory referred to by the git_strarray. This is an alias of /// `git_strarray_dispose` and is preserved for backward compatibility. /// @@ -20925,6 +20952,95 @@ class Libgit2 { _git_worktree_prune_init_optionsPtr.asFunction< int Function(ffi.Pointer, int)>(); + /// Create a diff for a commit in mbox format for sending via email. + /// + /// @param out buffer to store the e-mail patch in + /// @param diff the changes to include in the email + /// @param patch_idx the patch index + /// @param patch_count the total number of patches that will be included + /// @param commit_id the commit id for this change + /// @param summary the commit message for this change + /// @param body optional text to include above the diffstat + /// @param author the person who authored this commit + /// @param opts email creation options + int git_email_create_from_diff( + ffi.Pointer out, + ffi.Pointer diff, + int patch_idx, + int patch_count, + ffi.Pointer commit_id, + ffi.Pointer summary, + ffi.Pointer body, + ffi.Pointer author, + ffi.Pointer opts, + ) { + return _git_email_create_from_diff( + out, + diff, + patch_idx, + patch_count, + commit_id, + summary, + body, + author, + opts, + ); + } + + late final _git_email_create_from_diffPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function( + ffi.Pointer, + ffi.Pointer, + size_t, + size_t, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>>( + 'git_email_create_from_diff'); + late final _git_email_create_from_diff = + _git_email_create_from_diffPtr.asFunction< + int Function( + ffi.Pointer, + ffi.Pointer, + int, + int, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>(); + + /// Create a diff for a commit in mbox format for sending via email. + /// The commit must not be a merge commit. + /// + /// @param out buffer to store the e-mail patch in + /// @param commit commit to create a patch for + /// @param opts email creation options + int git_email_create_from_commit( + ffi.Pointer out, + ffi.Pointer commit, + ffi.Pointer opts, + ) { + return _git_email_create_from_commit( + out, + commit, + opts, + ); + } + + late final _git_email_create_from_commitPtr = _lookup< + ffi.NativeFunction< + ffi.Int32 Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>>( + 'git_email_create_from_commit'); + late final _git_email_create_from_commit = + _git_email_create_from_commitPtr.asFunction< + int Function(ffi.Pointer, ffi.Pointer, + ffi.Pointer)>(); + /// Init the global state /// /// This function must be called before any other libgit2 function in @@ -26449,6 +26565,8 @@ abstract class git_libgit2_opt_t { static const int GIT_OPT_SET_MWINDOW_FILE_LIMIT = 30; static const int GIT_OPT_SET_ODB_PACKED_PRIORITY = 31; static const int GIT_OPT_SET_ODB_LOOSE_PRIORITY = 32; + static const int GIT_OPT_GET_EXTENSIONS = 33; + static const int GIT_OPT_SET_EXTENSIONS = 34; } /// A data buffer for exporting data from libgit2 @@ -27607,6 +27725,9 @@ abstract class git_diff_option_t { /// diff hunks. static const int GIT_DIFF_INDENT_HEURISTIC = 262144; + /// Ignore blank lines + static const int GIT_DIFF_IGNORE_BLANK_LINES = 524288; + /// Treat all files as text, disabling binary attributes & detection static const int GIT_DIFF_FORCE_TEXT = 1048576; @@ -27643,9 +27764,6 @@ abstract class git_diff_option_t { /// Include the necessary deflate / delta information so that `git-apply` /// can apply given diff information to binary files. static const int GIT_DIFF_SHOW_BINARY = 1073741824; - - /// Ignore blank lines - static const int GIT_DIFF_IGNORE_BLANK_LINES = -2147483648; } class git_diff extends ffi.Opaque {} @@ -28298,45 +28416,6 @@ abstract class git_diff_stats_format_t { static const int GIT_DIFF_STATS_INCLUDE_SUMMARY = 8; } -/// Formatting options for diff e-mail generation -abstract class git_diff_format_email_flags_t { - /// Normal patch, the default - static const int GIT_DIFF_FORMAT_EMAIL_NONE = 0; - - /// Don't insert "[PATCH]" in the subject header - static const int GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER = 1; -} - -/// Options for controlling the formatting of the generated e-mail. -class git_diff_format_email_options extends ffi.Struct { - @ffi.Uint32() - external int version; - - /// see `git_diff_format_email_flags_t` above - @ffi.Uint32() - external int flags; - - /// This patch number - @size_t() - external int patch_no; - - /// Total number of patches in this series - @size_t() - external int total_patches; - - /// id to use for the commit - external ffi.Pointer id; - - /// Summary of the change - external ffi.Pointer summary; - - /// Commit message's body - external ffi.Pointer body; - - /// Author of the change - external ffi.Pointer author; -} - /// Patch ID options structure /// /// Initialize with `GIT_PATCHID_OPTIONS_INIT`. Alternatively, you can @@ -28446,9 +28525,11 @@ class git_attr_options extends ffi.Struct { @ffi.Uint32() external int flags; + external ffi.Pointer commit_id; + /// The commit to load attributes from, when /// `GIT_ATTR_CHECK_INCLUDE_COMMIT` is specified. - external ffi.Pointer commit_id; + external git_oid attr_commit_id; } /// The callback used with git_attr_foreach. @@ -28500,9 +28581,11 @@ class git_blob_filter_options extends ffi.Struct { @ffi.Uint32() external int flags; + external ffi.Pointer commit_id; + /// The commit to load attributes from, when /// `GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT` is specified. - external ffi.Pointer commit_id; + external git_oid attr_commit_id; } /// Flags for indicating option behavior for git_blame APIs. @@ -30208,9 +30291,11 @@ class git_filter_options extends ffi.Struct { @ffi.Uint32() external int flags; + external ffi.Pointer commit_id; + /// The commit to load attributes from, when /// `GIT_FILTER_ATTRIBUTES_FROM_COMMIT` is specified. - external ffi.Pointer commit_id; + external git_oid attr_commit_id; } class git_filter extends ffi.Opaque {} @@ -30872,6 +30957,92 @@ class git_credential_userpass_payload extends ffi.Struct { external ffi.Pointer password; } +/// Formatting options for diff e-mail generation +abstract class git_diff_format_email_flags_t { + /// Normal patch, the default + static const int GIT_DIFF_FORMAT_EMAIL_NONE = 0; + + /// Don't insert "[PATCH]" in the subject header + static const int GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER = 1; +} + +/// Options for controlling the formatting of the generated e-mail. +class git_diff_format_email_options extends ffi.Struct { + @ffi.Uint32() + external int version; + + /// see `git_diff_format_email_flags_t` above + @ffi.Uint32() + external int flags; + + /// This patch number + @size_t() + external int patch_no; + + /// Total number of patches in this series + @size_t() + external int total_patches; + + /// id to use for the commit + external ffi.Pointer id; + + /// Summary of the change + external ffi.Pointer summary; + + /// Commit message's body + external ffi.Pointer body; + + /// Author of the change + external ffi.Pointer author; +} + +/// Formatting options for diff e-mail generation +abstract class git_email_create_flags_t { + /// Normal patch, the default + static const int GIT_EMAIL_CREATE_DEFAULT = 0; + + /// Do not include patch numbers in the subject prefix. + static const int GIT_EMAIL_CREATE_OMIT_NUMBERS = 1; + + /// Include numbers in the subject prefix even when the + /// patch is for a single commit (1/1). + static const int GIT_EMAIL_CREATE_ALWAYS_NUMBER = 2; + + /// Do not perform rename or similarity detection. + static const int GIT_EMAIL_CREATE_NO_RENAMES = 4; +} + +/// Options for controlling the formatting of the generated e-mail. +class git_email_create_options extends ffi.Struct { + @ffi.Uint32() + external int version; + + /// see `git_email_create_flags_t` above + @ffi.Uint32() + external int flags; + + /// Options to use when creating diffs + external git_diff_options diff_opts; + + /// Options for finding similarities within diffs + external git_diff_find_options diff_find_opts; + + /// The subject prefix, by default "PATCH". If set to an empty + /// string ("") then only the patch numbers will be shown in the + /// prefix. If the subject_prefix is empty and patch numbers + /// are not being shown, the prefix will be omitted entirely. + external ffi.Pointer subject_prefix; + + /// The starting patch number; this cannot be 0. By default, + /// this is 1. + @size_t() + external int start_number; + + /// The "re-roll" number. By default, there is no re-roll. + @size_t() + external int reroll_number; +} + /// Represents a single git message trailer. class git_message_trailer extends ffi.Struct { external ffi.Pointer key; @@ -31791,8 +31962,6 @@ const int GIT_DIFF_HUNK_HEADER_SIZE = 128; const int GIT_DIFF_FIND_OPTIONS_VERSION = 1; -const int GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION = 1; - const int GIT_DIFF_PATCHID_OPTIONS_VERSION = 1; const int GIT_APPLY_OPTIONS_VERSION = 1; @@ -31901,6 +32070,8 @@ const int GIT_CVAR_INT32 = 2; const int GIT_CVAR_STRING = 3; +const int GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION = 1; + const int GITERR_NONE = 0; const int GITERR_NOMEMORY = 1; @@ -32071,14 +32242,16 @@ const int GIT_CREDTYPE_USERNAME = 32; const int GIT_CREDTYPE_SSH_MEMORY = 64; -const String LIBGIT2_VERSION = '1.2.0'; +const int GIT_EMAIL_CREATE_OPTIONS_VERSION = 1; + +const String LIBGIT2_VERSION = '1.3.0'; const int LIBGIT2_VER_MAJOR = 1; -const int LIBGIT2_VER_MINOR = 2; +const int LIBGIT2_VER_MINOR = 3; const int LIBGIT2_VER_REVISION = 0; const int LIBGIT2_VER_PATCH = 0; -const String LIBGIT2_SOVERSION = '1.2'; +const String LIBGIT2_SOVERSION = '1.3'; diff --git a/lib/src/bindings/remote_callbacks.dart b/lib/src/bindings/remote_callbacks.dart index 369138b..e0fca2d 100644 --- a/lib/src/bindings/remote_callbacks.dart +++ b/lib/src/bindings/remote_callbacks.dart @@ -1,10 +1,13 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; +import 'package:libgit2dart/libgit2dart.dart'; +import '../credentials.dart'; import '../callbacks.dart'; import '../repository.dart'; -import 'libgit2_bindings.dart'; import '../oid.dart'; import '../remote.dart'; +import 'libgit2_bindings.dart'; +import 'credentials.dart' as credentials_bindings; class RemoteCallbacks { /// Callback function that reports transfer progress. @@ -105,6 +108,56 @@ class RemoteCallbacks { return 0; } + /// [Credentials] object used for authentication in order to connect to remote. + static Credentials? credentials; + + /// Credential acquisition callback that will be called if the remote host requires + /// authentication in order to connect to it. + static int credentialsCb( + Pointer> credPointer, + Pointer url, + Pointer username, + int allowedTypes, + Pointer payload, + ) { + final credentialType = credentials!.credentialType; + if (allowedTypes & credentialType.value != credentialType.value) { + throw ArgumentError('Invalid credential type $credentialType'); + } + + if (credentials is Username) { + final cred = credentials as Username; + credPointer[0] = credentials_bindings.username(cred.username); + } else if (credentials is UserPass) { + final cred = credentials as UserPass; + credPointer[0] = credentials_bindings.userPass( + cred.username, + cred.password, + ); + } else if (credentials is Keypair) { + final cred = credentials as Keypair; + credPointer[0] = credentials_bindings.sshKey( + cred.username, + cred.pubKey, + cred.privateKey, + cred.passPhrase, + ); + } else if (credentials is KeypairFromAgent) { + final cred = credentials as KeypairFromAgent; + credPointer[0] = credentials_bindings.sshKeyFromAgent(cred.username); + } else if (credentials is KeypairFromMemory) { + final cred = credentials as KeypairFromMemory; + credPointer[0] = credentials_bindings.sshKeyFromMemory( + cred.username, + cred.pubKey, + cred.privateKey, + cred.passPhrase, + ); + } + + return 0; + } + /// Plugs provided callbacks into libgit2 callbacks. static void plug({ required git_remote_callbacks callbacksOptions, @@ -143,6 +196,14 @@ class RemoteCallbacks { except, ); } + + if (callbacks.credentials != null) { + credentials = callbacks.credentials; + callbacksOptions.credentials = Pointer.fromFunction( + credentialsCb, + except, + ); + } } /// Resets callback functions to their original null values. @@ -153,5 +214,6 @@ class RemoteCallbacks { pushUpdateReference = null; remoteFunction = null; repositoryFunction = null; + credentials = null; } } diff --git a/lib/src/callbacks.dart b/lib/src/callbacks.dart index 311f4f6..c95f2d2 100644 --- a/lib/src/callbacks.dart +++ b/lib/src/callbacks.dart @@ -1,14 +1,19 @@ +import 'credentials.dart'; import 'oid.dart'; import 'remote.dart'; class Callbacks { const Callbacks({ + this.credentials, this.transferProgress, this.sidebandProgress, this.updateTips, this.pushUpdateReference, }); + /// Credentials used for authentication. + final Credentials? credentials; + /// Callback function that reports transfer progress. final void Function(TransferProgress)? transferProgress; diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart new file mode 100644 index 0000000..a2c8759 --- /dev/null +++ b/lib/src/credentials.dart @@ -0,0 +1,92 @@ +import 'package:libgit2dart/src/git_types.dart'; + +abstract class Credentials { + /// Returns type of authentication method. + GitCredential get credentialType; +} + +/// Credential with specific username. +class Username implements Credentials { + const Username(this.username); + + /// The username to authenticate with. + final String username; + + @override + GitCredential get credentialType => GitCredential.username; +} + +/// Plain-text username and password credential. +class UserPass implements Credentials { + const UserPass({required this.username, required this.password}); + + /// The username to authenticate with. + final String username; + + /// The password of the credential. + final String password; + + @override + GitCredential get credentialType => GitCredential.userPassPlainText; +} + +/// Passphrase-protected ssh key credential. +class Keypair implements Credentials { + const Keypair({ + required this.username, + required this.pubKey, + required this.privateKey, + required this.passPhrase, + }); + + /// The username to authenticate with. + final String username; + + /// The path to the public key of the credential. + final String pubKey; + + /// The path to the private key of the credential. + final String privateKey; + + /// The passphrase of the credential. + final String passPhrase; + + @override + GitCredential get credentialType => GitCredential.sshKey; +} + +/// Ssh key credential used for querying an ssh-agent. +class KeypairFromAgent implements Credentials { + const KeypairFromAgent(this.username); + + /// The username to authenticate with. + final String username; + + @override + GitCredential get credentialType => GitCredential.sshKey; +} + +/// Ssh key credential used for reading the keys from memory. +class KeypairFromMemory implements Credentials { + const KeypairFromMemory({ + required this.username, + required this.pubKey, + required this.privateKey, + required this.passPhrase, + }); + + /// The username to authenticate with. + final String username; + + /// The path to the public key of the credential. + final String pubKey; + + /// The path to the private key of the credential. + final String privateKey; + + /// The passphrase of the credential. + final String passPhrase; + + @override + GitCredential get credentialType => GitCredential.sshMemory; +} diff --git a/lib/src/features.dart b/lib/src/features.dart new file mode 100644 index 0000000..65051ac --- /dev/null +++ b/lib/src/features.dart @@ -0,0 +1,18 @@ +import 'util.dart'; +import 'git_types.dart'; + +class Features { + /// Returns list of compile time options for libgit2. + static List get list { + var result = []; + final featuresInt = libgit2.git_libgit2_features(); + + for (var flag in GitFeature.values) { + if (featuresInt & flag.value == flag.value) { + result.add(flag); + } + } + + return result; + } +} diff --git a/lib/src/git_types.dart b/lib/src/git_types.dart index 56c67e0..99200f2 100644 --- a/lib/src/git_types.dart +++ b/lib/src/git_types.dart @@ -1269,3 +1269,90 @@ class GitRepositoryInit { @override String toString() => 'GitRepositoryInit.$_name'; } + +/// Supported credential types. +/// +/// This represents the various types of authentication methods supported by +/// the library. +class GitCredential { + const GitCredential._(this._value, this._name); + final int _value; + final String _name; + + /// A vanilla user/password request. + static const userPassPlainText = GitCredential._(1, 'userPassPlainText'); + + /// An SSH key-based authentication request. + static const sshKey = GitCredential._(2, 'sshKey'); + + /// An SSH key-based authentication request, with a custom signature. + static const sshCustom = GitCredential._(4, 'sshCustom'); + + /// An NTLM/Negotiate-based authentication request. + static const defaultAuth = GitCredential._(8, 'defaultAuth'); + + /// An SSH interactive authentication request. + static const sshInteractive = GitCredential._(16, 'sshInteractive'); + + /// Username-only authentication request. + /// + /// Used as a pre-authentication step if the underlying transport + /// (eg. SSH, with no username in its URL) does not know which username + /// to use. + static const username = GitCredential._(32, 'username'); + + /// An SSH key-based authentication request. + /// + /// Allows credentials to be read from memory instead of files. + /// Note that because of differences in crypto backend support, it might + /// not be functional. + static const sshMemory = GitCredential._(64, 'sshMemory'); + + static const List values = [ + userPassPlainText, + sshKey, + sshCustom, + defaultAuth, + sshInteractive, + username, + sshMemory, + ]; + + int get value => _value; + + @override + String toString() => 'GitCredential.$_name'; +} + +/// Combinations of these values describe the features with which libgit2 +/// was compiled. +class GitFeature { + const GitFeature._(this._value, this._name); + final int _value; + final String _name; + + /// If set, libgit2 was built thread-aware and can be safely used from multiple + /// threads. + static const threads = GitFeature._(1, 'threads'); + + /// If set, libgit2 was built with and linked against a TLS implementation. + /// Custom TLS streams may still be added by the user to support HTTPS + /// regardless of this. + static const https = GitFeature._(2, 'https'); + + /// If set, libgit2 was built with and linked against libssh2. A custom + /// transport may still be added by the user to support libssh2 regardless of + /// this. + static const ssh = GitFeature._(4, 'ssh'); + + /// If set, libgit2 was built with support for sub-second resolution in file + /// modification times. + static const nsec = GitFeature._(8, 'nsec'); + + static const List values = [threads, https, ssh, nsec]; + + int get value => _value; + + @override + String toString() => 'GitFeature.$_name'; +} diff --git a/lib/src/util.dart b/lib/src/util.dart index 1ae944c..c44b4db 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart @@ -5,7 +5,7 @@ import 'bindings/libgit2_bindings.dart'; DynamicLibrary loadLibrary() { if (Platform.isLinux || Platform.isAndroid || Platform.isFuchsia) { return DynamicLibrary.open( - '${Directory.current.path}/libgit2/libgit2-1.2.0.so'); + '${Directory.current.path}/libgit2/libgit2.so.1.3.0'); } if (Platform.isMacOS) { return DynamicLibrary.open( diff --git a/libgit2/headers/git2.h b/libgit2/headers/git2.h index f39d7fb..2961cc3 100644 --- a/libgit2/headers/git2.h +++ b/libgit2/headers/git2.h @@ -26,6 +26,7 @@ #include "git2/deprecated.h" #include "git2/describe.h" #include "git2/diff.h" +#include "git2/email.h" #include "git2/errors.h" #include "git2/filter.h" #include "git2/global.h" diff --git a/libgit2/headers/git2/attr.h b/libgit2/headers/git2/attr.h index 62c2ed6..3891a0c 100644 --- a/libgit2/headers/git2/attr.h +++ b/libgit2/headers/git2/attr.h @@ -147,11 +147,17 @@ typedef struct { /** A combination of GIT_ATTR_CHECK flags */ unsigned int flags; +#ifdef GIT_DEPRECATE_HARD + void *reserved; +#else + git_oid *commit_id; +#endif + /** * The commit to load attributes from, when * `GIT_ATTR_CHECK_INCLUDE_COMMIT` is specified. */ - git_oid *commit_id; + git_oid attr_commit_id; } git_attr_options; #define GIT_ATTR_OPTIONS_VERSION 1 diff --git a/libgit2/headers/git2/blob.h b/libgit2/headers/git2/blob.h index fceb5c7..8fc7391 100644 --- a/libgit2/headers/git2/blob.h +++ b/libgit2/headers/git2/blob.h @@ -135,11 +135,17 @@ typedef struct { /** Flags to control the filtering process, see `git_blob_filter_flag_t` above */ uint32_t flags; +#ifdef GIT_DEPRECATE_HARD + void *reserved; +#else + git_oid *commit_id; +#endif + /** * The commit to load attributes from, when * `GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT` is specified. */ - git_oid *commit_id; + git_oid attr_commit_id; } git_blob_filter_options; #define GIT_BLOB_FILTER_OPTIONS_VERSION 1 diff --git a/libgit2/headers/git2/clone.h b/libgit2/headers/git2/clone.h index 2d6f687..3c3ea26 100644 --- a/libgit2/headers/git2/clone.h +++ b/libgit2/headers/git2/clone.h @@ -133,7 +133,7 @@ typedef struct git_clone_options { * The name of the branch to checkout. NULL means use the * remote's default branch. */ - const char* checkout_branch; + const char *checkout_branch; /** * A callback used to create the new repository into which to diff --git a/libgit2/headers/git2/common.h b/libgit2/headers/git2/common.h index d278c01..2ee8290 100644 --- a/libgit2/headers/git2/common.h +++ b/libgit2/headers/git2/common.h @@ -209,7 +209,9 @@ typedef enum { GIT_OPT_GET_MWINDOW_FILE_LIMIT, GIT_OPT_SET_MWINDOW_FILE_LIMIT, GIT_OPT_SET_ODB_PACKED_PRIORITY, - GIT_OPT_SET_ODB_LOOSE_PRIORITY + GIT_OPT_SET_ODB_LOOSE_PRIORITY, + GIT_OPT_GET_EXTENSIONS, + GIT_OPT_SET_EXTENSIONS } git_libgit2_opt_t; /** @@ -431,6 +433,22 @@ typedef enum { * > Override the default priority of the loose ODB backend which * > is added when default backends are assigned to a repository * + * opts(GIT_OPT_GET_EXTENSIONS, git_strarray *out) + * > Returns the list of git extensions that are supported. This + * > is the list of built-in extensions supported by libgit2 and + * > custom extensions that have been added with + * > `GIT_OPT_SET_EXTENSIONS`. Extensions that have been negated + * > will not be returned. The returned list should be released + * > with `git_strarray_dispose`. + * + * opts(GIT_OPT_SET_EXTENSIONS, const char **extensions, size_t len) + * > Set that the given git extensions are supported by the caller. + * > Extensions supported by libgit2 may be negated by prefixing + * > them with a `!`. For example: setting extensions to + * > { "!noop", "newext" } indicates that the caller does not want + * > to support repositories with the `noop` extension but does want + * > to support repositories with the `newext` extension. + * * @param option Option key * @param ... value to set the option * @return 0 on success, <0 on failure diff --git a/libgit2/headers/git2/deprecated.h b/libgit2/headers/git2/deprecated.h index a2b117f..37b9fa0 100644 --- a/libgit2/headers/git2/deprecated.h +++ b/libgit2/headers/git2/deprecated.h @@ -294,6 +294,102 @@ typedef git_configmap git_cvar_map; /**@}*/ +/** @name Deprecated Diff Functions and Constants + * + * These functions and enumeration values are retained for backward + * compatibility. The newer versions of these functions and values + * should be preferred in all new code. + * + * There is no plan to remove these backward compatibility values at + * this time. + */ +/**@{*/ + +/** + * Formatting options for diff e-mail generation + */ +typedef enum { + /** Normal patch, the default */ + GIT_DIFF_FORMAT_EMAIL_NONE = 0, + + /** Don't insert "[PATCH]" in the subject header*/ + GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER = (1 << 0), + +} git_diff_format_email_flags_t; + +/** + * Options for controlling the formatting of the generated e-mail. + */ +typedef struct { + unsigned int version; + + /** see `git_diff_format_email_flags_t` above */ + uint32_t flags; + + /** This patch number */ + size_t patch_no; + + /** Total number of patches in this series */ + size_t total_patches; + + /** id to use for the commit */ + const git_oid *id; + + /** Summary of the change */ + const char *summary; + + /** Commit message's body */ + const char *body; + + /** Author of the change */ + const git_signature *author; +} git_diff_format_email_options; + +#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION 1 +#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL, NULL} + +/** + * Create an e-mail ready patch from a diff. + * + * @deprecated git_email_create_from_diff + * @see git_email_create_from_diff + */ +GIT_EXTERN(int) git_diff_format_email( + git_buf *out, + git_diff *diff, + const git_diff_format_email_options *opts); + +/** + * Create an e-mail ready patch for a commit. + * + * @deprecated git_email_create_from_commit + * @see git_email_create_from_commit + */ +GIT_EXTERN(int) git_diff_commit_as_email( + git_buf *out, + git_repository *repo, + git_commit *commit, + size_t patch_no, + size_t total_patches, + uint32_t flags, + const git_diff_options *diff_opts); + +/** + * Initialize git_diff_format_email_options structure + * + * Initializes a `git_diff_format_email_options` with default values. Equivalent + * to creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT. + * + * @param opts The `git_blame_options` struct to initialize. + * @param version The struct version; pass `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION`. + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_diff_format_email_options_init( + git_diff_format_email_options *opts, + unsigned int version); + +/**@}*/ + /** @name Deprecated Error Functions and Constants * * These functions and enumeration values are retained for backward @@ -683,6 +779,30 @@ GIT_EXTERN(int) git_oid_iszero(const git_oid *id); /**@}*/ +/** @name Deprecated OID Array Functions + * + * These types are retained for backward compatibility. The newer + * versions of these values should be preferred in all new code. + * + * There is no plan to remove these backward compatibility values at + * this time. + */ +/**@{*/ + +/** + * Free the memory referred to by the git_oidarray. This is an alias of + * `git_oidarray_dispose` and is preserved for backward compatibility. + * + * This function is deprecated, but there is no plan to remove this + * function at this time. + * + * @deprecated Use git_oidarray_dispose + * @see git_oidarray_dispose + */ +GIT_EXTERN(void) git_oidarray_free(git_oidarray *array); + +/**@}*/ + /** @name Deprecated Transfer Progress Types * * These types are retained for backward compatibility. The newer diff --git a/libgit2/headers/git2/diff.h b/libgit2/headers/git2/diff.h index 9497793..a14c019 100644 --- a/libgit2/headers/git2/diff.h +++ b/libgit2/headers/git2/diff.h @@ -133,6 +133,9 @@ typedef enum { */ GIT_DIFF_INDENT_HEURISTIC = (1u << 18), + /** Ignore blank lines */ + GIT_DIFF_IGNORE_BLANK_LINES = (1u << 19), + /** Treat all files as text, disabling binary attributes & detection */ GIT_DIFF_FORCE_TEXT = (1u << 20), /** Treat all files as binary, disabling text diffs */ @@ -168,10 +171,6 @@ typedef enum { * can apply given diff information to binary files. */ GIT_DIFF_SHOW_BINARY = (1u << 30), - - /** Ignore blank lines */ - GIT_DIFF_IGNORE_BLANK_LINES = (1u << 31), - } git_diff_option_t; /** @@ -1376,99 +1375,6 @@ GIT_EXTERN(int) git_diff_stats_to_buf( */ GIT_EXTERN(void) git_diff_stats_free(git_diff_stats *stats); -/** - * Formatting options for diff e-mail generation - */ -typedef enum { - /** Normal patch, the default */ - GIT_DIFF_FORMAT_EMAIL_NONE = 0, - - /** Don't insert "[PATCH]" in the subject header*/ - GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER = (1 << 0), - -} git_diff_format_email_flags_t; - -/** - * Options for controlling the formatting of the generated e-mail. - */ -typedef struct { - unsigned int version; - - /** see `git_diff_format_email_flags_t` above */ - uint32_t flags; - - /** This patch number */ - size_t patch_no; - - /** Total number of patches in this series */ - size_t total_patches; - - /** id to use for the commit */ - const git_oid *id; - - /** Summary of the change */ - const char *summary; - - /** Commit message's body */ - const char *body; - - /** Author of the change */ - const git_signature *author; -} git_diff_format_email_options; - -#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION 1 -#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL, NULL} - -/** - * Create an e-mail ready patch from a diff. - * - * @param out buffer to store the e-mail patch in - * @param diff containing the commit - * @param opts structure with options to influence content and formatting. - * @return 0 or an error code - */ -GIT_EXTERN(int) git_diff_format_email( - git_buf *out, - git_diff *diff, - const git_diff_format_email_options *opts); - -/** - * Create an e-mail ready patch for a commit. - * - * Does not support creating patches for merge commits (yet). - * - * @param out buffer to store the e-mail patch in - * @param repo containing the commit - * @param commit pointer to up commit - * @param patch_no patch number of the commit - * @param total_patches total number of patches in the patch set - * @param flags determines the formatting of the e-mail - * @param diff_opts structure with options to influence diff or NULL for defaults. - * @return 0 or an error code - */ -GIT_EXTERN(int) git_diff_commit_as_email( - git_buf *out, - git_repository *repo, - git_commit *commit, - size_t patch_no, - size_t total_patches, - uint32_t flags, - const git_diff_options *diff_opts); - -/** - * Initialize git_diff_format_email_options structure - * - * Initializes a `git_diff_format_email_options` with default values. Equivalent - * to creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT. - * - * @param opts The `git_blame_options` struct to initialize. - * @param version The struct version; pass `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION`. - * @return Zero on success; -1 on failure. - */ -GIT_EXTERN(int) git_diff_format_email_options_init( - git_diff_format_email_options *opts, - unsigned int version); - /** * Patch ID options structure * diff --git a/libgit2/headers/git2/email.h b/libgit2/headers/git2/email.h new file mode 100644 index 0000000..b56be5d --- /dev/null +++ b/libgit2/headers/git2/email.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_git_email_h__ +#define INCLUDE_git_email_h__ + +#include "common.h" + +/** + * @file git2/email.h + * @brief Git email formatting and application routines. + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Formatting options for diff e-mail generation + */ +typedef enum { + /** Normal patch, the default */ + GIT_EMAIL_CREATE_DEFAULT = 0, + + /** Do not include patch numbers in the subject prefix. */ + GIT_EMAIL_CREATE_OMIT_NUMBERS = (1u << 0), + + /** + * Include numbers in the subject prefix even when the + * patch is for a single commit (1/1). + */ + GIT_EMAIL_CREATE_ALWAYS_NUMBER = (1u << 1), + + /** Do not perform rename or similarity detection. */ + GIT_EMAIL_CREATE_NO_RENAMES = (1u << 2), +} git_email_create_flags_t; + +/** + * Options for controlling the formatting of the generated e-mail. + */ +typedef struct { + unsigned int version; + + /** see `git_email_create_flags_t` above */ + uint32_t flags; + + /** Options to use when creating diffs */ + git_diff_options diff_opts; + + /** Options for finding similarities within diffs */ + git_diff_find_options diff_find_opts; + + /** + * The subject prefix, by default "PATCH". If set to an empty + * string ("") then only the patch numbers will be shown in the + * prefix. If the subject_prefix is empty and patch numbers + * are not being shown, the prefix will be omitted entirely. + */ + const char *subject_prefix; + + /** + * The starting patch number; this cannot be 0. By default, + * this is 1. + */ + size_t start_number; + + /** The "re-roll" number. By default, there is no re-roll. */ + size_t reroll_number; +} git_email_create_options; + +/* + * By default, our options include rename detection and binary + * diffs to match `git format-patch`. + */ +#define GIT_EMAIL_CREATE_OPTIONS_VERSION 1 +#define GIT_EMAIL_CREATE_OPTIONS_INIT \ +{ \ + GIT_EMAIL_CREATE_OPTIONS_VERSION, \ + GIT_EMAIL_CREATE_DEFAULT, \ + { GIT_DIFF_OPTIONS_VERSION, GIT_DIFF_SHOW_BINARY, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, NULL, 3 }, \ + GIT_DIFF_FIND_OPTIONS_INIT \ +} + +/** + * Create a diff for a commit in mbox format for sending via email. + * + * @param out buffer to store the e-mail patch in + * @param diff the changes to include in the email + * @param patch_idx the patch index + * @param patch_count the total number of patches that will be included + * @param commit_id the commit id for this change + * @param summary the commit message for this change + * @param body optional text to include above the diffstat + * @param author the person who authored this commit + * @param opts email creation options + */ +GIT_EXTERN(int) git_email_create_from_diff( + git_buf *out, + git_diff *diff, + size_t patch_idx, + size_t patch_count, + const git_oid *commit_id, + const char *summary, + const char *body, + const git_signature *author, + const git_email_create_options *opts); + +/** + * Create a diff for a commit in mbox format for sending via email. + * The commit must not be a merge commit. + * + * @param out buffer to store the e-mail patch in + * @param commit commit to create a patch for + * @param opts email creation options + */ +GIT_EXTERN(int) git_email_create_from_commit( + git_buf *out, + git_commit *commit, + const git_email_create_options *opts); + +GIT_END_DECL + +/** @} */ + +#endif diff --git a/libgit2/headers/git2/filter.h b/libgit2/headers/git2/filter.h index 044c3b8..0465e5b 100644 --- a/libgit2/headers/git2/filter.h +++ b/libgit2/headers/git2/filter.h @@ -66,11 +66,17 @@ typedef struct { /** See `git_filter_flag_t` above */ uint32_t flags; +#ifdef GIT_DEPRECATE_HARD + void *reserved; +#else + git_oid *commit_id; +#endif + /** * The commit to load attributes from, when * `GIT_FILTER_ATTRIBUTES_FROM_COMMIT` is specified. */ - git_oid *commit_id; + git_oid attr_commit_id; } git_filter_options; #define GIT_FILTER_OPTIONS_VERSION 1 diff --git a/libgit2/headers/git2/notes.h b/libgit2/headers/git2/notes.h index c36149e..c135881 100644 --- a/libgit2/headers/git2/notes.h +++ b/libgit2/headers/git2/notes.h @@ -84,8 +84,8 @@ GIT_EXTERN(void) git_note_iterator_free(git_note_iterator *it); * (negative value) */ GIT_EXTERN(int) git_note_next( - git_oid* note_id, - git_oid* annotated_id, + git_oid *note_id, + git_oid *annotated_id, git_note_iterator *it); diff --git a/libgit2/headers/git2/oidarray.h b/libgit2/headers/git2/oidarray.h index 0b32045..94fc58d 100644 --- a/libgit2/headers/git2/oidarray.h +++ b/libgit2/headers/git2/oidarray.h @@ -19,19 +19,16 @@ typedef struct git_oidarray { } git_oidarray; /** - * Free the OID array - * - * This method must (and must only) be called on `git_oidarray` - * objects where the array is allocated by the library. Not doing so, - * will result in a memory leak. + * Free the object IDs contained in an oid_array. This method should + * be called on `git_oidarray` objects that were provided by the + * library. Not doing so will result in a memory leak. * * This does not free the `git_oidarray` itself, since the library will - * never allocate that object directly itself (it is more commonly embedded - * inside another struct or created on the stack). + * never allocate that object directly itself. * * @param array git_oidarray from which to free oid data */ -GIT_EXTERN(void) git_oidarray_free(git_oidarray *array); +GIT_EXTERN(void) git_oidarray_dispose(git_oidarray *array); /** @} */ GIT_END_DECL diff --git a/libgit2/headers/git2/remote.h b/libgit2/headers/git2/remote.h index 1f52fcd..4d57eaa 100644 --- a/libgit2/headers/git2/remote.h +++ b/libgit2/headers/git2/remote.h @@ -243,7 +243,7 @@ GIT_EXTERN(const char *) git_remote_pushurl(const git_remote *remote); * @param url the url to set * @return 0 or an error value */ -GIT_EXTERN(int) git_remote_set_url(git_repository *repo, const char *remote, const char* url); +GIT_EXTERN(int) git_remote_set_url(git_repository *repo, const char *remote, const char *url); /** * Set the remote's url for pushing in the configuration. @@ -257,7 +257,7 @@ GIT_EXTERN(int) git_remote_set_url(git_repository *repo, const char *remote, con * @param url the url to set * @return 0, or an error code */ -GIT_EXTERN(int) git_remote_set_pushurl(git_repository *repo, const char *remote, const char* url); +GIT_EXTERN(int) git_remote_set_pushurl(git_repository *repo, const char *remote, const char *url); /** * Set the url for this particular url instance. The URL in the @@ -451,7 +451,7 @@ typedef int GIT_CALLBACK(git_push_transfer_progress_cb)( unsigned int current, unsigned int total, size_t bytes, - void* payload); + void *payload); /** * Represents an update which will be performed on the remote during push @@ -971,7 +971,7 @@ GIT_EXTERN(int) git_remote_rename( * @param remote_name name to be checked. * @return 0 on success or an error code */ -int git_remote_name_is_valid(int *valid, const char *remote_name); +GIT_EXTERN(int) git_remote_name_is_valid(int *valid, const char *remote_name); /** * Delete an existing persisted remote. diff --git a/libgit2/headers/git2/repository.h b/libgit2/headers/git2/repository.h index 45becc7..8d1cffc 100644 --- a/libgit2/headers/git2/repository.h +++ b/libgit2/headers/git2/repository.h @@ -762,13 +762,15 @@ GIT_EXTERN(int) git_repository_mergehead_foreach( * * @param out Output value of calculated SHA * @param repo Repository pointer - * @param path Path to file on disk whose contents should be hashed. If the - * repository is not NULL, this can be a relative path. + * @param path Path to file on disk whose contents should be hashed. This + * may be an absolute path or a relative path, in which case it + * will be treated as a path within the working directory. * @param type The object type to hash as (e.g. GIT_OBJECT_BLOB) * @param as_path The path to use to look up filtering rules. If this is - * NULL, then the `path` parameter will be used instead. If - * this is passed as the empty string, then no filters will be - * applied when calculating the hash. + * an empty string then no filters will be applied when + * calculating the hash. If this is `NULL` and the `path` + * parameter is a file within the repository's working + * directory, then the `path` will be used. * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository_hashfile( @@ -797,8 +799,8 @@ GIT_EXTERN(int) git_repository_hashfile( * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository_set_head( - git_repository* repo, - const char* refname); + git_repository *repo, + const char *refname); /** * Make the repository HEAD directly point to the Commit. @@ -817,8 +819,8 @@ GIT_EXTERN(int) git_repository_set_head( * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository_set_head_detached( - git_repository* repo, - const git_oid* commitish); + git_repository *repo, + const git_oid *commitish); /** * Make the repository HEAD directly point to the Commit. @@ -854,7 +856,7 @@ GIT_EXTERN(int) git_repository_set_head_detached_from_annotated( * branch or an error code */ GIT_EXTERN(int) git_repository_detach_head( - git_repository* repo); + git_repository *repo); /** * Repository state diff --git a/libgit2/headers/git2/stash.h b/libgit2/headers/git2/stash.h index 625e51b..795920e 100644 --- a/libgit2/headers/git2/stash.h +++ b/libgit2/headers/git2/stash.h @@ -200,7 +200,7 @@ GIT_EXTERN(int) git_stash_apply( */ typedef int GIT_CALLBACK(git_stash_cb)( size_t index, - const char* message, + const char *message, const git_oid *stash_id, void *payload); diff --git a/libgit2/headers/git2/stdint.h b/libgit2/headers/git2/stdint.h index c66fbb8..6950427 100644 --- a/libgit2/headers/git2/stdint.h +++ b/libgit2/headers/git2/stdint.h @@ -29,9 +29,7 @@ // /////////////////////////////////////////////////////////////////////////////// -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] +#ifdef _MSC_VER // [ #ifndef _MSC_STDINT_H_ // [ #define _MSC_STDINT_H_ @@ -245,3 +243,5 @@ typedef uint64_t uintmax_t; #endif // _MSC_STDINT_H_ ] + +#endif // _MSC_VER ] \ No newline at end of file diff --git a/libgit2/headers/git2/sys/email.h b/libgit2/headers/git2/sys/email.h new file mode 100644 index 0000000..6f4a286 --- /dev/null +++ b/libgit2/headers/git2/sys/email.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_sys_git_email_h__ +#define INCLUDE_sys_git_email_h__ + +/** + * @file git2/sys/email.h + * @brief Advanced git email creation routines + * @defgroup git_email Advanced git email creation routines + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Create a diff for a commit in mbox format for sending via email. + * + * @param out buffer to store the e-mail patch in + * @param diff the changes to include in the email + * @param patch_idx the patch index + * @param patch_count the total number of patches that will be included + * @param commit_id the commit id for this change + * @param summary the commit message for this change + * @param body optional text to include above the diffstat + * @param author the person who authored this commit + * @param opts email creation options + */ +GIT_EXTERN(int) git_email_create_from_diff( + git_buf *out, + git_diff *diff, + size_t patch_idx, + size_t patch_count, + const git_oid *commit_id, + const char *summary, + const char *body, + const git_signature *author, + const git_email_create_options *opts); + +/** @} */ +GIT_END_DECL +#endif diff --git a/libgit2/headers/git2/version.h b/libgit2/headers/git2/version.h index 46adfec..3503a62 100644 --- a/libgit2/headers/git2/version.h +++ b/libgit2/headers/git2/version.h @@ -7,12 +7,12 @@ #ifndef INCLUDE_git_version_h__ #define INCLUDE_git_version_h__ -#define LIBGIT2_VERSION "1.2.0" +#define LIBGIT2_VERSION "1.3.0" #define LIBGIT2_VER_MAJOR 1 -#define LIBGIT2_VER_MINOR 2 +#define LIBGIT2_VER_MINOR 3 #define LIBGIT2_VER_REVISION 0 #define LIBGIT2_VER_PATCH 0 -#define LIBGIT2_SOVERSION "1.2" +#define LIBGIT2_SOVERSION "1.3" #endif diff --git a/libgit2/libgit2-1.2.0.so b/libgit2/libgit2.so.1.3.0 similarity index 51% rename from libgit2/libgit2-1.2.0.so rename to libgit2/libgit2.so.1.3.0 index 446c3f7..7aeba91 100755 Binary files a/libgit2/libgit2-1.2.0.so and b/libgit2/libgit2.so.1.3.0 differ diff --git a/test/assets/keys/id_rsa b/test/assets/keys/id_rsa new file mode 100644 index 0000000..26797dd --- /dev/null +++ b/test/assets/keys/id_rsa @@ -0,0 +1,39 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCcT0eRuC +NRvnDkorGSaQqaAAAAEAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQC5bu0UuVdG +eimAo2mu87uy/hWCbvie6N8H4a7ZjnSThpDIv0clcnbycCukDb/d8o9FRIPNNp2astBlWp +nZ9vCHByP8z8ITO/pG5Mu0Wp0n1Hrja5UVdk4cBvvoEcr2sdNqj3wUUhqeAnNCYeeJuW9V +j2JNV2wwJYPAZBDi6GJTM9FPlCJz+QA6dJLkUiIwngrL8bSPzXd+1mzHOrIcqjxKm8J+ed +3tVSlRq7YvefrACUOTI9ymXwYr8a/TiwA76nTHvNPYIhmzH8PEF65QsFTs/qk0nqSwmqKA +vineapBMmrb3Vf/Hd/c0nEMgHt6gy/+dtlgPh1bTqaQaS/oeo5OtjMDK/w93SR9M3UZBX+ +eeMUGfyhvYT4Nvo3TumKzaZ6peGu1T0MKprSwgRdI+v1q+58sKfzSK5QevTpKiX1+4Leo6 +BiNKKu6doD19fgNSD/dwfOWehxFtD8q/1J5k0QPgqslFkqyZJXRCzzowRYunSN+AaHVD3W +o4AuqtfTiazPMAAAWQVrRkwWjO1Fcw7zebagqfBufB05nc08wL911ZPCVwqVSIepcEK/hM +CJ/5/N+UILn9BXGe9qmOHPUuMa9UaLBSyzmlJ1s/NMGLzYWiv62SX1QNEXPegxwLasQvbL +njjzdESGX+qUHxT4okNH52zi4DcBLX4HPL/TYQsKTNxCOclOljPDo+3IfHzx76yG5dAl5L +C7ghLsd1zxpwZI+ag7NhNzZ4hBxX9JUenAfGyuOL+YCTp8JnU+dXJ3XaA3WAVGnvsZlAaq +GJUGCdLlMiacO0eXNTm53xc92X9tPmetEVwhuD/Af7Vc4dOmH9Zu+7n9z9bLPrOowNr7ue +w8YCqCg83iuQYmSSPj/JTvCzaoGDfW+yjALlb5RJUAIMJ51k0WyVIyqS0TE8+EINKETlj7 +iIx1Y5z54ZnldlqrD2vLImO2b401oOb7fJUEU9Ke5NPi93tsps8nYKhatcRYLnLq9gsFv9 +YlDCueoJJobg1k9TO+IwxraPgz3jl24zskSKT/tLFvsz0fQM5QWha2vB8kyZI067ojuNLb +/mj5itgLIDmISa9cf98HhafeE8kGAtKEJR2rLwvb79EAhZ2ypt2I8LVur5hCM4cC9vSVyS +dq/f4sgQpyQqSByMXeLEFYJSCDDc/PL3RC0Q9PqrQYZ1+pqj/6esV3ujLogMAHqEuW4EVw +tMDUvjzfnC8MVUQpc5H4yonsWjGeGhH+VEkBSVISpABTSrYFN5kBodPD16wmRTbFF4tTQq +Egmj5vUmxSY+a2EjDJREQBerMhj3W5sPhAB1QGVVn5kyFvmsjM4t06zzZj/R5muIcX0cT+ +Th3N+xeYIuVi9kS5v7yOBlMk0KGq8QATSL/u+niO0e0neoT5Jv6E7EIafAFrn3Ji0rNave +ObCqse3yZct0pbspM4f0c9mHaVbzmvwwtjmUFGdMJgse0UARXqvOlF9PUaN/AhqQlIyVjj +ednPLrOz617XDSixiP+tKzKmqjZsBASZzpGwpHKii9/k7Q7aG5/Int8ulBS3H8C6ipMSxW +EKSMJ4g6k33RY1EFL3dWtJYWhReAhY6kvIc3lmSeo7I9SQWXLupx0NUnkXeO63hLmJ9tjD +CXeI0cwb2a6DWKLh6c2gQ5LPNb/8xzvYJfdop2Oxr+9L2NP7pIgvYr/cmgPtF5WkLT2Ypk +z+KgwWxUKRiK/3G+dVe27u0Id7Yi596wnNGxJfZmlnbfioY4i+K9KcyS08LxlmgsIsQHiY +Scv6SuamPdjiHdSwK/GuAcQUVwXQRA7DoV2uxOosAaUXWMiiSjJ3n1L8IVgp17OKxVN0Bd +5phre4VhYFoXGnq43xFAY3XQJctBqLPdb47RNi3JlhVK+Q1WKzK9OWbDoiseoNnMD5NXOt +Wqf/vxD6AJEyO8sOT55l6hZAkNHIfFUGx4MNmLl12hJYSZgY9tx7aizz8RMT6GMBammQcU +Q0pNDF1RBFOtxgb/QE+9/Vym4dMGnJrhhdbcYZbKngcsho4Qs39qMQvv0V23zAExreQH8U +TBTZYyYkiPqdUiB2fNCW89QWksvBe3CXZAC0T0tdBcEYe5UPJRQ/K2FS6bJTYmxDkDWzHD +9iHbiu3Z8JGB9kHT6B5AgM+fYgEhpCgieDEHdF85cXtGSt8rjFFW6SMS70aLkgzFpYVeD0 +zgzumI6JRY3vSMpUY60NCz+FOmIxy7oIpv7nDf/6Ubvah/heUF/P6IQwOQXumVMK9/Khqx +j5TxaRCZ7fXV7IXH1hjQgWSZkGNUHc+rEAZdPOYFXlQb/2+DkO8IE7SxSWwk8tDzS0L7+H +hWXgIm7mjIB6HDNfRb2zPL7gOgm83qZfrhSdP76XqnuV1LvvZMIs2dC8lKFfLk6oayzUvQ +z5AMR0EutUSCby7+DKyBmaYSq0s= +-----END OPENSSH PRIVATE KEY----- \ No newline at end of file diff --git a/test/assets/keys/id_rsa.pub b/test/assets/keys/id_rsa.pub new file mode 100644 index 0000000..3f65b26 --- /dev/null +++ b/test/assets/keys/id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC5bu0UuVdGeimAo2mu87uy/hWCbvie6N8H4a7ZjnSThpDIv0clcnbycCukDb/d8o9FRIPNNp2astBlWpnZ9vCHByP8z8ITO/pG5Mu0Wp0n1Hrja5UVdk4cBvvoEcr2sdNqj3wUUhqeAnNCYeeJuW9Vj2JNV2wwJYPAZBDi6GJTM9FPlCJz+QA6dJLkUiIwngrL8bSPzXd+1mzHOrIcqjxKm8J+ed3tVSlRq7YvefrACUOTI9ymXwYr8a/TiwA76nTHvNPYIhmzH8PEF65QsFTs/qk0nqSwmqKAvineapBMmrb3Vf/Hd/c0nEMgHt6gy/+dtlgPh1bTqaQaS/oeo5OtjMDK/w93SR9M3UZBX+eeMUGfyhvYT4Nvo3TumKzaZ6peGu1T0MKprSwgRdI+v1q+58sKfzSK5QevTpKiX1+4Leo6BiNKKu6doD19fgNSD/dwfOWehxFtD8q/1J5k0QPgqslFkqyZJXRCzzowRYunSN+AaHVD3Wo4AuqtfTiazPM= libgit2dart \ No newline at end of file diff --git a/test/credentials_test.dart b/test/credentials_test.dart new file mode 100644 index 0000000..c61073a --- /dev/null +++ b/test/credentials_test.dart @@ -0,0 +1,170 @@ +import 'dart:io'; +import 'package:test/test.dart'; +import 'package:libgit2dart/libgit2dart.dart'; + +void main() { + final cloneDir = Directory('${Directory.systemTemp.path}/credentials_cloned'); + + setUp(() async { + if (await cloneDir.exists()) { + cloneDir.delete(recursive: true); + } + }); + + tearDown(() async { + if (await cloneDir.exists()) { + cloneDir.delete(recursive: true); + } + }); + group('Credentials', () { + test('successfully initializes username credentials', () { + final credentials = const Username('user'); + + expect(credentials, isA()); + expect(credentials.username, 'user'); + expect(credentials.credentialType, GitCredential.username); + }); + + test('successfully initializes username/password credentials', () { + final credentials = const UserPass( + username: 'user', + password: 'password', + ); + + expect(credentials, isA()); + expect(credentials.username, 'user'); + expect(credentials.password, 'password'); + expect(credentials.credentialType, GitCredential.userPassPlainText); + }); + + test('successfully initializes keypair credentials', () { + final credentials = const Keypair( + username: 'user', + pubKey: 'id_rsa.pub', + privateKey: 'id_rsa', + passPhrase: 'passphrase', + ); + + expect(credentials, isA()); + expect(credentials.username, 'user'); + expect(credentials.pubKey, 'id_rsa.pub'); + expect(credentials.privateKey, 'id_rsa'); + expect(credentials.passPhrase, 'passphrase'); + expect(credentials.credentialType, GitCredential.sshKey); + }); + + test('successfully initializes keypair from memory credentials', () { + final credentials = const KeypairFromMemory( + username: 'user', + pubKey: 'pubkey data', + privateKey: 'private key data', + passPhrase: 'passphrase', + ); + + expect(credentials, isA()); + expect(credentials.username, 'user'); + expect(credentials.pubKey, 'pubkey data'); + expect(credentials.privateKey, 'private key data'); + expect(credentials.passPhrase, 'passphrase'); + expect(credentials.credentialType, GitCredential.sshMemory); + }); + + test('successfully initializes keypair from agent credentials', () { + final credentials = const KeypairFromAgent('user'); + + expect(credentials, isA()); + expect(credentials.username, 'user'); + expect(credentials.credentialType, GitCredential.sshKey); + }); + + test('sucessfully clones repository with provided keypair', () { + final keypair = const Keypair( + username: 'git', + pubKey: 'test/assets/keys/id_rsa.pub', + privateKey: 'test/assets/keys/id_rsa', + passPhrase: 'empty', + ); + final callbacks = Callbacks(credentials: keypair); + + final repo = Repository.clone( + url: 'ssh://git@github.com/libgit2/TestGitRepository', + localPath: cloneDir.path, + callbacks: callbacks, + ); + + expect(repo.isEmpty, false); + + repo.free(); + }); + + test('throws when no credentials is provided', () { + expect( + () => Repository.clone( + url: 'ssh://git@github.com/libgit2/TestGitRepository', + localPath: cloneDir.path, + ), + throwsA(isA()), + ); + }); + + test('throws when provided keypair is invalid', () { + final keypair = const Keypair( + username: 'git', + pubKey: 'invalid.pub', + privateKey: 'invalid', + passPhrase: 'invalid', + ); + final callbacks = Callbacks(credentials: keypair); + + expect( + () => Repository.clone( + url: 'ssh://git@github.com/libgit2/TestGitRepository', + localPath: cloneDir.path, + callbacks: callbacks, + ), + throwsA(isA()), + ); + }); + + test('sucessfully clones repository with provided keypair from memory', () { + final pubKey = File('test/assets/keys/id_rsa.pub').readAsStringSync(); + final privateKey = File('test/assets/keys/id_rsa').readAsStringSync(); + final keypair = KeypairFromMemory( + username: 'git', + pubKey: pubKey, + privateKey: privateKey, + passPhrase: 'empty', + ); + final callbacks = Callbacks(credentials: keypair); + + final repo = Repository.clone( + url: 'ssh://git@github.com/libgit2/TestGitRepository', + localPath: cloneDir.path, + callbacks: callbacks, + ); + + expect(repo.isEmpty, false); + + repo.free(); + }); + + test('sucessfully clones repository with provided username and password', + () { + final userPass = const UserPass( + username: 'libgit2', + password: 'libgit2', + ); + final callbacks = Callbacks(credentials: userPass); + + final repo = Repository.clone( + url: 'https://github.com/libgit2/TestGitRepository', + localPath: cloneDir.path, + callbacks: callbacks, + ); + + expect(repo.isEmpty, false); + + repo.free(); + }); + }); +} diff --git a/test/features_test.dart b/test/features_test.dart new file mode 100644 index 0000000..63e1673 --- /dev/null +++ b/test/features_test.dart @@ -0,0 +1,14 @@ +import 'package:libgit2dart/src/features.dart'; +import 'package:test/test.dart'; +import 'package:libgit2dart/libgit2dart.dart'; + +void main() { + group('Features', () { + test('returns list of compile time options for libgit2', () { + expect( + Features.list, + [GitFeature.threads, GitFeature.https, GitFeature.ssh, GitFeature.nsec], + ); + }); + }); +}