- Resolving conflicted revision has become more robust.
- LiveSync now try to keep local changes when fetching from the rebuilt remote database.
- Now, all files will be restored after performing `fetch` immediately.
This commit is contained in:
vorotamoroz
2023-09-05 09:16:11 +01:00
parent 10decb7909
commit b8cb94c498
5 changed files with 62 additions and 31 deletions

View File

@@ -99,6 +99,7 @@ export class SetupLiveSync extends LiveSyncCommands {
newSettingW.encryptedCouchDBConnection = ""; newSettingW.encryptedCouchDBConnection = "";
const setupJustImport = "Just import setting"; const setupJustImport = "Just import setting";
const setupAsNew = "Set it up as secondary or subsequent device"; const setupAsNew = "Set it up as secondary or subsequent device";
const setupAsMerge = "Secondary device but try keeping local changes";
const setupAgain = "Reconfigure and reconstitute the data"; const setupAgain = "Reconfigure and reconstitute the data";
const setupManually = "Leave everything to me"; const setupManually = "Leave everything to me";
newSettingW.syncInternalFiles = false; newSettingW.syncInternalFiles = false;
@@ -108,7 +109,7 @@ export class SetupLiveSync extends LiveSyncCommands {
newSettingW.useIndexedDBAdapter = true; newSettingW.useIndexedDBAdapter = true;
} }
const setupType = await askSelectString(this.app, "How would you like to set it up?", [setupAsNew, setupAgain, setupJustImport, setupManually]); const setupType = await askSelectString(this.app, "How would you like to set it up?", [setupAsNew, setupAgain, setupAsMerge, setupJustImport, setupManually]);
if (setupType == setupJustImport) { if (setupType == setupJustImport) {
this.plugin.settings = newSettingW; this.plugin.settings = newSettingW;
this.plugin.usedPassphrase = ""; this.plugin.usedPassphrase = "";
@@ -117,6 +118,10 @@ export class SetupLiveSync extends LiveSyncCommands {
this.plugin.settings = newSettingW; this.plugin.settings = newSettingW;
this.plugin.usedPassphrase = ""; this.plugin.usedPassphrase = "";
await this.fetchLocal(); await this.fetchLocal();
} else if (setupType == setupAsMerge) {
this.plugin.settings = newSettingW;
this.plugin.usedPassphrase = "";
await this.fetchLocalWithKeepLocal();
} else if (setupType == setupAgain) { } else if (setupType == setupAgain) {
const confirm = "I know this operation will rebuild all my databases with files on this device, and files that are on the remote database and I didn't synchronize to any other devices will be lost and want to proceed indeed."; const confirm = "I know this operation will rebuild all my databases with files on this device, and files that are on the remote database and I didn't synchronize to any other devices will be lost and want to proceed indeed.";
if (await askSelectString(this.app, "Do you really want to do this?", ["Cancel", confirm]) != confirm) { if (await askSelectString(this.app, "Do you really want to do this?", ["Cancel", confirm]) != confirm) {
@@ -302,13 +307,11 @@ Of course, we are able to disable these features.`
Logger(`Database and storage reflection has been resumed!`, LOG_LEVEL_NOTICE); Logger(`Database and storage reflection has been resumed!`, LOG_LEVEL_NOTICE);
this.plugin.settings.suspendParseReplicationResult = false; this.plugin.settings.suspendParseReplicationResult = false;
this.plugin.settings.suspendFileWatching = false; this.plugin.settings.suspendFileWatching = false;
await this.plugin.saveSettings();
if (this.plugin.settings.readChunksOnline) {
await this.plugin.syncAllFiles(true); await this.plugin.syncAllFiles(true);
await this.plugin.loadQueuedFiles(); await this.plugin.loadQueuedFiles();
// Start processing
this.plugin.procQueuedFiles(); this.plugin.procQueuedFiles();
} await this.plugin.saveSettings();
} }
async askUseNewAdapter() { async askUseNewAdapter() {
if (!this.plugin.settings.useIndexedDBAdapter) { if (!this.plugin.settings.useIndexedDBAdapter) {
@@ -353,6 +356,25 @@ Of course, we are able to disable these features.`
await this.resumeReflectingDatabase(); await this.resumeReflectingDatabase();
await this.askHiddenFileConfiguration({ enableFetch: true }); await this.askHiddenFileConfiguration({ enableFetch: true });
} }
async fetchLocalWithKeepLocal() {
this.suspendExtraSync();
this.askUseNewAdapter();
await this.suspendReflectingDatabase();
await this.plugin.realizeSettingSyncMode();
await this.plugin.resetLocalDatabase();
await delay(1000);
await this.plugin.initializeDatabase(true);
await this.plugin.markRemoteResolved();
await this.plugin.openDatabase();
this.plugin.isReady = true;
await delay(500);
await this.plugin.replicateAllFromServer(true);
await delay(1000);
await this.plugin.replicateAllFromServer(true);
await this.fetchRemoteChunks();
await this.resumeReflectingDatabase();
await this.askHiddenFileConfiguration({ enableFetch: true });
}
async rebuildRemote() { async rebuildRemote() {
this.suspendExtraSync(); this.suspendExtraSync();
await this.plugin.realizeSettingSyncMode(); await this.plugin.realizeSettingSyncMode();

Submodule src/lib updated: 8872807f47...70eb916288

View File

@@ -1167,17 +1167,25 @@ export default class ObsidianLiveSyncPlugin extends Plugin
if (docEntry._deleted || docEntry.deleted) { if (docEntry._deleted || docEntry.deleted) {
// This occurs not only when files are deleted, but also when conflicts are resolved. // This occurs not only when files are deleted, but also when conflicts are resolved.
// We have to check no other revisions are left. // We have to check no other revisions are left.
const lastDocs = await this.localDatabase.getDBEntry(path); const existDoc = await this.localDatabase.getDBEntry(path, { conflicts: true });
if (path != file.path) { if (path != file.path) {
Logger(`delete skipped: ${file.path} :Not exactly matched`, LOG_LEVEL_VERBOSE); Logger(`delete skipped: ${file.path} :Not exactly matched`, LOG_LEVEL_VERBOSE);
} }
if (lastDocs === false) { if (existDoc === false) {
await this.deleteVaultItem(file); await this.deleteVaultItem(file);
} else { } else {
// it perhaps delete some revisions. if (existDoc._conflicts) {
// may be we have to reload this if (this.settings.writeDocumentsIfConflicted) {
Logger(`Delete: ${file.path}: Conflicted revision has been deleted, but there were more conflicts. `, LOG_LEVEL_INFO);
await this.pullFile(path, null, true); await this.pullFile(path, null, true);
Logger(`delete skipped:${file.path}`, LOG_LEVEL_VERBOSE); } else {
Logger(`Delete: ${file.path}: Conflicted revision has been deleted, but there were more conflicts...`);
this.queueConflictedOnlyActiveFile(file);
}
} else {
Logger(`Delete: ${file.path}: Conflict revision has been deleted and resolved`);
await this.pullFile(path, null, true);
}
} }
return; return;
} }
@@ -1274,6 +1282,19 @@ export default class ObsidianLiveSyncPlugin extends Plugin
this.dbChangeProcRunning = false; this.dbChangeProcRunning = false;
} }
} }
queueConflictedOnlyActiveFile(file: TFile) {
if (!this.settings.checkConflictOnlyOnOpen) {
this.queueConflictedCheck(file);
return true;
} else {
const af = this.app.workspace.getActiveFile();
if (af && af.path == file.path) {
this.queueConflictedCheck(file);
return true;
}
}
return false;
}
async handleDBChangedAsync(change: EntryBody) { async handleDBChangedAsync(change: EntryBody) {
const targetFile = getAbstractFileByPath(this.getPathWithoutPrefix(change)); const targetFile = getAbstractFileByPath(this.getPathWithoutPrefix(change));
@@ -1286,29 +1307,16 @@ export default class ObsidianLiveSyncPlugin extends Plugin
} else if (targetFile instanceof TFile) { } else if (targetFile instanceof TFile) {
const doc = change; const doc = change;
const file = targetFile; const file = targetFile;
const queueConflictCheck = () => {
if (!this.settings.checkConflictOnlyOnOpen) {
this.queueConflictedCheck(file);
return true;
} else {
const af = app.workspace.getActiveFile();
if (af && af.path == file.path) {
this.queueConflictedCheck(file);
return true;
}
}
return false;
}
if (this.settings.writeDocumentsIfConflicted) { if (this.settings.writeDocumentsIfConflicted) {
await this.doc2storage(doc, file); await this.doc2storage(doc, file);
queueConflictCheck(); this.queueConflictedOnlyActiveFile(file);
} else { } else {
const d = await this.localDatabase.getDBEntryMeta(this.getPath(change), { conflicts: true }, true); const d = await this.localDatabase.getDBEntryMeta(this.getPath(change), { conflicts: true }, true);
if (d && !d._conflicts) { if (d && !d._conflicts) {
await this.doc2storage(doc, file); await this.doc2storage(doc, file);
} else { } else {
if (!queueConflictCheck()) { if (!this.queueConflictedOnlyActiveFile(file)) {
Logger(`${this.getPath(change)} is conflicted, write to the storage has been pended.`, LOG_LEVEL_NOTICE); Logger(`${this.getPath(change)} is conflicted, write to the storage has been postponed.`, LOG_LEVEL_NOTICE);
} }
} }
} }

View File

@@ -477,7 +477,7 @@ export const requestToCouchDB = async (baseUri: string, username: string, passwo
export async function performRebuildDB(plugin: ObsidianLiveSyncPlugin, method: "localOnly" | "remoteOnly" | "rebuildBothByThisDevice") { export async function performRebuildDB(plugin: ObsidianLiveSyncPlugin, method: "localOnly" | "remoteOnly" | "rebuildBothByThisDevice") {
if (method == "localOnly") { if (method == "localOnly") {
await plugin.addOnSetup.fetchLocal(); await plugin.addOnSetup.fetchLocalWithKeepLocal();
} }
if (method == "remoteOnly") { if (method == "remoteOnly") {
await plugin.addOnSetup.rebuildRemote(); await plugin.addOnSetup.rebuildRemote();

View File

@@ -15,6 +15,7 @@
// "importsNotUsedAsValues": "error", // "importsNotUsedAsValues": "error",
"importHelpers": false, "importHelpers": false,
"alwaysStrict": true, "alwaysStrict": true,
"allowImportingTsExtensions": true,
"lib": [ "lib": [
"es2018", "es2018",
"DOM", "DOM",