mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-11-05 15:58:19 +00:00
Stop conflict checks and continuations after shutdown
This commit is contained in:
1 parent
6d811f80e1
commit
d923510698
4 files changed
+389
-8
No files matched your search
@@ -55,6 +55,8 @@ export interface ConflictResolutionOperationsDependencies {
|
||||
}
|
||||
|
||||
export interface ConflictResolutionOperations {
|
||||
/** Stop continuations without cancelling file or database operations already started. */
|
||||
readonly dispose: () => void;
|
||||
readonly cancelActiveResolution: (filename: FilePathWithPrefix) => void;
|
||||
readonly resolveByDeletingRevision: (
|
||||
path: FilePathWithPrefix,
|
||||
@@ -68,13 +70,21 @@ export interface ConflictResolutionOperations {
|
||||
readonly resolveAllConflictedFilesByNewerOnes: () => Promise<void>;
|
||||
}
|
||||
|
||||
/** Create conflict-resolution operations which stop their continuations when disposed. */
|
||||
export function createConflictResolutionOperations(
|
||||
dependencies: ConflictResolutionOperationsDependencies
|
||||
): ConflictResolutionOperations {
|
||||
let disposed = false;
|
||||
const latestResolveRequestByFilename = new Map<FilePathWithPrefix, number>();
|
||||
let nextResolveRequestId = 0;
|
||||
|
||||
const dispose = (): void => {
|
||||
disposed = true;
|
||||
latestResolveRequestByFilename.clear();
|
||||
};
|
||||
|
||||
const cancelActiveResolution = (filename: FilePathWithPrefix): void => {
|
||||
if (disposed) return;
|
||||
latestResolveRequestByFilename.delete(filename);
|
||||
if (isLockAcquired(`conflict-resolve:${filename}`)) {
|
||||
dependencies.events.emitEvent(EVENT_CONFLICT_CANCELLED, filename);
|
||||
@@ -87,8 +97,11 @@ export function createConflictResolutionOperations(
|
||||
subTitle = "",
|
||||
showNotice = true
|
||||
): Promise<typeof MISSING_OR_ERROR | typeof AUTO_MERGED> => {
|
||||
if (disposed) return MISSING_OR_ERROR;
|
||||
const title = `Resolving ${subTitle ? `[${subTitle}]` : ""}:`;
|
||||
if (!(await dependencies.fileHandler.deleteRevisionFromDB(path, deleteRevision))) {
|
||||
const deleted = await dependencies.fileHandler.deleteRevisionFromDB(path, deleteRevision);
|
||||
if (disposed) return MISSING_OR_ERROR;
|
||||
if (!deleted) {
|
||||
dependencies.log(
|
||||
`${title} Could not delete conflicted revision ${displayRev(deleteRevision)} of ${path}`,
|
||||
LOG_LEVEL_NOTICE
|
||||
@@ -100,7 +113,9 @@ export function createConflictResolutionOperations(
|
||||
`${title} Conflicted revision has been deleted ${displayRev(deleteRevision)} ${path}`,
|
||||
LOG_LEVEL_INFO
|
||||
);
|
||||
if ((await dependencies.databaseFileAccess.getConflictedRevs(path)).length != 0) {
|
||||
const conflicts = await dependencies.databaseFileAccess.getConflictedRevs(path);
|
||||
if (disposed) return MISSING_OR_ERROR;
|
||||
if (conflicts.length != 0) {
|
||||
dependencies.log(`${title} some conflicts are left in ${path}`, LOG_LEVEL_INFO);
|
||||
return AUTO_MERGED;
|
||||
}
|
||||
@@ -111,7 +126,9 @@ export function createConflictResolutionOperations(
|
||||
// If no conflicts were found, write the resolved content to the storage.
|
||||
const documentPath = parseDocumentPath(path);
|
||||
const { path: filePath } = splitDocumentPath(documentPath);
|
||||
if (!(await dependencies.fileHandler.dbToStorage(documentPath, filePath, true))) {
|
||||
const reflected = await dependencies.fileHandler.dbToStorage(documentPath, filePath, true);
|
||||
if (disposed) return MISSING_OR_ERROR;
|
||||
if (!reflected) {
|
||||
dependencies.log(`Could not write the resolved content to the storage: ${path}`, LOG_LEVEL_NOTICE);
|
||||
return MISSING_OR_ERROR;
|
||||
}
|
||||
@@ -121,9 +138,11 @@ export function createConflictResolutionOperations(
|
||||
};
|
||||
|
||||
const checkConflictAndPerformAutoMerge = async (path: FilePathWithPrefix): Promise<diff_check_result> => {
|
||||
if (disposed) return CANCELLED;
|
||||
const ret = await dependencies
|
||||
.localDatabase()
|
||||
.tryAutoMerge(path, !dependencies.currentSettings().disableMarkdownAutoMerge);
|
||||
if (disposed) return CANCELLED;
|
||||
if ("ok" in ret) {
|
||||
return ret.ok;
|
||||
}
|
||||
@@ -131,7 +150,9 @@ export function createConflictResolutionOperations(
|
||||
if ("result" in ret) {
|
||||
const p = ret.result;
|
||||
// 1. Store the merged content to the storage.
|
||||
if (!(await dependencies.databaseFileAccess.storeContent(path, p))) {
|
||||
const stored = await dependencies.databaseFileAccess.storeContent(path, p);
|
||||
if (disposed) return CANCELLED;
|
||||
if (!stored) {
|
||||
dependencies.log(`Merged content cannot be stored:${path}`, LOG_LEVEL_NOTICE);
|
||||
return MISSING_OR_ERROR;
|
||||
}
|
||||
@@ -186,6 +207,7 @@ export function createConflictResolutionOperations(
|
||||
};
|
||||
|
||||
const resolve = async (filename: FilePathWithPrefix): Promise<void> => {
|
||||
if (disposed) return;
|
||||
const requestId = ++nextResolveRequestId;
|
||||
latestResolveRequestByFilename.set(filename, requestId);
|
||||
const serialisationKey = `conflict-resolve:${filename}`;
|
||||
@@ -196,12 +218,12 @@ export function createConflictResolutionOperations(
|
||||
dependencies.events.emitEvent(EVENT_CONFLICT_CANCELLED, filename);
|
||||
}
|
||||
return await withLock(serialisationKey, async () => {
|
||||
if (latestResolveRequestByFilename.get(filename) !== requestId) {
|
||||
if (disposed || latestResolveRequestByFilename.get(filename) !== requestId) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const conflictCheckResult = await checkConflictAndPerformAutoMerge(filename);
|
||||
if (latestResolveRequestByFilename.get(filename) !== requestId) {
|
||||
if (disposed || latestResolveRequestByFilename.get(filename) !== requestId) {
|
||||
return;
|
||||
}
|
||||
if (conflictCheckResult === NOT_CONFLICTED) {
|
||||
@@ -223,6 +245,7 @@ export function createConflictResolutionOperations(
|
||||
interaction: NO_INTERACTION,
|
||||
});
|
||||
}
|
||||
if (disposed) return;
|
||||
dependencies.log("[conflict] Automatically merged, but we have to check it again");
|
||||
await dependencies.conflict.queueCheckFor(filename);
|
||||
return;
|
||||
@@ -249,12 +272,15 @@ export function createConflictResolutionOperations(
|
||||
};
|
||||
|
||||
const resolveByNewest = async (filename: FilePathWithPrefix, showNotice = true): Promise<boolean> => {
|
||||
if (disposed) return false;
|
||||
const currentRev = await dependencies.databaseFileAccess.fetchEntryMeta(filename, undefined, true);
|
||||
if (disposed) return false;
|
||||
if (currentRev == false) {
|
||||
dependencies.log(`Could not get current revision of ${filename}`);
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
const revs = await dependencies.databaseFileAccess.getConflictedRevs(filename);
|
||||
if (disposed) return false;
|
||||
if (revs.length == 0) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
@@ -278,25 +304,31 @@ export function createConflictResolutionOperations(
|
||||
}
|
||||
return diff;
|
||||
});
|
||||
if (disposed) return false;
|
||||
dependencies.log(
|
||||
`Resolving conflict by newest: ${filename} (Newest: ${new Date(mTimeAndRev[0][0]).toLocaleString()}) (${mTimeAndRev.length} revisions exists)`
|
||||
);
|
||||
for (let i = 1; i < mTimeAndRev.length; i++) {
|
||||
if (disposed) return false;
|
||||
dependencies.log(
|
||||
`conflict: Deleting the older revision ${mTimeAndRev[i][1]} (${new Date(mTimeAndRev[i][0]).toLocaleString()}) of ${filename}`
|
||||
);
|
||||
await resolveByDeletingRevision(filename, mTimeAndRev[i][1], "NEWEST", showNotice);
|
||||
}
|
||||
if (disposed) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
const resolveAllConflictedFilesByNewerOnes = async (): Promise<void> => {
|
||||
if (disposed) return;
|
||||
dependencies.log(`Resolving conflicts by newer ones`, LOG_LEVEL_NOTICE);
|
||||
|
||||
const files = await dependencies.storageAccess.getFileNames();
|
||||
if (disposed) return;
|
||||
|
||||
let i = 0;
|
||||
for (const file of files) {
|
||||
if (disposed) return;
|
||||
i++;
|
||||
if (i % 10 === 0)
|
||||
dependencies.log(
|
||||
@@ -306,10 +338,12 @@ export function createConflictResolutionOperations(
|
||||
);
|
||||
await resolveByNewest(file, false);
|
||||
}
|
||||
if (disposed) return;
|
||||
dependencies.log(`Done!`, LOG_LEVEL_NOTICE, "resolveAllConflictedFilesByNewerOnes");
|
||||
};
|
||||
|
||||
return {
|
||||
dispose,
|
||||
cancelActiveResolution,
|
||||
resolveByDeletingRevision,
|
||||
checkConflictAndPerformAutoMerge,
|
||||
|
||||
Reference in new issue
Block a user