From 127849519d687237baaf1e86503972e7d5565a44 Mon Sep 17 00:00:00 2001 From: Aleksey Kulikov Date: Tue, 19 Oct 2021 17:16:39 +0300 Subject: [PATCH] test: add more tests for throws and their messages --- lib/src/bindings/attr.dart | 22 +-- lib/src/bindings/blame.dart | 12 +- lib/src/bindings/commit.dart | 47 +---- lib/src/bindings/describe.dart | 30 +-- lib/src/bindings/diff.dart | 84 ++------- lib/src/bindings/index.dart | 49 +---- lib/src/bindings/libgit2_bindings.dart | 1 + lib/src/bindings/mailmap.dart | 69 ++----- lib/src/bindings/merge.dart | 71 ++----- lib/src/bindings/note.dart | 13 +- lib/src/bindings/odb.dart | 68 +------ lib/src/bindings/oid.dart | 65 ++----- lib/src/bindings/patch.dart | 118 +++--------- lib/src/bindings/rebase.dart | 32 +--- lib/src/bindings/refdb.dart | 11 +- lib/src/bindings/reference.dart | 75 +------- lib/src/bindings/reflog.dart | 12 +- lib/src/bindings/remote.dart | 137 ++++---------- lib/src/bindings/repository.dart | 167 ++-------------- lib/src/bindings/reset.dart | 14 +- lib/src/bindings/revwalk.dart | 16 +- lib/src/bindings/signature.dart | 12 +- lib/src/bindings/stash.dart | 20 +- lib/src/bindings/status.dart | 10 +- lib/src/bindings/submodule.dart | 112 ++--------- lib/src/bindings/tag.dart | 5 - lib/src/bindings/tree.dart | 10 - lib/src/bindings/treebuilder.dart | 22 +-- lib/src/bindings/worktree.dart | 39 +--- lib/src/blob.dart | 2 +- lib/src/branch.dart | 25 ++- lib/src/commit.dart | 28 ++- lib/src/diff.dart | 17 +- lib/src/index.dart | 8 - lib/src/mailmap.dart | 28 ++- lib/src/odb.dart | 4 - lib/src/patch.dart | 16 +- lib/src/rebase.dart | 44 ++--- lib/src/reference.dart | 4 - lib/src/reflog.dart | 2 - lib/src/remote.dart | 12 +- lib/src/repository.dart | 73 ++++--- lib/src/revwalk.dart | 4 - lib/src/signature.dart | 6 +- lib/src/submodule.dart | 21 +-- lib/src/tree.dart | 2 - lib/src/treebuilder.dart | 4 - lib/src/util.dart | 2 + lib/src/worktree.dart | 8 +- test/blame_test.dart | 85 ++++++++- test/blob_test.dart | 54 ++++-- test/branch_test.dart | 158 ++++++++++++++-- test/checkout_test.dart | 68 ++++++- test/commit_test.dart | 76 +++++++- test/credentials_test.dart | 14 ++ test/describe_test.dart | 26 ++- test/diff_test.dart | 166 ++++++++++++++-- test/index_test.dart | 168 ++++++++++++++++- test/mailmap_test.dart | 35 ++++ test/merge_test.dart | 209 +++++++++++--------- test/note_test.dart | 60 +++++- test/odb_test.dart | 75 +++++++- test/oid_test.dart | 21 ++- test/packbuilder_test.dart | 67 ++++++- test/patch_test.dart | 61 ++++-- test/rebase_test.dart | 108 ++++++++++- test/reference_test.dart | 150 +++++++++++++-- test/remote_prune_test.dart | 13 ++ test/remote_test.dart | 251 ++++++++++++++++++++++++- test/repository_empty_test.dart | 14 ++ test/repository_test.dart | 153 ++++++++++++++- test/revparse_test.dart | 63 ++++++- test/revwalk_test.dart | 61 +++++- test/signature_test.dart | 28 +++ test/stash_test.dart | 101 +++++++++- test/submodule_test.dart | 77 ++++++++ test/tag_test.dart | 91 +++++++++ test/tree_test.dart | 13 ++ test/treebuilder_test.dart | 67 +++++++ test/worktree_test.dart | 56 ++++++ 80 files changed, 2741 insertions(+), 1501 deletions(-) diff --git a/lib/src/bindings/attr.dart b/lib/src/bindings/attr.dart index 55b68ba..593ac78 100644 --- a/lib/src/bindings/attr.dart +++ b/lib/src/bindings/attr.dart @@ -1,6 +1,5 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; -import '../error.dart'; import '../util.dart'; import 'libgit2_bindings.dart'; @@ -8,8 +7,6 @@ import 'libgit2_bindings.dart'; /// /// Returned value can be either `true`, `false`, `null` (if the attribute was not set at all), /// or a [String] value, if the attribute was set to an actual string. -/// -/// Throws a [LibGit2Error] if error occured. Object? getAttribute({ required Pointer repoPointer, required int flags, @@ -19,33 +16,28 @@ Object? getAttribute({ final out = calloc>(); final pathC = path.toNativeUtf8().cast(); final nameC = name.toNativeUtf8().cast(); - final error = libgit2.git_attr_get(out, repoPointer, flags, pathC, nameC); + libgit2.git_attr_get(out, repoPointer, flags, pathC, nameC); calloc.free(pathC); calloc.free(nameC); - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } - final attributeValue = libgit2.git_attr_value(out.value); if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_UNSPECIFIED) { calloc.free(out); return null; - } else if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_TRUE) { + } + if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_TRUE) { calloc.free(out); return true; - } else if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_FALSE) { + } + if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_FALSE) { calloc.free(out); return false; - } else if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_STRING) { + } + if (attributeValue == git_attr_value_t.GIT_ATTR_VALUE_STRING) { final result = out.value.cast().toDartString(); calloc.free(out); return result; - } else { - calloc.free(out); - throw Exception('The attribute value from libgit2 is invalid'); } } diff --git a/lib/src/bindings/blame.dart b/lib/src/bindings/blame.dart index 6516c6e..1fbbecc 100644 --- a/lib/src/bindings/blame.dart +++ b/lib/src/bindings/blame.dart @@ -21,17 +21,7 @@ Pointer file({ final out = calloc>(); final pathC = path.toNativeUtf8().cast(); final options = calloc(); - final optionsError = libgit2.git_blame_options_init( - options, - GIT_BLAME_OPTIONS_VERSION, - ); - - if (optionsError < 0) { - calloc.free(out); - calloc.free(pathC); - calloc.free(options); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_blame_options_init(options, GIT_BLAME_OPTIONS_VERSION); options.ref.flags = flags; diff --git a/lib/src/bindings/commit.dart b/lib/src/bindings/commit.dart index 0d9d73a..5dbcae0 100644 --- a/lib/src/bindings/commit.dart +++ b/lib/src/bindings/commit.dart @@ -24,32 +24,8 @@ Pointer lookup({ } } -/// 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) { - calloc.free(out); - 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. +/// Creates an annotated commit from the given commit id. The resulting annotated commit +/// must be freed with [annotatedFree]. /// /// 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 @@ -77,7 +53,7 @@ Pointer> annotatedLookup({ } } -/// Frees a git_annotated_commit. +/// Frees an annotated commit. void annotatedFree(Pointer commit) { libgit2.git_annotated_commit_free(commit); } @@ -168,13 +144,7 @@ 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; - } + return libgit2.git_commit_parent_id(commitPointer, position); } /// Get the commit time (i.e. committer time) of a commit. @@ -209,14 +179,7 @@ Pointer revertCommit({ }) { final out = calloc>(); final opts = calloc(); - final optsError = - libgit2.git_merge_options_init(opts, GIT_MERGE_OPTIONS_VERSION); - - if (optsError < 0) { - calloc.free(out); - calloc.free(opts); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_merge_options_init(opts, GIT_MERGE_OPTIONS_VERSION); final error = libgit2.git_revert_commit( out, diff --git a/lib/src/bindings/describe.dart b/lib/src/bindings/describe.dart index 81539e0..83cd994 100644 --- a/lib/src/bindings/describe.dart +++ b/lib/src/bindings/describe.dart @@ -77,8 +77,6 @@ Pointer workdir({ } /// Print the describe result to a buffer. -/// -/// Throws a [LibGit2Error] if error occured. String format({ required Pointer describeResultPointer, int? abbreviatedSize, @@ -87,17 +85,11 @@ String format({ }) { final out = calloc(sizeOf()); final opts = calloc(); - final optsError = libgit2.git_describe_format_options_init( + libgit2.git_describe_format_options_init( opts, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, ); - if (optsError < 0) { - calloc.free(out); - calloc.free(opts); - throw LibGit2Error(libgit2.git_error_last()); - } - if (abbreviatedSize != null) { opts.ref.abbreviated_size = abbreviatedSize; } @@ -108,18 +100,14 @@ String format({ opts.ref.dirty_suffix = dirtySuffix.toNativeUtf8().cast(); } - final error = libgit2.git_describe_format(out, describeResultPointer, opts); + libgit2.git_describe_format(out, describeResultPointer, opts); + + final result = out.ref.ptr.cast().toDartString(); calloc.free(opts); + calloc.free(out); - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - final result = out.ref.ptr.cast().toDartString(); - calloc.free(out); - return result; - } + return result; } /// Free the describe result. @@ -136,15 +124,11 @@ Pointer _initOpts({ bool? showCommitOidAsFallback, }) { final opts = calloc(); - final error = libgit2.git_describe_options_init( + libgit2.git_describe_options_init( opts, GIT_DESCRIBE_OPTIONS_VERSION, ); - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } - if (maxCandidatesTags != null) { opts.ref.max_candidates_tags = maxCandidatesTags; } diff --git a/lib/src/bindings/diff.dart b/lib/src/bindings/diff.dart index 9856959..1625ba3 100644 --- a/lib/src/bindings/diff.dart +++ b/lib/src/bindings/diff.dart @@ -5,8 +5,6 @@ import 'libgit2_bindings.dart'; import '../util.dart'; /// Create a diff between the repository index and the workdir directory. -/// -/// Throws a [LibGit2Error] if error occured. Pointer indexToWorkdir({ required Pointer repoPointer, required Pointer indexPointer, @@ -21,16 +19,7 @@ Pointer indexToWorkdir({ interhunkLines: interhunkLines, ); - final error = libgit2.git_diff_index_to_workdir( - out, - repoPointer, - indexPointer, - opts, - ); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_diff_index_to_workdir(out, repoPointer, indexPointer, opts); calloc.free(opts); @@ -38,8 +27,6 @@ Pointer indexToWorkdir({ } /// Create a diff between a tree and repository index. -/// -/// Throws a [LibGit2Error] if error occured. Pointer treeToIndex({ required Pointer repoPointer, required Pointer treePointer, @@ -55,7 +42,7 @@ Pointer treeToIndex({ interhunkLines: interhunkLines, ); - final error = libgit2.git_diff_tree_to_index( + libgit2.git_diff_tree_to_index( out, repoPointer, treePointer, @@ -63,10 +50,6 @@ Pointer treeToIndex({ opts, ); - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } - calloc.free(opts); return out.value; @@ -166,21 +149,14 @@ void merge({ /// /// This function will only read patch files created by a git implementation, it will not /// read unified diffs produced by the `diff` program, nor any other types of patch files. -/// -/// Throws a [LibGit2Error] if error occured. Pointer parse(String content) { final out = calloc>(); final contentC = content.toNativeUtf8().cast(); - final error = libgit2.git_diff_from_buffer(out, contentC, content.length); + libgit2.git_diff_from_buffer(out, contentC, content.length); calloc.free(contentC); - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out.value; - } + return out.value; } /// Transform a diff marking file renames, copies, etc. @@ -200,15 +176,7 @@ void findSimilar({ required int renameLimit, }) { final opts = calloc(); - final optsError = libgit2.git_diff_find_options_init( - opts, - GIT_DIFF_FIND_OPTIONS_VERSION, - ); - - if (optsError < 0) { - calloc.free(opts); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_diff_find_options_init(opts, GIT_DIFF_FIND_OPTIONS_VERSION); opts.ref.flags = flags; opts.ref.rename_threshold = renameThreshold; @@ -236,7 +204,7 @@ void findSimilar({ /// and should in fact generate the same IDs as the upstream git project does. /// /// Throws a [LibGit2Error] if error occured. -Pointer patchId(Pointer diff) { +Pointer patchOid(Pointer diff) { final out = calloc(); final error = libgit2.git_diff_patchid(out, diff, nullptr); @@ -249,19 +217,11 @@ Pointer patchId(Pointer diff) { } /// Return the diff delta for an entry in the diff list. -/// -/// Throws [RangeError] if index out of range. Pointer getDeltaByIndex({ required Pointer diffPointer, required int index, }) { - final result = libgit2.git_diff_get_delta(diffPointer, index); - - if (result == nullptr) { - throw RangeError('$index is out of bounds'); - } else { - return result; - } + return libgit2.git_diff_get_delta(diffPointer, index); } /// Look up the single character abbreviation for a delta status code. @@ -323,19 +283,13 @@ String statsPrint({ } /// Add patch to buffer. -/// -/// Throws a [LibGit2Error] if error occured. Pointer addToBuf({ required Pointer patchPointer, required Pointer bufferPointer, }) { - final error = libgit2.git_patch_to_buf(bufferPointer, patchPointer); + libgit2.git_patch_to_buf(bufferPointer, patchPointer); - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } else { - return bufferPointer; - } + return bufferPointer; } /// Apply a diff to the given repository, making changes directly in the working directory, @@ -351,7 +305,7 @@ bool apply({ final opts = calloc(); libgit2.git_apply_options_init(opts, GIT_APPLY_OPTIONS_VERSION); if (check) { - opts.ref.flags = git_apply_flags_t.GIT_APPLY_CHECK; + opts.ref.flags |= git_apply_flags_t.GIT_APPLY_CHECK; } final error = libgit2.git_apply(repoPointer, diffPointer, location, opts); @@ -377,18 +331,10 @@ Pointer _diffOptionsInit({ required int interhunkLines, }) { final opts = calloc(); - final optsError = libgit2.git_diff_options_init( - opts, - GIT_DIFF_OPTIONS_VERSION, - ); + libgit2.git_diff_options_init(opts, GIT_DIFF_OPTIONS_VERSION); - if (optsError < 0) { - calloc.free(opts); - throw LibGit2Error(libgit2.git_error_last()); - } else { - opts.ref.flags = flags; - opts.ref.context_lines = contextLines; - opts.ref.interhunk_lines = interhunkLines; - return opts; - } + opts.ref.flags = flags; + opts.ref.context_lines = contextLines; + opts.ref.interhunk_lines = interhunkLines; + return opts; } diff --git a/lib/src/bindings/index.dart b/lib/src/bindings/index.dart index 583b274..32e75b1 100644 --- a/lib/src/bindings/index.dart +++ b/lib/src/bindings/index.dart @@ -14,31 +14,19 @@ import '../util.dart'; /// if it has changed since the last time it was loaded. Purely in-memory index data /// will be untouched. Be aware: if there are changes on disk, unwritten in-memory changes /// are discarded. -/// -/// Throws a [LibGit2Error] if error occured. void read({required Pointer indexPointer, required bool force}) { final forceC = force == true ? 1 : 0; - final error = libgit2.git_index_read(indexPointer, forceC); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_index_read(indexPointer, forceC); } /// Read a tree into the index file with stats. /// /// The current index contents will be replaced by the specified tree. -/// -/// Throws a [LibGit2Error] if error occured. void readTree({ required Pointer indexPointer, required Pointer treePointer, }) { - final error = libgit2.git_index_read_tree(indexPointer, treePointer); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_index_read_tree(indexPointer, treePointer); } /// Write the index as a tree. @@ -243,15 +231,7 @@ void addAll({ } /// Write an existing index object from memory back to disk using an atomic file lock. -/// -/// Throws a [LibGit2Error] if error occured. -void write(Pointer index) { - final error = libgit2.git_index_write(index); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } -} +void write(Pointer index) => libgit2.git_index_write(index); /// Remove an entry from the index. /// @@ -272,8 +252,6 @@ void remove({ } /// Remove all matching index entries. -/// -/// Throws a [LibGit2Error] if error occured. void removeAll({ required Pointer indexPointer, required List pathspec, @@ -290,22 +268,13 @@ void removeAll({ pathspecC.ref.strings = strArray; pathspecC.ref.count = pathspec.length; - final error = libgit2.git_index_remove_all( - indexPointer, - pathspecC, - nullptr, - nullptr, - ); + libgit2.git_index_remove_all(indexPointer, pathspecC, nullptr, nullptr); calloc.free(pathspecC); for (final p in pathPointers) { calloc.free(p); } calloc.free(strArray); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } } /// Determine if the index contains entries representing file conflicts. @@ -320,15 +289,7 @@ List>> conflictList( Pointer index, ) { final iterator = calloc>(); - final iteratorError = libgit2.git_index_conflict_iterator_new( - iterator, - index, - ); - - if (iteratorError < 0) { - calloc.free(iterator); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_index_conflict_iterator_new(iterator, index); var result = >>[]; var error = 0; diff --git a/lib/src/bindings/libgit2_bindings.dart b/lib/src/bindings/libgit2_bindings.dart index cf0493f..316ba11 100644 --- a/lib/src/bindings/libgit2_bindings.dart +++ b/lib/src/bindings/libgit2_bindings.dart @@ -1,3 +1,4 @@ +// coverage:ignore-file // AUTO GENERATED FILE, DO NOT EDIT. // // Generated by `package:ffigen`. diff --git a/lib/src/bindings/mailmap.dart b/lib/src/bindings/mailmap.dart index e9199ac..acc1288 100644 --- a/lib/src/bindings/mailmap.dart +++ b/lib/src/bindings/mailmap.dart @@ -8,37 +8,23 @@ import '../util.dart'; /// /// This object is empty, so you'll have to add a mailmap file before you can /// do anything with it. The mailmap must be freed with `free()`. -/// -/// Throws a [LibGit2Error] if error occured. Pointer init() { final out = calloc>(); - final error = libgit2.git_mailmap_new(out); + libgit2.git_mailmap_new(out); - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out.value; - } + return out.value; } /// Create a new mailmap instance containing a single mailmap file. -/// -/// Throws a [LibGit2Error] if error occured. Pointer fromBuffer(String buffer) { final out = calloc>(); final bufferC = buffer.toNativeUtf8().cast(); - final error = libgit2.git_mailmap_from_buffer(out, bufferC, buffer.length); + libgit2.git_mailmap_from_buffer(out, bufferC, buffer.length); calloc.free(bufferC); - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out.value; - } + return out.value; } /// Create a new mailmap instance from a repository, loading mailmap files based @@ -65,8 +51,6 @@ Pointer fromRepository(Pointer repo) { } /// Resolve a name and email to the corresponding real name and email. -/// -/// Throws a [LibGit2Error] if error occured. List resolve({ required Pointer mailmapPointer, required String name, @@ -76,7 +60,7 @@ List resolve({ final outRealEmail = calloc>(); final nameC = name.toNativeUtf8().cast(); final emailC = email.toNativeUtf8().cast(); - final error = libgit2.git_mailmap_resolve( + libgit2.git_mailmap_resolve( outRealName, outRealEmail, mailmapPointer, @@ -84,44 +68,25 @@ List resolve({ emailC, ); - if (error < 0) { - calloc.free(outRealName); - calloc.free(outRealEmail); - calloc.free(nameC); - calloc.free(emailC); - throw LibGit2Error(libgit2.git_error_last()); - } else { - final realName = outRealName.value.cast().toDartString(); - final realEmail = outRealEmail.value.cast().toDartString(); - calloc.free(outRealName); - calloc.free(outRealEmail); - calloc.free(nameC); - calloc.free(emailC); + final realName = outRealName.value.cast().toDartString(); + final realEmail = outRealEmail.value.cast().toDartString(); + calloc.free(outRealName); + calloc.free(outRealEmail); + calloc.free(nameC); + calloc.free(emailC); - return [realName, realEmail]; - } + return [realName, realEmail]; } /// Resolve a signature to use real names and emails with a mailmap. -/// -/// Throws a [LibGit2Error] if error occured. Pointer resolveSignature({ required Pointer mailmapPointer, required Pointer signaturePointer, }) { final out = calloc>(); - final error = libgit2.git_mailmap_resolve_signature( - out, - mailmapPointer, - signaturePointer, - ); + libgit2.git_mailmap_resolve_signature(out, mailmapPointer, signaturePointer); - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out.value; - } + return out.value; } /// Add a single entry to the given mailmap object. If the entry already exists, @@ -140,7 +105,7 @@ void addEntry({ final replaceNameC = replaceName?.toNativeUtf8().cast() ?? nullptr; final replaceEmailC = replaceEmail.toNativeUtf8().cast(); - final error = libgit2.git_mailmap_add_entry( + libgit2.git_mailmap_add_entry( mailmapPointer, realNameC, realEmailC, @@ -152,10 +117,6 @@ void addEntry({ calloc.free(realEmailC); calloc.free(replaceNameC); calloc.free(replaceEmailC); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } } /// Free the mailmap and its associated memory. diff --git a/lib/src/bindings/merge.dart b/lib/src/bindings/merge.dart index 7c73614..84a73a4 100644 --- a/lib/src/bindings/merge.dart +++ b/lib/src/bindings/merge.dart @@ -25,8 +25,6 @@ Pointer mergeBase({ /// Analyzes the given branch(es) and determines the opportunities for merging them /// into a reference. -/// -/// Throws a [LibGit2Error] if error occured. List analysis({ required Pointer repoPointer, required Pointer ourRefPointer, @@ -35,7 +33,7 @@ List analysis({ }) { final analysisOut = calloc(); final preferenceOut = calloc(); - final error = libgit2.git_merge_analysis_for_ref( + libgit2.git_merge_analysis_for_ref( analysisOut, preferenceOut, repoPointer, @@ -43,60 +41,35 @@ List analysis({ theirHeadPointer, theirHeadsLen, ); - var result = []; - if (error < 0) { - calloc.free(analysisOut); - calloc.free(preferenceOut); - throw LibGit2Error(libgit2.git_error_last()); - } else { - result.add(analysisOut.value); - result.add(preferenceOut.value); - calloc.free(analysisOut); - calloc.free(preferenceOut); - return result; - } + final result = [analysisOut.value, preferenceOut.value]; + + calloc.free(analysisOut); + calloc.free(preferenceOut); + + return result; } /// Merges the given commit(s) into HEAD, writing the results into the working directory. /// Any changes are staged for commit and any conflicts are written to the index. Callers /// should inspect the repository's index after this completes, resolve any conflicts and /// prepare a commit. -/// -/// Throws a [LibGit2Error] if error occured. void merge({ required Pointer repoPointer, required Pointer> theirHeadsPointer, required int theirHeadsLen, }) { final mergeOpts = calloc(); - final mergeError = libgit2.git_merge_options_init( - mergeOpts, - GIT_MERGE_OPTIONS_VERSION, - ); - - if (mergeError < 0) { - calloc.free(mergeOpts); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_merge_options_init(mergeOpts, GIT_MERGE_OPTIONS_VERSION); final checkoutOpts = calloc(); - final checkoutError = libgit2.git_checkout_options_init( - checkoutOpts, - GIT_CHECKOUT_OPTIONS_VERSION, - ); - - if (checkoutError < 0) { - calloc.free(mergeOpts); - calloc.free(checkoutOpts); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_checkout_options_init(checkoutOpts, GIT_CHECKOUT_OPTIONS_VERSION); checkoutOpts.ref.checkout_strategy = git_checkout_strategy_t.GIT_CHECKOUT_SAFE | git_checkout_strategy_t.GIT_CHECKOUT_RECREATE_MISSING; - final error = libgit2.git_merge( + libgit2.git_merge( repoPointer, theirHeadsPointer, theirHeadsLen, @@ -106,10 +79,6 @@ void merge({ calloc.free(mergeOpts); calloc.free(checkoutOpts); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } } /// Merge two files as they exist in the index, using the given common ancestor @@ -235,15 +204,7 @@ void cherryPick({ required Pointer commitPointer, }) { final opts = calloc(); - final optsError = libgit2.git_cherrypick_options_init( - opts, - GIT_CHERRYPICK_OPTIONS_VERSION, - ); - - if (optsError < 0) { - calloc.free(opts); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_cherrypick_options_init(opts, GIT_CHERRYPICK_OPTIONS_VERSION); opts.ref.checkout_opts.checkout_strategy = git_checkout_strategy_t.GIT_CHECKOUT_SAFE; @@ -263,15 +224,7 @@ Pointer _initMergeOptions({ required int fileFlags, }) { final opts = calloc(); - final error = libgit2.git_merge_options_init( - opts, - GIT_MERGE_OPTIONS_VERSION, - ); - - if (error < 0) { - calloc.free(opts); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_merge_options_init(opts, GIT_MERGE_OPTIONS_VERSION); opts.ref.file_favor = favor; opts.ref.flags = mergeFlags; diff --git a/lib/src/bindings/note.dart b/lib/src/bindings/note.dart index cb75213..575dc59 100644 --- a/lib/src/bindings/note.dart +++ b/lib/src/bindings/note.dart @@ -28,18 +28,9 @@ List> list(Pointer repo) { nextError = libgit2.git_note_next(noteOid, annotatedOid, iterator.value); if (nextError >= 0) { final out = calloc>(); - final error = libgit2.git_note_read(out, repo, notesRef, annotatedOid); - + libgit2.git_note_read(out, repo, notesRef, annotatedOid); calloc.free(noteOid); - - if (error < 0) { - calloc.free(out); - calloc.free(annotatedOid); - calloc.free(iterator); - throw LibGit2Error(libgit2.git_error_last()); - } else { - result.add({'note': out.value, 'annotatedOid': annotatedOid}); - } + result.add({'note': out.value, 'annotatedOid': annotatedOid}); } else { break; } diff --git a/lib/src/bindings/odb.dart b/lib/src/bindings/odb.dart index 584da77..e531a08 100644 --- a/lib/src/bindings/odb.dart +++ b/lib/src/bindings/odb.dart @@ -10,18 +10,10 @@ import '../util.dart'; /// /// Before the ODB can be used for read/writing, a custom database backend must be /// manually added. -/// -/// Throws a [LibGit2Error] if error occured. Pointer create() { final out = calloc>(); - final error = libgit2.git_odb_new(out); - - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out.value; - } + libgit2.git_odb_new(out); + return out.value; } /// Add an on-disk alternate to an existing Object DB. @@ -33,20 +25,13 @@ Pointer create() { /// have been exhausted. /// /// Writing is disabled on alternate backends. -/// -/// Throws a [LibGit2Error] if error occured. void addDiskAlternate({ required Pointer odbPointer, required String path, }) { final pathC = path.toNativeUtf8().cast(); - final error = libgit2.git_odb_add_disk_alternate(odbPointer, pathC); - + libgit2.git_odb_add_disk_alternate(odbPointer, pathC); calloc.free(pathC); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } } /// Determine if an object can be found in the object database by an abbreviated object ID. @@ -174,7 +159,7 @@ void objectFree(Pointer object) { libgit2.git_odb_object_free(object); } -/// Write raw data to into the object database. +/// Write raw data into the object database. /// /// Throws a [LibGit2Error] if error occured. Pointer write({ @@ -196,54 +181,15 @@ Pointer write({ } final buffer = data.toNativeUtf8().cast(); - final writeError = libgit2.git_odb_stream_write( - stream.value, - buffer, - data.length, - ); - - if (writeError < 0) { - calloc.free(buffer); - libgit2.git_odb_stream_free(stream.value); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_odb_stream_write(stream.value, buffer, data.length); final out = calloc(); - final finalizeError = libgit2.git_odb_stream_finalize_write( - out, - stream.value, - ); + libgit2.git_odb_stream_finalize_write(out, stream.value); calloc.free(buffer); libgit2.git_odb_stream_free(stream.value); - if (finalizeError < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out; - } -} - -/// Get the number of ODB backend objects. -int backendsCount(Pointer odb) => libgit2.git_odb_num_backends(odb); - -/// Lookup an ODB backend object by index. -/// -/// Throws a [LibGit2Error] if error occured. -Pointer getBackend({ - required Pointer odbPointer, - required int position, -}) { - final out = calloc>(); - final error = libgit2.git_odb_get_backend(out, odbPointer, position); - - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out.value; - } + return out; } /// Close an open object database. diff --git a/lib/src/bindings/oid.dart b/lib/src/bindings/oid.dart index bb41e92..7e74338 100644 --- a/lib/src/bindings/oid.dart +++ b/lib/src/bindings/oid.dart @@ -1,83 +1,54 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; -import '../error.dart'; import 'libgit2_bindings.dart'; import '../util.dart'; /// Parse N characters of a hex formatted object id into a git_oid. -/// -/// If N is odd, the last byte's high nibble will be read in and the low nibble set to zero. -/// -/// Throws a [LibGit2Error] if error occured. Pointer fromStrN(String hex) { final out = calloc(); final str = hex.toNativeUtf8().cast(); - final error = libgit2.git_oid_fromstrn(out, str, hex.length); + libgit2.git_oid_fromstrn(out, str, hex.length); calloc.free(str); - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out; - } + return out; } /// Parse a hex formatted object id into a git_oid. -/// -/// Throws a [LibGit2Error] if error occured. Pointer fromSHA(String hex) { final out = calloc(); final str = hex.toNativeUtf8().cast(); - final error = libgit2.git_oid_fromstr(out, str); + libgit2.git_oid_fromstr(out, str); calloc.free(str); - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out; - } + return out; } /// Copy an already raw oid into a git_oid structure. -/// -/// Throws a [LibGit2Error] if error occured. Pointer fromRaw(Array raw) { final out = calloc(); var rawC = calloc(20); + for (var i = 0; i < 20; i++) { rawC[i] = raw[i]; } - final error = libgit2.git_oid_fromraw(out, rawC); + + libgit2.git_oid_fromraw(out, rawC); calloc.free(rawC); - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out; - } + return out; } /// Format a git_oid into a hex string. -/// -/// Throws a [LibGit2Error] if error occured. String toSHA(Pointer id) { final out = calloc(40); - final error = libgit2.git_oid_fmt(out, id); + libgit2.git_oid_fmt(out, id); - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - final result = out.cast().toDartString(length: 40); - calloc.free(out); - return result; - } + final result = out.cast().toDartString(length: 40); + calloc.free(out); + return result; } /// Compare two oid structures. @@ -91,16 +62,8 @@ int compare({ } /// Copy an oid from one structure to another. -/// -/// Throws a [LibGit2Error] if error occured. Pointer copy(Pointer src) { final out = calloc(); - final error = libgit2.git_oid_cpy(out, src); - - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out; - } + libgit2.git_oid_cpy(out, src); + return out; } diff --git a/lib/src/bindings/patch.dart b/lib/src/bindings/patch.dart index 2ecae83..d728c0b 100644 --- a/lib/src/bindings/patch.dart +++ b/lib/src/bindings/patch.dart @@ -7,9 +7,7 @@ import '../util.dart'; /// Directly generate a patch from the difference between two buffers. /// /// You can use the standard patch accessor functions to read the patch data, and -/// you must call `free()` on the patch when done. -/// -/// Throws a [LibGit2Error] if error occured. +/// you must free the patch when done. Map fromBuffers({ String? oldBuffer, String? oldAsPath, @@ -32,7 +30,7 @@ Map fromBuffers({ interhunkLines: interhunkLines, ); - final error = libgit2.git_patch_from_buffers( + libgit2.git_patch_from_buffers( out, oldBufferC.cast(), oldLen, @@ -47,29 +45,20 @@ Map fromBuffers({ calloc.free(newAsPathC); calloc.free(opts); - if (error < 0) { - calloc.free(out); - calloc.free(oldBufferC); - calloc.free(newBufferC); - throw LibGit2Error(libgit2.git_error_last()); - } else { - // Returning map with pointers to patch and buffers because patch object does not - // have refenrece to underlying buffers or blobs. So if the buffer or blob is freed/removed - // the patch text becomes corrupted. - return {'patch': out.value, 'a': oldBufferC, 'b': newBufferC}; - } + // Returning map with pointers to patch and buffers because patch object does not + // have refenrece to underlying buffers or blobs. So if the buffer or blob is freed/removed + // the patch text becomes corrupted. + return {'patch': out.value, 'a': oldBufferC, 'b': newBufferC}; } /// Directly generate a patch from the difference between two blobs. /// /// You can use the standard patch accessor functions to read the patch data, and you -/// must call `free()` on the patch when done. -/// -/// Throws a [LibGit2Error] if error occured. +/// must free the patch when done. Map fromBlobs({ - Pointer? oldBlobPointer, + required Pointer oldBlobPointer, String? oldAsPath, - Pointer? newBlobPointer, + required Pointer newBlobPointer, String? newAsPath, required int flags, required int contextLines, @@ -84,11 +73,11 @@ Map fromBlobs({ interhunkLines: interhunkLines, ); - final error = libgit2.git_patch_from_blobs( + libgit2.git_patch_from_blobs( out, - oldBlobPointer ?? nullptr, + oldBlobPointer, oldAsPathC, - newBlobPointer ?? nullptr, + newBlobPointer, newAsPathC, opts, ); @@ -97,23 +86,16 @@ Map fromBlobs({ calloc.free(newAsPathC); calloc.free(opts); - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - // Returning map with pointers to patch and blobs because patch object does not - // have reference to underlying blobs. So if the blob is freed/removed the patch - // text becomes corrupted. - return {'patch': out.value, 'a': oldBlobPointer, 'b': newBlobPointer}; - } + // Returning map with pointers to patch and blobs because patch object does not + // have reference to underlying blobs. So if the blob is freed/removed the patch + // text becomes corrupted. + return {'patch': out.value, 'a': oldBlobPointer, 'b': newBlobPointer}; } /// Directly generate a patch from the difference between a blob and a buffer. /// /// You can use the standard patch accessor functions to read the patch data, and you must -/// call `free()` on the patch when done. -/// -/// Throws a [LibGit2Error] if error occured. +/// free the patch when done. Map fromBlobAndBuffer({ Pointer? oldBlobPointer, String? oldAsPath, @@ -134,7 +116,7 @@ Map fromBlobAndBuffer({ interhunkLines: interhunkLines, ); - final error = libgit2.git_patch_from_blob_and_buffer( + libgit2.git_patch_from_blob_and_buffer( out, oldBlobPointer ?? nullptr, oldAsPathC, @@ -148,16 +130,10 @@ Map fromBlobAndBuffer({ calloc.free(bufferAsPathC); calloc.free(opts); - if (error < 0) { - calloc.free(out); - calloc.free(bufferC); - throw LibGit2Error(libgit2.git_error_last()); - } else { - // Returning map with pointers to patch and buffers because patch object does not - // have reference to underlying buffers or blobs. So if the buffer or blob is freed/removed - // the patch text becomes corrupted. - return {'patch': out.value, 'a': oldBlobPointer, 'b': bufferC}; - } + // Returning map with pointers to patch and buffers because patch object does not + // have reference to underlying buffers or blobs. So if the buffer or blob is freed/removed + // the patch text becomes corrupted. + return {'patch': out.value, 'a': oldBlobPointer, 'b': bufferC}; } /// Return a patch for an entry in the diff list. @@ -191,55 +167,31 @@ int numHunks(Pointer patch) => libgit2.git_patch_num_hunks(patch); /// Get the information about a hunk in a patch. /// -/// Given a patch and a hunk index into the patch, this returns detailed information about that hunk. -/// -/// Throws a [LibGit2Error] if error occured. +/// Given a patch and a hunk index into the patch, this returns detailed information +/// about that hunk. Map hunk({ required Pointer patchPointer, required int hunkIndex, }) { final out = calloc>(); final linesInHunk = calloc(); - final error = libgit2.git_patch_get_hunk( - out, - linesInHunk.cast(), - patchPointer, - hunkIndex, - ); + libgit2.git_patch_get_hunk(out, linesInHunk.cast(), patchPointer, hunkIndex); - if (error < 0) { - calloc.free(out); - calloc.free(linesInHunk); - throw LibGit2Error(libgit2.git_error_last()); - } else { - final linesN = linesInHunk.value; - calloc.free(linesInHunk); - return {'hunk': out.value, 'linesN': linesN}; - } + final linesN = linesInHunk.value; + calloc.free(linesInHunk); + return {'hunk': out.value, 'linesN': linesN}; } /// Get data about a line in a hunk of a patch. -/// -/// Throws a [LibGit2Error] if error occured. Pointer lines({ required Pointer patchPointer, required int hunkIndex, required int lineOfHunk, }) { final out = calloc>(); - final error = libgit2.git_patch_get_line_in_hunk( - out, - patchPointer, - hunkIndex, - lineOfHunk, - ); + libgit2.git_patch_get_line_in_hunk(out, patchPointer, hunkIndex, lineOfHunk); - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out.value; - } + return out.value; } /// Get the content of a patch as a single diff text. @@ -294,15 +246,7 @@ Pointer _diffOptionsInit({ required int interhunkLines, }) { final opts = calloc(); - final optsError = libgit2.git_diff_options_init( - opts, - GIT_DIFF_OPTIONS_VERSION, - ); - - if (optsError < 0) { - calloc.free(opts); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_diff_options_init(opts, GIT_DIFF_OPTIONS_VERSION); opts.ref.flags = flags; opts.ref.context_lines = contextLines; diff --git a/lib/src/bindings/rebase.dart b/lib/src/bindings/rebase.dart index 5d895a1..d566a39 100644 --- a/lib/src/bindings/rebase.dart +++ b/lib/src/bindings/rebase.dart @@ -25,16 +25,7 @@ Pointer init({ final out = calloc>(); final opts = calloc(); - final optsError = libgit2.git_rebase_options_init( - opts, - GIT_REBASE_OPTIONS_VERSION, - ); - - if (optsError < 0) { - calloc.free(out); - calloc.free(opts); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_rebase_options_init(opts, GIT_REBASE_OPTIONS_VERSION); final error = libgit2.git_rebase_init( out, @@ -110,27 +101,12 @@ void commit({ } /// Finishes a rebase that is currently in progress once all patches have been applied. -/// -/// Throws a [LibGit2Error] if error occured. -void finish(Pointer rebase) { - final error = libgit2.git_rebase_finish(rebase, nullptr); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } -} +void finish(Pointer rebase) => + libgit2.git_rebase_finish(rebase, nullptr); /// Aborts a rebase that is currently in progress, resetting the repository and working /// directory to their state before rebase began. -/// -/// Throws a [LibGit2Error] if error occured. -void abort(Pointer rebase) { - final error = libgit2.git_rebase_abort(rebase); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } -} +void abort(Pointer rebase) => libgit2.git_rebase_abort(rebase); /// Free memory allocated for rebase object. void free(Pointer rebase) => libgit2.git_rebase_free(rebase); diff --git a/lib/src/bindings/refdb.dart b/lib/src/bindings/refdb.dart index e548411..d3e0b66 100644 --- a/lib/src/bindings/refdb.dart +++ b/lib/src/bindings/refdb.dart @@ -1,20 +1,11 @@ import 'dart:ffi'; -import '../error.dart'; import 'libgit2_bindings.dart'; import '../util.dart'; /// Suggests that the given refdb compress or optimize its references. /// This mechanism is implementation specific. For on-disk reference databases, /// for example, this may pack all loose references. -/// -/// Throws a [LibGit2Error] if error occured. -void compress(Pointer refdb) { - final error = libgit2.git_refdb_compress(refdb); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } -} +void compress(Pointer refdb) => libgit2.git_refdb_compress(refdb); /// Close an open reference database to release memory. void free(Pointer refdb) => libgit2.git_refdb_free(refdb); diff --git a/lib/src/bindings/reference.dart b/lib/src/bindings/reference.dart index 8555ca2..97c7036 100644 --- a/lib/src/bindings/reference.dart +++ b/lib/src/bindings/reference.dart @@ -13,17 +13,8 @@ int referenceType(Pointer ref) => /// Get the OID pointed to by a direct reference. /// /// Only available if the reference is direct (i.e. an object id reference, not a symbolic one). -/// -/// Throws an exception if error occured. -Pointer target(Pointer ref) { - final result = libgit2.git_reference_target(ref); - - if (result == nullptr) { - throw Exception('Oid for reference isn\'t available'); - } else { - return result; - } -} +Pointer target(Pointer ref) => + libgit2.git_reference_target(ref); /// Resolve a symbolic reference to a direct reference. /// @@ -73,30 +64,6 @@ Pointer lookup({ } } -/// Lookup a reference by DWIMing its short name. -/// -/// Apply the git precendence rules to the given shorthand to determine which reference -/// the user is referring to. -/// -/// Throws a [LibGit2Error] if error occured. -Pointer lookupDWIM({ - required Pointer repoPointer, - required String name, -}) { - final out = calloc>(); - final nameC = name.toNativeUtf8().cast(); - final error = libgit2.git_reference_dwim(out, repoPointer, nameC); - - calloc.free(nameC); - - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out.value; - } -} - /// Get the full name of a reference. String name(Pointer ref) { return libgit2.git_reference_name(ref).cast().toDartString(); @@ -175,22 +142,16 @@ List list(Pointer repo) { } /// Check if a reflog exists for the specified reference. -/// -/// Throws a [LibGit2Error] if error occured. bool hasLog({ required Pointer repoPointer, required String name, }) { final refname = name.toNativeUtf8().cast(); - final error = libgit2.git_reference_has_log(repoPointer, refname); + final result = libgit2.git_reference_has_log(repoPointer, refname); calloc.free(refname); - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } else { - return error == 1 ? true : false; - } + return result == 1 ? true : false; } /// Check if a reference is a local branch. @@ -329,15 +290,7 @@ Pointer createSymbolic({ /// /// This method works for both direct and symbolic references. /// The reference will be immediately removed on disk but the memory will not be freed. -/// -/// Throws a [LibGit2Error] if the reference has changed from the time it was looked up. -void delete(Pointer ref) { - final error = libgit2.git_reference_delete(ref); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } -} +void delete(Pointer ref) => libgit2.git_reference_delete(ref); /// Get the repository where a reference resides. Pointer owner(Pointer ref) { @@ -444,23 +397,5 @@ Pointer peel({ } } -/// Ensure the reference name is well-formed. -/// -/// Valid reference names must follow one of two patterns: -/// -/// Top-level names must contain only capital letters and underscores, -/// and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). -/// Names prefixed with "refs/" can be almost anything. You must avoid -/// the characters '~', '^', ':', '\', '?', '[', and '*', and the sequences ".." -/// and "@{" which have special meaning to revparse. -bool isValidName(String name) { - final refname = name.toNativeUtf8().cast(); - final result = libgit2.git_reference_is_valid_name(refname); - - calloc.free(refname); - - return result == 1 ? true : false; -} - /// Free the given reference. void free(Pointer ref) => libgit2.git_reference_free(ref); diff --git a/lib/src/bindings/reflog.dart b/lib/src/bindings/reflog.dart index 07f4b24..6d9d980 100644 --- a/lib/src/bindings/reflog.dart +++ b/lib/src/bindings/reflog.dart @@ -1,6 +1,5 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; -import '../error.dart'; import 'libgit2_bindings.dart'; import '../util.dart'; @@ -10,24 +9,17 @@ import '../util.dart'; /// object will be returned. /// /// The reflog must be freed manually. -/// -/// Throws a [LibGit2Error] if error occured. Pointer read({ required Pointer repoPointer, required String name, }) { final out = calloc>(); final nameC = name.toNativeUtf8().cast(); - final error = libgit2.git_reflog_read(out, repoPointer, nameC); + libgit2.git_reflog_read(out, repoPointer, nameC); calloc.free(nameC); - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out.value; - } + return out.value; } /// Get the number of log entries in a reflog. diff --git a/lib/src/bindings/remote.dart b/lib/src/bindings/remote.dart index 53d47b4..069eecf 100644 --- a/lib/src/bindings/remote.dart +++ b/lib/src/bindings/remote.dart @@ -12,19 +12,16 @@ import 'remote_callbacks.dart'; /// Throws a [LibGit2Error] if error occured. List list(Pointer repo) { final out = calloc(); - final error = libgit2.git_remote_list(out, repo); + libgit2.git_remote_list(out, repo); - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - var result = []; - for (var i = 0; i < out.ref.count; i++) { - result.add(out.ref.strings[i].cast().toDartString()); - } - calloc.free(out); - return result; + var result = []; + for (var i = 0; i < out.ref.count; i++) { + result.add(out.ref.strings[i].cast().toDartString()); } + + calloc.free(out); + + return result; } /// Get the information for a particular remote. @@ -210,11 +207,6 @@ void setPushUrl({ } } -/// Get the remote's repository. -Pointer owner(Pointer remote) { - return libgit2.git_remote_owner(remote); -} - /// Get the remote's name. String name(Pointer remote) { final result = libgit2.git_remote_name(remote); @@ -332,7 +324,7 @@ void connect({ String? proxyOption, }) { final callbacksOptions = calloc(); - final callbacksError = libgit2.git_remote_init_callbacks( + libgit2.git_remote_init_callbacks( callbacksOptions, GIT_REMOTE_CALLBACKS_VERSION, ); @@ -342,10 +334,6 @@ void connect({ callbacks: callbacks, ); - if (callbacksError < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } - final proxyOptions = _proxyOptionsInit(proxyOption); final error = libgit2.git_remote_connect( @@ -377,42 +365,32 @@ void connect({ List> lsRemotes(Pointer remote) { final out = calloc>>(); final size = calloc(); - final error = libgit2.git_remote_ls(out, size, remote); + libgit2.git_remote_ls(out, size, remote); - if (error < 0) { - calloc.free(out); - calloc.free(size); - throw LibGit2Error(libgit2.git_error_last()); - } else { - var result = >[]; + var result = >[]; - for (var i = 0; i < size.value; i++) { - var remote = {}; - Oid? loid; + for (var i = 0; i < size.value; i++) { + var remote = {}; - final bool local = out[0][i].ref.local == 1 ? true : false; - if (local) { - loid = Oid.fromRaw(out[0][i].ref.loid); - } + final local = out[0][i].ref.local == 1 ? true : false; - remote['local'] = local; - remote['loid'] = loid; - remote['name'] = out[0][i].ref.name == nullptr - ? '' - : out[0][i].ref.name.cast().toDartString(); - remote['symref'] = out[0][i].ref.symref_target == nullptr - ? '' - : out[0][i].ref.symref_target.cast().toDartString(); - remote['oid'] = Oid.fromRaw(out[0][i].ref.oid); + remote['local'] = local; + remote['loid'] = local ? Oid.fromRaw(out[0][i].ref.loid) : null; + remote['name'] = out[0][i].ref.name == nullptr + ? '' + : out[0][i].ref.name.cast().toDartString(); + remote['symref'] = out[0][i].ref.symref_target == nullptr + ? '' + : out[0][i].ref.symref_target.cast().toDartString(); + remote['oid'] = Oid.fromRaw(out[0][i].ref.oid); - result.add(remote); - } - - calloc.free(out); - calloc.free(size); - - return result; + result.add(remote); } + + calloc.free(out); + calloc.free(size); + + return result; } /// Download new data and update tips. @@ -445,22 +423,7 @@ void fetch({ final proxyOptions = _proxyOptionsInit(proxyOption); final opts = calloc(); - final optsError = libgit2.git_fetch_options_init( - opts, - GIT_FETCH_OPTIONS_VERSION, - ); - - if (optsError < 0) { - for (final p in refspecsPointers) { - calloc.free(p); - } - calloc.free(refspecsC); - calloc.free(strArray); - calloc.free(proxyOptions); - calloc.free(reflogMessageC); - calloc.free(opts); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_fetch_options_init(opts, GIT_FETCH_OPTIONS_VERSION); RemoteCallbacks.plug( callbacksOptions: opts.ref.callbacks, @@ -515,19 +478,7 @@ void push({ final proxyOptions = _proxyOptionsInit(proxyOption); final opts = calloc(); - final optsError = - libgit2.git_push_options_init(opts, GIT_PUSH_OPTIONS_VERSION); - - if (optsError < 0) { - for (final p in refspecsPointers) { - calloc.free(p); - } - calloc.free(strArray); - calloc.free(refspecsC); - calloc.free(proxyOptions); - calloc.free(opts); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_push_options_init(opts, GIT_PUSH_OPTIONS_VERSION); RemoteCallbacks.plug( callbacksOptions: opts.ref.callbacks, @@ -556,15 +507,8 @@ Pointer stats(Pointer remote) => libgit2.git_remote_stats(remote); /// Close the connection to the remote. -/// -/// Throws a [LibGit2Error] if error occured. -void disconnect(Pointer remote) { - final error = libgit2.git_remote_disconnect(remote); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } -} +void disconnect(Pointer remote) => + libgit2.git_remote_disconnect(remote); /// Prune tracking refs that are no longer present on remote. /// @@ -574,16 +518,11 @@ void prune({ required Callbacks callbacks, }) { final callbacksOptions = calloc(); - final callbacksError = libgit2.git_remote_init_callbacks( + libgit2.git_remote_init_callbacks( callbacksOptions, GIT_REMOTE_CALLBACKS_VERSION, ); - if (callbacksError < 0) { - calloc.free(callbacksOptions); - throw LibGit2Error(libgit2.git_error_last()); - } - RemoteCallbacks.plug( callbacksOptions: callbacksOptions.ref, callbacks: callbacks, @@ -608,13 +547,7 @@ void free(Pointer remote) => libgit2.git_remote_free(remote); /// Initializes git_proxy_options structure. Pointer _proxyOptionsInit(String? proxyOption) { final proxyOptions = calloc(); - final proxyOptionsError = - libgit2.git_proxy_options_init(proxyOptions, GIT_PROXY_OPTIONS_VERSION); - - if (proxyOptionsError < 0) { - calloc.free(proxyOptions); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_proxy_options_init(proxyOptions, GIT_PROXY_OPTIONS_VERSION); if (proxyOption == null) { proxyOptions.ref.type = git_proxy_t.GIT_PROXY_NONE; diff --git a/lib/src/bindings/repository.dart b/lib/src/bindings/repository.dart index f956c26..f47ef66 100644 --- a/lib/src/bindings/repository.dart +++ b/lib/src/bindings/repository.dart @@ -28,33 +28,11 @@ Pointer open(String path) { } } -/// Attempt to open an already-existing bare repository at [bare_path]. -/// -/// The [bare_path] can point to only a bare repository. -/// -/// Throws a [LibGit2Error] if error occured. -Pointer openBare(String barePath) { - final out = calloc>(); - final barePathC = barePath.toNativeUtf8().cast(); - final error = libgit2.git_repository_open_bare(out, barePathC); - - calloc.free(barePathC); - - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out.value; - } -} - /// Look for a git repository and return its path. The lookup start from [startPath] /// and walk across parent directories if nothing has been found. The lookup ends when /// the first repository is found, or when reaching a directory referenced in [ceilingDirs]. /// /// The method will automatically detect if the repository is bare (if there is a repository). -/// -/// Throws a [LibGit2Error] if error occured. String discover({ required String startPath, String? ceilingDirs, @@ -63,27 +41,14 @@ String discover({ final startPathC = startPath.toNativeUtf8().cast(); final ceilingDirsC = ceilingDirs?.toNativeUtf8().cast() ?? nullptr; - final error = libgit2.git_repository_discover( - out, - startPathC, - 0, - ceilingDirsC, - ); + libgit2.git_repository_discover(out, startPathC, 0, ceilingDirsC); calloc.free(startPathC); calloc.free(ceilingDirsC); - if (error == git_error_code.GIT_ENOTFOUND) { - calloc.free(out); - return ''; - } else if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - final result = out.ref.ptr.cast().toDartString(); - calloc.free(out); - return result; - } + final result = out.ref.ptr.cast().toDartString(); + calloc.free(out); + return result; } /// Creates a new Git repository in the given folder. @@ -107,23 +72,11 @@ Pointer init({ final initialHeadC = initialHead?.toNativeUtf8().cast() ?? nullptr; final originUrlC = originUrl?.toNativeUtf8().cast() ?? nullptr; final opts = calloc(); - final optsError = libgit2.git_repository_init_options_init( + libgit2.git_repository_init_options_init( opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION, ); - if (optsError < 0) { - calloc.free(out); - calloc.free(pathC); - calloc.free(workdirPathC); - calloc.free(descriptionC); - calloc.free(templatePathC); - calloc.free(initialHeadC); - calloc.free(originUrlC); - calloc.free(opts); - throw LibGit2Error(libgit2.git_error_last()); - } - opts.ref.flags = flags; opts.ref.mode = mode; opts.ref.workdir_path = workdirPathC; @@ -169,31 +122,10 @@ Pointer clone({ checkoutBranch?.toNativeUtf8().cast() ?? nullptr; final cloneOptions = calloc(); - final cloneOptionsError = - libgit2.git_clone_options_init(cloneOptions, GIT_CLONE_OPTIONS_VERSION); - - if (cloneOptionsError < 0) { - calloc.free(out); - calloc.free(urlC); - calloc.free(localPathC); - calloc.free(checkoutBranchC); - calloc.free(cloneOptions); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_clone_options_init(cloneOptions, GIT_CLONE_OPTIONS_VERSION); final fetchOptions = calloc(); - final fetchOptionsError = - libgit2.git_fetch_options_init(fetchOptions, GIT_FETCH_OPTIONS_VERSION); - - if (fetchOptionsError < 0) { - calloc.free(out); - calloc.free(urlC); - calloc.free(localPathC); - calloc.free(checkoutBranchC); - calloc.free(cloneOptions); - calloc.free(fetchOptions); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_fetch_options_init(fetchOptions, GIT_FETCH_OPTIONS_VERSION); RemoteCallbacks.plug( callbacksOptions: fetchOptions.ref.callbacks, @@ -271,20 +203,13 @@ String getNamespace(Pointer repo) { /// /// The [namespace] should not include the refs folder, e.g. to namespace all references /// under refs/namespaces/foo/, use foo as the namespace. -/// -/// Throws a [LibGit2Error] if error occured. void setNamespace({ required Pointer repoPointer, String? namespace, }) { final nmspace = namespace?.toNativeUtf8().cast() ?? nullptr; - final error = libgit2.git_repository_set_namespace(repoPointer, nmspace); - + libgit2.git_repository_set_namespace(repoPointer, nmspace); calloc.free(nmspace); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } } /// Check if a repository is bare or not. @@ -401,18 +326,10 @@ Map identity(Pointer repo) { /// will be returned, including global and system configurations (if they are available). /// /// The configuration file must be freed once it's no longer being used by the user. -/// -/// Throws a [LibGit2Error] if error occured. Pointer config(Pointer repo) { final out = calloc>(); - final error = libgit2.git_repository_config(out, repo); - - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out.value; - } + libgit2.git_repository_config(out, repo); + return out.value; } /// Get a snapshot of the repository's configuration. @@ -421,18 +338,10 @@ Pointer config(Pointer repo) { /// The contents of this snapshot will not change, even if the underlying config files are modified. /// /// The configuration file must be freed once it's no longer being used by the user. -/// -/// Throws a [LibGit2Error] if error occured. Pointer configSnapshot(Pointer repo) { final out = calloc>(); - final error = libgit2.git_repository_config_snapshot(out, repo); - - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out.value; - } + libgit2.git_repository_config_snapshot(out, repo); + return out.value; } /// Get the Index file for this repository. @@ -441,18 +350,10 @@ Pointer configSnapshot(Pointer repo) { /// will be returned (the one located in `.git/index`). /// /// The index must be freed once it's no longer being used. -/// -/// Throws a [LibGit2Error] if error occured. Pointer index(Pointer repo) { final out = calloc>(); - final error = libgit2.git_repository_index(out, repo); - - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out.value; - } + libgit2.git_repository_index(out, repo); + return out.value; } /// Determine if the repository was a shallow clone. @@ -585,27 +486,6 @@ void setHeadDetached({ } } -/// Make the repository HEAD directly point to the commit. -/// -/// This behaves like [setHeadDetached] but takes an annotated commit, -/// which lets you specify which extended sha syntax string was specified -/// by a user, allowing for more exact reflog messages. -/// -/// See the documentation for [setHeadDetached]. -void setHeadDetachedFromAnnotated({ - required Pointer repoPointer, - required Pointer commitishPointer, -}) { - final error = libgit2.git_repository_set_head_detached_from_annotated( - repoPointer, - commitishPointer, - ); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } -} - /// Set the path to the working directory for this repository. /// /// The working directory doesn't need to be the same one that contains the @@ -665,24 +545,5 @@ String workdir(Pointer repo) { } } -/// Create a "fake" repository to wrap an object database -/// -/// Create a repository object to wrap an object database to be used with the API -/// when all you have is an object database. This doesn't have any paths associated -/// with it, so use with care. -/// -/// Throws a [LibGit2Error] if error occured. -Pointer wrapODB(Pointer odb) { - final out = calloc>(); - final error = libgit2.git_repository_wrap_odb(out, odb); - - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out.value; - } -} - /// Free a previously allocated repository. void free(Pointer repo) => libgit2.git_repository_free(repo); diff --git a/lib/src/bindings/reset.dart b/lib/src/bindings/reset.dart index 9930781..66fed23 100644 --- a/lib/src/bindings/reset.dart +++ b/lib/src/bindings/reset.dart @@ -1,5 +1,4 @@ import 'dart:ffi'; -import '../error.dart'; import 'libgit2_bindings.dart'; import '../util.dart'; @@ -13,22 +12,11 @@ import '../util.dart'; /// /// HARD reset will trigger a MIXED reset and the working directory will be replaced /// with the content of the index. (Untracked and ignored files will be left alone, however.) -/// -/// Throws a [LibGit2Error] if error occured. void reset({ required Pointer repoPointer, required Pointer targetPointer, required int resetType, required Pointer checkoutOptsPointer, }) { - final error = libgit2.git_reset( - repoPointer, - targetPointer, - resetType, - checkoutOptsPointer, - ); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_reset(repoPointer, targetPointer, resetType, checkoutOptsPointer); } diff --git a/lib/src/bindings/revwalk.dart b/lib/src/bindings/revwalk.dart index 782089e..f2defee 100644 --- a/lib/src/bindings/revwalk.dart +++ b/lib/src/bindings/revwalk.dart @@ -32,17 +32,11 @@ Pointer create(Pointer repo) { /// Change the sorting mode when iterating through the repository's contents. /// /// Changing the sorting mode resets the walker. -/// -/// Throws a [LibGit2Error] if error occured. void sorting({ required Pointer walkerPointer, required int sortMode, }) { - final error = libgit2.git_revwalk_sorting(walkerPointer, sortMode); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_revwalk_sorting(walkerPointer, sortMode); } /// Add a new root for the traversal. @@ -130,14 +124,8 @@ void reset(Pointer walker) => libgit2.git_revwalk_reset(walker); /// Simplify the history by first-parent. /// /// No parents other than the first for each commit will be enqueued. -/// -/// Throws a [LibGit2Error] if error occured. void simplifyFirstParent(Pointer walker) { - final error = libgit2.git_revwalk_simplify_first_parent(walker); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_revwalk_simplify_first_parent(walker); } /// Return the repository on which this walker is operating. diff --git a/lib/src/bindings/signature.dart b/lib/src/bindings/signature.dart index 51220e6..4d8f2d2 100644 --- a/lib/src/bindings/signature.dart +++ b/lib/src/bindings/signature.dart @@ -56,18 +56,10 @@ Pointer now({required String name, required String email}) { /// /// This looks up the user.name and user.email from the configuration and uses the /// current time as the timestamp, and creates a new signature based on that information. -/// -/// Throws a [LibGit2Error] if error occured. Pointer defaultSignature(Pointer repo) { final out = calloc>(); - final error = libgit2.git_signature_default(out, repo); - - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out.value; - } + libgit2.git_signature_default(out, repo); + return out.value; } /// Free an existing signature. diff --git a/lib/src/bindings/stash.dart b/lib/src/bindings/stash.dart index 60aa05d..8e580bb 100644 --- a/lib/src/bindings/stash.dart +++ b/lib/src/bindings/stash.dart @@ -48,16 +48,11 @@ void apply({ List? paths, }) { final options = calloc(); - final optionsError = libgit2.git_stash_apply_options_init( + libgit2.git_stash_apply_options_init( options, GIT_STASH_APPLY_OPTIONS_VERSION, ); - if (optionsError < 0) { - calloc.free(options); - throw LibGit2Error(libgit2.git_error_last()); - } - final checkoutOptions = checkout_bindings.initOptions( strategy: strategy, directory: directory, @@ -88,7 +83,11 @@ void apply({ /// /// Throws a [LibGit2Error] if error occured. void drop({required Pointer repoPointer, required int index}) { - libgit2.git_stash_drop(repoPointer, index); + final error = libgit2.git_stash_drop(repoPointer, index); + + if (error < 0) { + throw LibGit2Error(libgit2.git_error_last()); + } } /// Apply a single stashed state from the stash list and remove it from the list if successful. @@ -103,16 +102,11 @@ void pop({ List? paths, }) { final options = calloc(); - final optionsError = libgit2.git_stash_apply_options_init( + libgit2.git_stash_apply_options_init( options, GIT_STASH_APPLY_OPTIONS_VERSION, ); - if (optionsError < 0) { - calloc.free(options); - throw LibGit2Error(libgit2.git_error_last()); - } - final checkoutOptions = checkout_bindings.initOptions( strategy: strategy, directory: directory, diff --git a/lib/src/bindings/status.dart b/lib/src/bindings/status.dart index 0cb75ff..5af5fba 100644 --- a/lib/src/bindings/status.dart +++ b/lib/src/bindings/status.dart @@ -31,19 +31,11 @@ int listEntryCount(Pointer statuslist) { /// Get a pointer to one of the entries in the status list. /// /// The entry is not modifiable and should not be freed. -/// -/// Throws [RangeError] if position is out of bounds Pointer getByIndex({ required Pointer statuslistPointer, required int index, }) { - final result = libgit2.git_status_byindex(statuslistPointer, index); - - if (result == nullptr) { - throw RangeError('Out of bounds'); - } else { - return result; - } + return libgit2.git_status_byindex(statuslistPointer, index); } /// Get file status for a single file. diff --git a/lib/src/bindings/submodule.dart b/lib/src/bindings/submodule.dart index f356db9..cf5f705 100644 --- a/lib/src/bindings/submodule.dart +++ b/lib/src/bindings/submodule.dart @@ -22,24 +22,17 @@ int _listCb( } /// Returns a list with all tracked submodules paths of a repository. -/// -/// Throws a [LibGit2Error] if error occured. List list(Pointer repo) { const except = -1; final callback = Pointer.fromFunction< Int32 Function(Pointer, Pointer, Pointer)>( _listCb, except); - final error = libgit2.git_submodule_foreach(repo, callback, nullptr); + libgit2.git_submodule_foreach(repo, callback, nullptr); - if (error < 0) { - _pathsList.clear(); - throw LibGit2Error(libgit2.git_error_last()); - } else { - final result = _pathsList.toList(growable: false); - _pathsList.clear(); - return result; - } + final result = _pathsList.toList(growable: false); + _pathsList.clear(); + return result; } /// Lookup submodule information by name or path. @@ -76,18 +69,12 @@ Pointer lookup({ /// /// By default, existing entries will not be overwritten, but setting [overwrite] /// to true forces them to be updated. -/// -/// Throws a [LibGit2Error] if error occured. void init({ required Pointer submodulePointer, bool overwrite = false, }) { final overwriteC = overwrite ? 1 : 0; - final error = libgit2.git_submodule_init(submodulePointer, overwriteC); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_submodule_init(submodulePointer, overwriteC); } /// Update a submodule. This will clone a missing submodule and checkout the @@ -108,16 +95,11 @@ void update({ }) { final initC = init ? 1 : 0; final options = calloc(); - final optionsError = libgit2.git_submodule_update_options_init( + libgit2.git_submodule_update_options_init( options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, ); - if (optionsError < 0) { - calloc.free(options); - throw LibGit2Error(libgit2.git_error_last()); - } - RemoteCallbacks.plug( callbacksOptions: options.ref.fetch_opts.callbacks, callbacks: callbacks, @@ -199,17 +181,11 @@ void clone({ }) { final out = calloc>(); final options = calloc(); - final optionsError = libgit2.git_submodule_update_options_init( + libgit2.git_submodule_update_options_init( options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, ); - if (optionsError < 0) { - calloc.free(out); - calloc.free(options); - throw LibGit2Error(libgit2.git_error_last()); - } - RemoteCallbacks.plug( callbacksOptions: options.ref.fetch_opts.callbacks, callbacks: callbacks, @@ -232,22 +208,14 @@ void clone({ /// the clone of the submodule. This adds the `.gitmodules` file and the newly /// cloned submodule to the index to be ready to be committed (but doesn't actually /// do the commit). -/// -/// Throws a [LibGit2Error] if error occured. void addFinalize(Pointer submodule) { - final error = libgit2.git_submodule_add_finalize(submodule); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_submodule_add_finalize(submodule); } /// Get the status for a submodule. /// /// This looks at a submodule and tries to determine the status. How deeply it examines /// the working directory to do this will depend on the [ignore] value. -/// -/// Throws a [LibGit2Error] if error occured. int status({ required Pointer repoPointer, required String name, @@ -255,18 +223,13 @@ int status({ }) { final out = calloc(); final nameC = name.toNativeUtf8().cast(); - final error = libgit2.git_submodule_status(out, repoPointer, nameC, ignore); + libgit2.git_submodule_status(out, repoPointer, nameC, ignore); calloc.free(nameC); - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - final result = out.value; - calloc.free(out); - return result; - } + final result = out.value; + calloc.free(out); + return result; } /// Copy submodule remote info into submodule repo. @@ -275,15 +238,8 @@ int status({ /// config, acting like `git submodule sync`. This is useful if you have altered the URL /// for the submodule (or it has been altered by a fetch of upstream changes) and you /// need to update your local repo. -/// -/// Throws a [LibGit2Error] if error occured. -void sync(Pointer submodule) { - final error = libgit2.git_submodule_sync(submodule); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } -} +void sync(Pointer submodule) => + libgit2.git_submodule_sync(submodule); /// Reread submodule info from config, index, and HEAD. /// @@ -296,11 +252,7 @@ void reload({ bool force = false, }) { final forceC = force ? 1 : 0; - final error = libgit2.git_submodule_reload(submodulePointer, forceC); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_submodule_reload(submodulePointer, forceC); } /// Get the name of submodule. @@ -325,8 +277,6 @@ String url(Pointer submodule) { /// /// After calling this, you may wish to call [sync] to write the changes to /// the checked out submodule repository. -/// -/// Throws a [LibGit2Error] if error occured. void setUrl({ required Pointer repoPointer, required String name, @@ -335,14 +285,10 @@ void setUrl({ final nameC = name.toNativeUtf8().cast(); final urlC = url.toNativeUtf8().cast(); - final error = libgit2.git_submodule_set_url(repoPointer, nameC, urlC); + libgit2.git_submodule_set_url(repoPointer, nameC, urlC); calloc.free(nameC); calloc.free(urlC); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } } /// Get the branch for the submodule. @@ -355,8 +301,6 @@ String branch(Pointer submodule) { /// /// After calling this, you may wish to call [sync] to write the changes to /// the checked out submodule repository. -/// -/// Throws a [LibGit2Error] if error occured. void setBranch({ required Pointer repoPointer, required String name, @@ -365,14 +309,10 @@ void setBranch({ final nameC = name.toNativeUtf8().cast(); final branchC = branch.toNativeUtf8().cast(); - final error = libgit2.git_submodule_set_branch(repoPointer, nameC, branchC); + libgit2.git_submodule_set_branch(repoPointer, nameC, branchC); calloc.free(nameC); calloc.free(branchC); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } } /// Get the OID for the submodule in the current HEAD tree. @@ -412,21 +352,14 @@ int ignore(Pointer submodule) { /// Set the ignore rule for the submodule in the configuration. /// /// This does not affect any currently-loaded instances. -/// -/// Throws a [LibGit2Error] if error occured. void setIgnore({ required Pointer repoPointer, required String name, required int ignore, }) { final nameC = name.toNativeUtf8().cast(); - final error = libgit2.git_submodule_set_ignore(repoPointer, nameC, ignore); - + libgit2.git_submodule_set_ignore(repoPointer, nameC, ignore); calloc.free(nameC); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } } /// Get the update rule that will be used for the submodule. @@ -439,21 +372,14 @@ int updateRule(Pointer submodule) { /// Set the update rule for the submodule in the configuration. /// /// This setting won't affect any existing instances. -/// -/// Throws a [LibGit2Error] if error occured. void setUpdateRule({ required Pointer repoPointer, required String name, required int update, }) { final nameC = name.toNativeUtf8().cast(); - final error = libgit2.git_submodule_set_update(repoPointer, nameC, update); - + libgit2.git_submodule_set_update(repoPointer, nameC, update); calloc.free(nameC); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } } /// Get the containing repository for a submodule. diff --git a/lib/src/bindings/tag.dart b/lib/src/bindings/tag.dart index b02f843..9c55e0f 100644 --- a/lib/src/bindings/tag.dart +++ b/lib/src/bindings/tag.dart @@ -142,10 +142,5 @@ void delete({ } } -/// Get the repository that contains the tag. -Pointer owner(Pointer tag) { - return libgit2.git_tag_owner(tag); -} - /// Close an open tag to release memory. void free(Pointer tag) => libgit2.git_tag_free(tag); diff --git a/lib/src/bindings/tree.dart b/lib/src/bindings/tree.dart index 1ca3889..a6b114b 100644 --- a/lib/src/bindings/tree.dart +++ b/lib/src/bindings/tree.dart @@ -109,16 +109,6 @@ String entryName(Pointer entry) => int entryFilemode(Pointer entry) => libgit2.git_tree_entry_filemode(entry); -/// Compare two tree entries. -/// -/// Returns <0 if e1 is before e2, 0 if e1 == e2, >0 if e1 is after e2. -int compare({ - required Pointer aPointer, - required Pointer bPointer, -}) { - return libgit2.git_tree_entry_cmp(aPointer, bPointer); -} - /// Free a user-owned tree entry. /// /// IMPORTANT: This function is only needed for tree entries owned by the user, diff --git a/lib/src/bindings/treebuilder.dart b/lib/src/bindings/treebuilder.dart index 1829827..91c7c84 100644 --- a/lib/src/bindings/treebuilder.dart +++ b/lib/src/bindings/treebuilder.dart @@ -32,30 +32,14 @@ Pointer create({ } /// Write the contents of the tree builder as a tree object. -/// -/// Throws a [LibGit2Error] if error occured. Pointer write(Pointer bld) { final out = calloc(); - final error = libgit2.git_treebuilder_write(out, bld); - - if (error < 0) { - calloc.free(out); - throw LibGit2Error(libgit2.git_error_last()); - } else { - return out; - } + libgit2.git_treebuilder_write(out, bld); + return out; } /// Clear all the entires in the builder. -/// -/// Throws a [LibGit2Error] if error occured. -void clear(Pointer bld) { - final error = libgit2.git_treebuilder_clear(bld); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } -} +void clear(Pointer bld) => libgit2.git_treebuilder_clear(bld); /// Get the number of entries listed in a treebuilder. int entryCount(Pointer bld) => diff --git a/lib/src/bindings/worktree.dart b/lib/src/bindings/worktree.dart index d1009ae..8aec0ea 100644 --- a/lib/src/bindings/worktree.dart +++ b/lib/src/bindings/worktree.dart @@ -22,18 +22,7 @@ Pointer create({ final pathC = path.toNativeUtf8().cast(); final opts = calloc(); - final optsError = libgit2.git_worktree_add_options_init( - opts, - GIT_WORKTREE_ADD_OPTIONS_VERSION, - ); - - if (optsError < 0) { - calloc.free(out); - calloc.free(nameC); - calloc.free(pathC); - calloc.free(opts); - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_worktree_add_options_init(opts, GIT_WORKTREE_ADD_OPTIONS_VERSION); opts.ref.ref = nullptr; if (refPointer != null) { @@ -84,29 +73,19 @@ Pointer lookup({ /// Throws a [LibGit2Error] if error occured. bool isPrunable(Pointer wt) { final opts = calloc(); - final optsError = libgit2.git_worktree_prune_options_init( + libgit2.git_worktree_prune_options_init( opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION, ); - if (optsError < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } - return libgit2.git_worktree_is_prunable(wt, opts) > 0 ? true : false; } /// Prune working tree. /// /// Prune the working tree, that is remove the git data structures on disk. -/// -/// Throws a [LibGit2Error] if error occured. void prune(Pointer wt) { - final error = libgit2.git_worktree_prune(wt, nullptr); - - if (error < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } + libgit2.git_worktree_prune(wt, nullptr); } /// List names of linked working trees. @@ -143,18 +122,8 @@ String path(Pointer wt) { /// /// A worktree may be locked if the linked working tree is stored on a portable /// device which is not available. -/// -/// Throws a [LibGit2Error] if error occured. bool isLocked(Pointer wt) { - final result = libgit2.git_worktree_is_locked(nullptr, wt); - - if (result < 0) { - throw LibGit2Error(libgit2.git_error_last()); - } else if (result == 0) { - return false; - } else { - return true; - } + return libgit2.git_worktree_is_locked(nullptr, wt) == 1 ? true : false; } /// Lock worktree if not already locked. diff --git a/lib/src/blob.dart b/lib/src/blob.dart index e9696ac..de4d07b 100644 --- a/lib/src/blob.dart +++ b/lib/src/blob.dart @@ -90,7 +90,7 @@ class Blob { final result = patch_bindings.fromBlobs( oldBlobPointer: _blobPointer, oldAsPath: oldAsPath, - newBlobPointer: newBlob?.pointer, + newBlobPointer: newBlob?.pointer ?? nullptr, newAsPath: newAsPath, flags: flags.fold(0, (acc, e) => acc | e.value), contextLines: contextLines, diff --git a/lib/src/branch.dart b/lib/src/branch.dart index b4a0388..d0bc570 100644 --- a/lib/src/branch.dart +++ b/lib/src/branch.dart @@ -37,29 +37,26 @@ class Branch { ); } - /// Lookups a branch by its [name] in a [repo]sitory. + /// Lookups a branch by its [name] and [type] in a [repo]sitory. /// /// The branch name will be checked for validity. /// - /// Should be freed with [free] to release allocated memory when no longer - /// needed. + /// If branch [type] is [GitBranch.remote] you must include the remote name + /// in the [name] (e.g. "origin/master"). + /// + /// Should be freed to release allocated memory when no longer needed. /// /// Throws a [LibGit2Error] if error occured. - Branch.lookup({required Repository repo, required String name}) { - final ref = Reference( - reference_bindings.lookupDWIM( - repoPointer: repo.pointer, - name: name, - ), - ); - + Branch.lookup({ + required Repository repo, + required String name, + GitBranch type = GitBranch.local, + }) { _branchPointer = bindings.lookup( repoPointer: repo.pointer, branchName: name, - branchType: ref.isBranch ? GitBranch.local.value : GitBranch.remote.value, + branchType: type.value, ); - - ref.free(); } late final Pointer _branchPointer; diff --git a/lib/src/commit.dart b/lib/src/commit.dart index ed3d913..1f32b33 100644 --- a/lib/src/commit.dart +++ b/lib/src/commit.dart @@ -80,8 +80,6 @@ class Commit { Signature get author => Signature(bindings.author(_commitPointer)); /// Returns list of parent commits [Oid]s. - /// - /// Throws a [LibGit2Error] if error occured. List get parents { var parents = []; final parentCount = bindings.parentCount(_commitPointer); @@ -114,3 +112,29 @@ class Commit { 'time: $time, committer: $committer, author: $author}'; } } + +/// 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. +/// +/// Note: for internal use. +class AnnotatedCommit { + /// Lookups an annotated commit from the given commit [oid]. The resulting annotated commit + /// must be freed to release allocated memory. + /// + /// Throws a [LibGit2Error] if error occured. + AnnotatedCommit.lookup({required Repository repo, required Oid oid}) { + _annotatedCommitPointer = bindings.annotatedLookup( + repoPointer: repo.pointer, + oidPointer: oid.pointer, + ); + } + + late final Pointer> _annotatedCommitPointer; + + /// Pointer to pointer to memory address for allocated commit object. + Pointer> get pointer => _annotatedCommitPointer; + + /// Releases memory allocated for commit object. + void free() => bindings.annotatedFree(_annotatedCommitPointer.value); +} diff --git a/lib/src/diff.dart b/lib/src/diff.dart index b5e57a5..d2ab0ad 100644 --- a/lib/src/diff.dart +++ b/lib/src/diff.dart @@ -13,6 +13,15 @@ class Diff { /// Should be freed with `free()` to release allocated memory. Diff(this._diffPointer); + /// Reads the contents of a git patch file into a git diff object. + /// + /// The diff object produced is similar to the one that would be produced if you actually + /// produced it computationally by comparing two trees, however there may be subtle differences. + /// For example, a patch file likely contains abbreviated object IDs, so the object IDs in a + /// diff delta produced by this function will also be abbreviated. + /// + /// This function will only read patch files created by a git implementation, it will not + /// read unified diffs produced by the `diff` program, nor any other types of patch files. Diff.parse(String content) { libgit2.git_libgit2_init(); _diffPointer = bindings.parse(content); @@ -63,7 +72,9 @@ class Diff { patch.free(); } - final result = buffer.ref.ptr.cast().toDartString(); + final result = buffer.ref.ptr == nullptr + ? '' + : buffer.ref.ptr.cast().toDartString(); calloc.free(buffer); return result; } @@ -115,7 +126,7 @@ class Diff { /// and should in fact generate the same IDs as the upstream git project does. /// /// Throws a [LibGit2Error] if error occured. - Oid get patchId => Oid(bindings.patchId(_diffPointer)); + Oid get patchOid => Oid(bindings.patchOid(_diffPointer)); /// Releases memory allocated for diff object. void free() => bindings.free(_diffPointer); @@ -301,8 +312,6 @@ class DiffHunk { } /// Returns list of lines in a hunk of a patch. - /// - /// Throws a [LibGit2Error] if error occured. List get lines { var lines = []; for (var i = 0; i < linesCount; i++) { diff --git a/lib/src/index.dart b/lib/src/index.dart index ee7353e..a01d429 100644 --- a/lib/src/index.dart +++ b/lib/src/index.dart @@ -131,8 +131,6 @@ class Index with IterableMixin { /// if it has changed since the last time it was loaded. Purely in-memory index data /// will be untouched. Be aware: if there are changes on disk, unwritten in-memory changes /// are discarded. - /// - /// Throws a [LibGit2Error] if error occured. void read({bool force = true}) => bindings.read(indexPointer: _indexPointer, force: force); @@ -143,8 +141,6 @@ class Index with IterableMixin { } /// Writes an existing index object from memory back to disk using an atomic file lock. - /// - /// Throws a [LibGit2Error] if error occured. void write() => bindings.write(_indexPointer); /// Writes the index as a tree. @@ -180,8 +176,6 @@ class Index with IterableMixin { bindings.removeAll(indexPointer: _indexPointer, pathspec: path); /// Creates a diff between the repository index and the workdir directory. - /// - /// Throws a [LibGit2Error] if error occured. Diff diffToWorkdir({ Set flags = const {GitDiff.normal}, int contextLines = 3, @@ -197,8 +191,6 @@ class Index with IterableMixin { } /// Creates a diff between a tree and repository index. - /// - /// Throws a [LibGit2Error] if error occured. Diff diffToTree({ required Tree tree, Set flags = const {GitDiff.normal}, diff --git a/lib/src/mailmap.dart b/lib/src/mailmap.dart index 79ccc6b..497c58d 100644 --- a/lib/src/mailmap.dart +++ b/lib/src/mailmap.dart @@ -9,8 +9,6 @@ class Mailmap { /// /// This object is empty, so you'll have to add a mailmap file before you can /// do anything with it. Must be freed with `free()`. - /// - /// Throws a [LibGit2Error] if error occured. Mailmap.empty() { libgit2.git_libgit2_init(); @@ -20,8 +18,6 @@ class Mailmap { /// Initializes a new instance of [Mailmap] class from provided buffer. /// /// Must be freed with `free()`. - /// - /// Throws a [LibGit2Error] if error occured. Mailmap.fromBuffer(String buffer) { libgit2.git_libgit2_init(); @@ -50,8 +46,6 @@ class Mailmap { /// Returns list containing resolved [name] and [email] to the corresponding real name /// and real email respectively. - /// - /// Throws a [LibGit2Error] if error occured. List resolve({ required String name, required String email, @@ -64,8 +58,6 @@ class Mailmap { } /// Resolves a signature to use real names and emails with a mailmap. - /// - /// Throws a [LibGit2Error] if error occured. Signature resolveSignature(Signature signature) { return Signature(bindings.resolveSignature( mailmapPointer: _mailmapPointer, @@ -76,20 +68,24 @@ class Mailmap { /// Adds a single entry to the given mailmap object. If the entry already exists, /// it will be replaced with the new entry. /// - /// Throws a [LibGit2Error] if error occured. + /// Throws a [ArgumentError] if [replaceEmail] is empty string. void addEntry({ String? realName, String? realEmail, String? replaceName, required String replaceEmail, }) { - bindings.addEntry( - mailmapPointer: _mailmapPointer, - realName: realName, - realEmail: realEmail, - replaceName: replaceName, - replaceEmail: replaceEmail, - ); + if (replaceEmail.trim().isEmpty) { + throw ArgumentError.value('replaceEmail can\'t be empty'); + } else { + bindings.addEntry( + mailmapPointer: _mailmapPointer, + realName: realName, + realEmail: realEmail, + replaceName: replaceName, + replaceEmail: replaceEmail, + ); + } } /// Releases memory allocated for mailmap object. diff --git a/lib/src/odb.dart b/lib/src/odb.dart index 29525d5..678d567 100644 --- a/lib/src/odb.dart +++ b/lib/src/odb.dart @@ -14,8 +14,6 @@ class Odb { /// /// Before the ODB can be used for read/writing, a custom database backend must be /// manually added. - /// - /// Throws a [LibGit2Error] if error occured. Odb.create() { libgit2.git_libgit2_init(); @@ -36,8 +34,6 @@ class Odb { /// have been exhausted. /// /// Writing is disabled on alternate backends. - /// - /// Throws a [LibGit2Error] if error occured. void addDiskAlternate(String path) { bindings.addDiskAlternate( odbPointer: _odbPointer, diff --git a/lib/src/patch.dart b/lib/src/patch.dart index 2002edc..2ad3fdb 100644 --- a/lib/src/patch.dart +++ b/lib/src/patch.dart @@ -9,7 +9,7 @@ class Patch { /// Initializes a new instance of [Patch] class from provided /// pointer to patch object in memory and pointers to old and new blobs/buffers. /// - /// Should be freed with `free()` to release allocated memory. + /// Should be freed to release allocated memory. Patch(this._patchPointer, this._aPointer, this._bPointer); /// Directly generates a patch from the difference between two blobs, buffers or @@ -17,9 +17,7 @@ class Patch { /// /// [a] and [b] can be [Blob], [String] or null. /// - /// Should be freed with `free()` to release allocated memory. - /// - /// Throws a [LibGit2Error] if error occured. + /// Should be freed to release allocated memory. Patch.create({ required Object? a, required Object? b, @@ -35,11 +33,11 @@ class Patch { var result = {}; if (a is Blob?) { - if (b is Blob) { + if (b is Blob?) { result = bindings.fromBlobs( oldBlobPointer: a?.pointer ?? nullptr, oldAsPath: aPath, - newBlobPointer: b.pointer, + newBlobPointer: b?.pointer ?? nullptr, newAsPath: bPath, flags: flagsInt, contextLines: contextLines, @@ -49,7 +47,7 @@ class Patch { result = bindings.fromBlobAndBuffer( oldBlobPointer: a?.pointer, oldAsPath: aPath, - buffer: b, + buffer: b as String?, bufferAsPath: bPath, flags: flagsInt, contextLines: contextLines, @@ -126,8 +124,6 @@ class Patch { DiffDelta get delta => DiffDelta(bindings.delta(_patchPointer)); /// Returns the list of hunks in a patch. - /// - /// Throws a [LibGit2Error] if error occured. List get hunks { final length = bindings.numHunks(_patchPointer); final hunks = []; @@ -157,5 +153,5 @@ class Patch { } @override - String toString() => 'Patch{size: $size, delta: $delta}'; + String toString() => 'Patch{size: ${size()}, delta: $delta}'; } diff --git a/lib/src/rebase.dart b/lib/src/rebase.dart index 87c56d7..83fb9da 100644 --- a/lib/src/rebase.dart +++ b/lib/src/rebase.dart @@ -2,7 +2,6 @@ import 'dart:ffi'; import 'package:libgit2dart/libgit2dart.dart'; import 'bindings/libgit2_bindings.dart'; import 'bindings/rebase.dart' as bindings; -import 'bindings/commit.dart' as commit_bindings; class Rebase { /// Initializes a new instance of the [Rebase] class by initializing a @@ -25,38 +24,33 @@ class Rebase { Oid? upstream, Oid? onto, }) { - Pointer? _branch, _upstream, _onto; + AnnotatedCommit? _branch, _upstream, _onto; if (branch != null) { - _branch = commit_bindings - .annotatedLookup( - repoPointer: repo.pointer, - oidPointer: branch.pointer, - ) - .value; + _branch = AnnotatedCommit.lookup(repo: repo, oid: branch); } if (upstream != null) { - _upstream = commit_bindings - .annotatedLookup( - repoPointer: repo.pointer, - oidPointer: upstream.pointer, - ) - .value; + _upstream = AnnotatedCommit.lookup(repo: repo, oid: upstream); } if (onto != null) { - _onto = commit_bindings - .annotatedLookup( - repoPointer: repo.pointer, - oidPointer: onto.pointer, - ) - .value; + _onto = AnnotatedCommit.lookup(repo: repo, oid: onto); } _rebasePointer = bindings.init( repoPointer: repo.pointer, - branchPointer: _branch, - upstreamPointer: _upstream, - ontoPointer: _onto, + branchPointer: _branch?.pointer.value, + upstreamPointer: _upstream?.pointer.value, + ontoPointer: _onto?.pointer.value, ); + + if (branch != null) { + _branch!.free(); + } + if (upstream != null) { + _upstream!.free(); + } + if (onto != null) { + _onto!.free(); + } } /// Pointer to memory address for allocated rebase object. @@ -96,14 +90,10 @@ class Rebase { } /// Finishes a rebase that is currently in progress once all patches have been applied. - /// - /// Throws a [LibGit2Error] if error occured. void finish() => bindings.finish(_rebasePointer); /// Aborts a rebase that is currently in progress, resetting the repository and working /// directory to their state before rebase began. - /// - /// Throws a [LibGit2Error] if error occured. void abort() => bindings.abort(_rebasePointer); /// Releases memory allocated for rebase object. diff --git a/lib/src/reference.dart b/lib/src/reference.dart index 5adf45c..e583c19 100644 --- a/lib/src/reference.dart +++ b/lib/src/reference.dart @@ -80,8 +80,6 @@ class Reference { /// Deletes an existing reference with provided [name]. /// /// This method works for both direct and symbolic references. - /// - /// Throws a [LibGit2Error] if error occured. static void delete({required Repository repo, required String name}) { final ref = Reference.lookup(repo: repo, name: name); bindings.delete(ref.pointer); @@ -226,8 +224,6 @@ class Reference { String get shorthand => bindings.shorthand(_refPointer); /// Checks if a reflog exists for the specified reference [name]. - /// - /// Throws a [LibGit2Error] if error occured. bool get hasLog { return bindings.hasLog( repoPointer: bindings.owner(_refPointer), diff --git a/lib/src/reflog.dart b/lib/src/reflog.dart index 96d47b0..18fab99 100644 --- a/lib/src/reflog.dart +++ b/lib/src/reflog.dart @@ -6,8 +6,6 @@ import 'bindings/reflog.dart' as bindings; class RefLog with IterableMixin { /// Initializes a new instance of [RefLog] class from provided [Reference]. - /// - /// Throws a [LibGit2Error] if error occured. RefLog(Reference ref) { _reflogPointer = bindings.read( repoPointer: ref.owner.pointer, diff --git a/lib/src/remote.dart b/lib/src/remote.dart index ef24f86..5951aff 100644 --- a/lib/src/remote.dart +++ b/lib/src/remote.dart @@ -80,8 +80,6 @@ class Remote { } /// Returns a list of the configured remotes for a [repo]sitory. - /// - /// Throws a [LibGit2Error] if error occured. static List list(Repository repo) { return bindings.list(repo.pointer); } @@ -184,11 +182,19 @@ class Remote { /// Get the remote's list of push refspecs. List get pushRefspecs => bindings.pushRefspecs(_remotePointer); - /// Get the remote repository's reference advertisement list. + /// Returns the remote repository's reference list and their associated commit ids. /// /// [proxy] can be 'auto' to try to auto-detect the proxy from the git configuration or some /// specified url. By default connection isn't done through proxy. /// + /// Returned map keys: + /// - `local` is true if remote head is available locally, false otherwise. + /// - `loid` is the oid of the object the local copy of the remote head is currently + /// pointing to. null if there is no local copy of the remote head. + /// - `name` is the name of the reference. + /// - `oid` is the oid of the object the remote head is currently pointing to. + /// - `symref` is the target of the symbolic reference or empty string. + /// /// Throws a [LibGit2Error] if error occured. List> ls({ String? proxy, diff --git a/lib/src/repository.dart b/lib/src/repository.dart index 022897e..7e210c0 100644 --- a/lib/src/repository.dart +++ b/lib/src/repository.dart @@ -120,8 +120,6 @@ class Repository { /// the first repository is found, or when reaching a directory referenced in [ceilingDirs]. /// /// The method will automatically detect if the repository is bare (if there is a repository). - /// - /// Throws a [LibGit2Error] if error occured. static String discover({required String startPath, String? ceilingDirs}) { return bindings.discover( startPath: startPath, @@ -164,8 +162,6 @@ class Repository { /// under refs/namespaces/foo/, use foo as the namespace. /// /// Pass null to unset. - /// - /// Throws a [LibGit2Error] if error occured. void setNamespace(String? namespace) { bindings.setNamespace( repoPointer: _repoPointer, @@ -322,8 +318,6 @@ class Repository { /// will be returned, including global and system configurations (if they are available). /// /// The configuration file must be freed once it's no longer being used by the user. - /// - /// Throws a [LibGit2Error] if error occured. Config get config => Config(bindings.config(_repoPointer)); /// Returns a snapshot of the repository's configuration. @@ -332,8 +326,6 @@ class Repository { /// The contents of this snapshot will not change, even if the underlying config files are modified. /// /// The configuration file must be freed once it's no longer being used by the user. - /// - /// Throws a [LibGit2Error] if error occured. Config get configSnapshot => Config(bindings.configSnapshot(_repoPointer)); /// Returns [Reference] object pointing to repository head. @@ -451,8 +443,6 @@ class Repository { /// /// This looks up the user.name and user.email from the configuration and uses the /// current time as the timestamp, and creates a new signature based on that information. - /// - /// Throws a [LibGit2Error] if error occured. Signature get defaultSignature => Signature.defaultSignature(this); /// Returns the list of commits starting from provided commit [oid]. @@ -648,15 +638,21 @@ class Repository { List get branchesRemote => Branch.list(repo: this, type: GitBranch.remote); - /// Lookups a branch by its [name] in a repository. + /// Lookups a branch by its [name] and [type] in a repository. /// - /// The branch name will be checked for validity. + /// The branch [name] will be checked for validity. + /// + /// If branch [type] is [GitBranch.remote] you must include the remote name + /// in the [name] (e.g. "origin/master"). /// /// Should be freed to release allocated memory when no longer needed. /// /// Throws a [LibGit2Error] if error occured. - Branch lookupBranch(String name) { - return Branch.lookup(repo: this, name: name); + Branch lookupBranch({ + required String name, + GitBranch type = GitBranch.local, + }) { + return Branch.lookup(repo: this, name: name, type: type); } /// Creates a new branch pointing at a [target] commit. @@ -706,6 +702,8 @@ class Repository { /// Checks status of the repository and returns map of file paths and their statuses. /// /// Returns empty map if there are no changes in statuses. + /// + /// Throws a [LibGit2Error] if error occured. Map> get status { var result = >{}; var list = status_bindings.listNew(_repoPointer); @@ -789,14 +787,14 @@ class Repository { String ourRef = 'HEAD', }) { final ref = lookupReference(ourRef); - final head = commit_bindings.annotatedLookup( - repoPointer: _repoPointer, - oidPointer: theirHead.pointer, + final head = AnnotatedCommit.lookup( + repo: this, + oid: theirHead, ); final analysisInt = merge_bindings.analysis( repoPointer: _repoPointer, ourRefPointer: ref.pointer, - theirHeadPointer: head, + theirHeadPointer: head.pointer, theirHeadsLen: 1, ); @@ -807,7 +805,7 @@ class Repository { (e) => analysisInt[1] == e.value, ); - commit_bindings.annotatedFree(head.value); + head.free(); ref.free(); return [analysisSet, mergePreference]; @@ -820,18 +818,18 @@ class Repository { /// /// Throws a [LibGit2Error] if error occured. void merge(Oid oid) { - final theirHead = commit_bindings.annotatedLookup( - repoPointer: _repoPointer, - oidPointer: oid.pointer, + final theirHead = AnnotatedCommit.lookup( + repo: this, + oid: oid, ); merge_bindings.merge( repoPointer: _repoPointer, - theirHeadsPointer: theirHead, + theirHeadsPointer: theirHead.pointer, theirHeadsLen: 1, ); - commit_bindings.annotatedFree(theirHead.value); + theirHead.free(); } /// Merges two files as they exist in the index, using the given common ancestor @@ -1092,23 +1090,30 @@ class Repository { return a.diff(newBlob: b, oldAsPath: aPath, newAsPath: bPath); } - /// Applies the [diff] to the given repository, making changes directly in the working directory. + /// Applies the [diff] to the given repository, making changes directly in the + /// working directory (default), the index, or both. /// /// Throws a [LibGit2Error] if error occured. - void apply(Diff diff) { + void apply({ + required Diff diff, + GitApplyLocation location = GitApplyLocation.workdir, + }) { diff_bindings.apply( repoPointer: _repoPointer, diffPointer: diff.pointer, - location: GitApplyLocation.workdir.value, + location: location.value, ); } - /// Checks if the [diff] will apply to HEAD. - bool applies(Diff diff) { + /// Checks if the [diff] will apply to the working directory (default), the index, or both. + bool applies({ + required Diff diff, + GitApplyLocation location = GitApplyLocation.workdir, + }) { return diff_bindings.apply( repoPointer: _repoPointer, diffPointer: diff.pointer, - location: GitApplyLocation.index.value, + location: location.value, check: true, ); } @@ -1162,7 +1167,7 @@ class Repository { /// Removes a single stashed state from the stash list. /// /// Throws a [LibGit2Error] if error occured. - void dropStash([int index = 0]) { + void dropStash({int index = 0}) { Stash.drop(repo: this, index: index); } @@ -1191,8 +1196,6 @@ class Repository { } /// Returns a list of the configured remotes for a repository. - /// - /// Throws a [LibGit2Error] if error occured. List get remotes => Remote.list(this); /// Lookups remote with provided [name]. @@ -1246,8 +1249,6 @@ class Repository { /// /// Returned value can be either `true`, `false`, `null` (if the attribute was not set at all), /// or a [String] value, if the attribute was set to an actual string. - /// - /// Throws a [LibGit2Error] if error occured. Object? getAttribute({ required String path, required String name, @@ -1511,8 +1512,6 @@ class Repository { } /// Returns a list with all tracked submodules paths of a repository. - /// - /// Throws a [LibGit2Error] if error occured. List get submodules => Submodule.list(this); /// Lookups submodule by name or path. diff --git a/lib/src/revwalk.dart b/lib/src/revwalk.dart index c4714bc..a87bd9c 100644 --- a/lib/src/revwalk.dart +++ b/lib/src/revwalk.dart @@ -28,8 +28,6 @@ class RevWalk { /// Changes the sorting mode when iterating through the repository's contents. /// /// Changing the sorting mode resets the walker. - /// - /// Throws a [LibGit2Error] if error occured. void sorting(Set sorting) { bindings.sorting( walkerPointer: _revWalkPointer, @@ -79,8 +77,6 @@ class RevWalk { /// Simplify the history by first-parent. /// /// No parents other than the first for each commit will be enqueued. - /// - /// Throws a [LibGit2Error] if error occured. void simplifyFirstParent() => bindings.simplifyFirstParent(_revWalkPointer); /// Releases memory allocated for [RevWalk] object. diff --git a/lib/src/signature.dart b/lib/src/signature.dart index b3938c4..84718d7 100644 --- a/lib/src/signature.dart +++ b/lib/src/signature.dart @@ -9,7 +9,7 @@ class Signature { /// Initializes a new instance of [Signature] class from provided pointer to /// signature object in memory. /// - /// Should be freed with `free()` to release allocated memory. + /// Should be freed to release allocated memory. Signature(this._signaturePointer); /// Initializes a new instance of [Signature] class from provided [name], [email], @@ -17,7 +17,7 @@ class Signature { /// /// If [time] isn't provided [Signature] will be created with a timestamp of 'now'. /// - /// Should be freed with `free()` to release allocated memory. + /// Should be freed to release allocated memory. Signature.create({ required String name, required String email, @@ -47,8 +47,6 @@ class Signature { /// /// This looks up the user.name and user.email from the configuration and uses the /// current time as the timestamp, and creates a new signature based on that information. - /// - /// Throws a [LibGit2Error] if error occured. static Signature defaultSignature(Repository repo) => Signature(bindings.defaultSignature(repo.pointer)); diff --git a/lib/src/submodule.dart b/lib/src/submodule.dart index 64077f5..68395f4 100644 --- a/lib/src/submodule.dart +++ b/lib/src/submodule.dart @@ -108,16 +108,13 @@ class Submodule { } /// Returns a list with all tracked submodules paths of a repository. - /// - /// Throws a [LibGit2Error] if error occured. static List list(Repository repo) => bindings.list(repo.pointer); /// Opens the repository for a submodule. /// - /// This is a newly opened repository object. The caller is responsible for calling - /// `free()` on it when done. Multiple calls to this function will return distinct - /// git repository objects. This will only work if the submodule is checked out into - /// the working directory. + /// This is a newly opened repository object. The caller is responsible for freeing it + /// when done. Multiple calls to this function will return distinct git repository objects. + /// This will only work if the submodule is checked out into the working directory. /// /// Throws a [LibGit2Error] if error occured. Repository open() { @@ -129,8 +126,6 @@ class Submodule { /// This looks at a submodule and tries to determine the status. How deeply it examines /// the working directory to do this will depend on the combination of [GitSubmoduleIgnore] /// values provided to [ignore] . - /// - /// Throws a [LibGit2Error] if error occured. Set status({ GitSubmoduleIgnore ignore = GitSubmoduleIgnore.unspecified, }) { @@ -151,8 +146,6 @@ class Submodule { /// config, acting like `git submodule sync`. This is useful if you have altered the URL /// for the submodule (or it has been altered by a fetch of upstream changes) and you /// need to update your local repo. - /// - /// Throws a [LibGit2Error] if error occured. void sync() => bindings.sync(_submodulePointer); /// Rereads submodule info from config, index, and HEAD. @@ -181,8 +174,6 @@ class Submodule { /// /// After calling this, you may wish to call [sync] to write the changes to /// the checked out submodule repository. - /// - /// Throws a [LibGit2Error] if error occured. set url(String url) { bindings.setUrl( repoPointer: bindings.owner(_submodulePointer), @@ -198,8 +189,6 @@ class Submodule { /// /// After calling this, you may wish to call [sync] to write the changes to /// the checked out submodule repository. - /// - /// Throws a [LibGit2Error] if error occured. set branch(String branch) { bindings.setBranch( repoPointer: bindings.owner(_submodulePointer), @@ -243,8 +232,6 @@ class Submodule { /// Sets the ignore rule for the submodule in the configuration. /// /// This does not affect any currently-loaded instances. - /// - /// Throws a [LibGit2Error] if error occured. set ignore(GitSubmoduleIgnore ignore) { final repo = bindings.owner(_submodulePointer); bindings.setIgnore(repoPointer: repo, name: name, ignore: ignore.value); @@ -261,8 +248,6 @@ class Submodule { /// Sets the update rule for the submodule in the configuration. /// /// This setting won't affect any existing instances. - /// - /// Throws a [LibGit2Error] if error occured. set updateRule(GitSubmoduleUpdate rule) { bindings.setUpdateRule( repoPointer: bindings.owner(_submodulePointer), diff --git a/lib/src/tree.dart b/lib/src/tree.dart index 602f953..e17f2c5 100644 --- a/lib/src/tree.dart +++ b/lib/src/tree.dart @@ -93,8 +93,6 @@ class Tree { } /// Creates a diff between a tree and repository index. - /// - /// Throws a [LibGit2Error] if error occured. Diff diffToIndex({ required Index index, Set flags = const {GitDiff.normal}, diff --git a/lib/src/treebuilder.dart b/lib/src/treebuilder.dart index efd0027..8e9816f 100644 --- a/lib/src/treebuilder.dart +++ b/lib/src/treebuilder.dart @@ -24,13 +24,9 @@ class TreeBuilder { int get length => bindings.entryCount(_treeBuilderPointer); /// Writes the contents of the tree builder as a tree object. - /// - /// Throws a [LibGit2Error] if error occured. Oid write() => Oid(bindings.write(_treeBuilderPointer)); /// Clears all the entires in the tree builder. - /// - /// Throws a [LibGit2Error] if error occured. void clear() => bindings.clear(_treeBuilderPointer); /// Returns an entry from the tree builder from its filename. diff --git a/lib/src/util.dart b/lib/src/util.dart index 973abb8..6c2f70c 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart @@ -1,3 +1,5 @@ +// coverage:ignore-file + import 'dart:io'; import 'dart:ffi'; import 'bindings/libgit2_bindings.dart'; diff --git a/lib/src/worktree.dart b/lib/src/worktree.dart index abbdfc0..812d305 100644 --- a/lib/src/worktree.dart +++ b/lib/src/worktree.dart @@ -11,7 +11,7 @@ class Worktree { /// If [ref] is provided, no new branch will be created but specified [ref] will /// be used instead. /// - /// Should be freed with `free()` to release allocated memory. + /// Should be freed to release allocated memory. /// /// Throws a [LibGit2Error] if error occured. Worktree.create({ @@ -31,7 +31,7 @@ class Worktree { /// Initializes a new instance of [Worktree] class by looking up existing worktree /// with provided [Repository] object and worktree [name]. /// - /// Should be freed with `free()` to release allocated memory. + /// Should be freed to release allocated memory. /// /// Throws a [LibGit2Error] if error occured. Worktree.lookup({required Repository repo, required String name}) { @@ -56,8 +56,6 @@ class Worktree { /// /// A worktree may be locked if the linked working tree is stored on a portable /// device which is not available. - /// - /// Throws a [LibGit2Error] if error occured. bool get isLocked => bindings.isLocked(_worktreePointer); /// Locks worktree if not already locked. @@ -78,8 +76,6 @@ class Worktree { /// Prunes working tree. /// /// Prune the working tree, that is remove the git data structures on disk. - /// - /// Throws a [LibGit2Error] if error occured. void prune() => bindings.prune(_worktreePointer); /// Checks if worktree is valid. diff --git a/test/blame_test.dart b/test/blame_test.dart index 248ad14..9c6f327 100644 --- a/test/blame_test.dart +++ b/test/blame_test.dart @@ -57,7 +57,10 @@ void main() { group('Blame', () { test('successfully gets the blame for provided file', () { - final blame = repo.blame(path: 'feature_file'); + final blame = repo.blame( + path: 'feature_file', + oldestCommit: repo['f17d0d4'], + ); expect(blame.length, 2); @@ -79,6 +82,33 @@ void main() { blame.free(); }); + test('throws when provided file path is invalid', () { + expect( + () => repo.blame(path: 'invalid'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "the path 'invalid' does not exist in the given tree", + ), + ), + ); + }); + + test( + 'successfully gets the blame for provided file with minMatchCharacters set', + () { + final blame = repo.blame( + path: 'feature_file', + minMatchCharacters: 1, + flags: {GitBlameFlag.trackCopiesSameFile}, + ); + + expect(blame.length, 2); + + blame.free(); + }); + test('successfully gets the blame for provided line', () { final blame = repo.blame(path: 'feature_file'); @@ -102,14 +132,32 @@ void main() { test('throws when provided index for hunk is invalid', () { final blame = repo.blame(path: 'feature_file'); - expect(() => blame[10], throwsA(isA())); + expect( + () => blame[10], + throwsA( + isA().having( + (e) => e.message, + 'error', + '10 is out of bounds', + ), + ), + ); blame.free(); }); test('throws when provided line number for hunk is invalid', () { final blame = repo.blame(path: 'feature_file'); - expect(() => blame.forLine(10), throwsA(isA())); + expect( + () => blame.forLine(10), + throwsA( + isA().having( + (e) => e.message, + 'error', + '10 is out of bounds', + ), + ), + ); blame.free(); }); @@ -119,7 +167,7 @@ void main() { () { final blame = repo.blame( path: 'feature_file', - newestCommit: repo['fc38877b2552ab554752d9a77e1f48f738cca79b'], + newestCommit: repo['fc38877'], flags: {GitBlameFlag.ignoreWhitespace}, ); @@ -143,6 +191,35 @@ void main() { blame.free(); }); + test( + 'successfully gets the blame for provided file with minLine and maxLine set', + () { + final blame = repo.blame( + path: 'feature_file', + minLine: 1, + maxLine: 1, + ); + + expect(blame.length, 1); + + for (var i = 0; i < blame.length; i++) { + expect(blame[i].linesCount, 1); + expect(blame[i].finalCommitOid.sha, hunks[i]['finalCommitOid']); + expect(blame[i].finalStartLineNumber, hunks[i]['finalStartLineNumber']); + expect(blame[i].finalCommitter, hunks[i]['finalCommitter']); + expect(blame[i].originCommitOid.sha, hunks[i]['originCommitOid']); + expect( + blame[i].originStartLineNumber, + hunks[i]['originStartLineNumber'], + ); + expect(blame[i].originCommitter, hunks[i]['originCommitter']); + expect(blame[i].isBoundary, hunks[i]['isBoundary']); + expect(blame[i].originPath, 'feature_file'); + } + + blame.free(); + }); + test('returns string representation of BlameHunk object', () { final blame = repo.blame(path: 'feature_file'); expect(blame.toString(), contains('BlameHunk{')); diff --git a/test/blob_test.dart b/test/blob_test.dart index 68db322..4655194 100644 --- a/test/blob_test.dart +++ b/test/blob_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -28,6 +29,19 @@ void main() { expect(blob, isA()); }); + test('throws when trying to lookup with invalid oid', () { + expect( + () => repo.lookupBlob(repo['0' * 40]), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + 'odb: cannot read object: null OID cannot exist', + ), + ), + ); + }); + test('returns correct values', () { expect(blob.oid.sha, blobSHA); expect(blob.isBinary, false); @@ -47,6 +61,20 @@ void main() { newBlob.free(); }); + test('throws when trying to create new blob and error occurs', () { + final nullRepo = Repository(nullptr); + expect( + () => Blob.create(repo: nullRepo, content: ''), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('successfully creates new blob from file at provided relative path', () { final oid = repo.createBlobFromWorkdir('feature_file'); @@ -66,24 +94,13 @@ void main() { throwsA( isA().having( (e) => e.toString(), - 'message', + 'error', "could not find '${repo.workdir}invalid/path.txt' to stat: No such file or directory", ), ), ); }); - test( - 'throws when creating new blob from path that is outside of working directory', - () { - final outsideFile = - File('${Directory.current.absolute.path}/test/blob_test.dart'); - expect( - () => repo.createBlobFromWorkdir(outsideFile.path), - throwsA(isA()), - ); - }); - test('successfully creates new blob from file at provided path', () { final outsideFile = File('${Directory.current.absolute.path}/test/blob_test.dart'); @@ -96,6 +113,19 @@ void main() { newBlob.free(); }); + test('throws when trying to create from invalid path', () { + expect( + () => repo.createBlobFromDisk('invalid.file'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to resolve path 'invalid.file': No such file or directory", + ), + ), + ); + }); + group('diff', () { const path = 'feature_file'; const blobPatch = """ diff --git a/test/branch_test.dart b/test/branch_test.dart index 1c9a24f..1c301cf 100644 --- a/test/branch_test.dart +++ b/test/branch_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -46,19 +47,53 @@ void main() { expect(repo.branchesRemote, []); }); + test('throws when trying to return list and error occurs', () { + final nullRepo = Repository(nullptr); + expect( + () => Branch.list(repo: nullRepo), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('returns a branch with provided name', () { - final branch = repo.lookupBranch('master'); + final branch = repo.lookupBranch(name: 'master'); expect(branch.target.sha, lastCommit.sha); branch.free(); }); test('throws when provided name not found', () { - expect(() => repo.lookupBranch('invalid'), throwsA(isA())); + expect( + () => repo.lookupBranch(name: 'invalid'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "cannot locate local branch 'invalid'", + ), + ), + ); + + expect( + () => repo.lookupBranch(name: 'origin/invalid', type: GitBranch.remote), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "cannot locate remote-tracking branch 'origin/invalid'", + ), + ), + ); }); test('checks if branch is current head', () { - final masterBranch = repo.lookupBranch('master'); - final featureBranch = repo.lookupBranch('feature'); + final masterBranch = repo.lookupBranch(name: 'master'); + final featureBranch = repo.lookupBranch(name: 'feature'); expect(masterBranch.isHead, true); expect(featureBranch.isHead, false); @@ -67,12 +102,65 @@ void main() { featureBranch.free(); }); + test('throws when checking if branch is current head and error occurs', () { + final nullBranch = Branch(nullptr); + expect( + () => nullBranch.isHead, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'branch'", + ), + ), + ); + }); + + test('checks if branch is checked out', () { + final masterBranch = repo.lookupBranch(name: 'master'); + final featureBranch = repo.lookupBranch(name: 'feature'); + + expect(masterBranch.isCheckedOut, true); + expect(featureBranch.isCheckedOut, false); + + masterBranch.free(); + featureBranch.free(); + }); + + test('throws when checking if branch is checked out and error occurs', () { + final nullBranch = Branch(nullptr); + expect( + () => nullBranch.isCheckedOut, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'branch'", + ), + ), + ); + }); + test('returns name', () { - final branch = repo.lookupBranch('master'); + final branch = repo.lookupBranch(name: 'master'); expect(branch.name, 'master'); branch.free(); }); + test('throws when getting name and error occurs', () { + final nullBranch = Branch(nullptr); + expect( + () => nullBranch.name, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'ref'", + ), + ), + ); + }); + group('create()', () { test('successfully creates', () { final commit = repo.lookupCommit(lastCommit); @@ -95,7 +183,14 @@ void main() { expect( () => repo.createBranch(name: 'feature', target: commit), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to write reference 'refs/heads/feature': " + "a reference with that name already exists.", + ), + ), ); commit.free(); @@ -127,15 +222,27 @@ void main() { repo.deleteBranch('feature'); expect( - () => repo.lookupBranch('feature'), - throwsA(isA()), + () => repo.lookupBranch(name: 'feature'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "cannot locate local branch 'feature'", + ), + ), ); }); test('throws when trying to delete current HEAD', () { expect( () => repo.deleteBranch('master'), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "cannot delete branch 'refs/heads/master' as it is the current HEAD of the repository.", + ), + ), ); }); }); @@ -143,13 +250,19 @@ void main() { group('rename()', () { test('successfully renames', () { repo.renameBranch(oldName: 'feature', newName: 'renamed'); - final branch = repo.lookupBranch('renamed'); + final branch = repo.lookupBranch(name: 'renamed'); final branches = repo.branches; expect(branches.length, 2); expect( - () => repo.lookupBranch('feature'), - throwsA(isA()), + () => repo.lookupBranch(name: 'feature'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "cannot locate local branch 'feature'", + ), + ), ); expect(branch.target, featureCommit); @@ -162,7 +275,14 @@ void main() { test('throws when name already exists', () { expect( () => repo.renameBranch(oldName: 'feature', newName: 'master'), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to write reference 'refs/heads/master': " + "a reference with that name already exists.", + ), + ), ); }); @@ -172,7 +292,7 @@ void main() { newName: 'feature', force: true, ); - final branch = repo.lookupBranch('feature'); + final branch = repo.lookupBranch(name: 'feature'); expect(branch.target, lastCommit); @@ -182,13 +302,19 @@ void main() { test('throws when name is invalid', () { expect( () => repo.renameBranch(oldName: 'feature', newName: 'inv@{id'), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "the given reference name 'refs/heads/inv@{id' is not valid", + ), + ), ); }); }); test('returns string representation of Branch object', () { - final branch = repo.lookupBranch('master'); + final branch = repo.lookupBranch(name: 'master'); expect(branch.toString(), contains('Branch{')); branch.free(); }); diff --git a/test/checkout_test.dart b/test/checkout_test.dart index 730ce2b..9af8d96 100644 --- a/test/checkout_test.dart +++ b/test/checkout_test.dart @@ -23,21 +23,61 @@ void main() { File('${tmpDir.path}/feature_file').writeAsStringSync('edit'); expect(repo.status, contains('feature_file')); - repo.checkout(refName: 'HEAD', strategy: {GitCheckout.force}); + repo.checkout( + refName: 'HEAD', + strategy: {GitCheckout.force}, + paths: ['feature_file'], + ); expect(repo.status, isEmpty); }); + test( + 'throws when trying to checkout head with invalid alternative directory', + () { + expect( + () => repo.checkout( + refName: 'HEAD', + directory: 'not/there', + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to make directory 'not/there': No such file or directory", + ), + ), + ); + }); + test('successfully checkouts index', () { File('${repo.workdir}feature_file').writeAsStringSync('edit'); expect(repo.status, contains('feature_file')); - repo.checkout(strategy: { - GitCheckout.force, - GitCheckout.conflictStyleMerge, - }); + repo.checkout( + strategy: { + GitCheckout.force, + GitCheckout.conflictStyleMerge, + }, + paths: ['feature_file'], + ); expect(repo.status, isEmpty); }); + test( + 'throws when trying to checkout index with invalid alternative directory', + () { + expect( + () => repo.checkout(directory: 'not/there'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to make directory 'not/there': No such file or directory", + ), + ), + ); + }); + test('successfully checkouts tree', () { final masterHead = repo.lookupCommit( repo['821ed6e80627b8769d170a293862f9fc60825226'], @@ -68,6 +108,24 @@ void main() { masterHead.free(); }); + test( + 'throws when trying to checkout tree with invalid alternative directory', + () { + expect( + () => repo.checkout( + refName: 'refs/heads/feature', + directory: 'not/there', + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to make directory 'not/there': No such file or directory", + ), + ), + ); + }); + test('successfully checkouts with alrenative directory', () { final altDir = Directory('${Directory.systemTemp.path}/alt_dir'); // making sure there is no directory diff --git a/test/commit_test.dart b/test/commit_test.dart index 68cd1e2..b52891d 100644 --- a/test/commit_test.dart +++ b/test/commit_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -41,12 +42,44 @@ void main() { }); group('Commit', () { - test('successfully returns when 40 char sha hex is provided', () { + test('successfully lookups for provided oid', () { final commit = repo.lookupCommit(mergeCommit); expect(commit, isA()); commit.free(); }); + test('throws when trying to lookup with invalid oid', () { + expect( + () => repo.lookupCommit(repo['0' * 40]), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "odb: cannot read object: null OID cannot exist", + ), + ), + ); + }); + + test('successfully lookups annotated commit for provided oid', () { + final annotated = AnnotatedCommit.lookup(repo: repo, oid: mergeCommit); + expect(annotated, isA()); + annotated.free(); + }); + + test('throws when trying to lookup annotated commit with invalid oid', () { + expect( + () => AnnotatedCommit.lookup(repo: repo, oid: repo['0' * 40]), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "odb: cannot read object: null OID cannot exist", + ), + ), + ); + }); + test('successfully reverts commit', () { final to = repo.lookupCommit( repo['78b8bf123e3952c970ae5c1ce0a3ea1d1336f6e8'], @@ -66,6 +99,23 @@ void main() { from.free(); }); + test('throws when trying to revert commit and error occurs', () { + final nullCommit = Commit(nullptr); + expect( + () => repo.revertCommit( + revertCommit: nullCommit, + ourCommit: nullCommit, + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'revert_commit'", + ), + ), + ); + }); + test('successfully creates commit', () { final parent = repo.lookupCommit(mergeCommit); final oid = repo.createCommit( @@ -148,6 +198,30 @@ void main() { commit.free(); }); + test('throws when trying to create commit and error occurs', () { + final parent = repo.lookupCommit(mergeCommit); + final nullRepo = Repository(nullptr); + + expect( + () => nullRepo.createCommit( + message: message, + author: author, + commiter: commiter, + tree: tree, + parents: [parent], + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'git_tree_owner(tree) == repo'", + ), + ), + ); + + parent.free(); + }); + test('returns string representation of Commit object', () { final commit = repo.lookupCommit(mergeCommit); expect(commit.toString(), contains('Commit{')); diff --git a/test/credentials_test.dart b/test/credentials_test.dart index 3bbf41f..4161fb9 100644 --- a/test/credentials_test.dart +++ b/test/credentials_test.dart @@ -82,6 +82,20 @@ void main() { expect(credentials.toString(), contains('KeypairFromAgent{')); }); + test('sucessfully clones repository with provided username', () { + final callbacks = const Callbacks(credentials: Username('git')); + + final repo = Repository.clone( + url: 'https://git@github.com/libgit2/TestGitRepository', + localPath: cloneDir.path, + callbacks: callbacks, + ); + + expect(repo.isEmpty, false); + + repo.free(); + }); + test('sucessfully clones repository with provided keypair', () { final keypair = const Keypair( username: 'git', diff --git a/test/describe_test.dart b/test/describe_test.dart index 1a70d6a..12ab8f4 100644 --- a/test/describe_test.dart +++ b/test/describe_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -22,6 +23,20 @@ void main() { expect(repo.describe(), 'v0.2'); }); + test('throws when trying to describe and error occurs', () { + final nullRepo = Repository(nullptr); + expect( + () => nullRepo.describe(), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('successfully describes commit', () { repo.deleteTag('v0.2'); @@ -33,7 +48,16 @@ void main() { test('throws when trying to describe and no reference found', () { final commit = repo.lookupCommit(repo['f17d0d48']); - expect(() => repo.describe(commit: commit), throwsA(isA())); + expect( + () => repo.describe(commit: commit), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "cannot describe - no tags can describe 'f17d0d48eae3aa08cecf29128a35e310c97b3521'.", + ), + ), + ); commit.free(); }); diff --git a/test/diff_test.dart b/test/diff_test.dart index 0a4bac9..559e90a 100644 --- a/test/diff_test.dart +++ b/test/diff_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -141,6 +142,22 @@ index e69de29..c217c63 100644 diff.free(); }); + test('throws when trying to diff between tree and workdir and error occurs', + () { + final nullRepo = Repository(nullptr); + final nullTree = Tree(nullptr); + expect( + () => nullRepo.diff(a: nullTree), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('successfully returns diff between tree and index', () { final index = repo.index; final head = repo.head; @@ -179,9 +196,34 @@ index e69de29..c217c63 100644 diff.free(); }); + test('throws when trying to diff between tree and tree and error occurs', + () { + final nullRepo = Repository(nullptr); + final nullTree = Tree(nullptr); + expect( + () => nullRepo.diff(a: nullTree, b: nullTree), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('throws when trying to diff between null and tree', () { final tree = repo.lookupTree(repo['b85d53c']); - expect(() => repo.diff(a: null, b: tree), throwsA(isA())); + expect( + () => repo.diff(a: null, b: tree), + throwsA( + isA().having( + (e) => e.message, + 'error', + "Must not be null", + ), + ), + ); tree.free(); }); @@ -216,31 +258,51 @@ index e69de29..c217c63 100644 expect(diff.length, 1); expect(stats.filesChanged, 1); expect(stats.insertions, 1); - expect(diff.patchId.sha, '699556913185bc38632ae20a49d5c18b9233335e'); + expect(diff.patchOid.sha, '699556913185bc38632ae20a49d5c18b9233335e'); stats.free(); diff.free(); }); - test( - 'checks if diff can be applied to repository and successfully applies it', - () { + test('checks if diff can be applied to repository', () { + final diff1 = repo.diff(); + expect(repo.applies(diff: diff1, location: GitApplyLocation.both), false); + + final diff2 = Diff.parse(patchText); + repo.checkout(refName: 'HEAD', strategy: {GitCheckout.force}); + expect(repo.applies(diff: diff2, location: GitApplyLocation.both), true); + + diff1.free(); + diff2.free(); + }); + + test('successfully applies diff to repository', () { final diff = Diff.parse(patchText); final file = File('${tmpDir.path}/subdir/modified_file'); - repo.reset( - oid: repo['a763aa560953e7cfb87ccbc2f536d665aa4dff22'], - resetType: GitReset.hard, - ); + repo.checkout(refName: 'HEAD', strategy: {GitCheckout.force}); expect(file.readAsStringSync(), ''); - expect(repo.applies(diff), true); - repo.apply(diff); + repo.apply(diff: diff); expect(file.readAsStringSync(), 'Modified content\n'); diff.free(); }); + test('throws when trying to apply diff and error occurs', () { + final nullDiff = Diff(nullptr); + expect( + () => repo.apply(diff: nullDiff), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'diff'", + ), + ), + ); + }); + test('successfully creates patch from entry index in diff', () { final diff = Diff.parse(patchText); final patch = Patch.fromDiff(diff: diff, index: 0); @@ -279,6 +341,34 @@ index e69de29..c217c63 100644 newTree.free(); }); + test('throws when trying to find similar entries and error occurs', () { + final nullDiff = Diff(nullptr); + expect( + () => nullDiff.findSimilar(), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'diff'", + ), + ), + ); + }); + + test('throws when trying to get patch Oid and error occurs', () { + final nullDiff = Diff(nullptr); + expect( + () => nullDiff.patchOid, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'diff'", + ), + ), + ); + }); + test('returns deltas', () { final index = repo.index; final diff = index.diffToWorkdir(); @@ -294,10 +384,7 @@ index e69de29..c217c63 100644 diff.deltas[0].oldFile.oid.sha, 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391', ); - expect( - diff.deltas[0].newFile.oid.sha, - '0000000000000000000000000000000000000000', - ); + expect(diff.deltas[0].newFile.oid.sha, '0' * 40); expect(diff.deltas[2].oldFile.size, 17); @@ -316,7 +403,26 @@ index e69de29..c217c63 100644 index.free(); }); - test('returns deltas', () { + test('throws when trying to get delta with invalid index', () { + final index = repo.index; + final diff = index.diffToWorkdir(); + + expect( + () => diff.deltas[-1], + throwsA( + isA().having( + (e) => e.message, + 'error', + "Invalid value", + ), + ), + ); + + diff.free(); + index.free(); + }); + + test('returns patches', () { final index = repo.index; final diff = index.diffToWorkdir(); final patches = diff.patches; @@ -346,6 +452,34 @@ index e69de29..c217c63 100644 index.free(); }); + test('throws when trying to get stats and error occurs', () { + final nullDiff = Diff(nullptr); + expect( + () => nullDiff.stats, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'diff'", + ), + ), + ); + }); + + test('throws when trying to print stats and error occurs', () { + final nullStats = DiffStats(nullptr); + expect( + () => nullStats.print(format: {GitDiffStats.full}, width: 80), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'stats'", + ), + ), + ); + }); + test('returns patch diff string', () { final diff = Diff.parse(patchText); diff --git a/test/index_test.dart b/test/index_test.dart index 6dc85b3..207e109 100644 --- a/test/index_test.dart +++ b/test/index_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -74,6 +75,19 @@ void main() { expect(index.length, 0); }); + test('throws when trying to clear the contents and error occurs', () { + expect( + () => Index(nullptr).clear(), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'index'", + ), + ), + ); + }); + group('add()', () { test('successfully adds with provided IndexEntry', () { final entry = index['file']; @@ -90,14 +104,45 @@ void main() { }); test('throws if file not found at provided path', () { - expect(() => index.add('not_there'), throwsA(isA())); + expect( + () => index.add('not_there'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "could not find '${repo.workdir}not_there' to stat: No such file or directory", + ), + ), + ); + }); + + test('throws if provided IndexEntry is invalid', () { + expect( + () => index.add(IndexEntry(nullptr)), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'source_entry && source_entry->path'", + ), + ), + ); }); test('throws if index of bare repository', () { final bare = Repository.open('test/assets/empty_bare.git'); final bareIndex = bare.index; - expect(() => bareIndex.add('config'), throwsA(isA())); + expect( + () => bareIndex.add('config'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "cannot create blob from file. This operation is not allowed against bare repositories.", + ), + ), + ); bareIndex.free(); bare.free(); @@ -126,6 +171,25 @@ void main() { expect(index.length, 1); expect(index['feature_file'].sha, featureFileSha); }); + + test('throws when trying to addAll in bare repository', () { + final bare = Repository.open('test/assets/empty_bare.git'); + final bareIndex = bare.index; + + expect( + () => bareIndex.addAll([]), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "cannot index add all. This operation is not allowed against bare repositories.", + ), + ), + ); + + bareIndex.free(); + bare.free(); + }); }); test('writes to disk', () { @@ -148,6 +212,19 @@ void main() { expect(index.find('feature_file'), false); }); + test('throws when trying to remove entry with invalid path', () { + expect( + () => index.remove('invalid'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "index does not contain invalid at stage 0", + ), + ), + ); + }); + test('removes all entries with matching pathspec', () { expect(index.find('file'), true); expect(index.find('feature_file'), true); @@ -177,11 +254,51 @@ void main() { expect(oid.sha, 'a8ae3dd59e6e1802c6f78e05e301bfd57c9f334f'); }); + test('throws when trying to write tree to invalid repository', () { + expect( + () => index.writeTree(Repository(nullptr)), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + + test('throws when trying to write tree while index have conflicts', () { + final tmpDir = setupRepo(Directory('test/assets/mergerepo/')); + final repo = Repository.open(tmpDir.path); + + final conflictBranch = repo.lookupBranch(name: 'conflict-branch'); + final index = repo.index; + repo.merge(conflictBranch.target); + + expect( + () => index.writeTree(), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "cannot create a tree from a not fully merged index.", + ), + ), + ); + + conflictBranch.free(); + index.free(); + repo.free(); + tmpDir.deleteSync(recursive: true); + }); + test('returns conflicts with ancestor, our and their present', () { final repoDir = setupRepo(Directory('test/assets/mergerepo/')); final conflictRepo = Repository.open(repoDir.path); - final conflictBranch = conflictRepo.lookupBranch('ancestor-conflict'); + final conflictBranch = conflictRepo.lookupBranch( + name: 'ancestor-conflict', + ); conflictRepo.checkout(refName: 'refs/heads/feature'); @@ -204,7 +321,7 @@ void main() { final repoDir = setupRepo(Directory('test/assets/mergerepo/')); final conflictRepo = Repository.open(repoDir.path); - final conflictBranch = conflictRepo.lookupBranch('conflict-branch'); + final conflictBranch = conflictRepo.lookupBranch(name: 'conflict-branch'); conflictRepo.merge(conflictBranch.target); @@ -225,7 +342,9 @@ void main() { final repoDir = setupRepo(Directory('test/assets/mergerepo/')); final conflictRepo = Repository.open(repoDir.path); - final conflictBranch = conflictRepo.lookupBranch('ancestor-conflict'); + final conflictBranch = conflictRepo.lookupBranch( + name: 'ancestor-conflict', + ); conflictRepo.checkout(refName: 'refs/heads/our-conflict'); @@ -248,7 +367,7 @@ void main() { final repoDir = setupRepo(Directory('test/assets/mergerepo/')); final conflictRepo = Repository.open(repoDir.path); - final conflictBranch = conflictRepo.lookupBranch('their-conflict'); + final conflictBranch = conflictRepo.lookupBranch(name: 'their-conflict'); conflictRepo.checkout(refName: 'refs/heads/feature'); @@ -267,6 +386,43 @@ void main() { repoDir.deleteSync(recursive: true); }); + test('successfully removes conflicts', () { + final repoDir = setupRepo(Directory('test/assets/mergerepo/')); + final conflictRepo = Repository.open(repoDir.path); + + final conflictBranch = conflictRepo.lookupBranch(name: 'conflict-branch'); + final index = conflictRepo.index; + + conflictRepo.merge(conflictBranch.target); + expect(index.hasConflicts, true); + expect(index.conflicts.length, 1); + + final conflictedFile = index.conflicts['conflict_file']!; + conflictedFile.remove(); + expect(index.hasConflicts, false); + expect(index.conflicts, isEmpty); + expect(index.conflicts['conflict_file'], null); + + index.free(); + conflictBranch.free(); + conflictRepo.free(); + repoDir.deleteSync(recursive: true); + }); + + test('throws when trying to remove conflict and error occurs', () { + expect( + () => ConflictEntry(index.pointer, 'invalid.path', null, null, null) + .remove(), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "index does not contain invalid.path", + ), + ), + ); + }); + test('returns string representation of Index and IndexEntry objects', () { final index = repo.index; diff --git a/test/mailmap_test.dart b/test/mailmap_test.dart index 5f08366..0d21594 100644 --- a/test/mailmap_test.dart +++ b/test/mailmap_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -172,6 +173,19 @@ Santa Claus mailmap.free(); }); + test('throws when initializing from repository and error occurs', () { + expect( + () => Mailmap.fromRepository(Repository(nullptr)), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('successfully resolves names and emails when mailmap is empty', () { final mailmap = Mailmap.empty(); @@ -207,6 +221,25 @@ Santa Claus mailmap.free(); }); + test('throws when trying to add entry with empty replace email', () { + final mailmap = Mailmap.empty(); + + expect( + () => mailmap.addEntry( + replaceEmail: ' ', + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + 'Invalid argument: "replaceEmail can\'t be empty"', + ), + ), + ); + + mailmap.free(); + }); + test('successfully resolves signature', () { final signature = Signature.create( name: 'nick1', @@ -225,6 +258,8 @@ Santa Claus ); expect(mailmap.resolveSignature(signature), realSignature); + + mailmap.free(); }); }); } diff --git a/test/merge_test.dart b/test/merge_test.dart index 45af540..1d0da86 100644 --- a/test/merge_test.dart +++ b/test/merge_test.dart @@ -1,5 +1,6 @@ // ignore_for_file: unnecessary_string_escapes +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -22,49 +23,32 @@ void main() { group('Merge', () { group('analysis', () { test('is up to date when no reference is provided', () { - final commit = repo.lookupCommit( - repo['c68ff54aabf660fcdd9a2838d401583fe31249e3'], - ); - - final result = repo.mergeAnalysis(theirHead: commit.oid); + final result = repo.mergeAnalysis(theirHead: repo['c68ff54']); expect(result, [ {GitMergeAnalysis.upToDate}, GitMergePreference.none, ]); expect(repo.status, isEmpty); - - commit.free(); }); test('is up to date for provided ref', () { - final commit = repo.lookupCommit( - repo['c68ff54aabf660fcdd9a2838d401583fe31249e3'], - ); - final result = repo.mergeAnalysis( - theirHead: commit.oid, + theirHead: repo['c68ff54'], ourRef: 'refs/tags/v0.1', ); expect(result[0], {GitMergeAnalysis.upToDate}); expect(repo.status, isEmpty); - - commit.free(); }); test('is fast forward', () { - final theirHead = repo.lookupCommit( - repo['6cbc22e509d72758ab4c8d9f287ea846b90c448b'], - ); - final ffCommit = repo.lookupCommit( - repo['f17d0d48eae3aa08cecf29128a35e310c97b3521'], - ); + final ffCommit = repo.lookupCommit(repo['f17d0d4']); final ffBranch = repo.createBranch( name: 'ff-branch', target: ffCommit, ); final result = repo.mergeAnalysis( - theirHead: theirHead.oid, + theirHead: repo['6cbc22e'], ourRef: 'refs/heads/${ffBranch.name}', ); expect( @@ -75,24 +59,17 @@ void main() { ffBranch.free(); ffCommit.free(); - theirHead.free(); }); test('is not fast forward and there is no conflicts', () { - final commit = repo.lookupCommit( - repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'], - ); - - final result = repo.mergeAnalysis(theirHead: commit.oid); + final result = repo.mergeAnalysis(theirHead: repo['5aecfa0']); expect(result[0], {GitMergeAnalysis.normal}); expect(repo.status, isEmpty); - - commit.free(); }); }); test('writes conflicts to index', () { - final conflictBranch = repo.lookupBranch('conflict-branch'); + final conflictBranch = repo.lookupBranch(name: 'conflict-branch'); final index = repo.index; final result = repo.mergeAnalysis(theirHead: conflictBranch.target); @@ -129,24 +106,6 @@ void main() { conflictBranch.free(); }); - test('successfully removes conflicts', () { - final conflictBranch = repo.lookupBranch('conflict-branch'); - final index = repo.index; - - repo.merge(conflictBranch.target); - expect(index.hasConflicts, true); - expect(index.conflicts.length, 1); - - final conflictedFile = index.conflicts['conflict_file']!; - conflictedFile.remove(); - expect(index.hasConflicts, false); - expect(index.conflicts, isEmpty); - expect(index.conflicts['conflict_file'], null); - - index.free(); - conflictBranch.free(); - }); - group('merge file from index', () { test('successfully merges without ancestor', () { const diffExpected = """ @@ -156,7 +115,7 @@ master conflict edit conflict branch edit >>>>>>> conflict_file """; - final conflictBranch = repo.lookupBranch('conflict-branch'); + final conflictBranch = repo.lookupBranch(name: 'conflict-branch'); final index = repo.index; repo.merge(conflictBranch.target); @@ -184,7 +143,7 @@ Feature edit on feature branch Another feature edit >>>>>>> feature_file """; - final conflictBranch = repo.lookupBranch('ancestor-conflict'); + final conflictBranch = repo.lookupBranch(name: 'ancestor-conflict'); repo.checkout(refName: 'refs/heads/feature'); final index = repo.index; repo.merge(conflictBranch.target); @@ -204,16 +163,29 @@ Another feature edit index.free(); conflictBranch.free(); }); + + test('throws when error occurs', () { + expect( + () => repo.mergeFileFromIndex( + ancestor: null, + ours: IndexEntry(nullptr), + theirs: IndexEntry(nullptr), + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'ours'", + ), + ), + ); + }); }); group('merge commits', () { test('successfully merges with default values', () { - final theirCommit = repo.lookupCommit( - repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'], - ); - final ourCommit = repo.lookupCommit( - repo['14905459d775f3f56a39ebc2ff081163f7da3529'], - ); + final theirCommit = repo.lookupCommit(repo['5aecfa0']); + final ourCommit = repo.lookupCommit(repo['1490545']); final mergeIndex = repo.mergeCommits( ourCommit: ourCommit, @@ -236,12 +208,8 @@ Another feature edit }); test('successfully merges with provided favor', () { - final theirCommit = repo.lookupCommit( - repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'], - ); - final ourCommit = repo.lookupCommit( - repo['14905459d775f3f56a39ebc2ff081163f7da3529'], - ); + final theirCommit = repo.lookupCommit(repo['5aecfa0']); + final ourCommit = repo.lookupCommit(repo['1490545']); final mergeIndex = repo.mergeCommits( ourCommit: ourCommit, @@ -256,12 +224,8 @@ Another feature edit }); test('successfully merges with provided merge and file flags', () { - final theirCommit = repo.lookupCommit( - repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'], - ); - final ourCommit = repo.lookupCommit( - repo['14905459d775f3f56a39ebc2ff081163f7da3529'], - ); + final theirCommit = repo.lookupCommit(repo['5aecfa0']); + final ourCommit = repo.lookupCommit(repo['1490545']); final mergeIndex = repo.mergeCommits( ourCommit: ourCommit, @@ -282,18 +246,54 @@ Another feature edit ourCommit.free(); theirCommit.free(); }); + + test('throws when error occurs', () { + expect( + () => repo.mergeCommits( + ourCommit: Commit(nullptr), + theirCommit: Commit(nullptr), + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'commit'", + ), + ), + ); + }); + }); + + test('successfully finds merge base', () { + var base = repo.mergeBase(a: repo['1490545'], b: repo['5aecfa0']); + expect(base.sha, 'fc38877b2552ab554752d9a77e1f48f738cca79b'); + + base = repo.mergeBase(a: repo['f17d0d4'], b: repo['5aecfa0']); + expect(base.sha, 'f17d0d48eae3aa08cecf29128a35e310c97b3521'); + }); + + test('throws when trying to find merge base for invalid oid', () { + expect( + () => repo.mergeBase(a: repo['0' * 40], b: repo['5aecfa0']), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "odb: cannot read object: null OID cannot exist", + ), + ), + ); }); group('merge trees', () { test('successfully merges with default values', () { - final theirCommit = repo.lookupCommit( - repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'], - ); - final ourCommit = repo.lookupCommit( - repo['14905459d775f3f56a39ebc2ff081163f7da3529'], - ); + final theirCommit = repo.lookupCommit(repo['5aecfa0']); + final ourCommit = repo.lookupCommit(repo['1490545']); final baseCommit = repo.lookupCommit( - repo.mergeBase(a: ourCommit.oid, b: theirCommit.oid), + repo.mergeBase( + a: ourCommit.oid, + b: theirCommit.oid, + ), ); final theirTree = theirCommit.tree; final ourTree = ourCommit.tree; @@ -326,14 +326,13 @@ Another feature edit }); test('successfully merges with provided favor', () { - final theirCommit = repo.lookupCommit( - repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'], - ); - final ourCommit = repo.lookupCommit( - repo['14905459d775f3f56a39ebc2ff081163f7da3529'], - ); + final theirCommit = repo.lookupCommit(repo['5aecfa0']); + final ourCommit = repo.lookupCommit(repo['1490545']); final baseCommit = repo.lookupCommit( - repo.mergeBase(a: ourCommit.oid, b: theirCommit.oid), + repo.mergeBase( + a: ourCommit.oid, + b: theirCommit.oid, + ), ); final theirTree = theirCommit.tree; final ourTree = ourCommit.tree; @@ -355,22 +354,60 @@ Another feature edit ourCommit.free(); theirCommit.free(); }); + + test('throws when error occurs', () { + expect( + () => Repository(nullptr).mergeTrees( + ancestorTree: Tree(nullptr), + ourTree: Tree(nullptr), + theirTree: Tree(nullptr), + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); }); test('successfully cherry-picks commit', () { - final cherry = repo.lookupCommit( - repo['5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'], - ); + final cherry = repo.lookupCommit(repo['5aecfa0']); repo.cherryPick(cherry); expect(repo.state, GitRepositoryState.cherrypick); expect(repo.message, 'add another feature file\n'); final index = repo.index; expect(index.conflicts, isEmpty); + // pretend we've done commit repo.removeMessage(); - expect(() => repo.message, throwsA(isA())); + expect( + () => repo.message, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "could not access message file: No such file or directory", + ), + ), + ); index.free(); }); + + test('throws when error occurs', () { + expect( + () => repo.cherryPick(Commit(nullptr)), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'commit'", + ), + ), + ); + }); }); } diff --git a/test/note_test.dart b/test/note_test.dart index dad9d87..990c2d9 100644 --- a/test/note_test.dart +++ b/test/note_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -43,6 +44,20 @@ void main() { } }); + test('throws when trying to get list of notes and error occurs', () { + Directory('${repo.workdir}.git/refs/notes').deleteSync(recursive: true); + expect( + () => repo.notes, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "reference 'refs/notes/commits' not found", + ), + ), + ); + }); + test('successfully lookups note', () { final head = repo.head; final note = repo.lookupNote(annotatedOid: head.target); @@ -75,7 +90,25 @@ void main() { signature.free(); }); - test('successfully removes note', () { + test('throws when trying to create note and error occurs', () { + expect( + () => Repository(nullptr).createNote( + author: Signature(nullptr), + committer: Signature(nullptr), + annotatedOid: repo['0' * 40], + note: '', + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + + test('successfully deletes note', () { final signature = repo.defaultSignature; final head = repo.head; @@ -87,13 +120,36 @@ void main() { expect( () => repo.lookupNote(annotatedOid: head.target), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "note could not be found", + ), + ), ); head.free(); signature.free(); }); + test('throws when trying to delete note and error occurs', () { + expect( + () => Repository(nullptr).deleteNote( + author: Signature(nullptr), + committer: Signature(nullptr), + annotatedOid: repo['0' * 40], + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('returns string representation of Note object', () { final note = repo.lookupNote(annotatedOid: repo['821ed6e']); expect(note.toString(), contains('Note{')); diff --git a/test/odb_test.dart b/test/odb_test.dart index 6fed9fb..d5ec1f0 100644 --- a/test/odb_test.dart +++ b/test/odb_test.dart @@ -27,6 +27,20 @@ void main() { odb.free(); }); + test('throws when trying to get odb and error occurs', () { + Directory('${repo.workdir}.git/objects/').deleteSync(recursive: true); + expect( + () => repo.odb, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + contains("failed to load object database"), + ), + ), + ); + }); + test('successfully creates new odb with no backends', () { final odb = Odb.create(); expect(odb, isA()); @@ -56,6 +70,23 @@ void main() { odb.free(); }); + test('throws when trying to read object and error occurs', () { + final odb = repo.odb; + + expect( + () => odb.read(repo['0' * 40]), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "odb: cannot read object: null OID cannot exist", + ), + ), + ); + + odb.free(); + }); + test('returns list of all objects oid\'s in database', () { final odb = repo.odb; @@ -65,6 +96,24 @@ void main() { odb.free(); }); + test('throws when trying to get list of all objects and error occurs', () { + final odb = repo.odb; + Directory('${repo.workdir}.git/objects').deleteSync(recursive: true); + + expect( + () => odb.objects, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "object not found - failed to refresh packfiles", + ), + ), + ); + + odb.free(); + }); + test('successfully writes data', () { final odb = repo.odb; final oid = odb.write(type: GitObject.blob, data: 'testing'); @@ -81,7 +130,31 @@ void main() { final odb = repo.odb; expect( () => odb.write(type: GitObject.any, data: 'testing'), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + 'Invalid argument: "GitObject.any is invalid type"', + ), + ), + ); + + odb.free(); + }); + + test('throws when trying to write to disk alternate odb', () { + final odb = Odb.create(); + odb.addDiskAlternate('${repo.workdir}.git/objects/'); + + expect( + () => odb.write(type: GitObject.blob, data: ''), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "cannot write object - unsupported in the loaded odb backends", + ), + ), ); odb.free(); diff --git a/test/oid_test.dart b/test/oid_test.dart index 0167fdd..356413f 100644 --- a/test/oid_test.dart +++ b/test/oid_test.dart @@ -23,13 +23,13 @@ void main() { group('Oid', () { group('fromSHA()', () { - test('initializes successfully', () { + test('successfully initializes', () { final oid = Oid.fromSHA(repo: repo, sha: sha); expect(oid, isA()); expect(oid.sha, sha); }); - test('initializes successfully from short hex string', () { + test('successfully initializes from short hex string', () { final oid = Oid.fromSHA(repo: repo, sha: sha.substring(0, 5)); expect(oid, isA()); @@ -41,9 +41,22 @@ void main() { () => Oid.fromSHA(repo: repo, sha: 'sha'), throwsA( isA().having( - (e) => e.invalidValue, + (e) => e.toString(), 'value', - 'sha is not a valid sha hex string', + 'Invalid argument: "sha is not a valid sha hex string"', + ), + ), + ); + }); + + test('throws when sha hex string is invalid', () { + expect( + () => Oid.fromSHA(repo: repo, sha: '0000000'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "object not found - no match for id prefix (0000000)", ), ), ); diff --git a/test/packbuilder_test.dart b/test/packbuilder_test.dart index 3db6669..fa7c470 100644 --- a/test/packbuilder_test.dart +++ b/test/packbuilder_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -27,6 +28,19 @@ void main() { packbuilder.free(); }); + test('throws when trying to initialize and error occurs', () { + expect( + () => PackBuilder(Repository(nullptr)), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('successfully adds objects', () { final packbuilder = PackBuilder(repo); final odb = repo.odb; @@ -41,7 +55,24 @@ void main() { packbuilder.free(); }); - test('successfully adds objects recursively', () { + test('throws when trying to add object and error occurs', () { + final packbuilder = PackBuilder(repo); + + expect( + () => packbuilder.add(Oid(nullptr)), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'oid'", + ), + ), + ); + + packbuilder.free(); + }); + + test('successfully adds object recursively', () { final packbuilder = PackBuilder(repo); final oid = Oid.fromSHA(repo: repo, sha: 'f17d0d48'); @@ -51,6 +82,23 @@ void main() { packbuilder.free(); }); + test('throws when trying to add object recursively and error occurs', () { + final packbuilder = PackBuilder(repo); + + expect( + () => packbuilder.addRecursively(Oid(nullptr)), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'id'", + ), + ), + ); + + packbuilder.free(); + }); + test('successfully sets number of threads', () { final packbuilder = PackBuilder(repo); @@ -105,6 +153,23 @@ void main() { expect(writtenCount, 18); }); + test('throws when trying to write pack into invalid path', () { + final packbuilder = PackBuilder(repo); + + expect( + () => packbuilder.write('invalid/path'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + contains('failed to create temporary file'), + ), + ), + ); + + packbuilder.free(); + }); + test('returns string representation of PackBuilder object', () { final packbuilder = PackBuilder(repo); expect(packbuilder.toString(), contains('PackBuilder{')); diff --git a/test/patch_test.dart b/test/patch_test.dart index e5e1fed..0fa5e6a 100644 --- a/test/patch_test.dart +++ b/test/patch_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -8,8 +9,8 @@ void main() { late Directory tmpDir; const oldBlob = ''; const newBlob = 'Feature edit\n'; - late Oid oldBlobID; - late Oid newBlobID; + late Oid oldBlobOid; + late Oid newBlobOid; const path = 'feature_file'; const blobPatch = """ diff --git a/feature_file b/feature_file @@ -41,8 +42,8 @@ index e69de29..0000000 setUp(() { tmpDir = setupRepo(Directory('test/assets/testrepo/')); repo = Repository.open(tmpDir.path); - oldBlobID = repo['e69de29bb2d1d6434b8b29ae775ad8c2e48c5391']; - newBlobID = repo['9c78c21d6680a7ffebc76f7ac68cacc11d8f48bc']; + oldBlobOid = repo['e69de29bb2d1d6434b8b29ae775ad8c2e48c5391']; + newBlobOid = repo['9c78c21d6680a7ffebc76f7ac68cacc11d8f48bc']; }); tearDown(() { @@ -92,8 +93,8 @@ index e69de29..0000000 }); test('successfully creates from blobs', () { - final a = repo.lookupBlob(oldBlobID); - final b = repo.lookupBlob(newBlobID); + final a = repo.lookupBlob(oldBlobOid); + final b = repo.lookupBlob(newBlobOid); final patch = Patch.create( a: a, b: b, @@ -107,7 +108,7 @@ index e69de29..0000000 }); test('successfully creates from one blob (add)', () { - final b = repo.lookupBlob(newBlobID); + final b = repo.lookupBlob(newBlobOid); final patch = Patch.create( a: null, b: b, @@ -121,7 +122,7 @@ index e69de29..0000000 }); test('successfully creates from one blob (delete)', () { - final a = repo.lookupBlob(oldBlobID); + final a = repo.lookupBlob(oldBlobOid); final patch = Patch.create( a: a, b: null, @@ -135,7 +136,7 @@ index e69de29..0000000 }); test('successfully creates from blob and buffer', () { - final a = repo.lookupBlob(oldBlobID); + final a = repo.lookupBlob(oldBlobOid); final patch = Patch.create( a: a, b: newBlob, @@ -159,7 +160,13 @@ index e69de29..0000000 aPath: 'file', bPath: 'file', ), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "Invalid argument(s): Provided argument(s) is not Blob or String", + ), + ), ); expect( @@ -169,12 +176,44 @@ index e69de29..0000000 aPath: 'file', bPath: 'file', ), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "Invalid argument(s): Provided argument(s) is not Blob or String", + ), + ), ); commit.free(); }); + test('throws when trying to create from diff and error occurs', () { + expect( + () => Patch.fromDiff(diff: Diff(nullptr), index: 0), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'diff'", + ), + ), + ); + }); + + test('throws when trying to text of patch and error occurs', () { + expect( + () => Patch(nullptr, nullptr, nullptr).text, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'patch'", + ), + ), + ); + }); + test('returns string representation of Patch object', () { final patch = Patch.create( a: oldBlob, diff --git a/test/rebase_test.dart b/test/rebase_test.dart index f62fccc..da3377d 100644 --- a/test/rebase_test.dart +++ b/test/rebase_test.dart @@ -29,7 +29,16 @@ void main() { final feature = repo.lookupReference('refs/heads/feature'); repo.checkout(refName: feature.name); - expect(() => repo.index['.gitignore'], throwsA(isA())); + expect( + () => repo.index['.gitignore'], + throwsA( + isA().having( + (e) => e.toString(), + 'error', + 'Invalid argument: ".gitignore was not found"', + ), + ), + ); final rebase = Rebase.init( repo: repo, @@ -62,6 +71,38 @@ void main() { signature.free(); }); + test('successfully performs rebase without branch provided', () { + final signature = repo.defaultSignature; + final feature = repo.lookupReference('refs/heads/feature'); + + final rebase = Rebase.init( + repo: repo, + onto: feature.target, + ); + + final operationsCount = rebase.operationsCount; + expect(operationsCount, 3); + + for (var i = 0; i < operationsCount; i++) { + final operation = rebase.next(); + expect(operation.type, GitRebaseOperation.pick); + expect(operation.oid.sha, shas[i]); + expect(operation.toString(), contains('RebaseOperation{')); + + rebase.commit( + committer: signature, + author: signature, + message: 'rebase message', + ); + } + + rebase.finish(); + + rebase.free(); + feature.free(); + signature.free(); + }); + test('successfully performs rebase with provided upstream', () { final signature = repo.defaultSignature; final master = repo.lookupReference('refs/heads/master'); @@ -71,13 +112,18 @@ void main() { repo.checkout(refName: feature.name); expect( () => repo.index['conflict_file'], - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + 'Invalid argument: "conflict_file was not found"', + ), + ), ); final rebase = Rebase.init( repo: repo, branch: master.target, - onto: feature.target, upstream: startCommit.oid, ); @@ -99,6 +145,21 @@ void main() { signature.free(); }); + test( + 'throws when trying to initialize rebase without upstream and onto provided', + () { + expect( + () => Rebase.init(repo: repo), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'upstream || onto'", + ), + ), + ); + }); + test('stops when there is conflicts', () { final signature = repo.defaultSignature; final master = repo.lookupReference('refs/heads/master'); @@ -118,7 +179,46 @@ void main() { expect(repo.state, GitRepositoryState.rebaseMerge); expect( () => rebase.commit(committer: signature), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + 'unstaged changes exist in workdir', + ), + ), + ); + + rebase.free(); + conflict.free(); + master.free(); + signature.free(); + }); + + test('throws when trying to perfrom next rebase operation and error occurs', + () { + final signature = repo.defaultSignature; + final master = repo.lookupReference('refs/heads/master'); + final conflict = repo.lookupReference('refs/heads/conflict-branch'); + + repo.checkout(refName: conflict.name); + + final rebase = Rebase.init( + repo: repo, + branch: master.target, + onto: conflict.target, + ); + expect(rebase.operationsCount, 1); + + rebase.next(); // repo now have conflicts + expect( + () => rebase.next(), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "object not found - failed to find pack entry (790b86f5fb50db485586370f27c5f90bada97d83)", + ), + ), ); rebase.free(); diff --git a/test/reference_test.dart b/test/reference_test.dart index 9116429..c76b12b 100644 --- a/test/reference_test.dart +++ b/test/reference_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -33,6 +34,19 @@ void main() { ); }); + test('throws when trying to get a list of references and error occurs', () { + expect( + () => Repository(nullptr).references, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('returns correct type of reference', () { final head = repo.head; expect(head.type, ReferenceType.direct); @@ -55,6 +69,19 @@ void main() { ref.free(); }); + test('throws when trying to resolve invalid reference', () { + expect( + () => Reference(nullptr).target, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid reference", + ), + ), + ); + }); + test('returns the full name', () { final head = repo.head; expect(head.name, 'refs/heads/master'); @@ -154,7 +181,13 @@ void main() { name: 'refs/tags/invalid', target: '78b', ), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "the given reference name '78b' is not valid", + ), + ), ); expect( @@ -162,7 +195,13 @@ void main() { name: 'refs/tags/invalid', target: 0, ), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + 'Invalid argument: "0 must be either Oid or String reference name"', + ), + ), ); }); @@ -172,7 +211,13 @@ void main() { name: 'refs/tags/invalid~', target: repo[lastCommit], ), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "the given reference name 'refs/tags/invalid~' is not valid", + ), + ), ); }); @@ -205,7 +250,14 @@ void main() { name: 'refs/tags/test', target: repo[lastCommit], ), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to write reference 'refs/tags/test': a reference with that " + "name already exists.", + ), + ), ); ref.free(); @@ -255,7 +307,14 @@ void main() { name: 'refs/tags/exists', target: 'refs/heads/master', ), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to write reference 'refs/tags/exists': a reference with that " + "name already exists.", + ), + ), ); ref.free(); @@ -267,7 +326,13 @@ void main() { name: 'refs/tags/invalid~', target: 'refs/heads/master', ), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "the given reference name 'refs/tags/invalid~' is not valid", + ), + ), ); }); @@ -293,15 +358,10 @@ void main() { }); test('successfully deletes reference', () { - final ref = repo.createReference( - name: 'refs/tags/test', - target: repo[lastCommit], - ); - expect(repo.references, contains('refs/tags/test')); + expect(repo.references, contains('refs/tags/v0.1')); - repo.deleteReference('refs/tags/test'); - expect(repo.references, isNot(contains('refs/tags/test'))); - ref.free(); + repo.deleteReference('refs/tags/v0.1'); + expect(repo.references, isNot(contains('refs/tags/v0.1'))); }); group('finds', () { @@ -314,7 +374,13 @@ void main() { test('throws when error occured', () { expect( () => repo.lookupReference('refs/heads/not/there'), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "reference 'refs/heads/not/there' not found", + ), + ), ); }); }); @@ -368,11 +434,35 @@ void main() { final ref = repo.lookupReference('HEAD'); expect( () => ref.setTarget(target: 'refs/heads/invalid~'), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "the given reference name 'refs/heads/invalid~' is not valid", + ), + ), ); + + expect( + () => ref.setTarget(target: Oid(nullptr)), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'id'", + ), + ), + ); + expect( () => ref.setTarget(target: 0), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + 'Invalid argument: "0 must be either Oid or String reference name"', + ), + ), ); ref.free(); @@ -497,6 +587,19 @@ void main() { ref.free(); }); + test('throws when trying to peel and error occurs', () { + expect( + () => Reference(nullptr).peel(), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'ref'", + ), + ), + ); + }); + test('successfully compresses references', () { final packedRefsFile = File('${tmpDir.path}/.git/packed-refs'); expect(packedRefsFile.existsSync(), false); @@ -509,6 +612,19 @@ void main() { expect(newRefs, oldRefs); }); + test('throws when trying to compress and error occurs', () { + expect( + () => Reference.compress(Repository(nullptr)), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('returns string representation of Reference object', () { final ref = repo.lookupReference('refs/heads/master'); expect(ref.toString(), contains('Reference{')); diff --git a/test/remote_prune_test.dart b/test/remote_prune_test.dart index f12fbf0..7cbfd7b 100644 --- a/test/remote_prune_test.dart +++ b/test/remote_prune_test.dart @@ -93,5 +93,18 @@ void main() { branch.free(); } }); + + test('throws when trying to prune remote refs and error occurs', () { + expect( + () => remote.prune(), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "this remote has never connected", + ), + ), + ); + }); }); } diff --git a/test/remote_test.dart b/test/remote_test.dart index 1d18750..c2d88a8 100644 --- a/test/remote_test.dart +++ b/test/remote_test.dart @@ -68,6 +68,24 @@ void main() { remote.free(); }); + test('throws when trying to create with fetchspec with invalid remote name', + () { + expect( + () => repo.createRemote( + name: '', + url: '', + fetch: '', + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "'' is not a valid remote name.", + ), + ), + ); + }); + test('successfully deletes', () { final remote = repo.createRemote(name: 'upstream', url: remoteUrl); expect(repo.remotes.length, 2); @@ -78,6 +96,19 @@ void main() { remote.free(); }); + test('throws when trying to delete non existing remote', () { + expect( + () => repo.deleteRemote('not/there'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "remote 'not/there' does not exist", + ), + ), + ); + }); + test('successfully renames', () { final remote = repo.lookupRemote(remoteName); @@ -92,10 +123,31 @@ void main() { remote.free(); }); + test('returns list of non-default refspecs that cannot be renamed', () { + final remote = repo.createRemote( + name: 'upstream', + url: remoteUrl, + fetch: '+refs/*:refs/*', + ); + + expect( + repo.renameRemote(oldName: remote.name, newName: 'renamed'), + ['+refs/*:refs/*'], + ); + + remote.free(); + }); + test('throws when renaming with invalid names', () { expect( () => repo.renameRemote(oldName: '', newName: ''), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "'' is not a valid remote name.", + ), + ), ); }); @@ -165,6 +217,36 @@ void main() { remote.free(); }); + test('throws when trying to transform refspec with invalid reference name', + () { + final remote = repo.lookupRemote('origin'); + final refspec = remote.getRefspec(0); + + expect( + () => refspec.transform('invalid/name'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "ref 'invalid/name' doesn't match the source", + ), + ), + ); + + expect( + () => refspec.rTransform('invalid/name'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "ref 'invalid/name' doesn't match the destination", + ), + ), + ); + + remote.free(); + }); + test('successfully adds fetch refspec', () { Remote.addFetch( repo: repo, @@ -184,6 +266,23 @@ void main() { remote.free(); }); + test('throws when trying to add fetch refspec for invalid remote name', () { + expect( + () => Remote.addFetch( + repo: repo, + remote: '', + refspec: '', + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "'' is not a valid remote name.", + ), + ), + ); + }); + test('successfully adds push refspec', () { Remote.addPush( repo: repo, @@ -197,6 +296,23 @@ void main() { remote.free(); }); + test('throws when trying to add push refspec for invalid remote name', () { + expect( + () => Remote.addPush( + repo: repo, + remote: '', + refspec: '', + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "'' is not a valid remote name.", + ), + ), + ); + }); + test('successfully returns remote repo\'s reference list', () { Remote.setUrl( repo: repo, @@ -218,15 +334,42 @@ void main() { remote.free(); }); + test( + 'throws when trying to get remote repo\'s reference list with invalid url', + () { + Remote.setUrl(repo: repo, remote: 'libgit2', url: 'invalid'); + final remote = repo.lookupRemote('libgit2'); + + expect( + () => remote.ls(), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "unsupported URL protocol", + ), + ), + ); + + remote.free(); + }); + test('successfully fetches data', () { Remote.setUrl( repo: repo, remote: 'libgit2', url: 'https://github.com/libgit2/TestGitRepository', ); + Remote.addFetch( + repo: repo, + remote: 'libgit2', + refspec: '+refs/heads/*:refs/remotes/origin/*', + ); final remote = repo.lookupRemote('libgit2'); - final stats = remote.fetch(); + final stats = remote.fetch( + refspecs: ['+refs/heads/*:refs/remotes/origin/*'], + ); expect(stats.totalObjects, 69); expect(stats.indexedObjects, 69); @@ -240,6 +383,84 @@ void main() { remote.free(); }); + test('successfully fetches data with proxy set to auto', () { + Remote.setUrl( + repo: repo, + remote: 'libgit2', + url: 'https://github.com/libgit2/TestGitRepository', + ); + Remote.addFetch( + repo: repo, + remote: 'libgit2', + refspec: '+refs/heads/*:refs/remotes/origin/*', + ); + final remote = repo.lookupRemote('libgit2'); + + final stats = remote.fetch( + refspecs: ['+refs/heads/*:refs/remotes/origin/*'], + proxy: 'auto', + ); + + expect(stats.totalObjects, 69); + expect(stats.indexedObjects, 69); + expect(stats.receivedObjects, 69); + expect(stats.localObjects, 0); + expect(stats.totalDeltas, 3); + expect(stats.indexedDeltas, 3); + expect(stats.receivedBytes, 0); + expect(stats.toString(), contains('TransferProgress{')); + + remote.free(); + }); + + test('uses specified proxy for fetch', () { + Remote.setUrl( + repo: repo, + remote: 'libgit2', + url: 'https://github.com/libgit2/TestGitRepository', + ); + Remote.addFetch( + repo: repo, + remote: 'libgit2', + refspec: '+refs/heads/*:refs/remotes/origin/*', + ); + final remote = repo.lookupRemote('libgit2'); + + expect( + () => remote.fetch( + refspecs: ['+refs/heads/*:refs/remotes/origin/*'], + proxy: 'https://1.1.1.1', + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "proxy returned unexpected status: 400", + ), + ), + ); + + remote.free(); + }); + + test('throws when trying to fetch data with invalid url', () { + Remote.setUrl(repo: repo, remote: 'libgit2', url: 'https://wrong.url'); + final remote = repo.lookupRemote('libgit2'); + + expect( + () => remote.fetch(), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to resolve address for wrong.url: Name or service not known", + ), + ), + ); + + remote.free(); + }); + test('successfully fetches data with provided transfer progress callback', () { Remote.setUrl( @@ -300,20 +521,20 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68 url: 'https://github.com/libgit2/TestGitRepository', ); final remote = repo.lookupRemote('libgit2'); - const tipsExpected = [ + final tipsExpected = [ { 'refname': 'refs/tags/annotated_tag', - 'oldSha': '0000000000000000000000000000000000000000', + 'oldSha': '0' * 40, 'newSha': 'd96c4e80345534eccee5ac7b07fc7603b56124cb', }, { 'refname': 'refs/tags/blob', - 'oldSha': '0000000000000000000000000000000000000000', + 'oldSha': '0' * 40, 'newSha': '55a1a760df4b86a02094a904dfa511deb5655905' }, { 'refname': 'refs/tags/commit_tree', - 'oldSha': '0000000000000000000000000000000000000000', + 'oldSha': '0' * 40, 'newSha': '8f50ba15d49353813cc6e20298002c0d17b0a9ee', }, ]; @@ -370,5 +591,23 @@ Total 69 (delta 0), reused 1 (delta 0), pack-reused 68 originRepo.free(); originDir.delete(recursive: true); }); + + test('throws when trying to push to invalid url', () { + Remote.setUrl(repo: repo, remote: 'libgit2', url: 'https://wrong.url'); + final remote = repo.lookupRemote('libgit2'); + + expect( + () => remote.push(refspecs: ['refs/heads/master']), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to resolve address for wrong.url: Name or service not known", + ), + ), + ); + + remote.free(); + }); }); } diff --git a/test/repository_empty_test.dart b/test/repository_empty_test.dart index 8aac9b6..3dfebfa 100644 --- a/test/repository_empty_test.dart +++ b/test/repository_empty_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -80,6 +81,19 @@ void main() { expect(repo.isEmpty, true); }); + test('throws when checking if it is empty and error occurs', () { + expect( + () => Repository(nullptr).isEmpty, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('checks if head is detached', () { expect(repo.isHeadDetached, false); }); diff --git a/test/repository_test.dart b/test/repository_test.dart index 06d8959..c6dac56 100644 --- a/test/repository_test.dart +++ b/test/repository_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -93,6 +94,58 @@ void main() { tmpWorkDir.deleteSync(); }); + test('throws when trying to set working directory to invalid', () { + expect( + () => repo.setWorkdir(path: 'invalid/path'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to resolve path 'invalid/path': No such file or directory", + ), + ), + ); + }); + + test('throws when trying to get head and error occurs', () { + File('${repo.workdir}.git/HEAD').deleteSync(); + expect( + () => repo.head, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "reference 'HEAD' not found", + ), + ), + ); + expect( + () => repo.isHeadDetached, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "reference 'HEAD' not found", + ), + ), + ); + }); + + test('throws when trying to check if branch is unborn and error occurs', + () { + File('${repo.workdir}.git/HEAD').deleteSync(); + expect( + () => repo.isBranchUnborn, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "reference 'HEAD' not found", + ), + ), + ); + }); + group('setHead', () { late Reference head; @@ -122,7 +175,39 @@ void main() { }); test('throws when target is invalid', () { - expect(() => repo.setHead(0), throwsA(isA())); + expect( + () => repo.setHead(0), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + 'Invalid argument: "0 must be either Oid or String reference name"', + ), + ), + ); + }); + + test('throws when error occurs', () { + expect( + () => Repository(nullptr).setHead('refs/heads/feature'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + expect( + () => Repository(nullptr).setHead(repo['0' * 40]), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); }); }); @@ -209,6 +294,23 @@ void main() { index.free(); }); + test('throws when trying to get status of bare repository', () { + final bare = Repository.open('test/assets/empty_bare.git'); + + expect( + () => bare.status, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "cannot status. This operation is not allowed against bare repositories.", + ), + ), + ); + + bare.free(); + }); + test('cleans up state', () { expect(repo.state, GitRepositoryState.none); final commit = repo.lookupCommit(repo['5aecfa0']); @@ -221,6 +323,19 @@ void main() { commit.free(); }); + test('throws when trying to clean up state and error occurs', () { + expect( + () => Repository(nullptr).stateCleanup(), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('returns status of a single file for provided path', () { final index = repo.index; index.remove('file'); @@ -236,7 +351,13 @@ void main() { test('throws when checking status of a single file for invalid path', () { expect( () => repo.statusFile('not-there'), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "attempt to get status of nonexistent file 'not-there'", + ), + ), ); }); @@ -269,24 +390,38 @@ void main() { }); test('checks if commit is a descendant of another commit', () { - final commit1 = repo.lookupCommit(repo['821ed6e8']); - final commit2 = repo.lookupCommit(repo['78b8bf12']); + final commit1 = repo['821ed6e8']; + final commit2 = repo['78b8bf12']; expect( - repo.descendantOf(commit: commit1.oid, ancestor: commit2.oid), + repo.descendantOf(commit: commit1, ancestor: commit2), true, ); expect( - repo.descendantOf(commit: commit1.oid, ancestor: commit1.oid), + repo.descendantOf(commit: commit1, ancestor: commit1), false, ); expect( - repo.descendantOf(commit: commit2.oid, ancestor: commit1.oid), + repo.descendantOf(commit: commit2, ancestor: commit1), false, ); + }); - commit1.free(); - commit2.free(); + test('throws when trying to check if commit is descendant and error occurs', + () { + final commit1 = repo['821ed6e8']; + final commit2 = repo['78b8bf12']; + final nullRepo = Repository(nullptr); + expect( + () => nullRepo.descendantOf(commit: commit1, ancestor: commit2), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); }); test('returns number of ahead behind commits', () { diff --git a/test/revparse_test.dart b/test/revparse_test.dart index 6d38ae1..7f73551 100644 --- a/test/revparse_test.dart +++ b/test/revparse_test.dart @@ -35,6 +35,29 @@ void main() { initCommit.free(); }); + test('.single() throws when spec string not found or invalid', () { + expect( + () => repo.revParseSingle('invalid'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "revspec 'invalid' not found", + ), + ), + ); + expect( + () => repo.revParseSingle(''), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to parse revision specifier - Invalid pattern ''", + ), + ), + ); + }); + test('.ext() returns commit and reference', () { final masterRef = repo.lookupReference('refs/heads/master'); var headParse = repo.revParseExt('master'); @@ -70,6 +93,29 @@ void main() { headParse.object.free(); }); + test('.ext() throws when spec string not found or invalid', () { + expect( + () => repo.revParseExt('invalid'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "revspec 'invalid' not found", + ), + ), + ); + expect( + () => repo.revParseExt(''), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to parse revision specifier - Invalid pattern ''", + ), + ), + ); + }); + test( '.range returns revspec with correct fields values based on provided spec', () { @@ -108,12 +154,23 @@ void main() { test('throws on invalid range spec', () { expect( () => repo.revParse('invalid..5aecfa'), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "revspec 'invalid' not found", + ), + ), ); - expect( () => repo.revParse('master........5aecfa'), - throwsA(isA()), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to parse revision specifier - Invalid pattern '.....5aecfa'", + ), + ), ); }); }); diff --git a/test/revwalk_test.dart b/test/revwalk_test.dart index 575ad18..6afda74 100644 --- a/test/revwalk_test.dart +++ b/test/revwalk_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -25,6 +26,25 @@ void main() { }); group('RevWalk', () { + test('successfully initializes', () { + final walker = RevWalk(repo); + expect(walker, isA()); + walker.free(); + }); + + test('throws when trying to initialize and error occurs', () { + expect( + () => RevWalk(Repository(nullptr)), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('returns list of commits with default sorting', () { final walker = RevWalk(repo); final start = Oid.fromSHA(repo: repo, sha: log.first); @@ -88,11 +108,9 @@ void main() { test('successfully hides commit and its ancestors', () { final walker = RevWalk(repo); - final start = Oid.fromSHA(repo: repo, sha: log.first); - final oidToHide = Oid.fromSHA(repo: repo, sha: log[2]); - walker.push(start); - walker.hide(oidToHide); + walker.push(repo[log.first]); + walker.hide(repo[log[2]]); final commits = walker.walk(); expect(commits.length, 2); @@ -103,6 +121,23 @@ void main() { walker.free(); }); + test('throws when trying to hide commit oid and error occurs', () { + final walker = RevWalk(repo); + + expect( + () => walker.hide(repo['0' * 40]), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "odb: cannot read object: null OID cannot exist", + ), + ), + ); + + walker.free(); + }); + test('successfully resets walker', () { final walker = RevWalk(repo); final start = Oid.fromSHA(repo: repo, sha: log.first); @@ -134,5 +169,23 @@ void main() { } walker.free(); }); + + test('throws when trying to add new root for traversal and error occurs', + () { + final walker = RevWalk(repo); + + expect( + () => walker.push(repo['0' * 40]), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "odb: cannot read object: null OID cannot exist", + ), + ), + ); + + walker.free(); + }); }); } diff --git a/test/signature_test.dart b/test/signature_test.dart index 987bad7..4ec69d0 100644 --- a/test/signature_test.dart +++ b/test/signature_test.dart @@ -25,6 +25,34 @@ void main() { expect(signature, isA()); }); + test('throws when trying to create with empty name and email', () { + expect( + () => Signature.create(name: '', email: '', time: 0), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to parse signature - Signature cannot have an empty name or email", + ), + ), + ); + }); + + test( + 'throws when trying to create with empty name and email and default time', + () { + expect( + () => Signature.create(name: '', email: ''), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to parse signature - Signature cannot have an empty name or email", + ), + ), + ); + }); + test('successfully creates without provided time and offset', () { final sig = Signature.create(name: 'Name', email: 'email@example.com'); expect(sig, isA()); diff --git a/test/stash_test.dart b/test/stash_test.dart index ac7f25f..2c4f6f0 100644 --- a/test/stash_test.dart +++ b/test/stash_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -34,6 +35,19 @@ void main() { expect(repo.status.isEmpty, true); }); + test('throws when trying to save and error occurs', () { + expect( + () => Repository(nullptr).createStash(stasher: stasher), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('successfully saves changes to stash including ignored', () { final swpPath = File('${tmpDir.path}/some.swp'); swpPath.writeAsStringSync('ignored'); @@ -78,6 +92,19 @@ void main() { expect(repo.status, contains('file')); }); + test('successfully applies changes from stash with paths provided', () { + File('${tmpDir.path}/file').writeAsStringSync( + 'edit', + mode: FileMode.append, + ); + + repo.createStash(stasher: stasher); + expect(repo.status.isEmpty, true); + + repo.applyStash(paths: ['file']); + expect(repo.status, contains('file')); + }); + test('successfully applies changes from stash including index changes', () { File('${tmpDir.path}/stash.this').writeAsStringSync('stash'); final index = repo.index; @@ -95,6 +122,26 @@ void main() { index.free(); }); + test('throws when trying to apply with wrong index', () { + File('${tmpDir.path}/file').writeAsStringSync( + 'edit', + mode: FileMode.append, + ); + + repo.createStash(stasher: stasher); + + expect( + () => repo.applyStash(index: 10), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "no stashed state at position 10", + ), + ), + ); + }); + test('successfully drops stash', () { File('${tmpDir.path}/file').writeAsStringSync( 'edit', @@ -103,10 +150,30 @@ void main() { repo.createStash(stasher: stasher); final stash = repo.stashes.first; - repo.dropStash(stash.index); + repo.dropStash(index: stash.index); expect(() => repo.applyStash(), throwsA(isA())); }); + test('throws when trying to drop with wrong index', () { + File('${tmpDir.path}/file').writeAsStringSync( + 'edit', + mode: FileMode.append, + ); + + repo.createStash(stasher: stasher); + + expect( + () => repo.dropStash(index: 10), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "no stashed state at position 10", + ), + ), + ); + }); + test('successfully pops from stash', () { File('${tmpDir.path}/file').writeAsStringSync( 'edit', @@ -119,6 +186,18 @@ void main() { expect(() => repo.applyStash(), throwsA(isA())); }); + test('successfully pops from stash with provided path', () { + File('${tmpDir.path}/file').writeAsStringSync( + 'edit', + mode: FileMode.append, + ); + + repo.createStash(stasher: stasher); + repo.popStash(paths: ['file']); + expect(repo.status, contains('file')); + expect(() => repo.applyStash(), throwsA(isA())); + }); + test('successfully pops from stash including index changes', () { File('${tmpDir.path}/stash.this').writeAsStringSync('stash'); final index = repo.index; @@ -136,6 +215,26 @@ void main() { index.free(); }); + test('throws when trying to pop with wrong index', () { + File('${tmpDir.path}/file').writeAsStringSync( + 'edit', + mode: FileMode.append, + ); + + repo.createStash(stasher: stasher); + + expect( + () => repo.popStash(index: 10), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "no stashed state at position 10", + ), + ), + ); + }); + test('returns list of stashes', () { File('${tmpDir.path}/file').writeAsStringSync( 'edit', diff --git a/test/submodule_test.dart b/test/submodule_test.dart index 70e49ab..a14886e 100644 --- a/test/submodule_test.dart +++ b/test/submodule_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -43,6 +44,19 @@ void main() { submodule.free(); }); + test('throws when trying to lookup and submodule not found', () { + expect( + () => repo.lookupSubmodule('not/there'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "no submodule named 'not/there'", + ), + ), + ); + }); + test('successfully inits and updates', () { final submoduleFilePath = '${repo.workdir}$testSubmodule/master.txt'; expect(File(submoduleFilePath).existsSync(), false); @@ -62,6 +76,19 @@ void main() { expect(File(submoduleFilePath).existsSync(), true); }); + test('throws when trying to update not initialized submodule', () { + expect( + () => repo.updateSubmodule(submodule: testSubmodule), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "submodule is not initialized", + ), + ), + ); + }); + test('successfully opens repository for a submodule', () { final submodule = repo.lookupSubmodule(testSubmodule); repo.initSubmodule(submodule: testSubmodule); @@ -81,6 +108,24 @@ void main() { submodule.free(); }); + test('throws when trying to open repository for not initialized submodule', + () { + final submodule = repo.lookupSubmodule(testSubmodule); + + expect( + () => submodule.open(), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + contains("failed to resolve path"), + ), + ), + ); + + submodule.free(); + }); + test('successfully adds submodule', () { final submodule = repo.addSubmodule( url: submoduleUrl, @@ -96,6 +141,38 @@ void main() { submodule.free(); }); + test('throws when trying to add submodule with wrong url', () { + expect( + () => repo.addSubmodule( + url: 'https://wrong.url/', + path: 'test', + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + 'failed to resolve address for wrong.url: Name or service not known', + ), + ), + ); + }); + + test('throws when trying to add submodule and error occurs', () { + expect( + () => Repository(nullptr).addSubmodule( + url: 'https://wrong.url/', + path: 'test', + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('successfully sets configuration values', () { final submodule = repo.lookupSubmodule(testSubmodule); expect(submodule.url, submoduleUrl); diff --git a/test/tag_test.dart b/test/tag_test.dart index b12cffc..a37ba1f 100644 --- a/test/tag_test.dart +++ b/test/tag_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -27,6 +28,32 @@ void main() { expect(tag, isA()); }); + test('throws when trying to lookup tag for invalid oid', () { + expect( + () => repo.lookupTag(repo['0' * 40]), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "odb: cannot read object: null OID cannot exist", + ), + ), + ); + }); + + test('throws when trying to get target of a tag and error occurs', () { + expect( + () => Tag(nullptr).target, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 't'", + ), + ), + ); + }); + test('returns correct values', () { final signature = Signature.create( name: 'Aleksey Kulikov', @@ -179,15 +206,79 @@ void main() { signature.free(); }); + test('throws when trying to create tag with invalid name', () { + expect( + () => repo.createTag( + tagName: '', + target: repo['9c78c21'], + targetType: GitObject.any, + tagger: Signature(nullptr), + message: '', + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: '!create_tag_annotation || (tagger && message)'", + ), + ), + ); + }); + + test('throws when trying to create tag with invalid target', () { + expect( + () => repo.createTag( + tagName: '', + target: repo['0' * 40], + targetType: GitObject.any, + tagger: Signature(nullptr), + message: '', + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "odb: cannot read object: null OID cannot exist", + ), + ), + ); + }); + test('returns list of tags in repository', () { expect(Tag.list(repo), ['v0.1', 'v0.2']); }); + test('throws when trying to get list of tags and error occurs', () { + expect( + () => Repository(nullptr).tags, + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('successfully deletes tag', () { expect(repo.tags, ['v0.1', 'v0.2']); repo.deleteTag('v0.2'); expect(repo.tags, ['v0.1']); }); + + test('throws when trying to delete non existing tag', () { + expect( + () => repo.deleteTag('not.there'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "reference 'refs/tags/not.there' not found", + ), + ), + ); + }); }); } diff --git a/test/tree_test.dart b/test/tree_test.dart index c76548d..1a4b47c 100644 --- a/test/tree_test.dart +++ b/test/tree_test.dart @@ -29,6 +29,19 @@ void main() { expect(tree.toString(), contains('Tree{')); }); + test('throws when looking up tree for invalid oid', () { + expect( + () => repo.lookupTree(repo['0' * 40]), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "odb: cannot read object: null OID cannot exist", + ), + ), + ); + }); + test('returns correct values', () { expect(tree.length, 4); expect(tree.entries.first.oid.sha, fileSHA); diff --git a/test/treebuilder_test.dart b/test/treebuilder_test.dart index 5ce8d33..59b2ce3 100644 --- a/test/treebuilder_test.dart +++ b/test/treebuilder_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -39,6 +40,19 @@ void main() { builder.free(); }); + test('throws when trying to initialize and error occurs', () { + expect( + () => TreeBuilder(repo: Repository(nullptr)), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('clears all the entries in the builder', () { final builder = TreeBuilder(repo: repo, tree: tree); @@ -65,6 +79,42 @@ void main() { builder.free(); }); + test('throws when trying to add entry with invalid name or invalid oid', + () { + final builder = TreeBuilder(repo: repo); + + expect( + () => builder.add( + filename: '', + oid: repo['0' * 40], + filemode: GitFilemode.blob, + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to insert entry: invalid name for a tree entry - ", + ), + ), + ); + expect( + () => builder.add( + filename: 'some.file', + oid: repo['0' * 40], + filemode: GitFilemode.blob, + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to insert entry: invalid null OID - some.file", + ), + ), + ); + + builder.free(); + }); + test('successfully removes an entry', () { final builder = TreeBuilder(repo: repo, tree: tree); @@ -76,5 +126,22 @@ void main() { builder.free(); }); + + test('throws when trying to remove entry that is not in the tree', () { + final builder = TreeBuilder(repo: repo); + + expect( + () => builder.remove('not.there'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "failed to remove entry: file isn't in the tree - not.there", + ), + ), + ); + + builder.free(); + }); }); } diff --git a/test/worktree_test.dart b/test/worktree_test.dart index 7d831c0..c00487b 100644 --- a/test/worktree_test.dart +++ b/test/worktree_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:io'; import 'package:test/test.dart'; import 'package:libgit2dart/libgit2dart.dart'; @@ -85,6 +86,35 @@ void main() { worktree.free(); }); + test('throws when trying to create worktree with invalid name or path', () { + expect( + () => repo.createWorktree( + name: '', + path: worktreeDir.path, + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + contains('failed to make directory'), + ), + ), + ); + expect( + () => repo.createWorktree( + name: 'name', + path: '', + ), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + 'attempt to create empty path: Invalid argument', + ), + ), + ); + }); + test('successfully lookups worktree', () { final worktree = repo.createWorktree( name: worktreeName, @@ -100,6 +130,19 @@ void main() { worktree.free(); }); + test('throws when trying to lookup and error occurs', () { + expect( + () => Worktree.lookup(repo: Repository(nullptr), name: 'name'), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); + test('successfully locks and unlocks worktree', () { final worktree = repo.createWorktree( name: worktreeName, @@ -136,5 +179,18 @@ void main() { worktree.free(); }); + + test('throws when trying get list of worktrees and error occurs', () { + expect( + () => Worktree.list(Repository(nullptr)), + throwsA( + isA().having( + (e) => e.toString(), + 'error', + "invalid argument: 'repo'", + ), + ), + ); + }); }); }