mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2025-12-13 17:55:56 +00:00
## 0.24.21
### Fixed
- No longer conflicted files are handled in the boot-up process. No more unexpected overwriting.
- It ignores `Always overwrite with a newer file`, and always be prevented for the safety. Please pick it manually or open the file.
- Some log messages on conflict resolution has been corrected.
- Automatic merge notifications, displayed on the grounds of `same`, have been degraded to logs.
### Improved
- Now we can fetch the remote database with keeping local files completely intact.
- In new option, all files are stored into the local database before the fetching, and will be merged automatically or detected as conflicts.
- The dialogue presenting options when performing `Fetch` are now more informative.
### Refactored
- Some class methods have been fixed its arguments to be more consistent.
- Types have been defined for some conditional results.
This commit is contained in:
@@ -1306,7 +1306,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
|
||||
);
|
||||
return;
|
||||
}
|
||||
const docXDoc = await this.localDatabase.getDBEntryFromMeta(old, {}, false, false);
|
||||
const docXDoc = await this.localDatabase.getDBEntryFromMeta(old, false, false);
|
||||
if (docXDoc == false) {
|
||||
throw "Could not load the document";
|
||||
}
|
||||
@@ -1440,7 +1440,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
|
||||
// this._log(`STORAGE --> DB:${prefixedFileName}: (config) Skipped (Same time)`, LOG_LEVEL_VERBOSE);
|
||||
return true;
|
||||
}
|
||||
const oldC = await this.localDatabase.getDBEntryFromMeta(old, {}, false, false);
|
||||
const oldC = await this.localDatabase.getDBEntryFromMeta(old, false, false);
|
||||
if (oldC) {
|
||||
const d = (await deserialize(getDocDataAsArray(oldC.data), {})) as PluginDataEx;
|
||||
if (d.files.length == dt.files.length) {
|
||||
|
||||
@@ -1490,7 +1490,7 @@ Offline Changed files: ${files.length}`;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
const fileOnDB = await this.localDatabase.getDBEntryFromMeta(metaOnDB, {}, false, true, true);
|
||||
const fileOnDB = await this.localDatabase.getDBEntryFromMeta(metaOnDB, false, true);
|
||||
if (fileOnDB === false) {
|
||||
throw new Error(`Failed to read file from database:${storageFilePath}`);
|
||||
}
|
||||
|
||||
2
src/lib
2
src/lib
Submodule src/lib updated: a5d21afb61...1e24d2af19
@@ -313,13 +313,7 @@ export class ModuleDatabaseFileAccess extends AbstractModule implements IObsidia
|
||||
if (skipCheck && !(await this.checkIsTargetFile(meta.path))) {
|
||||
return false;
|
||||
}
|
||||
const doc = await this.localDatabase.getDBEntryFromMeta(
|
||||
meta as LoadedEntry,
|
||||
undefined,
|
||||
false,
|
||||
waitForReady,
|
||||
true
|
||||
);
|
||||
const doc = await this.localDatabase.getDBEntryFromMeta(meta as LoadedEntry, false, waitForReady);
|
||||
if (doc === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
||||
}
|
||||
}
|
||||
}
|
||||
async fetchLocal(makeLocalChunkBeforeSync?: boolean) {
|
||||
async fetchLocal(makeLocalChunkBeforeSync?: boolean, preventMakeLocalFilesBeforeSync?: boolean) {
|
||||
await this.core.$allSuspendExtraSync();
|
||||
await this.askUseNewAdapter();
|
||||
this.core.settings.isConfigured = true;
|
||||
@@ -189,6 +189,10 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
||||
this.core.$$markIsReady();
|
||||
if (makeLocalChunkBeforeSync) {
|
||||
await this.core.fileHandler.createAllChunks(true);
|
||||
} else if (!preventMakeLocalFilesBeforeSync) {
|
||||
await this.core.$$initializeDatabase(true);
|
||||
} else {
|
||||
// Do not create local file entries before sync (Means use remote information)
|
||||
}
|
||||
await this.core.$$markRemoteResolved();
|
||||
await delay(500);
|
||||
|
||||
@@ -338,7 +338,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
||||
|
||||
// If `Read chunks online` is disabled, chunks should be transferred before here.
|
||||
// However, in some cases, chunks are after that. So, if missing chunks exist, we have to wait for them.
|
||||
const doc = await this.localDatabase.getDBEntryFromMeta({ ...dbDoc }, {}, false, true, true);
|
||||
const doc = await this.localDatabase.getDBEntryFromMeta({ ...dbDoc }, false, true);
|
||||
if (!doc) {
|
||||
Logger(
|
||||
`Something went wrong while gathering content of ${path} (${dbDoc._id.substring(0, 8)}, ${dbDoc._rev?.substring(0, 10)}) `,
|
||||
|
||||
@@ -44,13 +44,16 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
|
||||
return MISSING_OR_ERROR;
|
||||
}
|
||||
eventHub.emitEvent("conflict-cancelled", path);
|
||||
this._log(`${title} Conflicted revision deleted ${displayRev(deleteRevision)} ${path}`, LOG_LEVEL_INFO);
|
||||
this._log(
|
||||
`${title} Conflicted revision has been deleted ${displayRev(deleteRevision)} ${path}`,
|
||||
LOG_LEVEL_INFO
|
||||
);
|
||||
if ((await this.core.databaseFileAccess.getConflictedRevs(path)).length != 0) {
|
||||
this._log(`${title} some conflicts are left in ${path}`, LOG_LEVEL_INFO);
|
||||
return AUTO_MERGED;
|
||||
}
|
||||
this._log(`${title} ${path} is a plugin metadata file, no need to write to storage`, LOG_LEVEL_INFO);
|
||||
if (isPluginMetadata(path) || isCustomisationSyncMetadata(path)) {
|
||||
this._log(`${title} ${path} is a plugin metadata file, no need to write to storage`, LOG_LEVEL_INFO);
|
||||
return AUTO_MERGED;
|
||||
}
|
||||
// If no conflicts were found, write the resolved content to the storage.
|
||||
@@ -58,7 +61,8 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
|
||||
this._log(`Could not write the resolved content to the storage: ${path}`, LOG_LEVEL_NOTICE);
|
||||
return MISSING_OR_ERROR;
|
||||
}
|
||||
this._log(`${path} Has been merged automatically`, LOG_LEVEL_NOTICE);
|
||||
const level = subTitle.indexOf("same") !== -1 ? LOG_LEVEL_INFO : LOG_LEVEL_NOTICE;
|
||||
this._log(`${path} has been merged automatically`, level);
|
||||
return AUTO_MERGED;
|
||||
}
|
||||
|
||||
@@ -108,7 +112,9 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
|
||||
`${isSame ? "same" : ""}`,
|
||||
`${isBinary ? "binary" : ""}`,
|
||||
`${alwaysNewer ? "alwaysNewer" : ""}`,
|
||||
].join(",");
|
||||
]
|
||||
.filter((e) => e.trim())
|
||||
.join(",");
|
||||
return await this.core.$$resolveConflictByDeletingRev(path, loser.rev, subTitle);
|
||||
}
|
||||
// make diff.
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from "../../lib/src/common/types.ts";
|
||||
import { AbstractModule } from "../AbstractModule.ts";
|
||||
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||
import { $msg } from "../../lib/src/common/i18n.ts";
|
||||
|
||||
export class ModuleRedFlag extends AbstractModule implements ICoreModule {
|
||||
async isFlagFileExist(path: string) {
|
||||
@@ -105,16 +106,35 @@ export class ModuleRedFlag extends AbstractModule implements ICoreModule {
|
||||
`${FLAGMD_REDFLAG3} or ${FLAGMD_REDFLAG3_HR} has been detected! Self-hosted LiveSync will discard the local database and fetch everything from the remote once again.`,
|
||||
LOG_LEVEL_NOTICE
|
||||
);
|
||||
const makeLocalChunkBeforeSync =
|
||||
(await this.core.confirm.askYesNoDialog(
|
||||
`Do you want to create local chunks before fetching?
|
||||
> [!MORE]-
|
||||
> If creating local chunks before fetching, only the difference between the local and remote will be fetched.
|
||||
const method1 = $msg("RedFlag.Fetch.Method.FetchSafer");
|
||||
const method2 = $msg("RedFlag.Fetch.Method.FetchSmoother");
|
||||
const method3 = $msg("RedFlag.Fetch.Method.FetchTraditional");
|
||||
|
||||
const methods = [method1, method2, method3] as const;
|
||||
const chunkMode = await this.core.confirm.askSelectStringDialogue(
|
||||
$msg("RedFlag.Fetch.Method.Desc"),
|
||||
methods,
|
||||
{
|
||||
defaultAction: method1,
|
||||
timeout: 0,
|
||||
title: $msg("RedFlag.Fetch.Method.Title"),
|
||||
}
|
||||
);
|
||||
let makeLocalChunkBeforeSync = false;
|
||||
let preventMakeLocalFilesBeforeSync = false;
|
||||
if (chunkMode === method1) {
|
||||
preventMakeLocalFilesBeforeSync = true;
|
||||
} else if (chunkMode === method2) {
|
||||
makeLocalChunkBeforeSync = true;
|
||||
} else if (chunkMode === method3) {
|
||||
// Do nothing.
|
||||
} else {
|
||||
this._log("Cancelled the fetch operation", LOG_LEVEL_NOTICE);
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.core.rebuilder.$fetchLocal(makeLocalChunkBeforeSync, preventMakeLocalFilesBeforeSync);
|
||||
|
||||
`,
|
||||
{ defaultOption: "Yes", title: "Trick to transfer efficiently" }
|
||||
)) == "yes";
|
||||
await this.core.rebuilder.$fetchLocal(makeLocalChunkBeforeSync);
|
||||
await this.deleteRedFlag3();
|
||||
if (this.settings.suspendFileWatching) {
|
||||
if (
|
||||
|
||||
@@ -180,7 +180,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
||||
};
|
||||
initProcess.push(
|
||||
runAll("UPDATE DATABASE", filesExistOnlyInStorage, async (e) => {
|
||||
// console.warn("UPDATE DATABASE", e);
|
||||
// Exists in storage but not in database.
|
||||
const file = storageFileNameMap[storageFileNameCI2CS[e]];
|
||||
if (!this.core.$$isFileSizeExceeded(file.stat.size)) {
|
||||
const path = file.path;
|
||||
@@ -195,9 +195,16 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
||||
initProcess.push(
|
||||
runAll("UPDATE STORAGE", filesExistOnlyInDatabase, async (e) => {
|
||||
const w = databaseFileNameMap[databaseFileNameCI2CS[e]];
|
||||
// Exists in database but not in storage.
|
||||
const path = getPath(w) ?? e;
|
||||
if (w && !(w.deleted || w._deleted)) {
|
||||
if (!this.core.$$isFileSizeExceeded(w.size)) {
|
||||
// Prevent applying the conflicted state to the storage.
|
||||
const conflicted = await this.core.databaseFileAccess.getConflictedRevs(path);
|
||||
if (conflicted.length > 0) {
|
||||
this._log(`UPDATE STORAGE: ${path} has conflicts. skipped`, LOG_LEVEL_INFO);
|
||||
return;
|
||||
}
|
||||
// await this.pullFile(path, undefined, false, undefined, false);
|
||||
// Memo: No need to force
|
||||
await this.core.fileHandler.dbToStorage(path, null, true);
|
||||
@@ -229,6 +236,12 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
||||
initProcess.push(
|
||||
runAll("SYNC DATABASE AND STORAGE", fileMap, async (e) => {
|
||||
const { file, doc } = e;
|
||||
// Prevent applying the conflicted state to the storage.
|
||||
const conflicted = await this.core.databaseFileAccess.getConflictedRevs(file.path);
|
||||
if (conflicted.length > 0) {
|
||||
this._log(`SYNC DATABASE AND STORAGE: ${file.path} has conflicts. skipped`, LOG_LEVEL_INFO);
|
||||
return;
|
||||
}
|
||||
if (!this.core.$$isFileSizeExceeded(file.stat.size) && !this.core.$$isFileSizeExceeded(doc.size)) {
|
||||
await this.syncFileBetweenDBandStorage(file, doc);
|
||||
} else {
|
||||
|
||||
@@ -4,7 +4,7 @@ export interface Rebuilder {
|
||||
): Promise<void>;
|
||||
$rebuildRemote(): Promise<void>;
|
||||
$rebuildEverything(): Promise<void>;
|
||||
$fetchLocal(makeLocalChunkBeforeSync?: boolean): Promise<void>;
|
||||
$fetchLocal(makeLocalChunkBeforeSync?: boolean, preventMakeLocalFilesBeforeSync?: boolean): Promise<void>;
|
||||
|
||||
scheduleRebuild(): Promise<void>;
|
||||
scheduleFetch(): Promise<void>;
|
||||
|
||||
Reference in New Issue
Block a user