mirror of
https://github.com/GitJournal/GitJournal.git
synced 2025-06-29 02:07:39 +08:00
Improve repo migrations
Earlier we had one folder 'journal_local', when the remote would be setup a new folder called 'journal' would be created, and each all the files would be copied over. This meant the local history was being destroyed. Now, we only have 1 folder 'journal', and on 'cloning', we add the url as a remote, and do a git fetch + merge. This simplifies everything drastically, and opens the door for multiple remotes.
This commit is contained in:
@ -1,49 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:git_bindings/git_bindings.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import 'package:gitjournal/utils/logger.dart';
|
||||
|
||||
//
|
||||
// FIXME: This isn't ideal as we are skipping all the edits / deletes
|
||||
//
|
||||
Future migrateGitRepo({
|
||||
@required String gitBasePath,
|
||||
@required String fromGitBasePath,
|
||||
@required String toGitBaseFolder,
|
||||
@required String gitAuthor,
|
||||
@required String gitAuthorEmail,
|
||||
}) async {
|
||||
Log.d("migrateGitRepo $fromGitBasePath $toGitBaseFolder");
|
||||
var fromBasePath = p.join(gitBasePath, fromGitBasePath);
|
||||
var toGitRepoPath = p.join(gitBasePath, toGitBaseFolder);
|
||||
Log.d("toGitRemotePath $toGitRepoPath");
|
||||
|
||||
final dir = Directory(fromBasePath);
|
||||
var lister = dir.list(recursive: false);
|
||||
await for (var fileEntity in lister) {
|
||||
if (fileEntity is! File) {
|
||||
continue;
|
||||
}
|
||||
File file = fileEntity;
|
||||
var fileName = p.basename(file.path);
|
||||
var toPath = p.join(toGitRepoPath, fileName);
|
||||
|
||||
Log.d("Migrating " + file.path + " --> " + toPath);
|
||||
|
||||
await file.copy(toPath);
|
||||
|
||||
var gitRepo = GitRepo(folderPath: toGitRepoPath);
|
||||
await gitRepo.add(fileName);
|
||||
await gitRepo.commit(
|
||||
message: "Added Note",
|
||||
authorEmail: gitAuthorEmail,
|
||||
authorName: gitAuthor,
|
||||
);
|
||||
}
|
||||
Log.d("migrateGitRepo: Done");
|
||||
}
|
13
lib/app.dart
13
lib/app.dart
@ -67,12 +67,16 @@ class JournalApp extends StatefulWidget {
|
||||
}
|
||||
Log.i('-----');
|
||||
|
||||
await settings.migrate(pref, appState.gitBaseDirectory);
|
||||
|
||||
// FIXME: This can be replaced with a fs stat
|
||||
if (settings.localGitRepoConfigured == false) {
|
||||
settings.internalRepoFolderName = "journal";
|
||||
|
||||
// FIXME: What about exceptions!
|
||||
settings.localGitRepoFolderName = "journal_local";
|
||||
var repoPath = p.join(
|
||||
appState.gitBaseDirectory,
|
||||
settings.localGitRepoFolderName,
|
||||
settings.internalRepoFolderName,
|
||||
);
|
||||
await GitRepository.init(repoPath);
|
||||
|
||||
@ -381,8 +385,9 @@ class _JournalAppState extends State<JournalApp> {
|
||||
return SettingsScreen();
|
||||
case '/setupRemoteGit':
|
||||
return GitHostSetupScreen(
|
||||
"journal",
|
||||
stateContainer.completeGitHostSetup,
|
||||
repoFolderName: settings.internalRepoFolderName,
|
||||
remoteName: "origin",
|
||||
onCompletedFunction: stateContainer.completeGitHostSetup,
|
||||
);
|
||||
case '/onBoarding':
|
||||
return OnBoardingScreen();
|
||||
|
@ -180,14 +180,18 @@ class GitNoteRepository {
|
||||
Future<void> merge() async {
|
||||
var repo = await git.GitRepository.load(gitDirPath);
|
||||
var branch = await repo.currentBranch();
|
||||
if (branch == null) {
|
||||
var branchConfig = repo.config.branch(branch);
|
||||
if (branchConfig == null) {
|
||||
logExceptionWarning(Exception("Current Branch null"), StackTrace.current);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(branchConfig.name != null);
|
||||
assert(branchConfig.merge != null);
|
||||
|
||||
try {
|
||||
await _gitRepo.merge(
|
||||
branch: branch.remoteTrackingBranch(),
|
||||
branch: branchConfig.remoteTrackingBranch(),
|
||||
authorEmail: settings.gitAuthorEmail,
|
||||
authorName: settings.gitAuthor,
|
||||
);
|
||||
|
@ -148,8 +148,9 @@ class _GitRemoteSettingsScreenState extends State<GitRemoteSettingsScreen> {
|
||||
|
||||
var route = MaterialPageRoute(
|
||||
builder: (context) => GitHostSetupScreen(
|
||||
repoFolderName,
|
||||
stateContainer.completeGitHostSetup,
|
||||
repoFolderName: repoFolderName,
|
||||
remoteName: 'origin',
|
||||
onCompletedFunction: stateContainer.completeGitHostSetup,
|
||||
),
|
||||
settings: const RouteSettings(name: '/setupRemoteGit'),
|
||||
);
|
||||
|
@ -1,12 +1,16 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'package:gitjournal/core/sorting_mode.dart';
|
||||
import 'package:gitjournal/folder_views/common.dart';
|
||||
import 'package:gitjournal/screens/note_editor.dart';
|
||||
import 'package:gitjournal/utils/logger.dart';
|
||||
|
||||
class Settings extends ChangeNotifier {
|
||||
Settings();
|
||||
@ -34,7 +38,7 @@ class Settings extends ChangeNotifier {
|
||||
SettingsFolderViewType defaultView = SettingsFolderViewType.Default;
|
||||
bool showNoteSummary = true;
|
||||
String folderViewHeaderType = "TitleGenerated";
|
||||
int version = 0;
|
||||
int version = 1;
|
||||
|
||||
SettingsHomeScreen homeScreen = SettingsHomeScreen.Default;
|
||||
|
||||
@ -56,10 +60,9 @@ class Settings extends ChangeNotifier {
|
||||
bool bottomMenuBar = false;
|
||||
|
||||
// From AppState
|
||||
String localGitRepoFolderName = "";
|
||||
String internalRepoFolderName = "";
|
||||
bool localGitRepoConfigured = false;
|
||||
|
||||
String remoteGitRepoFolderName = "";
|
||||
bool remoteGitRepoConfigured = false;
|
||||
|
||||
bool storeInternally = true;
|
||||
@ -131,8 +134,7 @@ class Settings extends ChangeNotifier {
|
||||
// From AppState
|
||||
localGitRepoConfigured = pref.getBool("localGitRepoConfigured") ?? false;
|
||||
remoteGitRepoConfigured = pref.getBool("remoteGitRepoConfigured") ?? false;
|
||||
localGitRepoFolderName = pref.getString("localGitRepoPath") ?? "";
|
||||
remoteGitRepoFolderName = pref.getString("remoteGitRepoPath") ?? "";
|
||||
internalRepoFolderName = pref.getString("remoteGitRepoPath") ?? "";
|
||||
|
||||
bottomMenuBar = pref.getBool("bottomMenuBar") ?? bottomMenuBar;
|
||||
storeInternally = pref.getBool("storeInternally") ?? storeInternally;
|
||||
@ -221,8 +223,7 @@ class Settings extends ChangeNotifier {
|
||||
|
||||
pref.setBool("localGitRepoConfigured", localGitRepoConfigured);
|
||||
pref.setBool("remoteGitRepoConfigured", remoteGitRepoConfigured);
|
||||
pref.setString("localGitRepoPath", localGitRepoFolderName);
|
||||
pref.setString("remoteGitRepoPath", remoteGitRepoFolderName);
|
||||
pref.setString("remoteGitRepoPath", internalRepoFolderName);
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
@ -302,8 +303,7 @@ class Settings extends ChangeNotifier {
|
||||
'emojiParser': emojiParser.toString(),
|
||||
'localGitRepoConfigured': localGitRepoConfigured.toString(),
|
||||
'remoteGitRepoConfigured': remoteGitRepoConfigured.toString(),
|
||||
'localGitRepoFolderName': localGitRepoFolderName.toString(),
|
||||
'remoteGitRepoFolderName': remoteGitRepoFolderName.toString(),
|
||||
'remoteGitRepoPath': internalRepoFolderName.toString(),
|
||||
'bottomMenuBar': bottomMenuBar.toString(),
|
||||
'storeInternally': storeInternally.toString(),
|
||||
'storageLocation': storageLocation,
|
||||
@ -317,6 +317,24 @@ class Settings extends ChangeNotifier {
|
||||
m.remove("defaultNewNoteFolderSpec");
|
||||
return m;
|
||||
}
|
||||
|
||||
Future<void> migrate(SharedPreferences pref, String gitBaseDir) async {
|
||||
if (version == 0) {
|
||||
if (localGitRepoConfigured && !remoteGitRepoConfigured) {
|
||||
Log.i("Migrating from local and remote repos to a single one");
|
||||
var oldName = p.join(gitBaseDir, "journal_local");
|
||||
var newName = p.join(gitBaseDir, "journal");
|
||||
|
||||
await Directory(oldName).rename(newName);
|
||||
internalRepoFolderName = "journal";
|
||||
|
||||
version = 1;
|
||||
pref.setInt("settingsVersion", version);
|
||||
pref.setString('remoteGitRepoPath', internalRepoFolderName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NoteFileNameFormat {
|
||||
|
@ -3,10 +3,11 @@ import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:dart_git/git.dart';
|
||||
import 'package:dots_indicator/dots_indicator.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:function_types/function_types.dart';
|
||||
import 'package:git_bindings/git_bindings.dart';
|
||||
import 'package:git_bindings/git_bindings.dart' as git_bindings;
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
@ -27,9 +28,14 @@ import 'package:gitjournal/utils/logger.dart';
|
||||
|
||||
class GitHostSetupScreen extends StatefulWidget {
|
||||
final String repoFolderName;
|
||||
final Func1<String, void> onCompletedFunction;
|
||||
final String remoteName;
|
||||
final Func2<String, String, void> onCompletedFunction;
|
||||
|
||||
GitHostSetupScreen(this.repoFolderName, this.onCompletedFunction);
|
||||
GitHostSetupScreen({
|
||||
@required this.repoFolderName,
|
||||
@required this.remoteName,
|
||||
@required this.onCompletedFunction,
|
||||
});
|
||||
|
||||
@override
|
||||
GitHostSetupScreenState createState() {
|
||||
@ -203,7 +209,10 @@ class GitHostSetupScreenState extends State<GitHostSetupScreen> {
|
||||
} else if (_keyGenerationChoice == KeyGenerationChoice.UserProvided) {
|
||||
return GitHostUserProvidedKeys(
|
||||
doneFunction: (String publicKey, String privateKey) async {
|
||||
await setSshKeys(publicKey: publicKey, privateKey: privateKey);
|
||||
await git_bindings.setSshKeys(
|
||||
publicKey: publicKey,
|
||||
privateKey: privateKey,
|
||||
);
|
||||
setState(() {
|
||||
this.publicKey = publicKey;
|
||||
_pageCount = pos + 2;
|
||||
@ -284,7 +293,8 @@ class GitHostSetupScreenState extends State<GitHostSetupScreen> {
|
||||
} else if (_keyGenerationChoice == KeyGenerationChoice.UserProvided) {
|
||||
return GitHostUserProvidedKeys(
|
||||
doneFunction: (String publicKey, String privateKey) async {
|
||||
await setSshKeys(publicKey: publicKey, privateKey: privateKey);
|
||||
await git_bindings.setSshKeys(
|
||||
publicKey: publicKey, privateKey: privateKey);
|
||||
setState(() {
|
||||
this.publicKey = publicKey;
|
||||
_pageCount = pos + 2;
|
||||
@ -399,7 +409,7 @@ class GitHostSetupScreenState extends State<GitHostSetupScreen> {
|
||||
"-" +
|
||||
DateTime.now().toIso8601String().substring(0, 10); // only the date
|
||||
|
||||
generateSSHKeys(comment: comment).then((String publicKey) {
|
||||
git_bindings.generateSSHKeys(comment: comment).then((String publicKey) {
|
||||
setState(() {
|
||||
this.publicKey = publicKey;
|
||||
Log.d("PublicKey: " + publicKey);
|
||||
@ -471,17 +481,21 @@ class GitHostSetupScreenState extends State<GitHostSetupScreen> {
|
||||
var stateContainer = Provider.of<StateContainer>(context, listen: false);
|
||||
var basePath = stateContainer.appState.gitBaseDirectory;
|
||||
|
||||
// Just in case it was half cloned because of an error
|
||||
String repoPath = p.join(basePath, widget.repoFolderName);
|
||||
await _removeExistingClone(repoPath);
|
||||
var settings = Provider.of<Settings>(context, listen: false);
|
||||
var repoName = settings.internalRepoFolderName;
|
||||
var repoPath = p.join(basePath, repoName);
|
||||
Log.i("RepoPath: $repoPath");
|
||||
|
||||
String error;
|
||||
try {
|
||||
Log.d("Cloning " + _gitCloneUrl);
|
||||
await GitRepo.clone(repoPath, _gitCloneUrl);
|
||||
} on GitException catch (e) {
|
||||
var repo = await GitRepository.load(repoPath);
|
||||
await repo.addRemote(widget.remoteName, _gitCloneUrl);
|
||||
|
||||
var repoN = git_bindings.GitRepo(folderPath: repoPath);
|
||||
await repoN.fetch(widget.remoteName);
|
||||
} on Exception catch (e) {
|
||||
Log.e(e.toString());
|
||||
error = e.cause;
|
||||
error = e.toString();
|
||||
}
|
||||
|
||||
if (error != null && error.isNotEmpty) {
|
||||
@ -507,9 +521,7 @@ class GitHostSetupScreenState extends State<GitHostSetupScreen> {
|
||||
var ignoreFile = File(p.join(repoPath, ".gitignore"));
|
||||
ignoreFile.createSync();
|
||||
|
||||
var repo = GitRepo(
|
||||
folderPath: repoPath,
|
||||
);
|
||||
var repo = git_bindings.GitRepo(folderPath: repoPath);
|
||||
await repo.add('.gitignore');
|
||||
|
||||
var settings = Provider.of<Settings>(context, listen: false);
|
||||
@ -525,7 +537,7 @@ class GitHostSetupScreenState extends State<GitHostSetupScreen> {
|
||||
parameters: _buildOnboardingAnalytics(),
|
||||
);
|
||||
Navigator.pop(context);
|
||||
widget.onCompletedFunction(widget.repoFolderName);
|
||||
widget.onCompletedFunction(widget.repoFolderName, widget.remoteName);
|
||||
}
|
||||
|
||||
Future<void> _completeAutoConfigure() async {
|
||||
@ -536,7 +548,7 @@ class GitHostSetupScreenState extends State<GitHostSetupScreen> {
|
||||
setState(() {
|
||||
_autoConfigureMessage = tr('setup.sshKey.generate');
|
||||
});
|
||||
var publicKey = await generateSSHKeys(comment: "GitJournal");
|
||||
var publicKey = await git_bindings.generateSSHKeys(comment: "GitJournal");
|
||||
|
||||
Log.i("Adding as a deploy key");
|
||||
_autoConfigureMessage = tr('setup.sshKey.addDeploy');
|
||||
@ -594,17 +606,6 @@ class GitHostSetupScreenState extends State<GitHostSetupScreen> {
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
Future _removeExistingClone(String baseDirPath) async {
|
||||
var baseDir = Directory(baseDirPath);
|
||||
var dotGitDir = Directory(p.join(baseDir.path, ".git"));
|
||||
bool exists = dotGitDir.existsSync();
|
||||
if (exists) {
|
||||
Log.d("Removing " + baseDir.path);
|
||||
await baseDir.delete(recursive: true);
|
||||
await baseDir.create();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GitHostChoicePage extends StatelessWidget {
|
||||
|
@ -4,11 +4,11 @@ import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:dart_git/dart_git.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:synchronized/synchronized.dart';
|
||||
|
||||
import 'package:gitjournal/analytics.dart';
|
||||
import 'package:gitjournal/apis/git_migration.dart';
|
||||
import 'package:gitjournal/appstate.dart';
|
||||
import 'package:gitjournal/core/git_repo.dart';
|
||||
import 'package:gitjournal/core/note.dart';
|
||||
@ -40,22 +40,11 @@ class StateContainer with ChangeNotifier {
|
||||
@required this.gitBaseDirectory,
|
||||
}) {
|
||||
assert(settings.localGitRepoConfigured);
|
||||
|
||||
String repoPath;
|
||||
if (settings.remoteGitRepoConfigured) {
|
||||
repoPath = p.join(gitBaseDirectory, settings.remoteGitRepoFolderName);
|
||||
} else if (settings.localGitRepoConfigured) {
|
||||
repoPath = p.join(gitBaseDirectory, settings.localGitRepoFolderName);
|
||||
}
|
||||
var repoPath = p.join(gitBaseDirectory, settings.internalRepoFolderName);
|
||||
|
||||
_gitRepo = GitNoteRepository(gitDirPath: repoPath, settings: settings);
|
||||
appState.notesFolder = NotesFolderFS(null, _gitRepo.gitDirPath, settings);
|
||||
|
||||
// Just a fail safe
|
||||
if (!settings.remoteGitRepoConfigured) {
|
||||
removeExistingRemoteClone();
|
||||
}
|
||||
|
||||
// Makes it easier to filter the analytics
|
||||
getAnalytics().firebase.setUserProperty(
|
||||
name: 'onboarded',
|
||||
@ -81,17 +70,6 @@ class StateContainer with ChangeNotifier {
|
||||
Log.i("Finished loading all the notes");
|
||||
}
|
||||
|
||||
void removeExistingRemoteClone() async {
|
||||
var remoteGitDir =
|
||||
Directory(p.join(gitBaseDirectory, settings.remoteGitRepoFolderName));
|
||||
var dotGitDir = Directory(p.join(remoteGitDir.path, ".git"));
|
||||
|
||||
bool exists = dotGitDir.existsSync();
|
||||
if (exists) {
|
||||
await remoteGitDir.delete(recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadNotes() async {
|
||||
// FIXME: We should report the notes that failed to load
|
||||
return _loadLock.synchronized(() async {
|
||||
@ -351,29 +329,25 @@ class StateContainer with ChangeNotifier {
|
||||
});
|
||||
}
|
||||
|
||||
void completeGitHostSetup(String repoFolderName) {
|
||||
// FIXME: Pass the remote name that was added
|
||||
void completeGitHostSetup(String repoFolderName, String remoteName) {
|
||||
() async {
|
||||
var reconfiguringRemote = settings.remoteGitRepoConfigured;
|
||||
var repo = await GitRepository.load(_gitRepo.gitDirPath);
|
||||
var remote = repo.config.remote(remoteName);
|
||||
var remoteBranchName = 'master';
|
||||
|
||||
// FIXME: How to get this?
|
||||
//
|
||||
// There is no way to get it, just need to iterate over the refs
|
||||
// and look for one!
|
||||
await repo.setUpstreamTo(remote, remoteBranchName);
|
||||
|
||||
// At this point the remote should have been added and fetched
|
||||
|
||||
await _gitRepo.merge();
|
||||
settings.remoteGitRepoConfigured = true;
|
||||
settings.remoteGitRepoFolderName = repoFolderName;
|
||||
|
||||
if (!reconfiguringRemote) {
|
||||
await migrateGitRepo(
|
||||
fromGitBasePath: settings.localGitRepoFolderName,
|
||||
toGitBaseFolder: settings.remoteGitRepoFolderName,
|
||||
gitBasePath: gitBaseDirectory,
|
||||
gitAuthor: settings.gitAuthor,
|
||||
gitAuthorEmail: settings.gitAuthorEmail,
|
||||
);
|
||||
}
|
||||
|
||||
var repoPath = p.join(gitBaseDirectory, settings.remoteGitRepoFolderName);
|
||||
_gitRepo = GitNoteRepository(gitDirPath: repoPath, settings: settings);
|
||||
appState.notesFolder.reset(_gitRepo.gitDirPath);
|
||||
|
||||
await _persistConfig();
|
||||
await _notesCache.clear();
|
||||
_loadNotes();
|
||||
_syncNotes();
|
||||
|
||||
|
@ -181,7 +181,7 @@ packages:
|
||||
description:
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: "8e7e782f32f6cb1f39e39036d795f06ddecc56aa"
|
||||
resolved-ref: aed5883fd17219c778cdf2d0d613a873d0b20f6c
|
||||
url: "https://github.com/GitJournal/dart_git.git"
|
||||
source: git
|
||||
version: "0.0.1"
|
||||
@ -226,7 +226,7 @@ packages:
|
||||
name: equatable
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
version: "1.2.5"
|
||||
ext_storage:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
Reference in New Issue
Block a user