diff --git a/lib/setup/clone.dart b/lib/setup/clone.dart index aead8a6a..1530e40c 100644 --- a/lib/setup/clone.dart +++ b/lib/setup/clone.dart @@ -1,13 +1,11 @@ import 'dart:async'; -import 'dart:io' show Platform, Directory, File; +import 'dart:io' show Directory, File; import 'package:dart_git/dart_git.dart'; import 'package:dart_git/exceptions.dart'; import 'package:function_types/function_types.dart'; -import 'package:git_bindings/git_bindings.dart' as git_bindings; import 'package:path/path.dart' as p; -import 'package:gitjournal/utils/git_desktop.dart'; import 'package:gitjournal/utils/logger.dart'; class GitTransferProgress { @@ -25,8 +23,6 @@ class GitTransferProgress { } var str = await File(statusFile).readAsString(); var parts = str.split(' '); - print('Str #$str#'); - print('Parts $parts'); var tp = GitTransferProgress(); tp.totalObjects = int.parse(parts[0]); @@ -40,7 +36,32 @@ class GitTransferProgress { } } -Future> cloneRemote({ +typedef GitFetchFunction = Future> Function( + String repoPath, + String remoteName, + String sshPublicKey, + String sshPrivateKey, + String sshPassword, + String statusFile, +); + +typedef GitDefaultBranchFunction = Future> Function( + String repoPath, + String remoteName, + String sshPublicKey, + String sshPrivateKey, + String sshPassword, +); + +typedef GitMergeFn = Future> Function( + String repoPath, + String remoteName, + String remoteBranchName, + String authorName, + String authorEmail, +); + +Future> cloneRemotePluggable({ required String repoPath, required String cloneUrl, required String remoteName, @@ -50,61 +71,38 @@ Future> cloneRemote({ required String authorName, required String authorEmail, required Func1 progressUpdate, + required GitFetchFunction gitFetchFn, + required GitDefaultBranchFunction defaultBranchFn, + required GitMergeFn gitMergeFn, }) async { + // FIXME: Do not throw exceptions var repo = await GitRepository.load(repoPath).getOrThrow(); var remote = await repo.addOrUpdateRemote(remoteName, cloneUrl).getOrThrow(); - var remoteBranchName = "master"; - var _gitRepo = git_bindings.GitRepo(folderPath: repoPath); - - if (Platform.isAndroid || Platform.isIOS) { - var statusFile = p.join(Directory.systemTemp.path, 'gj'); - var duration = const Duration(milliseconds: 10); - var timer = Timer.periodic(duration, (_) async { - var progress = await GitTransferProgress.load(statusFile); - if (progress != null) { - progressUpdate(progress); - } - }); - await _gitRepo.fetch( - remote: remoteName, - publicKey: sshPublicKey, - privateKey: sshPrivateKey, - password: sshPassword, - statusFile: statusFile, - ); - timer.cancel(); - - remoteBranchName = await _remoteDefaultBranch( - repo: repo, - libGit2Repo: _gitRepo, - remoteName: remoteName, - sshPublicKey: sshPublicKey, - sshPrivateKey: sshPrivateKey, - sshPassword: sshPassword, - ); - } else if (Platform.isMacOS) { - var r = await gitFetchViaExecutable( - repoPath: repoPath, - privateKey: sshPrivateKey, - privateKeyPassword: sshPassword, - remoteName: remoteName, - ); - if (r.isFailure) { - return fail(r); + var statusFile = p.join(Directory.systemTemp.path, 'gj'); + var duration = const Duration(milliseconds: 50); + var timer = Timer.periodic(duration, (_) async { + var progress = await GitTransferProgress.load(statusFile); + if (progress != null) { + progressUpdate(progress); } + }); - var branchR = await gitDefaultBranchViaExecutable( - repoPath: repoPath, - privateKey: sshPrivateKey, - privateKeyPassword: sshPassword, - remoteName: remoteName, - ); - if (r.isFailure) { - return fail(r); - } - remoteBranchName = branchR.getOrThrow(); + var fetchR = await gitFetchFn(repoPath, remoteName, sshPublicKey, + sshPrivateKey, sshPassword, statusFile); + timer.cancel(); + if (fetchR.isFailure) { + // FIXME: Give a better error? + return fail(fetchR); } + + var branchNameR = await defaultBranchFn( + repoPath, remoteName, sshPublicKey, sshPrivateKey, sshPassword); + if (branchNameR.isFailure) { + return fail(branchNameR); + } + var remoteBranchName = branchNameR.getOrThrow(); + Log.i("Using remote branch: $remoteBranchName"); var branches = await repo.branches().getOrThrow(); @@ -145,16 +143,10 @@ Future> cloneRemote({ var remoteBranchR = await repo.remoteBranch(remoteName, remoteBranchName); if (remoteBranchR.isSuccess) { Log.i("Merging '$remoteName/$remoteBranchName'"); - if (Platform.isAndroid || Platform.isIOS) { - await _gitRepo.merge( - branch: '$remoteName/$remoteBranchName', - authorName: authorName, - authorEmail: authorEmail, - ); - } else { - var repo = await GitRepository.load(repoPath).getOrThrow(); - var author = GitAuthor(name: authorName, email: authorEmail); - repo.mergeCurrentTrackingBranch(author: author).throwOnError(); + var r = await gitMergeFn( + repoPath, remoteName, remoteBranchName, authorName, authorEmail); + if (r.isFailure) { + return fail(r); } } } else { @@ -166,16 +158,10 @@ Future> cloneRemote({ await repo.setUpstreamTo(remote, remoteBranchName).getOrThrow(); Log.i("Merging '$remoteName/$remoteBranchName'"); - if (Platform.isAndroid || Platform.isIOS) { - await _gitRepo.merge( - branch: '$remoteName/$remoteBranchName', - authorName: authorName, - authorEmail: authorEmail, - ); - } else { - var repo = await GitRepository.load(repoPath).getOrThrow(); - var author = GitAuthor(name: authorName, email: authorEmail); - repo.mergeCurrentTrackingBranch(author: author).throwOnError(); + var r = await gitMergeFn( + repoPath, remoteName, remoteBranchName, authorName, authorEmail); + if (r.isFailure) { + return fail(r); } } } @@ -191,36 +177,6 @@ Future> cloneRemote({ return Result(null); } -Future _remoteDefaultBranch({ - required GitRepository repo, - required git_bindings.GitRepo libGit2Repo, - required String remoteName, - required String sshPublicKey, - required String sshPrivateKey, - required String sshPassword, -}) async { - try { - var branch = await libGit2Repo.defaultBranch( - remote: remoteName, - publicKey: sshPublicKey, - privateKey: sshPrivateKey, - password: sshPassword, - ); - Log.i("Got default branch: $branch"); - if (branch != null && branch.isNotEmpty) { - return branch; - } - } catch (ex) { - Log.w("Could not fetch git main branch", ex: ex); - } - - var remoteBranch = await repo.guessRemoteHead(remoteName); - if (remoteBranch == null) { - return 'master'; - } - return remoteBranch.target!.branchName()!; -} - String folderNameFromCloneUrl(String cloneUrl) { var name = p.basename(cloneUrl); if (name.endsWith('.git')) { diff --git a/lib/setup/clone_auto_select.dart b/lib/setup/clone_auto_select.dart new file mode 100644 index 00000000..70677f54 --- /dev/null +++ b/lib/setup/clone_auto_select.dart @@ -0,0 +1,8 @@ +import 'dart:io' show Platform; + +import 'clone_gitExec.dart' as git_exec; +import 'clone_libgit2.dart' as libgit2; + +final isMobileApp = Platform.isIOS || Platform.isAndroid; + +var cloneRemote = isMobileApp ? libgit2.cloneRemote : git_exec.cloneRemote; diff --git a/lib/setup/clone_gitExec.dart b/lib/setup/clone_gitExec.dart new file mode 100644 index 00000000..cafde597 --- /dev/null +++ b/lib/setup/clone_gitExec.dart @@ -0,0 +1,79 @@ +import 'package:dart_git/dart_git.dart'; +import 'package:dart_git/utils/result.dart'; +import 'package:function_types/function_types.dart'; + +import 'package:gitjournal/utils/git_desktop.dart'; +import 'clone.dart'; + +Future> cloneRemote({ + required String repoPath, + required String cloneUrl, + required String remoteName, + required String sshPublicKey, + required String sshPrivateKey, + required String sshPassword, + required String authorName, + required String authorEmail, + required Func1 progressUpdate, +}) => + cloneRemotePluggable( + repoPath: repoPath, + cloneUrl: cloneUrl, + remoteName: remoteName, + sshPublicKey: sshPublicKey, + sshPrivateKey: sshPrivateKey, + sshPassword: sshPassword, + authorName: authorName, + authorEmail: authorEmail, + progressUpdate: progressUpdate, + gitFetchFn: _fetch, + defaultBranchFn: _defaultBranch, + gitMergeFn: _merge, + ); + +Future> _fetch( + String repoPath, + String remoteName, + String sshPublicKey, + String sshPrivateKey, + String sshPassword, + String statusFile, +) { + // FIXME: Stop ignoring the statusFile + return gitFetchViaExecutable( + repoPath: repoPath, + privateKey: sshPrivateKey, + privateKeyPassword: sshPassword, + remoteName: remoteName, + ); +} + +Future> _defaultBranch( + String repoPath, + String remoteName, + String sshPublicKey, + String sshPrivateKey, + String sshPassword, +) { + return gitDefaultBranchViaExecutable( + repoPath: repoPath, + privateKey: sshPrivateKey, + privateKeyPassword: sshPassword, + remoteName: remoteName, + ); +} + +Future> _merge( + String repoPath, + String remoteName, + String remoteBranchName, + String authorName, + String authorEmail, +) { + return catchAll(() async { + var repo = await GitRepository.load(repoPath).getOrThrow(); + var author = GitAuthor(name: authorName, email: authorEmail); + await repo.mergeCurrentTrackingBranch(author: author).throwOnError(); + return Result(null); + }); +} diff --git a/lib/setup/clone_libgit2.dart b/lib/setup/clone_libgit2.dart new file mode 100644 index 00000000..102b4e51 --- /dev/null +++ b/lib/setup/clone_libgit2.dart @@ -0,0 +1,102 @@ +import 'package:dart_git/utils/result.dart'; +import 'package:function_types/function_types.dart'; +import 'package:git_bindings/git_bindings.dart' as git_bindings; + +import 'clone.dart'; + +Future> cloneRemote({ + required String repoPath, + required String cloneUrl, + required String remoteName, + required String sshPublicKey, + required String sshPrivateKey, + required String sshPassword, + required String authorName, + required String authorEmail, + required Func1 progressUpdate, +}) => + cloneRemotePluggable( + repoPath: repoPath, + cloneUrl: cloneUrl, + remoteName: remoteName, + sshPublicKey: sshPublicKey, + sshPrivateKey: sshPrivateKey, + sshPassword: sshPassword, + authorName: authorName, + authorEmail: authorEmail, + progressUpdate: progressUpdate, + gitFetchFn: _fetch, + defaultBranchFn: _defaultBranch, + gitMergeFn: _merge, + ); + +Future> _fetch( + String repoPath, + String remoteName, + String sshPublicKey, + String sshPrivateKey, + String sshPassword, + String statusFile, +) async { + try { + var gitRepo = git_bindings.GitRepo(folderPath: repoPath); + await gitRepo.fetch( + remote: remoteName, + publicKey: sshPublicKey, + privateKey: sshPrivateKey, + password: sshPassword, + statusFile: statusFile, + ); + } on Exception catch (e, st) { + return Result.fail(e, st); + } + + return Result(null); +} + +Future> _defaultBranch( + String repoPath, + String remoteName, + String sshPublicKey, + String sshPrivateKey, + String sshPassword, +) async { + try { + var gitRepo = git_bindings.GitRepo(folderPath: repoPath); + var branch = await gitRepo.defaultBranch( + remote: remoteName, + publicKey: sshPublicKey, + privateKey: sshPrivateKey, + password: sshPassword, + ); + if (branch != null && branch.isNotEmpty) { + return Result(branch); + } + } on Exception catch (e, st) { + return Result.fail(e, st); + } + + var ex = Exception("No Remote Branch found"); + return Result.fail(ex); +} + +Future> _merge( + String repoPath, + String remoteName, + String remoteBranchName, + String authorName, + String authorEmail, +) async { + try { + var gitRepo = git_bindings.GitRepo(folderPath: repoPath); + await gitRepo.merge( + branch: '$remoteName/$remoteBranchName', + authorName: authorName, + authorEmail: authorEmail, + ); + } on Exception catch (e, st) { + return Result.fail(e, st); + } + + return Result(null); +} diff --git a/lib/setup/screens.dart b/lib/setup/screens.dart index 763403e6..61a42ec0 100644 --- a/lib/setup/screens.dart +++ b/lib/setup/screens.dart @@ -20,6 +20,7 @@ import 'package:gitjournal/settings/settings.dart'; import 'package:gitjournal/setup/autoconfigure.dart'; import 'package:gitjournal/setup/button.dart'; import 'package:gitjournal/setup/clone.dart'; +import 'package:gitjournal/setup/clone_auto_select.dart'; import 'package:gitjournal/setup/clone_url.dart'; import 'package:gitjournal/setup/loading_error.dart'; import 'package:gitjournal/setup/repo_selector.dart'; @@ -566,7 +567,7 @@ class GitHostSetupScreenState extends State { _cloneProgress = p; }); }, - ); + ).throwOnError(); } on Exception catch (e, stacktrace) { Log.e("Failed to clone", ex: e, stacktrace: stacktrace); error = e.toString();