mirror of
https://github.com/SkinnyMind/libgit2dart.git
synced 2025-05-05 04:39:07 -04:00
feat(repository): add ability to check status of repo and single file
This commit is contained in:
parent
db21f2e890
commit
1f2d00b177
4 changed files with 171 additions and 7 deletions
81
lib/src/bindings/status.dart
Normal file
81
lib/src/bindings/status.dart
Normal 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);
|
|
@ -23,15 +23,10 @@ class GitFilemode {
|
||||||
final int _value;
|
final int _value;
|
||||||
|
|
||||||
static const unreadable = GitFilemode._(0);
|
static const unreadable = GitFilemode._(0);
|
||||||
|
|
||||||
static const tree = GitFilemode._(16384);
|
static const tree = GitFilemode._(16384);
|
||||||
|
|
||||||
static const blob = GitFilemode._(33188);
|
static const blob = GitFilemode._(33188);
|
||||||
|
|
||||||
static const blobExecutable = GitFilemode._(33261);
|
static const blobExecutable = GitFilemode._(33261);
|
||||||
|
|
||||||
static const link = GitFilemode._(40960);
|
static const link = GitFilemode._(40960);
|
||||||
|
|
||||||
static const commit = GitFilemode._(57344);
|
static const commit = GitFilemode._(57344);
|
||||||
|
|
||||||
int get value => _value;
|
int get value => _value;
|
||||||
|
@ -117,10 +112,38 @@ class GitBranch {
|
||||||
final int _value;
|
final int _value;
|
||||||
|
|
||||||
static const local = GitBranch._(1);
|
static const local = GitBranch._(1);
|
||||||
|
|
||||||
static const remote = GitBranch._(2);
|
static const remote = GitBranch._(2);
|
||||||
|
|
||||||
static const all = GitBranch._(3);
|
static const all = GitBranch._(3);
|
||||||
|
|
||||||
int get value => _value;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
|
import 'package:ffi/ffi.dart';
|
||||||
import 'bindings/libgit2_bindings.dart';
|
import 'bindings/libgit2_bindings.dart';
|
||||||
import 'bindings/repository.dart' as bindings;
|
import 'bindings/repository.dart' as bindings;
|
||||||
import 'bindings/merge.dart' as merge_bindings;
|
import 'bindings/merge.dart' as merge_bindings;
|
||||||
import 'bindings/object.dart' as object_bindings;
|
import 'bindings/object.dart' as object_bindings;
|
||||||
|
import 'bindings/status.dart' as status_bindings;
|
||||||
import 'branch.dart';
|
import 'branch.dart';
|
||||||
import 'commit.dart';
|
import 'commit.dart';
|
||||||
import 'config.dart';
|
import 'config.dart';
|
||||||
|
@ -459,4 +461,37 @@ class Repository {
|
||||||
|
|
||||||
/// Returns a [Branches] object.
|
/// Returns a [Branches] object.
|
||||||
Branches get branches => Branches(this);
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -337,6 +337,31 @@ void main() {
|
||||||
newTagTarget.free();
|
newTagTarget.free();
|
||||||
signature.free();
|
signature.free();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('returns status of a repository', () {
|
||||||
|
File('${tmpDir}new_file.txt').createSync();
|
||||||
|
final index = repo.index;
|
||||||
|
index.remove('file');
|
||||||
|
index.add('new_file.txt');
|
||||||
|
expect(repo.status, {'file': 132, 'new_file.txt': 1});
|
||||||
|
|
||||||
|
index.free();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns status of a single file for provided path', () {
|
||||||
|
final index = repo.index;
|
||||||
|
index.remove('file');
|
||||||
|
expect(repo.statusFile('file'), 132);
|
||||||
|
|
||||||
|
index.free();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws when checking status of a single file for invalid path', () {
|
||||||
|
expect(
|
||||||
|
() => repo.statusFile('not-there'),
|
||||||
|
throwsA(isA<LibGit2Error>()),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue