From 80f92ce17dcce5860550a1aff9d0b94cad1769ad Mon Sep 17 00:00:00 2001 From: tiagoarroz Date: Mon, 4 May 2026 16:44:47 +0100 Subject: [PATCH 1/4] Auto-resolve log conflicts locally --- src/sync-manager.ts | 69 +++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/src/sync-manager.ts b/src/sync-manager.ts index da11a5a..a073ae9 100644 --- a/src/sync-manager.ts +++ b/src/sync-manager.ts @@ -452,37 +452,60 @@ export default class SyncManager { // commit the sync. let conflictResolutions: ConflictResolution[] = []; - if (conflicts.length > 0) { - await this.logger.warn("Found conflicts", conflicts); + const logConflictResolutions = conflicts + .filter((conflict) => this.isLogFile(conflict.filePath)) + .map((conflict) => ({ + filePath: conflict.filePath, + content: conflict.localContent, + })); + if (logConflictResolutions.length > 0) { + // Os logs são específicos de cada cofre e podem mudar em cada execução, + // por isso estes conflitos são resolvidos mantendo sempre o conteúdo local. + await this.logger.info( + "Automatically resolved log conflicts", + logConflictResolutions.map((resolution) => resolution.filePath), + ); + conflictResolutions.push(...logConflictResolutions); + conflictActions.push( + ...logConflictResolutions.map((resolution: ConflictResolution) => { + return { type: "upload" as const, filePath: resolution.filePath }; + }), + ); + } + + const remainingConflicts = conflicts.filter( + (conflict) => !this.isLogFile(conflict.filePath), + ); + + if (remainingConflicts.length > 0) { + await this.logger.warn("Found conflicts", remainingConflicts); if (this.settings.conflictHandling === "ask") { // Here we block the sync process until the user has resolved all the conflicts - conflictResolutions = await this.onConflicts(conflicts); - conflictActions = conflictResolutions.map( - (resolution: ConflictResolution) => { - return { type: "upload", filePath: resolution.filePath }; - }, + const manualConflictResolutions = + await this.onConflicts(remainingConflicts); + conflictResolutions.push(...manualConflictResolutions); + conflictActions.push( + ...manualConflictResolutions.map( + (resolution: ConflictResolution) => { + return { type: "upload" as const, filePath: resolution.filePath }; + }, + ), ); } else if (this.settings.conflictHandling === "overwriteLocal") { // The user explicitly wants to always overwrite the local file // in case of conflicts so we just download the remote file to solve it - - // It's not necessary to set conflict resolutions as the content the - // user expect must be the content of the remote file with no changes. - conflictActions = conflictResolutions.map( - (resolution: ConflictResolution) => { - return { type: "download", filePath: resolution.filePath }; - }, + conflictActions.push( + ...remainingConflicts.map((conflict: ConflictFile) => { + return { type: "download" as const, filePath: conflict.filePath }; + }), ); } else if (this.settings.conflictHandling === "overwriteRemote") { // The user explicitly wants to always overwrite the remote file // in case of conflicts so we just upload the remote file to solve it. - - // It's not necessary to set conflict resolutions as the content the - // user expect must be the content of the local file with no changes. - conflictActions = conflictResolutions.map( - (resolution: ConflictResolution) => { - return { type: "upload", filePath: resolution.filePath }; - }, + conflictActions.push( + ...remainingConflicts.map((conflict: ConflictFile) => { + return { type: "upload" as const, filePath: conflict.filePath }; + }), ); } } @@ -574,6 +597,10 @@ export default class SyncManager { await this.commitSync(newTreeFiles, treeSha, conflictResolutions); } + private isLogFile(filePath: string): boolean { + return filePath === `${this.vault.configDir}/${LOG_FILE_NAME}`; + } + /** * Finds conflicts between local and remote files. * @param filesMetadata Remote files metadata From bcf3369667488a1cd73f29eb206dbe1311659461 Mon Sep 17 00:00:00 2001 From: tiagoarroz Date: Mon, 4 May 2026 16:53:40 +0100 Subject: [PATCH 2/4] Make log conflict auto-resolution configurable --- src/settings/settings.ts | 2 ++ src/settings/tab.ts | 14 ++++++++++++++ src/sync-manager.ts | 11 ++++++++--- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/settings/settings.ts b/src/settings/settings.ts index 97baf09..0a3e637 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -9,6 +9,7 @@ export interface GitHubSyncSettings { syncOnStartup: boolean; syncConfigDir: boolean; conflictHandling: "overwriteLocal" | "ask" | "overwriteRemote"; + autoResolveLogConflicts: boolean; conflictViewMode: "default" | "unified" | "split"; showStatusBarItem: boolean; showSyncRibbonButton: boolean; @@ -27,6 +28,7 @@ export const DEFAULT_SETTINGS: GitHubSyncSettings = { syncOnStartup: false, syncConfigDir: false, conflictHandling: "ask", + autoResolveLogConflicts: true, conflictViewMode: "default", showStatusBarItem: true, showSyncRibbonButton: true, diff --git a/src/settings/tab.ts b/src/settings/tab.ts index 8921460..0eccb27 100644 --- a/src/settings/tab.ts +++ b/src/settings/tab.ts @@ -189,6 +189,20 @@ export default class GitHubSyncSettingsTab extends PluginSettingTab { }); }); + new Setting(containerEl) + .setName("Automatically resolve conflits in logs") + .setDesc( + "Automatically resolves conflits in this plugin's logs prioritizing the local file", + ) + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.autoResolveLogConflicts) + .onChange(async (value) => { + this.plugin.settings.autoResolveLogConflicts = value; + await this.plugin.saveSettings(); + }); + }); + new Setting(containerEl).setName("Interface").setHeading(); new Setting(containerEl) diff --git a/src/sync-manager.ts b/src/sync-manager.ts index a073ae9..9358313 100644 --- a/src/sync-manager.ts +++ b/src/sync-manager.ts @@ -452,8 +452,11 @@ export default class SyncManager { // commit the sync. let conflictResolutions: ConflictResolution[] = []; - const logConflictResolutions = conflicts - .filter((conflict) => this.isLogFile(conflict.filePath)) + const autoResolvableLogConflicts = this.settings.autoResolveLogConflicts + ? conflicts.filter((conflict) => this.isLogFile(conflict.filePath)) + : []; + + const logConflictResolutions = autoResolvableLogConflicts .map((conflict) => ({ filePath: conflict.filePath, content: conflict.localContent, @@ -474,7 +477,9 @@ export default class SyncManager { } const remainingConflicts = conflicts.filter( - (conflict) => !this.isLogFile(conflict.filePath), + (conflict) => + !this.settings.autoResolveLogConflicts || + !this.isLogFile(conflict.filePath), ); if (remainingConflicts.length > 0) { From 5e3020e719fb94a43e2c74d8cc30ed8e4c84a8a3 Mon Sep 17 00:00:00 2001 From: tiagoarroz Date: Tue, 5 May 2026 09:43:35 +0100 Subject: [PATCH 3/4] Translate log conflict comment to English --- src/sync-manager.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sync-manager.ts b/src/sync-manager.ts index 9358313..b8ac9bc 100644 --- a/src/sync-manager.ts +++ b/src/sync-manager.ts @@ -460,10 +460,10 @@ export default class SyncManager { .map((conflict) => ({ filePath: conflict.filePath, content: conflict.localContent, - })); + })); if (logConflictResolutions.length > 0) { - // Os logs são específicos de cada cofre e podem mudar em cada execução, - // por isso estes conflitos são resolvidos mantendo sempre o conteúdo local. + // Logs are specific to each vault and can change on every run, + // so these conflicts are always resolved by keeping local content. await this.logger.info( "Automatically resolved log conflicts", logConflictResolutions.map((resolution) => resolution.filePath), From c397c173f33d2b707c4da3fe9866f4bbdad94bed Mon Sep 17 00:00:00 2001 From: tiagoarroz Date: Wed, 6 May 2026 09:06:12 +0100 Subject: [PATCH 4/4] Fix conflict spelling in log auto-resolve setting --- src/settings/tab.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/settings/tab.ts b/src/settings/tab.ts index 0eccb27..cca35fc 100644 --- a/src/settings/tab.ts +++ b/src/settings/tab.ts @@ -190,9 +190,9 @@ export default class GitHubSyncSettingsTab extends PluginSettingTab { }); new Setting(containerEl) - .setName("Automatically resolve conflits in logs") + .setName("Automatically resolve conflicts in logs") .setDesc( - "Automatically resolves conflits in this plugin's logs prioritizing the local file", + "Automatically resolves conflicts in this plugin's logs prioritizing the local file", ) .addToggle((toggle) => { toggle