mirror of
https://github.com/SkinnyMind/libgit2dart.git
synced 2025-05-05 04:39:07 -04:00
feat(reflog): add more bindings and API methods
This commit is contained in:
parent
ff2dd8b408
commit
bdd12a10ca
5 changed files with 305 additions and 12 deletions
|
@ -2,6 +2,7 @@ import 'dart:ffi';
|
||||||
|
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
import 'package:libgit2dart/src/bindings/libgit2_bindings.dart';
|
import 'package:libgit2dart/src/bindings/libgit2_bindings.dart';
|
||||||
|
import 'package:libgit2dart/src/error.dart';
|
||||||
import 'package:libgit2dart/src/util.dart';
|
import 'package:libgit2dart/src/util.dart';
|
||||||
|
|
||||||
/// Read the reflog for the given reference.
|
/// Read the reflog for the given reference.
|
||||||
|
@ -23,6 +24,90 @@ Pointer<git_reflog> read({
|
||||||
return out.value;
|
return out.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write an existing in-memory reflog object back to disk using an atomic file
|
||||||
|
/// lock.
|
||||||
|
void write(Pointer<git_reflog> reflog) {
|
||||||
|
final error = libgit2.git_reflog_write(reflog);
|
||||||
|
|
||||||
|
if (error < 0) {
|
||||||
|
throw LibGit2Error(libgit2.git_error_last());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete the reflog for the given reference.
|
||||||
|
void delete({
|
||||||
|
required Pointer<git_repository> repoPointer,
|
||||||
|
required String name,
|
||||||
|
}) {
|
||||||
|
final nameC = name.toNativeUtf8().cast<Int8>();
|
||||||
|
libgit2.git_reflog_delete(repoPointer, nameC);
|
||||||
|
calloc.free(nameC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rename a reflog.
|
||||||
|
///
|
||||||
|
/// The reflog to be renamed is expected to already exist.
|
||||||
|
///
|
||||||
|
/// The new name will be checked for validity.
|
||||||
|
///
|
||||||
|
/// Throws a [LibGit2Error] if error occured.
|
||||||
|
void rename({
|
||||||
|
required Pointer<git_repository> repoPointer,
|
||||||
|
required String oldName,
|
||||||
|
required String newName,
|
||||||
|
}) {
|
||||||
|
final oldNameC = oldName.toNativeUtf8().cast<Int8>();
|
||||||
|
final newNameC = newName.toNativeUtf8().cast<Int8>();
|
||||||
|
final error = libgit2.git_reflog_rename(repoPointer, oldNameC, newNameC);
|
||||||
|
|
||||||
|
calloc.free(oldNameC);
|
||||||
|
calloc.free(newNameC);
|
||||||
|
|
||||||
|
if (error < 0) {
|
||||||
|
throw LibGit2Error(libgit2.git_error_last());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a new entry to the in-memory reflog.
|
||||||
|
///
|
||||||
|
/// Throws a [LibGit2Error] if error occured.
|
||||||
|
void add({
|
||||||
|
required Pointer<git_reflog> reflogPointer,
|
||||||
|
required Pointer<git_oid> oidPointer,
|
||||||
|
required Pointer<git_signature> committerPointer,
|
||||||
|
required String message,
|
||||||
|
}) {
|
||||||
|
final messageC =
|
||||||
|
message.isEmpty ? nullptr : message.toNativeUtf8().cast<Int8>();
|
||||||
|
|
||||||
|
final error = libgit2.git_reflog_append(
|
||||||
|
reflogPointer,
|
||||||
|
oidPointer,
|
||||||
|
committerPointer,
|
||||||
|
messageC,
|
||||||
|
);
|
||||||
|
|
||||||
|
calloc.free(messageC);
|
||||||
|
|
||||||
|
if (error < 0) {
|
||||||
|
throw LibGit2Error(libgit2.git_error_last());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove an entry from the reflog by its index.
|
||||||
|
///
|
||||||
|
/// Throws a [LibGit2Error] if error occured.
|
||||||
|
void remove({
|
||||||
|
required Pointer<git_reflog> reflogPointer,
|
||||||
|
required int index,
|
||||||
|
}) {
|
||||||
|
final error = libgit2.git_reflog_drop(reflogPointer, index, 1);
|
||||||
|
|
||||||
|
if (error < 0) {
|
||||||
|
throw LibGit2Error(libgit2.git_error_last());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the number of log entries in a reflog.
|
/// Get the number of log entries in a reflog.
|
||||||
int entryCount(Pointer<git_reflog> reflog) =>
|
int entryCount(Pointer<git_reflog> reflog) =>
|
||||||
libgit2.git_reflog_entrycount(reflog);
|
libgit2.git_reflog_entrycount(reflog);
|
||||||
|
@ -40,7 +125,8 @@ Pointer<git_reflog_entry> getByIndex({
|
||||||
|
|
||||||
/// Get the log message.
|
/// Get the log message.
|
||||||
String entryMessage(Pointer<git_reflog_entry> entry) {
|
String entryMessage(Pointer<git_reflog_entry> entry) {
|
||||||
return libgit2.git_reflog_entry_message(entry).cast<Utf8>().toDartString();
|
final result = libgit2.git_reflog_entry_message(entry);
|
||||||
|
return result == nullptr ? '' : result.cast<Utf8>().toDartString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the committer of this entry.
|
/// Get the committer of this entry.
|
||||||
|
@ -48,5 +134,15 @@ Pointer<git_signature> entryCommiter(Pointer<git_reflog_entry> entry) {
|
||||||
return libgit2.git_reflog_entry_committer(entry);
|
return libgit2.git_reflog_entry_committer(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the new oid.
|
||||||
|
Pointer<git_oid> entryOidNew(Pointer<git_reflog_entry> entry) {
|
||||||
|
return libgit2.git_reflog_entry_id_new(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the old oid.
|
||||||
|
Pointer<git_oid> entryOidOld(Pointer<git_reflog_entry> entry) {
|
||||||
|
return libgit2.git_reflog_entry_id_old(entry);
|
||||||
|
}
|
||||||
|
|
||||||
/// Free the reflog.
|
/// Free the reflog.
|
||||||
void free(Pointer<git_reflog> reflog) => libgit2.git_reflog_free(reflog);
|
void free(Pointer<git_reflog> reflog) => libgit2.git_reflog_free(reflog);
|
||||||
|
|
|
@ -139,6 +139,19 @@ class Reference {
|
||||||
refdb_bindings.free(refdb);
|
refdb_bindings.free(refdb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ensures there is a reflog for a particular reference.
|
||||||
|
///
|
||||||
|
/// Makes sure that successive updates to the reference will append to its
|
||||||
|
/// log.
|
||||||
|
///
|
||||||
|
/// Throws a [LibGit2Error] if error occured.
|
||||||
|
static void ensureLog({
|
||||||
|
required Repository repo,
|
||||||
|
required String refName,
|
||||||
|
}) {
|
||||||
|
bindings.ensureLog(repoPointer: repo.pointer, refName: refName);
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a copy of an existing reference.
|
/// Creates a copy of an existing reference.
|
||||||
///
|
///
|
||||||
/// **IMPORTANT**: Should be freed to release allocated memory.
|
/// **IMPORTANT**: Should be freed to release allocated memory.
|
||||||
|
@ -247,17 +260,6 @@ class Reference {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensures there is a reflog for a particular reference.
|
|
||||||
///
|
|
||||||
/// Makes sure that successive updates to the reference will append to its
|
|
||||||
/// log.
|
|
||||||
///
|
|
||||||
/// Throws a [LibGit2Error] if error occured.
|
|
||||||
void ensureLog() {
|
|
||||||
final owner = bindings.owner(_refPointer);
|
|
||||||
bindings.ensureLog(repoPointer: owner, refName: name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// [RefLog] object.
|
/// [RefLog] object.
|
||||||
///
|
///
|
||||||
/// **IMPORTANT**: Should be freed to release allocated memory.
|
/// **IMPORTANT**: Should be freed to release allocated memory.
|
||||||
|
|
|
@ -18,6 +18,30 @@ class RefLog with IterableMixin<RefLogEntry> {
|
||||||
/// Pointer to memory address for allocated reflog object.
|
/// Pointer to memory address for allocated reflog object.
|
||||||
late final Pointer<git_reflog> _reflogPointer;
|
late final Pointer<git_reflog> _reflogPointer;
|
||||||
|
|
||||||
|
/// Deletes the reflog for the given reference.
|
||||||
|
static void delete(Reference ref) {
|
||||||
|
bindings.delete(repoPointer: ref.owner.pointer, name: ref.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renames a reflog.
|
||||||
|
///
|
||||||
|
/// The reflog to be renamed is expected to already exist.
|
||||||
|
///
|
||||||
|
/// The new name will be checked for validity.
|
||||||
|
///
|
||||||
|
/// Throws a [LibGit2Error] if error occured.
|
||||||
|
static void rename({
|
||||||
|
required Repository repo,
|
||||||
|
required String oldName,
|
||||||
|
required String newName,
|
||||||
|
}) {
|
||||||
|
bindings.rename(
|
||||||
|
repoPointer: repo.pointer,
|
||||||
|
oldName: oldName,
|
||||||
|
newName: newName,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Lookups an entry by its index.
|
/// Lookups an entry by its index.
|
||||||
///
|
///
|
||||||
/// Requesting the reflog entry with an index of 0 will return the most
|
/// Requesting the reflog entry with an index of 0 will return the most
|
||||||
|
@ -31,6 +55,39 @@ class RefLog with IterableMixin<RefLogEntry> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a new entry to the in-memory reflog.
|
||||||
|
///
|
||||||
|
/// [oid] is the OID the reference is now pointing to.
|
||||||
|
///
|
||||||
|
/// [committer] is the signature of the committer.
|
||||||
|
///
|
||||||
|
/// [message] is optional reflog message.
|
||||||
|
///
|
||||||
|
/// Throws a [LibGit2Error] if error occured.
|
||||||
|
void add({
|
||||||
|
required Oid oid,
|
||||||
|
required Signature committer,
|
||||||
|
String message = '',
|
||||||
|
}) {
|
||||||
|
bindings.add(
|
||||||
|
reflogPointer: _reflogPointer,
|
||||||
|
oidPointer: oid.pointer,
|
||||||
|
committerPointer: committer.pointer,
|
||||||
|
message: message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes an entry from the reflog by its [index].
|
||||||
|
///
|
||||||
|
/// Throws a [LibGit2Error] if error occured.
|
||||||
|
void remove(int index) {
|
||||||
|
bindings.remove(reflogPointer: _reflogPointer, index: index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes an existing in-memory reflog object back to disk using an atomic
|
||||||
|
/// file lock.
|
||||||
|
void write() => bindings.write(_reflogPointer);
|
||||||
|
|
||||||
/// Releases memory allocated for reflog object.
|
/// Releases memory allocated for reflog object.
|
||||||
void free() => bindings.free(_reflogPointer);
|
void free() => bindings.free(_reflogPointer);
|
||||||
|
|
||||||
|
@ -47,11 +104,19 @@ class RefLogEntry {
|
||||||
final Pointer<git_reflog_entry> _entryPointer;
|
final Pointer<git_reflog_entry> _entryPointer;
|
||||||
|
|
||||||
/// Log message.
|
/// Log message.
|
||||||
|
///
|
||||||
|
/// Returns empty string if there is no message.
|
||||||
String get message => bindings.entryMessage(_entryPointer);
|
String get message => bindings.entryMessage(_entryPointer);
|
||||||
|
|
||||||
/// Committer of this entry.
|
/// Committer of this entry.
|
||||||
Signature get committer => Signature(bindings.entryCommiter(_entryPointer));
|
Signature get committer => Signature(bindings.entryCommiter(_entryPointer));
|
||||||
|
|
||||||
|
/// New oid of entry at this time.
|
||||||
|
Oid get newOid => Oid(bindings.entryOidNew(_entryPointer));
|
||||||
|
|
||||||
|
/// Old oid of entry.
|
||||||
|
Oid get oldOid => Oid(bindings.entryOidOld(_entryPointer));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'RefLogEntry{message: $message, committer: $committer}';
|
String toString() => 'RefLogEntry{message: $message, committer: $committer}';
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,31 @@ void main() {
|
||||||
ref.free();
|
ref.free();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('ensures updates to the reference will append to its log', () {
|
||||||
|
Reference.ensureLog(repo: repo, refName: 'refs/tags/tag');
|
||||||
|
|
||||||
|
final ref = repo.createReference(
|
||||||
|
name: 'refs/tags/tag',
|
||||||
|
target: repo[lastCommit],
|
||||||
|
);
|
||||||
|
final reflog = ref.log;
|
||||||
|
|
||||||
|
expect(reflog.length, 1);
|
||||||
|
|
||||||
|
reflog.free();
|
||||||
|
ref.free();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws when trying to ensure there is a reflog and error occurs', () {
|
||||||
|
expect(
|
||||||
|
() => Reference.ensureLog(
|
||||||
|
repo: Repository(nullptr),
|
||||||
|
refName: 'refs/tags/tag',
|
||||||
|
),
|
||||||
|
throwsA(isA<LibGit2Error>()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('duplicates existing reference', () {
|
test('duplicates existing reference', () {
|
||||||
expect(repo.references.length, 6);
|
expect(repo.references.length, 6);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:ffi';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:libgit2dart/libgit2dart.dart';
|
import 'package:libgit2dart/libgit2dart.dart';
|
||||||
|
@ -44,6 +45,110 @@ void main() {
|
||||||
expect(reflog[0].committer.time, 1630568461);
|
expect(reflog[0].committer.time, 1630568461);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('returns new and old oids of entry', () {
|
||||||
|
expect(reflog[0].newOid.sha, '821ed6e80627b8769d170a293862f9fc60825226');
|
||||||
|
expect(reflog.last.oldOid.sha, '0' * 40);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('deletes the reflog of provided reference', () {
|
||||||
|
expect(head.hasLog, true);
|
||||||
|
RefLog.delete(head);
|
||||||
|
expect(head.hasLog, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('renames existing reflog', () {
|
||||||
|
expect(
|
||||||
|
File('${repo.workdir}.git/logs/refs/heads/master').existsSync(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
File('${repo.workdir}.git/logs/refs/heads/renamed').existsSync(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
RefLog.rename(
|
||||||
|
repo: repo,
|
||||||
|
oldName: 'refs/heads/master',
|
||||||
|
newName: 'refs/heads/renamed',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
File('${repo.workdir}.git/logs/refs/heads/master').existsSync(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
File('${repo.workdir}.git/logs/refs/heads/renamed').existsSync(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws when trying to rename reflog and provided new name is invalid',
|
||||||
|
() {
|
||||||
|
expect(
|
||||||
|
() => RefLog.rename(
|
||||||
|
repo: repo,
|
||||||
|
oldName: 'refs/heads/master',
|
||||||
|
newName: '',
|
||||||
|
),
|
||||||
|
throwsA(isA<LibGit2Error>()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('adds a new entry to the in-memory reflog', () {
|
||||||
|
final committer = Signature.create(
|
||||||
|
name: 'Commiter',
|
||||||
|
email: 'commiter@email.com',
|
||||||
|
time: 124,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(reflog.length, 4);
|
||||||
|
reflog.add(oid: head.target, committer: committer);
|
||||||
|
expect(reflog.length, 5);
|
||||||
|
|
||||||
|
reflog.add(oid: head.target, committer: committer, message: 'new entry');
|
||||||
|
expect(reflog.length, 6);
|
||||||
|
expect(reflog[0].message, 'new entry');
|
||||||
|
|
||||||
|
committer.free();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws when trying to add new entry', () {
|
||||||
|
expect(
|
||||||
|
() => reflog.add(oid: head.target, committer: Signature(nullptr)),
|
||||||
|
throwsA(isA<LibGit2Error>()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('removes entry from reflog with provided index', () {
|
||||||
|
expect(reflog.length, 4);
|
||||||
|
expect(reflog[0].message, 'commit: add subdirectory file');
|
||||||
|
|
||||||
|
reflog.remove(0);
|
||||||
|
expect(reflog.length, 3);
|
||||||
|
expect(
|
||||||
|
reflog[0].message,
|
||||||
|
"merge feature: Merge made by the 'recursive' strategy.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws when trying to remove entry from reflog at invalid index', () {
|
||||||
|
expect(() => reflog.remove(-1), throwsA(isA<LibGit2Error>()));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('writes in-memory reflog to disk', () {
|
||||||
|
expect(reflog.length, 4);
|
||||||
|
reflog.remove(0);
|
||||||
|
|
||||||
|
// making sure change is only in memory
|
||||||
|
final oldReflog = RefLog(head);
|
||||||
|
expect(oldReflog.length, 4);
|
||||||
|
|
||||||
|
reflog.write();
|
||||||
|
|
||||||
|
final newReflog = RefLog(head);
|
||||||
|
expect(newReflog.length, 3);
|
||||||
|
});
|
||||||
|
|
||||||
test('returns string representation of RefLogEntry object', () {
|
test('returns string representation of RefLogEntry object', () {
|
||||||
expect(reflog[0].toString(), contains('RefLogEntry{'));
|
expect(reflog[0].toString(), contains('RefLogEntry{'));
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue