feat(repository): add ability to check status of repo and single file

This commit is contained in:
Aleksey Kulikov 2021-09-07 19:08:28 +03:00
parent db21f2e890
commit 1f2d00b177
4 changed files with 171 additions and 7 deletions

View file

@ -0,0 +1,81 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:libgit2dart/libgit2dart.dart';
import 'libgit2_bindings.dart';
import '../error.dart';
import '../util.dart';
/// Gather file status information and populate the git_status_list.
///
/// Throws a [LibGit2Error] if error occured.
Pointer<git_status_list> listNew(Pointer<git_repository> repo) {
final out = calloc<Pointer<git_status_list>>();
final error = libgit2.git_status_list_new(out, repo, nullptr);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
return out.value;
}
}
/// Gets the count of status entries in this list.
///
/// If there are no changes in status (at least according the options given when
/// the status list was created), this can return 0.
int listEntryCount(Pointer<git_status_list> statuslist) {
return libgit2.git_status_list_entrycount(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<git_status_entry> getByIndex(
Pointer<git_status_list> statuslist,
int idx,
) {
final result = libgit2.git_status_byindex(statuslist, idx);
if (result == nullptr) {
throw RangeError('Out of bounds');
} else {
return result;
}
}
/// Get file status for a single file.
///
/// This tries to get status for the filename that you give. If no files match that name
/// (in either the HEAD, index, or working directory), this returns GIT_ENOTFOUND.
///
/// If the name matches multiple files (for example, if the path names a directory or if
/// running on a case- insensitive filesystem and yet the HEAD has two entries that both
/// match the path), then this returns GIT_EAMBIGUOUS because it cannot give correct results.
///
/// This does not do any sort of rename detection. Renames require a set of targets and because
/// of the path filtering, there is not enough information to check renames correctly. To check
/// file status with rename detection, there is no choice but to do a full listNew
/// and scan through looking for the path that you are interested in.
///
/// Throws a [LibGit2Error] if error occured.
int file(Pointer<git_repository> repo, String path) {
final out = calloc<Uint32>();
final pathC = path.toNativeUtf8().cast<Int8>();
final error = libgit2.git_status_file(out, repo, pathC);
calloc.free(pathC);
if (error < 0) {
throw LibGit2Error(libgit2.git_error_last());
} else {
final result = out.value;
calloc.free(out);
return result;
}
}
/// Free an existing status list.
void listFree(Pointer<git_status_list> statuslist) =>
libgit2.git_status_list_free(statuslist);

View file

@ -23,15 +23,10 @@ class GitFilemode {
final int _value;
static const unreadable = GitFilemode._(0);
static const tree = GitFilemode._(16384);
static const blob = GitFilemode._(33188);
static const blobExecutable = GitFilemode._(33261);
static const link = GitFilemode._(40960);
static const commit = GitFilemode._(57344);
int get value => _value;
@ -117,10 +112,38 @@ class GitBranch {
final int _value;
static const local = GitBranch._(1);
static const remote = GitBranch._(2);
static const all = GitBranch._(3);
int get value => _value;
}
/// Status flags for a single file.
///
/// A combination of these values will be returned to indicate the status of
/// a file. Status compares the working directory, the index, and the
/// current HEAD of the repository. The `GitStatus.index` set of flags
/// represents the status of file in the index relative to the HEAD, and the
/// `GitStatus.wt` set of flags represent the status of the file in the
/// working directory relative to the index.
class GitStatus {
const GitStatus._(this._value);
final int _value;
static const current = GitStatus._(0);
static const indexNew = GitStatus._(1);
static const indexModified = GitStatus._(2);
static const indexDeleted = GitStatus._(4);
static const indexRenamed = GitStatus._(8);
static const indexTypeChange = GitStatus._(16);
static const wtNew = GitStatus._(128);
static const wtModified = GitStatus._(256);
static const wtDeleted = GitStatus._(512);
static const wtTypeChange = GitStatus._(1024);
static const wtRenamed = GitStatus._(2048);
static const wtUnreadable = GitStatus._(4096);
static const ignored = GitStatus._(16384);
static const conflicted = GitStatus._(32768);
int get value => _value;
}

View file

@ -1,8 +1,10 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'bindings/libgit2_bindings.dart';
import 'bindings/repository.dart' as bindings;
import 'bindings/merge.dart' as merge_bindings;
import 'bindings/object.dart' as object_bindings;
import 'bindings/status.dart' as status_bindings;
import 'branch.dart';
import 'commit.dart';
import 'config.dart';
@ -459,4 +461,37 @@ class Repository {
/// Returns a [Branches] object.
Branches get branches => Branches(this);
/// Checks status of the repository and returns map of file paths and their statuses.
///
/// Returns empty map if there are no changes in statuses.
Map<String, int> get status {
var result = <String, int>{};
var list = status_bindings.listNew(_repoPointer);
var count = status_bindings.listEntryCount(list);
for (var i = 0; i < count; i++) {
final entry = status_bindings.getByIndex(list, i);
if (entry.ref.head_to_index != nullptr) {
final path = entry.ref.head_to_index.ref.old_file.path
.cast<Utf8>()
.toDartString();
result[path] = entry.ref.status;
}
}
status_bindings.listFree(list);
return result;
}
/// Returns file status for a single file.
///
/// This does not do any sort of rename detection. Renames require a set of targets and because
/// of the path filtering, there is not enough information to check renames correctly. To check
/// file status with rename detection, there is no choice but to do a full [status] and scan
/// through looking for the path that you are interested in.
///
/// Throws a [LibGit2Error] if error occured.
int statusFile(String path) => status_bindings.file(_repoPointer, path);
}