mirror of
https://github.com/GitJournal/GitJournal.git
synced 2025-08-05 22:57:41 +08:00
Avoid showing the "Reading Git History .." screen
This way startup feels much faster.
This commit is contained in:
@ -4,8 +4,6 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:dart_git/plumbing/git_hash.dart';
|
||||
@ -123,7 +121,7 @@ class File {
|
||||
static File fromProtoBuf(pb.File pbFile) {
|
||||
return File(
|
||||
repoPath: pbFile.repoPath,
|
||||
oid: GitHash.fromBytes(Uint8List.fromList(pbFile.hash)),
|
||||
oid: GitHash.fromBytes(pbFile.hash),
|
||||
filePath: pbFile.filePath,
|
||||
created: pbFile.created.toDateTime(),
|
||||
modified: pbFile.modified.toDateTime(),
|
||||
|
@ -4,14 +4,13 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:dart_git/blob_ctime_builder.dart';
|
||||
import 'package:dart_git/dart_git.dart';
|
||||
import 'package:dart_git/file_mtime_builder.dart';
|
||||
import 'package:dart_git/utils/date_time.dart';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:tuple/tuple.dart';
|
||||
import 'package:universal_io/io.dart' as io;
|
||||
|
||||
import 'package:gitjournal/core/file/file.dart';
|
||||
@ -21,6 +20,7 @@ import 'package:gitjournal/logger/logger.dart';
|
||||
|
||||
class FileStorageCache {
|
||||
final String cacheFolderPath;
|
||||
var lastProcessedHead = GitHash.zero();
|
||||
|
||||
FileStorageCache(this.cacheFolderPath) {
|
||||
assert(cacheFolderPath.startsWith(p.separator));
|
||||
@ -42,18 +42,26 @@ class FileStorageCache {
|
||||
}
|
||||
|
||||
Future<FileStorage> load(GitRepository gitRepo) async {
|
||||
var blobVisitor = await _buildCTimeBuilder();
|
||||
var mTimeBuilder = await _buildMTimeBuilder();
|
||||
var blobVisitorTuple = await _buildCTimeBuilder();
|
||||
var mTimeBuilderTuple = await _buildMTimeBuilder();
|
||||
|
||||
lastProcessedHead = blobVisitorTuple.item2;
|
||||
if (mTimeBuilderTuple.item2 != lastProcessedHead) {
|
||||
lastProcessedHead = GitHash.zero();
|
||||
}
|
||||
|
||||
return FileStorage(
|
||||
gitRepo: gitRepo,
|
||||
blobCTimeBuilder: blobVisitor,
|
||||
fileMTimeBuilder: mTimeBuilder,
|
||||
blobCTimeBuilder: blobVisitorTuple.item1,
|
||||
fileMTimeBuilder: mTimeBuilderTuple.item1,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Result<void>> save(FileStorage fileStorage) async {
|
||||
return catchAll(() async {
|
||||
var headR = await fileStorage.gitRepo.headHash();
|
||||
lastProcessedHead = headR.isFailure ? GitHash.zero() : headR.getOrThrow();
|
||||
|
||||
await _saveCTime(fileStorage.blobCTimeBuilder);
|
||||
await _saveMTime(fileStorage.fileMTimeBuilder);
|
||||
return Result(null);
|
||||
@ -63,12 +71,12 @@ class FileStorageCache {
|
||||
String get _cTimeFilePath => p.join(cacheFolderPath, 'blob_ctime_v1');
|
||||
String get _mTimeFilePath => p.join(cacheFolderPath, 'file_mtime_v1');
|
||||
|
||||
Future<BlobCTimeBuilder> _buildCTimeBuilder() async {
|
||||
Future<Tuple2<BlobCTimeBuilder, GitHash>> _buildCTimeBuilder() async {
|
||||
var file = io.File(_cTimeFilePath);
|
||||
|
||||
var stat = file.statSync();
|
||||
if (stat.type == io.FileSystemEntityType.notFound) {
|
||||
return BlobCTimeBuilder();
|
||||
return Tuple2(BlobCTimeBuilder(), GitHash.zero());
|
||||
}
|
||||
|
||||
var size = (stat.size / 1024).toStringAsFixed(2);
|
||||
@ -77,30 +85,29 @@ class FileStorageCache {
|
||||
var buffer = await file.readAsBytes();
|
||||
var data = pb.BlobCTimeBuilderData.fromBuffer(buffer);
|
||||
|
||||
var commitHashes = data.commitHashes
|
||||
.map((bytes) => GitHash.fromBytes(Uint8List.fromList(bytes)))
|
||||
.toSet();
|
||||
var commitHashes =
|
||||
data.commitHashes.map((bytes) => GitHash.fromBytes(bytes)).toSet();
|
||||
|
||||
var treeHashes = data.treeHashes
|
||||
.map((bytes) => GitHash.fromBytes(Uint8List.fromList(bytes)))
|
||||
.toSet();
|
||||
var treeHashes =
|
||||
data.treeHashes.map((bytes) => GitHash.fromBytes(bytes)).toSet();
|
||||
|
||||
var map = data.map
|
||||
.map((hashStr, pbDt) => MapEntry(GitHash(hashStr), _fromProto(pbDt)));
|
||||
|
||||
return BlobCTimeBuilder(
|
||||
var builder = BlobCTimeBuilder(
|
||||
processedCommits: commitHashes,
|
||||
processedTrees: treeHashes,
|
||||
map: map,
|
||||
);
|
||||
return Tuple2(builder, GitHash.fromBytes(data.headHash));
|
||||
}
|
||||
|
||||
Future<FileMTimeBuilder> _buildMTimeBuilder() async {
|
||||
Future<Tuple2<FileMTimeBuilder, GitHash>> _buildMTimeBuilder() async {
|
||||
var file = io.File(_mTimeFilePath);
|
||||
|
||||
var stat = file.statSync();
|
||||
if (stat.type == io.FileSystemEntityType.notFound) {
|
||||
return FileMTimeBuilder();
|
||||
return Tuple2(FileMTimeBuilder(), GitHash.zero());
|
||||
}
|
||||
|
||||
var size = (stat.size / 1024).toStringAsFixed(2);
|
||||
@ -109,19 +116,19 @@ class FileStorageCache {
|
||||
var buffer = await file.readAsBytes();
|
||||
var data = pb.FileMTimeBuilderData.fromBuffer(buffer);
|
||||
|
||||
var commitHashes = data.commitHashes
|
||||
.map((bytes) => GitHash.fromBytes(Uint8List.fromList(bytes)))
|
||||
.toSet();
|
||||
var commitHashes =
|
||||
data.commitHashes.map((bytes) => GitHash.fromBytes(bytes)).toSet();
|
||||
|
||||
var map = data.map.map((filePath, pbInfo) {
|
||||
var hash = GitHash.fromBytes(Uint8List.fromList(pbInfo.hash));
|
||||
var hash = GitHash.fromBytes(pbInfo.hash);
|
||||
var dt = _fromProto(pbInfo.dt);
|
||||
var info = FileMTimeInfo(pbInfo.filePath, hash, dt);
|
||||
|
||||
return MapEntry(filePath, info);
|
||||
});
|
||||
|
||||
return FileMTimeBuilder(processedCommits: commitHashes, map: map);
|
||||
var builder = FileMTimeBuilder(processedCommits: commitHashes, map: map);
|
||||
return Tuple2(builder, GitHash.fromBytes(data.headHash));
|
||||
}
|
||||
|
||||
Future<void> _saveCTime(BlobCTimeBuilder builder) async {
|
||||
@ -136,6 +143,7 @@ class FileStorageCache {
|
||||
});
|
||||
|
||||
var data = pb.BlobCTimeBuilderData(
|
||||
headHash: lastProcessedHead.bytes,
|
||||
commitHashes: commitHashes,
|
||||
treeHashes: treeHashes,
|
||||
map: map,
|
||||
@ -162,7 +170,11 @@ class FileStorageCache {
|
||||
return MapEntry(filePath, info);
|
||||
});
|
||||
|
||||
var data = pb.FileMTimeBuilderData(commitHashes: commitHashes, map: map);
|
||||
var data = pb.FileMTimeBuilderData(
|
||||
headHash: lastProcessedHead.bytes,
|
||||
commitHashes: commitHashes,
|
||||
map: map,
|
||||
);
|
||||
|
||||
var file = io.File(_mTimeFilePath);
|
||||
var _ = await file.writeAsBytes(data.writeToBuffer());
|
||||
|
@ -48,6 +48,13 @@ class BlobCTimeBuilderData extends $pb.GeneratedMessage {
|
||||
valueFieldType: $pb.PbFieldType.OM,
|
||||
valueCreator: TzDateTime.create,
|
||||
packageName: const $pb.PackageName('gitjournal'))
|
||||
..a<$core.List<$core.int>>(
|
||||
4,
|
||||
const $core.bool.fromEnvironment('protobuf.omit_field_names')
|
||||
? ''
|
||||
: 'headHash',
|
||||
$pb.PbFieldType.OY,
|
||||
protoName: 'headHash')
|
||||
..hasRequiredFields = false;
|
||||
|
||||
BlobCTimeBuilderData._() : super();
|
||||
@ -55,6 +62,7 @@ class BlobCTimeBuilderData extends $pb.GeneratedMessage {
|
||||
$core.Iterable<$core.List<$core.int>>? commitHashes,
|
||||
$core.Iterable<$core.List<$core.int>>? treeHashes,
|
||||
$core.Map<$core.String, TzDateTime>? map,
|
||||
$core.List<$core.int>? headHash,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (commitHashes != null) {
|
||||
@ -66,6 +74,9 @@ class BlobCTimeBuilderData extends $pb.GeneratedMessage {
|
||||
if (map != null) {
|
||||
_result.map.addAll(map);
|
||||
}
|
||||
if (headHash != null) {
|
||||
_result.headHash = headHash;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory BlobCTimeBuilderData.fromBuffer($core.List<$core.int> i,
|
||||
@ -104,6 +115,18 @@ class BlobCTimeBuilderData extends $pb.GeneratedMessage {
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.Map<$core.String, TzDateTime> get map => $_getMap(2);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.List<$core.int> get headHash => $_getN(3);
|
||||
@$pb.TagNumber(4)
|
||||
set headHash($core.List<$core.int> v) {
|
||||
$_setBytes(3, v);
|
||||
}
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasHeadHash() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearHeadHash() => clearField(4);
|
||||
}
|
||||
|
||||
class FileMTimeBuilderData extends $pb.GeneratedMessage {
|
||||
@ -133,12 +156,20 @@ class FileMTimeBuilderData extends $pb.GeneratedMessage {
|
||||
valueFieldType: $pb.PbFieldType.OM,
|
||||
valueCreator: FileMTimeInfo.create,
|
||||
packageName: const $pb.PackageName('gitjournal'))
|
||||
..a<$core.List<$core.int>>(
|
||||
4,
|
||||
const $core.bool.fromEnvironment('protobuf.omit_field_names')
|
||||
? ''
|
||||
: 'headHash',
|
||||
$pb.PbFieldType.OY,
|
||||
protoName: 'headHash')
|
||||
..hasRequiredFields = false;
|
||||
|
||||
FileMTimeBuilderData._() : super();
|
||||
factory FileMTimeBuilderData({
|
||||
$core.Iterable<$core.List<$core.int>>? commitHashes,
|
||||
$core.Map<$core.String, FileMTimeInfo>? map,
|
||||
$core.List<$core.int>? headHash,
|
||||
}) {
|
||||
final _result = create();
|
||||
if (commitHashes != null) {
|
||||
@ -147,6 +178,9 @@ class FileMTimeBuilderData extends $pb.GeneratedMessage {
|
||||
if (map != null) {
|
||||
_result.map.addAll(map);
|
||||
}
|
||||
if (headHash != null) {
|
||||
_result.headHash = headHash;
|
||||
}
|
||||
return _result;
|
||||
}
|
||||
factory FileMTimeBuilderData.fromBuffer($core.List<$core.int> i,
|
||||
@ -182,6 +216,18 @@ class FileMTimeBuilderData extends $pb.GeneratedMessage {
|
||||
|
||||
@$pb.TagNumber(3)
|
||||
$core.Map<$core.String, FileMTimeInfo> get map => $_getMap(1);
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.List<$core.int> get headHash => $_getN(2);
|
||||
@$pb.TagNumber(4)
|
||||
set headHash($core.List<$core.int> v) {
|
||||
$_setBytes(2, v);
|
||||
}
|
||||
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasHeadHash() => $_has(2);
|
||||
@$pb.TagNumber(4)
|
||||
void clearHeadHash() => clearField(4);
|
||||
}
|
||||
|
||||
class TzDateTime extends $pb.GeneratedMessage {
|
||||
|
@ -27,6 +27,7 @@ const BlobCTimeBuilderData$json = const {
|
||||
'6': '.gitjournal.BlobCTimeBuilderData.MapEntry',
|
||||
'10': 'map'
|
||||
},
|
||||
const {'1': 'headHash', '3': 4, '4': 1, '5': 12, '10': 'headHash'},
|
||||
],
|
||||
'3': const [BlobCTimeBuilderData_MapEntry$json],
|
||||
};
|
||||
@ -50,7 +51,7 @@ const BlobCTimeBuilderData_MapEntry$json = const {
|
||||
|
||||
/// Descriptor for `BlobCTimeBuilderData`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List blobCTimeBuilderDataDescriptor = $convert.base64Decode(
|
||||
'ChRCbG9iQ1RpbWVCdWlsZGVyRGF0YRIiCgxjb21taXRIYXNoZXMYASADKAxSDGNvbW1pdEhhc2hlcxIeCgp0cmVlSGFzaGVzGAIgAygMUgp0cmVlSGFzaGVzEjsKA21hcBgDIAMoCzIpLmdpdGpvdXJuYWwuQmxvYkNUaW1lQnVpbGRlckRhdGEuTWFwRW50cnlSA21hcBpOCghNYXBFbnRyeRIQCgNrZXkYASABKAlSA2tleRIsCgV2YWx1ZRgCIAEoCzIWLmdpdGpvdXJuYWwuVHpEYXRlVGltZVIFdmFsdWU6AjgB');
|
||||
'ChRCbG9iQ1RpbWVCdWlsZGVyRGF0YRIiCgxjb21taXRIYXNoZXMYASADKAxSDGNvbW1pdEhhc2hlcxIeCgp0cmVlSGFzaGVzGAIgAygMUgp0cmVlSGFzaGVzEjsKA21hcBgDIAMoCzIpLmdpdGpvdXJuYWwuQmxvYkNUaW1lQnVpbGRlckRhdGEuTWFwRW50cnlSA21hcBIaCghoZWFkSGFzaBgEIAEoDFIIaGVhZEhhc2gaTgoITWFwRW50cnkSEAoDa2V5GAEgASgJUgNrZXkSLAoFdmFsdWUYAiABKAsyFi5naXRqb3VybmFsLlR6RGF0ZVRpbWVSBXZhbHVlOgI4AQ==');
|
||||
@$core.Deprecated('Use fileMTimeBuilderDataDescriptor instead')
|
||||
const FileMTimeBuilderData$json = const {
|
||||
'1': 'FileMTimeBuilderData',
|
||||
@ -64,6 +65,7 @@ const FileMTimeBuilderData$json = const {
|
||||
'6': '.gitjournal.FileMTimeBuilderData.MapEntry',
|
||||
'10': 'map'
|
||||
},
|
||||
const {'1': 'headHash', '3': 4, '4': 1, '5': 12, '10': 'headHash'},
|
||||
],
|
||||
'3': const [FileMTimeBuilderData_MapEntry$json],
|
||||
};
|
||||
@ -87,7 +89,7 @@ const FileMTimeBuilderData_MapEntry$json = const {
|
||||
|
||||
/// Descriptor for `FileMTimeBuilderData`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List fileMTimeBuilderDataDescriptor = $convert.base64Decode(
|
||||
'ChRGaWxlTVRpbWVCdWlsZGVyRGF0YRIiCgxjb21taXRIYXNoZXMYASADKAxSDGNvbW1pdEhhc2hlcxI7CgNtYXAYAyADKAsyKS5naXRqb3VybmFsLkZpbGVNVGltZUJ1aWxkZXJEYXRhLk1hcEVudHJ5UgNtYXAaUQoITWFwRW50cnkSEAoDa2V5GAEgASgJUgNrZXkSLwoFdmFsdWUYAiABKAsyGS5naXRqb3VybmFsLkZpbGVNVGltZUluZm9SBXZhbHVlOgI4AQ==');
|
||||
'ChRGaWxlTVRpbWVCdWlsZGVyRGF0YRIiCgxjb21taXRIYXNoZXMYASADKAxSDGNvbW1pdEhhc2hlcxI7CgNtYXAYAyADKAsyKS5naXRqb3VybmFsLkZpbGVNVGltZUJ1aWxkZXJEYXRhLk1hcEVudHJ5UgNtYXASGgoIaGVhZEhhc2gYBCABKAxSCGhlYWRIYXNoGlEKCE1hcEVudHJ5EhAKA2tleRgBIAEoCVIDa2V5Ei8KBXZhbHVlGAIgASgLMhkuZ2l0am91cm5hbC5GaWxlTVRpbWVJbmZvUgV2YWx1ZToCOAE=');
|
||||
@$core.Deprecated('Use tzDateTimeDescriptor instead')
|
||||
const TzDateTime$json = const {
|
||||
'1': 'TzDateTime',
|
||||
|
@ -13,6 +13,7 @@ import 'package:collection/collection.dart';
|
||||
import 'package:dart_git/config.dart';
|
||||
import 'package:dart_git/dart_git.dart';
|
||||
import 'package:dart_git/exceptions.dart';
|
||||
import 'package:dart_git/plumbing/git_hash.dart';
|
||||
import 'package:git_bindings/git_bindings.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
@ -78,7 +79,7 @@ class GitJournalRepo with ChangeNotifier {
|
||||
late NotesFolderFS notesFolder;
|
||||
|
||||
bool remoteGitRepoConfigured = false;
|
||||
bool fileStorageCacheReady = false;
|
||||
late bool fileStorageCacheReady;
|
||||
|
||||
static Future<GitJournalRepo> load(
|
||||
{required String gitBaseDir,
|
||||
@ -152,6 +153,9 @@ class GitJournalRepo with ChangeNotifier {
|
||||
var fileStorageCache = FileStorageCache(cacheDir);
|
||||
var fileStorage = await fileStorageCache.load(repo);
|
||||
|
||||
var headR = await repo.headHash();
|
||||
var head = headR.isFailure ? GitHash.zero() : headR.getOrThrow();
|
||||
|
||||
return GitJournalRepo._internal(
|
||||
repoPath: repoPath,
|
||||
gitBaseDirectory: gitBaseDir,
|
||||
@ -165,6 +169,7 @@ class GitJournalRepo with ChangeNotifier {
|
||||
fileStorage: fileStorage,
|
||||
fileStorageCache: fileStorageCache,
|
||||
currentBranch: await repo.currentBranch().getOrThrow(),
|
||||
headHash: head,
|
||||
);
|
||||
}
|
||||
|
||||
@ -181,6 +186,7 @@ class GitJournalRepo with ChangeNotifier {
|
||||
required this.fileStorage,
|
||||
required this.fileStorageCache,
|
||||
required String? currentBranch,
|
||||
required GitHash headHash,
|
||||
}) {
|
||||
_gitRepo = GitNoteRepository(gitRepoPath: repoPath, config: gitConfig);
|
||||
notesFolder = NotesFolderFS.root(folderConfig, fileStorage);
|
||||
@ -202,6 +208,8 @@ class GitJournalRepo with ChangeNotifier {
|
||||
fileStorage: fileStorage,
|
||||
);
|
||||
|
||||
fileStorageCacheReady = headHash == fileStorageCache.lastProcessedHead;
|
||||
|
||||
_loadFromCache();
|
||||
_syncNotes();
|
||||
}
|
||||
@ -277,7 +285,7 @@ class GitJournalRepo with ChangeNotifier {
|
||||
|
||||
// Notify that the cache is ready
|
||||
Log.i("Done building the FileStorageCache");
|
||||
fileStorageCacheReady = true;
|
||||
fileStorageCacheReady = fileStorageCache.lastProcessedHead == head;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
@ -12,11 +12,15 @@ message BlobCTimeBuilderData {
|
||||
repeated bytes commitHashes = 1;
|
||||
repeated bytes treeHashes = 2;
|
||||
map<string, TzDateTime> map = 3;
|
||||
|
||||
bytes headHash = 4;
|
||||
}
|
||||
|
||||
message FileMTimeBuilderData {
|
||||
repeated bytes commitHashes = 1;
|
||||
map<string, FileMTimeInfo> map = 3;
|
||||
|
||||
bytes headHash = 4;
|
||||
}
|
||||
|
||||
message TzDateTime {
|
||||
|
@ -328,7 +328,7 @@ packages:
|
||||
description:
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: "0369ae80dfde703b3a22ade5adf350800d649153"
|
||||
resolved-ref: "6d4b7f26bc2c7b8c83ec7f608fe05eec561d6757"
|
||||
url: "https://github.com/GitJournal/dart-git.git"
|
||||
source: git
|
||||
version: "0.0.2"
|
||||
|
Reference in New Issue
Block a user