From bad40bdb6192eb901e51cba0d161cc03eb608019 Mon Sep 17 00:00:00 2001 From: Aleksey Kulikov Date: Tue, 10 May 2022 16:18:55 +0300 Subject: [PATCH] feat: add ability to compare objects (#54) --- lib/src/annotated.dart | 8 +++- lib/src/bindings/reference.dart | 8 ---- lib/src/blame.dart | 18 ++++++- lib/src/blob.dart | 8 +++- lib/src/branch.dart | 8 +++- lib/src/commit.dart | 8 +++- lib/src/config.dart | 10 +++- lib/src/diff.dart | 29 ++++++++++-- lib/src/index.dart | 8 +++- lib/src/note.dart | 8 +++- lib/src/odb.dart | 14 +++++- lib/src/oid.dart | 16 ++----- lib/src/patch.dart | 36 ++++++++++++-- lib/src/reference.dart | 83 ++++++++++++++++----------------- lib/src/reflog.dart | 8 +++- lib/src/refspec.dart | 10 +++- lib/src/remote.dart | 14 +++++- lib/src/signature.dart | 24 ++++------ lib/src/stash.dart | 8 +++- lib/src/submodule.dart | 24 ++++++++-- lib/src/tag.dart | 8 +++- lib/src/tree.dart | 16 +++++-- lib/src/worktree.dart | 11 ++++- pubspec.yaml | 1 + test/annotated_test.dart | 7 +++ test/blame_test.dart | 7 +++ test/blob_test.dart | 7 +++ test/branch_test.dart | 7 +++ test/commit_test.dart | 7 +++ test/config_test.dart | 4 ++ test/diff_test.dart | 10 ++++ test/index_test.dart | 4 ++ test/note_test.dart | 8 ++++ test/odb_test.dart | 5 ++ test/patch_test.dart | 20 ++++++++ test/reference_test.dart | 64 +++++++++++++++---------- test/reflog_test.dart | 5 ++ test/remote_test.dart | 11 +++++ test/revparse_test.dart | 4 +- test/signature_test.dart | 8 ++++ test/stash_test.dart | 7 +++ test/submodule_test.dart | 7 +++ test/tag_test.dart | 7 +++ test/tree_test.dart | 9 ++++ test/worktree_test.dart | 9 ++++ 45 files changed, 466 insertions(+), 137 deletions(-) diff --git a/lib/src/annotated.dart b/lib/src/annotated.dart index 06030e2..c500dd9 100644 --- a/lib/src/annotated.dart +++ b/lib/src/annotated.dart @@ -1,10 +1,13 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/annotated.dart' as bindings; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; +import 'package:meta/meta.dart'; -class AnnotatedCommit { +@immutable +class AnnotatedCommit extends Equatable { /// Lookups an annotated commit from the given commit [oid]. /// /// It is preferable to use [AnnotatedCommit.fromReference] instead of this @@ -96,6 +99,9 @@ class AnnotatedCommit { bindings.free(_annotatedCommitPointer); _finalizer.detach(this); } + + @override + List get props => [oid]; } // coverage:ignore-start diff --git a/lib/src/bindings/reference.dart b/lib/src/bindings/reference.dart index 2fc23ab..95355d4 100644 --- a/lib/src/bindings/reference.dart +++ b/lib/src/bindings/reference.dart @@ -410,14 +410,6 @@ Pointer setTargetSymbolic({ } } -/// Compare two references. -bool compare({ - required Pointer ref1Pointer, - required Pointer ref2Pointer, -}) { - return libgit2.git_reference_cmp(ref1Pointer, ref2Pointer) == 0 || false; -} - /// Recursively peel reference until object of the specified type is found. /// /// The retrieved peeled object is owned by the repository and should be closed diff --git a/lib/src/blame.dart b/lib/src/blame.dart index 3354218..b1f02e3 100644 --- a/lib/src/blame.dart +++ b/lib/src/blame.dart @@ -1,10 +1,12 @@ import 'dart:collection'; import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:ffi/ffi.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/blame.dart' as bindings; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; +import 'package:meta/meta.dart'; class Blame with IterableMixin { /// Returns the blame for a single file. @@ -120,7 +122,8 @@ final _finalizer = Finalizer>( ); // coverage:ignore-end -class BlameHunk { +@immutable +class BlameHunk extends Equatable { /// Initializes a new instance of the [BlameHunk] class from /// provided pointer to blame hunk object in memory. const BlameHunk._(this._blameHunkPointer); @@ -183,6 +186,19 @@ class BlameHunk { 'originCommitter: $originCommitter, originCommitOid: $originCommitOid, ' 'originPath: $originPath}'; } + + @override + List get props => [ + linesCount, + isBoundary, + finalStartLineNumber, + finalCommitter, + finalCommitOid, + originStartLineNumber, + originCommitter, + originCommitOid, + originPath, + ]; } class _BlameIterator implements Iterator { diff --git a/lib/src/blob.dart b/lib/src/blob.dart index 8c629ae..89ea241 100644 --- a/lib/src/blob.dart +++ b/lib/src/blob.dart @@ -1,10 +1,13 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/blob.dart' as bindings; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; +import 'package:meta/meta.dart'; -class Blob { +@immutable +class Blob extends Equatable { /// Initializes a new instance of [Blob] class from provided pointer to /// blob object in memory. /// @@ -124,6 +127,9 @@ class Blob { String toString() { return 'Blob{oid: $oid, isBinary: $isBinary, size: $size}'; } + + @override + List get props => [oid]; } // coverage:ignore-start diff --git a/lib/src/branch.dart b/lib/src/branch.dart index 0c63691..976598c 100644 --- a/lib/src/branch.dart +++ b/lib/src/branch.dart @@ -1,11 +1,14 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/branch.dart' as bindings; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/reference.dart' as reference_bindings; +import 'package:meta/meta.dart'; -class Branch { +@immutable +class Branch extends Equatable { /// Initializes a new instance of [Branch] class from provided pointer to /// branch object in memory. /// @@ -228,6 +231,9 @@ class Branch { return 'Branch{name: $name, target: $target, isHead: $isHead, ' 'isCheckedOut: $isCheckedOut}'; } + + @override + List get props => [target, name]; } // coverage:ignore-start diff --git a/lib/src/commit.dart b/lib/src/commit.dart index 1f13b02..6bedac2 100644 --- a/lib/src/commit.dart +++ b/lib/src/commit.dart @@ -1,11 +1,14 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/commit.dart' as bindings; import 'package:libgit2dart/src/bindings/graph.dart' as graph_bindings; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; +import 'package:meta/meta.dart'; -class Commit { +@immutable +class Commit extends Equatable { /// Initializes a new instance of [Commit] class from provided pointer to /// commit object in memory. /// @@ -338,6 +341,9 @@ class Commit { 'messageEncoding: $messageEncoding, time: $time, committer: $committer,' ' author: $author}'; } + + @override + List get props => [oid]; } // coverage:ignore-start diff --git a/lib/src/config.dart b/lib/src/config.dart index efbc796..2a63645 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -2,11 +2,13 @@ import 'dart:collection'; import 'dart:ffi'; import 'dart:io'; +import 'package:equatable/equatable.dart'; import 'package:ffi/ffi.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/config.dart' as bindings; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/util.dart'; +import 'package:meta/meta.dart'; class Config with IterableMixin { /// Initializes a new instance of [Config] class from provided @@ -209,8 +211,9 @@ final _finalizer = Finalizer>( ); // coverage:ignore-end -class ConfigEntry { - ConfigEntry._({ +@immutable +class ConfigEntry extends Equatable { + const ConfigEntry._({ required this.name, required this.value, required this.includeDepth, @@ -234,6 +237,9 @@ class ConfigEntry { return 'ConfigEntry{name: $name, value: $value, ' 'includeDepth: $includeDepth, level: $level}'; } + + @override + List get props => [name, value, includeDepth, level]; } class _ConfigIterator implements Iterator { diff --git a/lib/src/diff.dart b/lib/src/diff.dart index bd1ce85..6480172 100644 --- a/lib/src/diff.dart +++ b/lib/src/diff.dart @@ -1,12 +1,15 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:ffi/ffi.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/diff.dart' as bindings; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/util.dart'; +import 'package:meta/meta.dart'; -class Diff { +@immutable +class Diff extends Equatable { /// Initializes a new instance of [Diff] class from provided /// pointer to diff object in memory. /// @@ -461,8 +464,11 @@ class Diff { @override String toString() { - return 'Diff{length: $length}'; + return 'Diff{length: $length, patchOid: $patchOid}'; } + + @override + List get props => [patchOid]; } // coverage:ignore-start @@ -471,7 +477,8 @@ final _finalizer = Finalizer>( ); // coverage:ignore-end -class DiffDelta { +@immutable +class DiffDelta extends Equatable { /// Initializes a new instance of [DiffDelta] class from provided /// pointer to diff delta object in memory. const DiffDelta(this._diffDeltaPointer); @@ -519,6 +526,16 @@ class DiffDelta { return 'DiffDelta{status: $status, flags: $flags, similarity: $similarity, ' 'numberOfFiles: $numberOfFiles, oldFile: $oldFile, newFile: $newFile}'; } + + @override + List get props => [ + status, + flags, + similarity, + numberOfFiles, + oldFile, + newFile, + ]; } /// Description of one side of a delta. @@ -526,7 +543,8 @@ class DiffDelta { /// Although this is called a "file", it could represent a file, a symbolic /// link, a submodule commit id, or even a tree (although that only if you /// are tracking type changes or ignored/untracked directories). -class DiffFile { +@immutable +class DiffFile extends Equatable { /// Initializes a new instance of [DiffFile] class from provided diff file /// object. const DiffFile._(this._diffFile); @@ -560,6 +578,9 @@ class DiffFile { return 'DiffFile{oid: $oid, path: $path, size: $size, flags: $flags, ' 'mode: $mode}'; } + + @override + List get props => [oid, path, size, flags, mode]; } class DiffStats { diff --git a/lib/src/index.dart b/lib/src/index.dart index 803ac56..e3280c9 100644 --- a/lib/src/index.dart +++ b/lib/src/index.dart @@ -1,10 +1,12 @@ import 'dart:collection'; import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:ffi/ffi.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/index.dart' as bindings; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; +import 'package:meta/meta.dart'; class Index with IterableMixin { /// Initializes a new instance of [Index] class from provided @@ -323,7 +325,8 @@ final _finalizer = Finalizer>( ); // coverage:ignore-end -class IndexEntry { +@immutable +class IndexEntry extends Equatable { /// Initializes a new instance of [IndexEntry] class. /// /// Note: For internal use. @@ -367,6 +370,9 @@ class IndexEntry { String toString() { return 'IndexEntry{oid: $oid, path: $path, mode: $mode, stage: $stage}'; } + + @override + List get props => [oid, path, mode, stage]; } class ConflictEntry { diff --git a/lib/src/note.dart b/lib/src/note.dart index 54126e1..6ba83c2 100644 --- a/lib/src/note.dart +++ b/lib/src/note.dart @@ -1,9 +1,12 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/note.dart' as bindings; +import 'package:meta/meta.dart'; -class Note { +@immutable +class Note extends Equatable { /// Initializes a new instance of the [Note] class from provided /// pointer to note and annotatedOid objects in memory. /// @@ -143,6 +146,9 @@ class Note { String toString() { return 'Note{oid: $oid, message: $message, annotatedOid: $annotatedOid}'; } + + @override + List get props => [oid]; } // coverage:ignore-start diff --git a/lib/src/odb.dart b/lib/src/odb.dart index 10f9aee..0969f2f 100644 --- a/lib/src/odb.dart +++ b/lib/src/odb.dart @@ -1,10 +1,13 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/odb.dart' as bindings; import 'package:libgit2dart/src/util.dart'; +import 'package:meta/meta.dart'; -class Odb { +@immutable +class Odb extends Equatable { /// Initializes a new instance of [Odb] class from provided /// pointer to Odb object in memory. /// @@ -101,6 +104,9 @@ class Odb { bindings.free(_odbPointer); _finalizer.detach(this); } + + @override + List get props => [objects]; } // coverage:ignore-start @@ -109,7 +115,8 @@ final _finalizer = Finalizer>( ); // coverage:ignore-end -class OdbObject { +@immutable +class OdbObject extends Equatable { /// Initializes a new instance of the [OdbObject] class from /// provided pointer to odbObject object in memory. OdbObject._(this._odbObjectPointer) { @@ -144,6 +151,9 @@ class OdbObject { String toString() { return 'OdbObject{oid: $oid, type: $type, size: $size}'; } + + @override + List get props => [oid]; } // coverage:ignore-start diff --git a/lib/src/oid.dart b/lib/src/oid.dart index d1387b9..74a794c 100644 --- a/lib/src/oid.dart +++ b/lib/src/oid.dart @@ -1,5 +1,6 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/odb.dart' as odb_bindings; @@ -8,7 +9,7 @@ import 'package:libgit2dart/src/util.dart'; import 'package:meta/meta.dart'; @immutable -class Oid { +class Oid extends Equatable { /// Initializes a new instance of [Oid] class from provided /// pointer to Oid object in memory. /// @@ -54,13 +55,6 @@ class Oid { /// Hexadecimal SHA string. String get sha => bindings.toSHA(_oidPointer); - @override - bool operator ==(Object other) { - return (other is Oid) && - (bindings.compare(aPointer: _oidPointer, bPointer: other._oidPointer) == - 0); - } - bool operator <(Oid other) { return bindings.compare( aPointer: _oidPointer, @@ -93,9 +87,9 @@ class Oid { 1; } - @override // coverage:ignore-line - int get hashCode => _oidPointer.address.hashCode; // coverage:ignore-line - @override String toString() => 'Oid{sha: $sha}'; + + @override + List get props => [sha]; } diff --git a/lib/src/patch.dart b/lib/src/patch.dart index d6cf4e5..36fd51f 100644 --- a/lib/src/patch.dart +++ b/lib/src/patch.dart @@ -1,12 +1,15 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:ffi/ffi.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/patch.dart' as bindings; import 'package:libgit2dart/src/util.dart'; +import 'package:meta/meta.dart'; -class Patch { +@immutable +class Patch extends Equatable { /// Initializes a new instance of [Patch] class from provided /// pointer to patch object in memory and pointers to old and new blobs/buffers. /// @@ -264,6 +267,9 @@ class Patch { @override String toString() => 'Patch{size: ${size()}, delta: $delta}'; + + @override + List get props => [delta]; } // coverage:ignore-start @@ -296,7 +302,8 @@ class PatchStats { } } -class DiffHunk { +@immutable +class DiffHunk extends Equatable { const DiffHunk._({ required this.linesCount, required this.index, @@ -338,9 +345,22 @@ class DiffHunk { 'oldStart: $oldStart, oldLines: $oldLines, newStart: $newStart, ' 'newLines: $newLines, header: $header}'; } + + @override + List get props => [ + linesCount, + index, + oldStart, + oldLines, + newStart, + newLines, + header, + lines + ]; } -class DiffLine { +@immutable +class DiffLine extends Equatable { const DiffLine._({ required this.origin, required this.oldLineNumber, @@ -374,4 +394,14 @@ class DiffLine { 'newLineNumber: $newLineNumber, numLines: $numLines, ' 'contentOffset: $contentOffset, content: $content}'; } + + @override + List get props => [ + origin, + oldLineNumber, + newLineNumber, + numLines, + contentOffset, + content, + ]; } diff --git a/lib/src/reference.dart b/lib/src/reference.dart index 8ea93c7..0a06c01 100644 --- a/lib/src/reference.dart +++ b/lib/src/reference.dart @@ -1,5 +1,6 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/object.dart' as object_bindings; @@ -7,8 +8,10 @@ import 'package:libgit2dart/src/bindings/refdb.dart' as refdb_bindings; import 'package:libgit2dart/src/bindings/reference.dart' as bindings; import 'package:libgit2dart/src/bindings/repository.dart' as repository_bindings; +import 'package:meta/meta.dart'; -class Reference { +@immutable +class Reference extends Equatable { /// Initializes a new instance of the [Reference] class. /// /// Note: For internal use. Instead, use one of: @@ -79,7 +82,7 @@ class Reference { _finalizer.attach(this, _refPointer, detach: this); } - late Pointer _refPointer; + late final Pointer _refPointer; /// Pointer to memory address for allocated reference object. /// @@ -124,6 +127,39 @@ class Reference { ); } + /// Updates the [target] of reference with provided [name]. + /// + /// [target] being either Oid for direct reference or string reference name + /// for symbolic reference. + /// + /// Throws a [LibGit2Error] if error occured or [ArgumentError] if [target] + /// is not [Oid] or string. + static void setTarget({ + required Repository repo, + required String name, + required Object target, + String? logMessage, + }) { + final ref = Reference.lookup(repo: repo, name: name); + if (target is Oid) { + bindings.setTarget( + refPointer: ref.pointer, + oidPointer: target.pointer, + logMessage: logMessage, + ); + } else if (target is String) { + bindings.setTargetSymbolic( + refPointer: ref.pointer, + target: target, + logMessage: logMessage, + ); + } else { + throw ArgumentError.value( + '$target must be either Oid or String reference name', + ); + } + } + /// List of all the references names that can be found in a [repo]sitory. /// /// Throws a [LibGit2Error] if error occured. @@ -177,35 +213,6 @@ class Reference { return Oid(oidPointer); } - /// Updates the [target] of this reference. - /// - /// [target] being either Oid for direct reference or string reference name - /// for symbolic reference. - /// - /// Throws a [LibGit2Error] if error occured or [ArgumentError] if [target] - /// is not [Oid] or string. - void setTarget({required Object target, String? logMessage}) { - if (target is Oid) { - final newPointer = bindings.setTarget( - refPointer: _refPointer, - oidPointer: target.pointer, - logMessage: logMessage, - ); - _refPointer = newPointer; - } else if (target is String) { - final newPointer = bindings.setTargetSymbolic( - refPointer: _refPointer, - target: target, - logMessage: logMessage, - ); - _refPointer = newPointer; - } else { - throw ArgumentError.value( - '$target must be either Oid or String reference name', - ); - } - } - /// Recursively peel reference until object of the specified [type] is found. /// /// The retrieved peeled object is owned by the repository and should be @@ -272,17 +279,6 @@ class Reference { /// Whether reference is a tag. bool get isTag => bindings.isTag(_refPointer); - /// Compares two references. - bool equals(Reference other) { - return bindings.compare( - ref1Pointer: _refPointer, - ref2Pointer: other._refPointer, - ); - } - - /// Compares two references. - bool notEquals(Reference other) => !equals(other); - /// Releases memory allocated for reference object. void free() { bindings.free(_refPointer); @@ -295,6 +291,9 @@ class Reference { 'isBranch: $isBranch, isNote: $isNote, isRemote: $isRemote, ' 'isTag: $isTag}'; } + + @override + List get props => [target]; } // coverage:ignore-start diff --git a/lib/src/reflog.dart b/lib/src/reflog.dart index ac71bb3..998d2f6 100644 --- a/lib/src/reflog.dart +++ b/lib/src/reflog.dart @@ -1,9 +1,11 @@ import 'dart:collection'; import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/reference.dart' as reference_bindings; import 'package:libgit2dart/src/bindings/reflog.dart' as bindings; +import 'package:meta/meta.dart'; class RefLog with IterableMixin { /// Initializes a new instance of [RefLog] class from provided [Reference]. @@ -109,7 +111,8 @@ final _finalizer = Finalizer>( ); // coverage:ignore-end -class RefLogEntry { +@immutable +class RefLogEntry extends Equatable { /// Initializes a new instance of [RefLogEntry] class from provided /// pointer to RefLogEntry object in memory. const RefLogEntry._(this._entryPointer); @@ -133,6 +136,9 @@ class RefLogEntry { @override String toString() => 'RefLogEntry{message: $message, committer: $committer}'; + + @override + List get props => [message, committer, newOid, oldOid]; } class _RefLogIterator implements Iterator { diff --git a/lib/src/refspec.dart b/lib/src/refspec.dart index c66059f..679abca 100644 --- a/lib/src/refspec.dart +++ b/lib/src/refspec.dart @@ -1,9 +1,12 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/refspec.dart' as bindings; +import 'package:meta/meta.dart'; -class Refspec { +@immutable +class Refspec extends Equatable { /// Initializes a new instance of the [Refspec] class /// from provided pointer to refspec object in memory. /// @@ -72,6 +75,9 @@ class Refspec { @override String toString() { return 'Refspec{source: $source, destination: $destination, force: $force, ' - 'string: $string}'; + 'string: $string, direction: $direction}'; } + + @override + List get props => [source, destination, force, string, direction]; } diff --git a/lib/src/remote.dart b/lib/src/remote.dart index 7ba5120..20659ba 100644 --- a/lib/src/remote.dart +++ b/lib/src/remote.dart @@ -1,9 +1,12 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/remote.dart' as bindings; +import 'package:meta/meta.dart'; -class Remote { +@immutable +class Remote extends Equatable { /// Lookups remote with provided [name] in a [repo]sitory. /// /// The [name] will be checked for validity. @@ -310,6 +313,9 @@ class Remote { return 'Remote{name: $name, url: $url, pushUrl: $pushUrl, ' 'refspecCount: $refspecCount}'; } + + @override + List get props => [name]; } // coverage:ignore-start @@ -378,7 +384,8 @@ class RemoteCallback { final String? fetch; } -class RemoteReference { +@immutable +class RemoteReference extends Equatable { const RemoteReference._({ required this.isLocal, required this.localId, @@ -408,4 +415,7 @@ class RemoteReference { return 'RemoteReference{isLocal: $isLocal, localId: $localId, ' 'name: $name, oid: $oid, symRef: $symRef}'; } + + @override + List get props => [isLocal, localId, name, oid, symRef]; } diff --git a/lib/src/signature.dart b/lib/src/signature.dart index aa4dcda..fb410ed 100644 --- a/lib/src/signature.dart +++ b/lib/src/signature.dart @@ -1,4 +1,5 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:ffi/ffi.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; @@ -7,7 +8,7 @@ import 'package:libgit2dart/src/util.dart'; import 'package:meta/meta.dart'; @immutable -class Signature { +class Signature extends Equatable { /// Initializes a new instance of [Signature] class from provided pointer to /// signature object in memory. /// @@ -74,16 +75,8 @@ class Signature { /// Timezone offset in minutes. int get offset => _signaturePointer.ref.when.offset; - @override - bool operator ==(Object other) { - return (other is Signature) && - (name == other.name) && - (email == other.email) && - (time == other.time) && - (offset == other.offset) && - (_signaturePointer.ref.when.sign == - other._signaturePointer.ref.when.sign); - } + /// Indicator for questionable '-0000' offsets in signature. + String get sign => String.fromCharCode(_signaturePointer.ref.when.sign); /// Releases memory allocated for signature object. void free() { @@ -91,15 +84,14 @@ class Signature { _finalizer.detach(this); } - @override // coverage:ignore-line - int get hashCode => - _signaturePointer.address.hashCode; // coverage:ignore-line - @override String toString() { return 'Signature{name: $name, email: $email, time: $time, ' - 'offset: $offset}'; + 'offset: $sign$offset}'; } + + @override + List get props => [name, email, time, offset, sign]; } // coverage:ignore-start diff --git a/lib/src/stash.dart b/lib/src/stash.dart index 1ab04e8..27e22ab 100644 --- a/lib/src/stash.dart +++ b/lib/src/stash.dart @@ -1,7 +1,10 @@ +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/stash.dart' as bindings; +import 'package:meta/meta.dart'; -class Stash { +@immutable +class Stash extends Equatable { /// Initializes a new instance of [Stash] class from provided stash [index], /// [message] and [oid]. const Stash({ @@ -146,4 +149,7 @@ class Stash { String toString() { return 'Stash{index: $index, message: $message, oid: $oid}'; } + + @override + List get props => [index, message, oid]; } diff --git a/lib/src/submodule.dart b/lib/src/submodule.dart index c924eb7..fd94785 100644 --- a/lib/src/submodule.dart +++ b/lib/src/submodule.dart @@ -1,9 +1,12 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/submodule.dart' as bindings; +import 'package:meta/meta.dart'; -class Submodule { +@immutable +class Submodule extends Equatable { /// Lookups submodule information by [name] or path (they are usually the /// same). /// @@ -267,10 +270,23 @@ class Submodule { @override String toString() { - return 'Submodule{name: $name, path: $path, url: $url, status: $status, ' - 'branch: $branch, headOid: $headOid, indexOid: $indexOid, ' - 'workdirOid: $workdirOid, ignore: $ignore, updateRule: $updateRule}'; + return 'Submodule{name: $name, path: $path, url: $url, branch: $branch, ' + 'headOid: $headOid, indexOid: $indexOid, workdirOid: $workdirOid, ' + 'ignore: $ignore, updateRule: $updateRule}'; } + + @override + List get props => [ + name, + path, + url, + branch, + headOid, + indexOid, + workdirOid, + ignore, + updateRule, + ]; } // coverage:ignore-start diff --git a/lib/src/tag.dart b/lib/src/tag.dart index 8cddf8f..ad6867b 100644 --- a/lib/src/tag.dart +++ b/lib/src/tag.dart @@ -1,11 +1,14 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/object.dart' as object_bindings; import 'package:libgit2dart/src/bindings/tag.dart' as bindings; +import 'package:meta/meta.dart'; -class Tag { +@immutable +class Tag extends Equatable { /// Initializes a new instance of [Tag] class from provided pointer to /// tag object in memory. /// @@ -223,6 +226,9 @@ class Tag { return 'Tag{oid: $oid, name: $name, message: $message, target: $target, ' 'tagger: $tagger}'; } + + @override + List get props => [name]; } // coverage:ignore-start diff --git a/lib/src/tree.dart b/lib/src/tree.dart index aa22c3e..218bba5 100644 --- a/lib/src/tree.dart +++ b/lib/src/tree.dart @@ -1,10 +1,13 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/tree.dart' as bindings; +import 'package:meta/meta.dart'; -class Tree { +@immutable +class Tree extends Equatable { /// Initializes a new instance of [Tree] class from provided pointer to /// tree object in memory. /// @@ -102,6 +105,9 @@ class Tree { String toString() { return 'Tree{oid: $oid, length: $length}'; } + + @override + List get props => [oid]; } // coverage:ignore-start @@ -110,12 +116,13 @@ final _finalizer = Finalizer>( ); // coverage:ignore-end -class TreeEntry { +@immutable +class TreeEntry extends Equatable { /// Initializes a new instance of [TreeEntry] class from provided pointer to /// tree entry object in memory. /// /// Note: For internal use. - TreeEntry(this._treeEntryPointer); + const TreeEntry(this._treeEntryPointer); /// Initializes a new instance of [TreeEntry] class from provided pointer to /// tree entry object in memory. @@ -150,6 +157,9 @@ class TreeEntry { @override String toString() => 'TreeEntry{oid: $oid, name: $name, filemode: $filemode}'; + + @override + List get props => [oid, name, filemode]; } // coverage:ignore-start diff --git a/lib/src/worktree.dart b/lib/src/worktree.dart index bca7e3d..32939c6 100644 --- a/lib/src/worktree.dart +++ b/lib/src/worktree.dart @@ -1,9 +1,12 @@ import 'dart:ffi'; +import 'package:equatable/equatable.dart'; import 'package:libgit2dart/libgit2dart.dart'; import 'package:libgit2dart/src/bindings/libgit2_bindings.dart'; import 'package:libgit2dart/src/bindings/worktree.dart' as bindings; +import 'package:meta/meta.dart'; -class Worktree { +@immutable +class Worktree extends Equatable { /// Creates new worktree. /// /// If [ref] is provided, no new branch will be created but specified [ref] @@ -93,8 +96,12 @@ class Worktree { @override String toString() { - return 'Worktree{name: $name, path: $path}'; + return 'Worktree{name: $name, path: $path, isLocked: $isLocked, ' + 'isPrunable: $isPrunable, isValid: $isValid}'; } + + @override + List get props => [name, path, isLocked, isValid]; } // coverage:ignore-start diff --git a/pubspec.yaml b/pubspec.yaml index 2e585f9..0bbc59e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,6 +13,7 @@ environment: dependencies: args: ^2.3.0 cli_util: ^0.3.5 + equatable: ^2.0.3 ffi: ^1.1.2 meta: ^1.7.0 path: ^1.8.1 diff --git a/test/annotated_test.dart b/test/annotated_test.dart index 6ac8165..6a0545c 100644 --- a/test/annotated_test.dart +++ b/test/annotated_test.dart @@ -107,5 +107,12 @@ void main() { final annotated = AnnotatedCommit.lookup(repo: repo, oid: tip); expect(() => annotated.free(), returnsNormally); }); + + test('supports value comparison', () { + expect( + AnnotatedCommit.lookup(repo: repo, oid: tip), + equals(AnnotatedCommit.lookup(repo: repo, oid: tip)), + ); + }); }); } diff --git a/test/blame_test.dart b/test/blame_test.dart index 123f99f..bfe4e1e 100644 --- a/test/blame_test.dart +++ b/test/blame_test.dart @@ -211,5 +211,12 @@ void main() { final blame = Blame.file(repo: repo, path: 'feature_file'); expect(blame.toString(), contains('BlameHunk{')); }); + + test('supports value comparison', () { + expect( + Blame.file(repo: repo, path: 'feature_file'), + equals(Blame.file(repo: repo, path: 'feature_file')), + ); + }); }); } diff --git a/test/blob_test.dart b/test/blob_test.dart index c1d47a5..73e9b27 100644 --- a/test/blob_test.dart +++ b/test/blob_test.dart @@ -153,5 +153,12 @@ void main() { final blob = Blob.lookup(repo: repo, oid: repo[blobSHA]); expect(blob.toString(), contains('Blob{')); }); + + test('supports value comparison', () { + expect( + Blob.lookup(repo: repo, oid: repo[blobSHA]), + equals(Blob.lookup(repo: repo, oid: repo[blobSHA])), + ); + }); }); } diff --git a/test/branch_test.dart b/test/branch_test.dart index 7b60460..9728c6b 100644 --- a/test/branch_test.dart +++ b/test/branch_test.dart @@ -327,5 +327,12 @@ void main() { final branch = Branch.lookup(repo: repo, name: 'master'); expect(branch.toString(), contains('Branch{')); }); + + test('supports value comparison', () { + expect( + Branch.lookup(repo: repo, name: 'master'), + equals(Branch.lookup(repo: repo, name: 'master')), + ); + }); }); } diff --git a/test/commit_test.dart b/test/commit_test.dart index bb735ab..7e95471 100644 --- a/test/commit_test.dart +++ b/test/commit_test.dart @@ -396,5 +396,12 @@ Some description. final commit = Commit.lookup(repo: repo, oid: tip); expect(commit.toString(), contains('Commit{')); }); + + test('supports value comparison', () { + expect( + Commit.lookup(repo: repo, oid: tip), + equals(Commit.lookup(repo: repo, oid: tip)), + ); + }); }); } diff --git a/test/config_test.dart b/test/config_test.dart index b14a8b7..4f6047b 100644 --- a/test/config_test.dart +++ b/test/config_test.dart @@ -217,5 +217,9 @@ void main() { final entry = config.first; expect(entry.toString(), contains('ConfigEntry{')); }); + + test('supports value comparison', () { + expect(Config.open(filePath), equals(Config.open(filePath))); + }); }); } diff --git a/test/diff_test.dart b/test/diff_test.dart index 1135c5e..5c21bb6 100644 --- a/test/diff_test.dart +++ b/test/diff_test.dart @@ -580,5 +580,15 @@ index e69de29..c217c63 100644 expect(patch.delta.oldFile.toString(), contains('DiffFile{')); expect(stats.toString(), contains('DiffStats{')); }); + + test('supports value comparison', () { + expect(Diff.parse(patchText), equals(Diff.parse(patchText))); + + final diff = Diff.parse(patchText); + expect(diff.deltas[0], equals(diff.deltas[0])); + + final delta = diff.deltas[0]; + expect(delta.oldFile, equals(delta.oldFile)); + }); }); } diff --git a/test/index_test.dart b/test/index_test.dart index 9a848e4..9a496a1 100644 --- a/test/index_test.dart +++ b/test/index_test.dart @@ -509,5 +509,9 @@ void main() { expect(index.toString(), contains('Index{')); expect(index['file'].toString(), contains('IndexEntry{')); }); + + test('supports value comparison', () { + expect(repo.index, equals(repo.index)); + }); }); } diff --git a/test/note_test.dart b/test/note_test.dart index f2923ee..63d82ee 100644 --- a/test/note_test.dart +++ b/test/note_test.dart @@ -134,5 +134,13 @@ void main() { final note = Note.lookup(repo: repo, annotatedOid: repo['821ed6e']); expect(note.toString(), contains('Note{')); }); + + test('supports value comparison', () { + final oid = repo.head.target; + expect( + Note.lookup(repo: repo, annotatedOid: oid), + equals(Note.lookup(repo: repo, annotatedOid: oid)), + ); + }); }); } diff --git a/test/odb_test.dart b/test/odb_test.dart index 18ffcfd..de44bac 100644 --- a/test/odb_test.dart +++ b/test/odb_test.dart @@ -50,6 +50,7 @@ void main() { expect(object.type, GitObject.blob); expect(object.data, blobContent); expect(object.size, 13); + expect(object, equals(repo.odb.read(repo[blobSha]))); }); test('throws when trying to read object and error occurs', () { @@ -115,5 +116,9 @@ void main() { final object = repo.odb.read(repo[blobSha]); expect(object.toString(), contains('OdbObject{')); }); + + test('supports value comparison', () { + expect(repo.odb, equals(repo.odb)); + }); }); } diff --git a/test/patch_test.dart b/test/patch_test.dart index 055f89d..8e791d1 100644 --- a/test/patch_test.dart +++ b/test/patch_test.dart @@ -230,5 +230,25 @@ index e69de29..0000000 expect(patch.hunks[0].toString(), contains('DiffHunk{')); expect(patch.hunks[0].lines[0].toString(), contains('DiffLine{')); }); + + test('supports value comparison', () { + final patch = Patch.fromBuffers( + oldBuffer: oldBuffer, + newBuffer: newBuffer, + oldBufferPath: path, + newBufferPath: path, + ); + final anotherPatch = Patch.fromBuffers( + oldBuffer: oldBuffer, + newBuffer: newBuffer, + oldBufferPath: path, + newBufferPath: path, + ); + expect(patch, equals(anotherPatch)); + expect(patch.hunks[0], equals(patch.hunks[0])); + + final hunk = patch.hunks[0]; + expect(hunk.lines[0], equals(hunk.lines[0])); + }); }); } diff --git a/test/reference_test.dart b/test/reference_test.dart index 8c485da..7b74b87 100644 --- a/test/reference_test.dart +++ b/test/reference_test.dart @@ -155,7 +155,7 @@ void main() { final duplicate = ref.duplicate(); expect(repo.references.length, 6); - expect(duplicate.equals(ref), true); + expect(duplicate, equals(ref)); }); group('create direct', () { @@ -354,25 +354,35 @@ void main() { group('set target', () { test('sets direct reference with provided oid target', () { + Reference.setTarget( + repo: repo, + name: 'refs/heads/master', + target: repo[newCommit], + ); final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); - ref.setTarget(target: repo[newCommit]); expect(ref.target.sha, newCommit); }); test('sets symbolic target with provided reference name', () { + Reference.setTarget( + repo: repo, + name: 'HEAD', + target: 'refs/heads/feature', + ); final ref = Reference.lookup(repo: repo, name: 'HEAD'); - expect(ref.target.sha, lastCommit); - - ref.setTarget(target: 'refs/heads/feature'); expect(ref.target.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); }); test('sets target with log message', () { - final ref = Reference.lookup(repo: repo, name: 'HEAD'); - expect(ref.target.sha, lastCommit); - repo.setIdentity(name: 'name', email: 'email'); - ref.setTarget(target: 'refs/heads/feature', logMessage: 'log message'); + Reference.setTarget( + repo: repo, + name: 'HEAD', + target: 'refs/heads/feature', + logMessage: 'log message', + ); + + final ref = Reference.lookup(repo: repo, name: 'HEAD'); expect(ref.target.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4'); final logEntry = ref.log.first; expect(logEntry.message, 'log message'); @@ -381,18 +391,28 @@ void main() { }); test('throws on invalid target', () { - final ref = Reference.lookup(repo: repo, name: 'HEAD'); expect( - () => ref.setTarget(target: 'refs/heads/invalid~'), + () => Reference.setTarget( + repo: repo, + name: 'HEAD', + target: 'refs/heads/invalid~', + ), throwsA(isA()), ); expect( - () => ref.setTarget(target: Oid(nullptr)), + () => Reference.setTarget( + repo: repo, + name: 'HEAD', + target: Oid(nullptr), + ), throwsA(isA()), ); - expect(() => ref.setTarget(target: 0), throwsA(isA())); + expect( + () => Reference.setTarget(repo: repo, name: 'HEAD', target: 0), + throwsA(isA()), + ); }); }); @@ -458,17 +478,6 @@ void main() { }); }); - test('checks equality', () { - final ref1 = Reference.lookup(repo: repo, name: 'refs/heads/master'); - final ref2 = Reference.lookup(repo: repo, name: 'refs/heads/master'); - final ref3 = Reference.lookup(repo: repo, name: 'refs/heads/feature'); - - expect(ref1.equals(ref2), true); - expect(ref1.notEquals(ref2), false); - expect(ref1.equals(ref3), false); - expect(ref1.notEquals(ref3), true); - }); - test('peels to non-tag object when no type is provided', () { final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); final commit = Commit.lookup(repo: repo, oid: ref.target); @@ -530,5 +539,12 @@ void main() { final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); expect(ref.toString(), contains('Reference{')); }); + + test('supports value comparison', () { + expect( + Reference.lookup(repo: repo, name: 'HEAD'), + equals(Reference.lookup(repo: repo, name: 'refs/heads/master')), + ); + }); }); } diff --git a/test/reflog_test.dart b/test/reflog_test.dart index 9bbea43..a37f2ac 100644 --- a/test/reflog_test.dart +++ b/test/reflog_test.dart @@ -153,5 +153,10 @@ void main() { test('returns string representation of RefLogEntry object', () { expect(reflog[0].toString(), contains('RefLogEntry{')); }); + + test('supports value comparison', () { + final ref = Reference.lookup(repo: repo, name: 'refs/heads/master'); + expect(RefLog(repo.head), equals(RefLog(ref))); + }); }); } diff --git a/test/remote_test.dart b/test/remote_test.dart index 07e7c34..126f317 100644 --- a/test/remote_test.dart +++ b/test/remote_test.dart @@ -33,6 +33,7 @@ void main() { expect(remote.url, remoteUrl); expect(remote.pushUrl, ''); expect(remote.toString(), contains('Remote{')); + expect(remote, equals(Remote.lookup(repo: repo, name: 'origin'))); }); test('throws when provided name for lookup is not found', () { @@ -42,6 +43,13 @@ void main() { ); }); + test('throws when trying to create remote and name already exists', () { + expect( + () => Remote.create(repo: repo, name: 'origin', url: remoteUrl), + throwsA(isA()), + ); + }); + test('creates without fetchspec', () { final remote = Remote.create( repo: repo, @@ -188,6 +196,8 @@ void main() { refspec.rTransform('refs/remotes/origin/master'), 'refs/heads/master', ); + + expect(refspec, equals(remote.getRefspec(0))); }); test('throws when trying to transform refspec with invalid reference name', @@ -270,6 +280,7 @@ void main() { expect(refs.first.symRef, 'refs/heads/master'); expect((refs.first.oid).sha, '49322bb17d3acc9146f98c97d078513228bbf3c0'); expect(refs.first.toString(), contains('RemoteReference{')); + expect(refs.first, remote.ls().first); }); test( diff --git a/test/revparse_test.dart b/test/revparse_test.dart index 78821f5..398e23b 100644 --- a/test/revparse_test.dart +++ b/test/revparse_test.dart @@ -59,7 +59,7 @@ void main() { var headParse = RevParse.ext(repo: repo, spec: 'master'); expect(headParse.object.oid.sha, headSHA); - expect(headParse.reference?.equals(masterRef), true); + expect(headParse.reference, equals(masterRef)); expect(headParse.toString(), contains('RevParse{')); final featureRef = Reference.lookup( @@ -72,7 +72,7 @@ void main() { headParse.object.oid.sha, '5aecfa0fb97eadaac050ccb99f03c3fb65460ad4', ); - expect(headParse.reference?.equals(featureRef), true); + expect(headParse.reference, equals(featureRef)); }); test('.ext() returns only commit when no intermidiate reference found', () { diff --git a/test/signature_test.dart b/test/signature_test.dart index 303a607..c7e07c5 100644 --- a/test/signature_test.dart +++ b/test/signature_test.dart @@ -47,6 +47,7 @@ void main() { lessThan(5), ); expect(sig.offset, isA()); + expect(sig.sign, isNotEmpty); }); test('returns correct values', () { @@ -77,5 +78,12 @@ void main() { test('returns string representation of Signature object', () { expect(signature.toString(), contains('Signature{')); }); + + test('supports value comparison', () { + expect( + Signature.create(name: name, email: email, time: time), + equals(Signature.create(name: name, email: email, time: time)), + ); + }); }); } diff --git a/test/stash_test.dart b/test/stash_test.dart index 6f811bc..a3d089c 100644 --- a/test/stash_test.dart +++ b/test/stash_test.dart @@ -211,5 +211,12 @@ void main() { expect(repo.stashes[0].toString(), contains('Stash{')); }); + + test('supports value comparison', () { + File(filePath).writeAsStringSync('edit', mode: FileMode.append); + Stash.create(repo: repo, stasher: stasher, message: 'WIP'); + + expect(repo.stashes.first, equals(repo.stashes.first)); + }); }); } diff --git a/test/submodule_test.dart b/test/submodule_test.dart index 411ee36..972ac08 100644 --- a/test/submodule_test.dart +++ b/test/submodule_test.dart @@ -243,5 +243,12 @@ void main() { final submodule = Submodule.lookup(repo: repo, name: testSubmodule); expect(() => submodule.free(), returnsNormally); }); + + test('supports value comparison', () { + expect( + Submodule.lookup(repo: repo, name: testSubmodule), + equals(Submodule.lookup(repo: repo, name: testSubmodule)), + ); + }); }); } diff --git a/test/tag_test.dart b/test/tag_test.dart index 15a7b33..af9eb74 100644 --- a/test/tag_test.dart +++ b/test/tag_test.dart @@ -384,5 +384,12 @@ void main() { tag = Tag.lookup(repo: repo, oid: tagOid); expect(() => tag.free(), returnsNormally); }); + + test('supports value comparison', () { + expect( + Tag.lookup(repo: repo, oid: tagOid), + equals(Tag.lookup(repo: repo, oid: tagOid)), + ); + }); }); } diff --git a/test/tree_test.dart b/test/tree_test.dart index e405c26..7e27b06 100644 --- a/test/tree_test.dart +++ b/test/tree_test.dart @@ -101,5 +101,14 @@ void main() { 'looked up by path', () { expect(() => tree['dir/dir_file.txt'].free(), returnsNormally); }); + + test('supports value comparison', () { + expect( + Tree.lookup(repo: repo, oid: repo['a8ae3dd']), + equals(Tree.lookup(repo: repo, oid: repo['a8ae3dd'])), + ); + + expect(tree[0], equals(tree[0])); + }); }); } diff --git a/test/worktree_test.dart b/test/worktree_test.dart index 0ff6cd9..0da572a 100644 --- a/test/worktree_test.dart +++ b/test/worktree_test.dart @@ -166,5 +166,14 @@ void main() { ); expect(() => worktree.free(), returnsNormally); }); + + test('supports value comparison', () { + final worktree = Worktree.create( + repo: repo, + name: worktreeName, + path: worktreeDir.path, + ); + expect(worktree, equals(Worktree.lookup(repo: repo, name: worktreeName))); + }); }); }