From 9b1a8f606861b932d13f86f4be225a04de371f5e Mon Sep 17 00:00:00 2001 From: fyears <1142836+fyears@users.noreply.github.com> Date: Sun, 16 Jun 2024 19:48:40 +0800 Subject: [PATCH] push and pull add delete --- docs/sync_algorithm/v3/design.md | 22 +++++- pro/src/sync.ts | 118 +++++++++++++++++++++++++------ src/baseTypes.ts | 4 +- src/langs/en.json | 12 ++-- src/langs/zh_cn.json | 12 ++-- src/langs/zh_tw.json | 12 ++-- src/settings.ts | 10 ++- 7 files changed, 153 insertions(+), 37 deletions(-) diff --git a/docs/sync_algorithm/v3/design.md b/docs/sync_algorithm/v3/design.md index 8f0a0c0..8b8c0c7 100644 --- a/docs/sync_algorithm/v3/design.md +++ b/docs/sync_algorithm/v3/design.md @@ -52,7 +52,7 @@ Bidirectional: | local deleted | (04) delete remote | (05) pull | (01) clean history | (03) pull | | local created | (??) conflict | (??) conflict | (06) push | (11/12/13/14/15) conflict | -Incremental push only: +Incremental push: | local\remote | remote unchanged | remote modified | remote deleted | remote created | | --------------- | ---------------------------- | ---------------------------- | ---------------------- | ---------------------------- | @@ -61,7 +61,7 @@ Incremental push only: | local deleted | **(29) conflict do nothing** | **(30) conflict do nothing** | (01) clean history | **(28) conflict do nothing** | | local created | (??) conflict | (??) conflict | (06) push | **(23) conflict push** | -Incremental pull only: +Incremental pull: | local\remote | remote unchanged | remote modified | remote deleted | remote created | | --------------- | ---------------------- | ---------------------- | ---------------------------- | ---------------------- | @@ -69,3 +69,21 @@ Incremental pull only: | local modified | **(27) conflict pull** | **(24) conflict pull** | **(34) conflict do nothing** | (??) conflict | | local deleted | **(35) conflict pull** | (05) pull | (01) clean history | (03) pull | | local created | (??) conflict | (??) conflict | **(31) conflict do nothing** | **(22) conflict pull** | + +Incremental push and delete (diff decisionBranch: 38): + +| local\remote | remote unchanged | remote modified | remote deleted | remote created | +| --------------- | ---------------------------- | ---------------------------- | ---------------------- | ---------------------------- | +| local unchanged | (02/21) do nothing | **(26) conflict push** | **(32) conflict push** | (??) conflict | +| local modified | (10) push | **(25) conflict push** | (08) push | (??) conflict | +| local deleted | **(38) delete remote** | **(30) conflict do nothing** | (01) clean history | **(28) conflict do nothing** | +| local created | (??) conflict | (??) conflict | (06) push | **(23) conflict push** | + +Incremental pull and delete (diff decisionBranch: 39): + +| local\remote | remote unchanged | remote modified | remote deleted | remote created | +| --------------- | ---------------------- | ---------------------- | ---------------------------- | ---------------------- | +| local unchanged | (02/21) do nothing | (09) pull | **(39) delete local** | (??) conflict | +| local modified | **(27) conflict pull** | **(24) conflict pull** | **(34) conflict do nothing** | (??) conflict | +| local deleted | **(35) conflict pull** | (05) pull | (01) clean history | (03) pull | +| local created | (??) conflict | (??) conflict | **(31) conflict do nothing** | **(22) conflict pull** | diff --git a/pro/src/sync.ts b/pro/src/sync.ts index 578343e..df1c53d 100644 --- a/pro/src/sync.ts +++ b/pro/src/sync.ts @@ -335,7 +335,10 @@ const getSyncPlanInplace = async ( mixedEntry.decision = "folder_existed_both_then_do_nothing"; mixedEntry.change = false; } else if (local !== undefined && remote === undefined) { - if (syncDirection === "incremental_pull_only") { + if ( + syncDirection === "incremental_pull_only" || + syncDirection === "incremental_pull_and_delete_only" + ) { mixedEntry.decisionBranch = 107; mixedEntry.decision = "folder_to_skip"; mixedEntry.change = false; @@ -346,7 +349,10 @@ const getSyncPlanInplace = async ( mixedEntry.change = true; } } else if (local === undefined && remote !== undefined) { - if (syncDirection === "incremental_push_only") { + if ( + syncDirection === "incremental_push_only" || + syncDirection === "incremental_push_and_delete_only" + ) { mixedEntry.decisionBranch = 108; mixedEntry.decision = "folder_to_skip"; mixedEntry.change = false; @@ -373,7 +379,10 @@ const getSyncPlanInplace = async ( } else if (local !== undefined && remote === undefined) { if (prevSync !== undefined) { // then the folder is deleted on remote - if (syncDirection === "incremental_push_only") { + if ( + syncDirection === "incremental_push_only" || + syncDirection === "incremental_push_and_delete_only" + ) { mixedEntry.decisionBranch = 122; mixedEntry.decision = "folder_to_skip"; keptFolder.add(getParentFolder(key)); @@ -383,6 +392,10 @@ const getSyncPlanInplace = async ( mixedEntry.decision = "folder_to_skip"; mixedEntry.change = false; keptFolder.add(getParentFolder(key)); + } else if (syncDirection === "incremental_pull_and_delete_only") { + mixedEntry.decisionBranch = 135; + mixedEntry.decision = "folder_to_be_deleted_on_local"; + mixedEntry.change = true; } else { // bidirectional mixedEntry.decisionBranch = 124; @@ -392,13 +405,19 @@ const getSyncPlanInplace = async ( } else { // then the folder is created on local - if (syncDirection === "incremental_push_only") { + if ( + syncDirection === "incremental_push_only" || + syncDirection === "incremental_push_and_delete_only" + ) { mixedEntry.decisionBranch = 125; mixedEntry.decision = "folder_existed_local_then_also_create_remote"; mixedEntry.change = true; keptFolder.add(getParentFolder(key)); - } else if (syncDirection === "incremental_pull_only") { + } else if ( + syncDirection === "incremental_pull_only" || + syncDirection === "incremental_pull_and_delete_only" + ) { mixedEntry.decisionBranch = 126; mixedEntry.decision = "folder_to_skip"; mixedEntry.change = false; @@ -420,7 +439,14 @@ const getSyncPlanInplace = async ( mixedEntry.decision = "folder_to_skip"; mixedEntry.change = false; keptFolder.add(getParentFolder(key)); - } else if (syncDirection === "incremental_pull_only") { + } else if (syncDirection === "incremental_push_and_delete_only") { + mixedEntry.decisionBranch = 136; + mixedEntry.decision = "folder_to_be_deleted_on_remote"; + mixedEntry.change = true; + } else if ( + syncDirection === "incremental_pull_only" || + syncDirection === "incremental_pull_and_delete_only" + ) { mixedEntry.decisionBranch = 129; mixedEntry.decision = "folder_to_skip"; mixedEntry.change = false; @@ -433,12 +459,18 @@ const getSyncPlanInplace = async ( } } else { // then the folder is created on remote - if (syncDirection === "incremental_push_only") { + if ( + syncDirection === "incremental_push_only" || + syncDirection === "incremental_push_and_delete_only" + ) { mixedEntry.decisionBranch = 131; mixedEntry.decision = "folder_to_skip"; mixedEntry.change = false; keptFolder.add(getParentFolder(key)); - } else if (syncDirection === "incremental_pull_only") { + } else if ( + syncDirection === "incremental_pull_only" || + syncDirection === "incremental_pull_and_delete_only" + ) { mixedEntry.decisionBranch = 132; mixedEntry.decision = "folder_existed_remote_then_also_create_local"; @@ -497,7 +529,10 @@ const getSyncPlanInplace = async ( skipSizeLargerThan <= 0 || remote.sizeEnc! <= skipSizeLargerThan ) { - if (syncDirection === "incremental_push_only") { + if ( + syncDirection === "incremental_push_only" || + syncDirection === "incremental_push_and_delete_only" + ) { mixedEntry.decisionBranch = 26; mixedEntry.decision = "conflict_modified_then_keep_local"; mixedEntry.change = true; @@ -521,7 +556,10 @@ const getSyncPlanInplace = async ( skipSizeLargerThan <= 0 || local.sizeEnc! <= skipSizeLargerThan ) { - if (syncDirection === "incremental_pull_only") { + if ( + syncDirection === "incremental_pull_only" || + syncDirection === "incremental_pull_and_delete_only" + ) { mixedEntry.decisionBranch = 27; mixedEntry.decision = "conflict_modified_then_keep_remote"; mixedEntry.change = true; @@ -578,12 +616,18 @@ const getSyncPlanInplace = async ( mixedEntry.change = true; keptFolder.add(getParentFolder(key)); } - } else if (syncDirection === "incremental_pull_only") { + } else if ( + syncDirection === "incremental_pull_only" || + syncDirection === "incremental_pull_and_delete_only" + ) { mixedEntry.decisionBranch = 22; mixedEntry.decision = "conflict_created_then_keep_remote"; mixedEntry.change = true; keptFolder.add(getParentFolder(key)); - } else if (syncDirection === "incremental_push_only") { + } else if ( + syncDirection === "incremental_push_only" || + syncDirection === "incremental_push_and_delete_only" + ) { mixedEntry.decisionBranch = 23; mixedEntry.decision = "conflict_created_then_keep_local"; mixedEntry.change = true; @@ -630,12 +674,18 @@ const getSyncPlanInplace = async ( mixedEntry.change = true; keptFolder.add(getParentFolder(key)); } - } else if (syncDirection === "incremental_pull_only") { + } else if ( + syncDirection === "incremental_pull_only" || + syncDirection === "incremental_pull_and_delete_only" + ) { mixedEntry.decisionBranch = 24; mixedEntry.decision = "conflict_modified_then_keep_remote"; mixedEntry.change = true; keptFolder.add(getParentFolder(key)); - } else if (syncDirection === "incremental_push_only") { + } else if ( + syncDirection === "incremental_push_only" || + syncDirection === "incremental_push_and_delete_only" + ) { mixedEntry.decisionBranch = 25; mixedEntry.decision = "conflict_modified_then_keep_local"; mixedEntry.change = true; @@ -664,7 +714,10 @@ const getSyncPlanInplace = async ( skipSizeLargerThan <= 0 || remote.sizeEnc! <= skipSizeLargerThan ) { - if (syncDirection === "incremental_push_only") { + if ( + syncDirection === "incremental_push_only" || + syncDirection === "incremental_push_and_delete_only" + ) { mixedEntry.decisionBranch = 28; mixedEntry.decision = "conflict_created_then_do_nothing"; mixedEntry.change = false; @@ -692,7 +745,14 @@ const getSyncPlanInplace = async ( mixedEntry.decision = "conflict_created_then_do_nothing"; mixedEntry.change = false; keptFolder.add(getParentFolder(key)); - } else if (syncDirection === "incremental_pull_only") { + } else if (syncDirection === "incremental_push_and_delete_only") { + mixedEntry.decisionBranch = 38; + mixedEntry.decision = "local_is_deleted_thus_also_delete_remote"; + mixedEntry.change = true; + } else if ( + syncDirection === "incremental_pull_only" || + syncDirection === "incremental_pull_and_delete_only" + ) { mixedEntry.decisionBranch = 35; mixedEntry.decision = "conflict_created_then_keep_remote"; mixedEntry.change = true; @@ -708,7 +768,10 @@ const getSyncPlanInplace = async ( skipSizeLargerThan <= 0 || remote.sizeEnc! <= skipSizeLargerThan ) { - if (syncDirection === "incremental_push_only") { + if ( + syncDirection === "incremental_push_only" || + syncDirection === "incremental_push_and_delete_only" + ) { mixedEntry.decisionBranch = 30; mixedEntry.decision = "conflict_created_then_do_nothing"; mixedEntry.change = false; @@ -733,7 +796,10 @@ const getSyncPlanInplace = async ( if (prevSync === undefined) { // if A is not in the previous list, A is new if (skipSizeLargerThan <= 0 || local.sizeEnc! <= skipSizeLargerThan) { - if (syncDirection === "incremental_pull_only") { + if ( + syncDirection === "incremental_pull_only" || + syncDirection === "incremental_pull_and_delete_only" + ) { mixedEntry.decisionBranch = 31; mixedEntry.decision = "conflict_created_then_do_nothing"; mixedEntry.change = false; @@ -756,7 +822,10 @@ const getSyncPlanInplace = async ( prevSync.sizeEnc === local.sizeEnc ) { // if A is in the previous list and UNMODIFIED, A has been deleted by B - if (syncDirection === "incremental_push_only") { + if ( + syncDirection === "incremental_push_only" || + syncDirection === "incremental_push_and_delete_only" + ) { mixedEntry.decisionBranch = 32; mixedEntry.decision = "conflict_created_then_keep_local"; mixedEntry.change = true; @@ -764,6 +833,10 @@ const getSyncPlanInplace = async ( mixedEntry.decisionBranch = 33; mixedEntry.decision = "conflict_created_then_do_nothing"; mixedEntry.change = false; + } else if (syncDirection === "incremental_pull_and_delete_only") { + mixedEntry.decisionBranch = 39; + mixedEntry.decision = "remote_is_deleted_thus_also_delete_local"; + mixedEntry.change = true; } else { mixedEntry.decisionBranch = 7; mixedEntry.decision = "remote_is_deleted_thus_also_delete_local"; @@ -772,7 +845,10 @@ const getSyncPlanInplace = async ( } else { // if A is in the previous list and MODIFIED, A has been deleted by B but modified by A if (skipSizeLargerThan <= 0 || local.sizeEnc! <= skipSizeLargerThan) { - if (syncDirection === "incremental_pull_only") { + if ( + syncDirection === "incremental_pull_only" || + syncDirection === "incremental_pull_and_delete_only" + ) { mixedEntry.decisionBranch = 34; mixedEntry.decision = "conflict_created_then_do_nothing"; mixedEntry.change = false; @@ -826,7 +902,7 @@ const getSyncPlanInplace = async ( mixedEntityMappings["/$@meta"] = { key: "/$@meta", // don't mess up with the types sideNotes: { - version: "20240525 fs version", + version: "20240616 fs version", generateTime: currTime, generateTimeFmt: currTimeFmt, service: settings.serviceType, diff --git a/src/baseTypes.ts b/src/baseTypes.ts index e37f233..fe9a545 100644 --- a/src/baseTypes.ts +++ b/src/baseTypes.ts @@ -120,7 +120,9 @@ export interface WebdisConfig { export type SyncDirectionType = | "bidirectional" | "incremental_pull_only" - | "incremental_push_only"; + | "incremental_push_only" + | "incremental_pull_and_delete_only" + | "incremental_push_and_delete_only"; export type CipherMethodType = "rclone-base64" | "openssl-base64" | "unknown"; diff --git a/src/langs/en.json b/src/langs/en.json index 62ae6a4..0a0ce8d 100644 --- a/src/langs/en.json +++ b/src/langs/en.json @@ -292,11 +292,15 @@ "settings_protectmodifypercentage_custom_desc": "custom", "settings_protectmodifypercentage_customfield": "Custom Abort Sync If Modification Above Percentage", "settings_protectmodifypercentage_customfield_desc": "You need to enter a number between 0 (inclusive) and 100 (inclusive). Float number is also allowed.", - "setting_syncdirection": "Sync Direction", - "setting_syncdirection_desc": "Which direction should the plugin sync to? Please be aware that only CHANGED files (based on time and size) are synced regardless any option.", + + "setting_syncdirection": "Sync Direction (experimental)", + "setting_syncdirection_desc": "

Which direction should the plugin sync to? Please be aware that only CHANGED files (based on time and size) are synced regardless any option.

  1. Bidirectional: Changes on both side are synced to the other side.
  2. Incremental Push: Locally created or modifed files are copied to remote.
  3. Incremental Pull: Remotely created or modifed files are copied to local.
  4. Incremental Push And Delete: Locally created or modifed files are copied to remote, and local deletion operations are run on remote too.
  5. Incremental Pull And Delete: Remotely created or modifed files are copied to local, and remote deletion operations are run on local too.
", "setting_syncdirection_bidirectional_desc": "Bidirectional (default)", - "setting_syncdirection_incremental_push_only_desc": "Incremental Push Only (aka backup mode)", - "setting_syncdirection_incremental_pull_only_desc": "Incremental Pull Only", + "setting_syncdirection_incremental_push_only_desc": "Incremental Push (aka backup mode)", + "setting_syncdirection_incremental_pull_only_desc": "Incremental Pull", + "setting_syncdirection_incremental_push_and_delete_only_desc": "Incremental Push And Delete", + "setting_syncdirection_incremental_pull_and_delete_only_desc": "Incremental Pull And Delete", + "settings_enablemobilestatusbar": "Mobile Status Bar (experimental)", "settings_enablemobilestatusbar_desc": "By default Obsidian mobile hides status bar. But some users want to show it up. So here is a hack.", "settings_importexport": "Import and Export Partial Settings", diff --git a/src/langs/zh_cn.json b/src/langs/zh_cn.json index 0026b97..a449ba3 100644 --- a/src/langs/zh_cn.json +++ b/src/langs/zh_cn.json @@ -291,11 +291,15 @@ "settings_protectmodifypercentage_custom_desc": "自定义", "settings_protectmodifypercentage_customfield": "如果修改超过自定义百分比则中止同步", "settings_protectmodifypercentage_customfield_desc": "您需要输入 0(含)~ 100(含)的数字。小数也是可以的。", - "setting_syncdirection": "同步方向", - "setting_syncdirection_desc": "插件应该向哪里同步?注意每个选项都是只有修改了的文件(基于修改时间和大小判断)才会触发同步动作。", + + "setting_syncdirection": "同步方向(实验性质)", + "setting_syncdirection_desc": "

W插件应该向哪里同步?注意每个选项都是只有修改了的文件(基于修改时间和大小判断)才会触发同步动作。

  1. 双向同步:一边的变动会同步到另一边。
  2. 增量推送:本地创建或修改的文件会复制到远端。
  3. 增量拉取:远端创建或修改的文件会复制到本地。
  4. 增量推送带删除:本地创建或修改的文件会复制到远端,本地的删除也会在远端运行。
  5. 增量拉取带删除:远端创建或修改的文件会复制到本地,远端的删除也会在本地运行。
", "setting_syncdirection_bidirectional_desc": "双向同步(默认)", - "setting_syncdirection_incremental_push_only_desc": "只增量推送(也即:备份模式)", - "setting_syncdirection_incremental_pull_only_desc": "只增量拉取", + "setting_syncdirection_incremental_push_only_desc": "增量推送(也即:备份模式)", + "setting_syncdirection_incremental_pull_only_desc": "增量拉取", + "setting_syncdirection_incremental_push_and_delete_only_desc": "增量推送带删除", + "setting_syncdirection_incremental_pull_and_delete_only_desc": "增量拉取带删除", + "settings_enablemobilestatusbar": "手机的状态栏(实验性质)", "settings_enablemobilestatusbar_desc": "Obsidian 手机版默认隐藏了状态栏。有些用户希望展示它。这里提供了设置选项。", "settings_importexport": "导入导出部分设置", diff --git a/src/langs/zh_tw.json b/src/langs/zh_tw.json index 72120b0..b5b8e1b 100644 --- a/src/langs/zh_tw.json +++ b/src/langs/zh_tw.json @@ -290,11 +290,15 @@ "settings_protectmodifypercentage_custom_desc": "自定義", "settings_protectmodifypercentage_customfield": "如果修改超過自定義百分比則中止同步", "settings_protectmodifypercentage_customfield_desc": "您需要輸入 0(含)~ 100(含)的數字。小數也是可以的。", - "setting_syncdirection": "同步方向", - "setting_syncdirection_desc": "外掛應該向哪裡同步?注意每個選項都是隻有修改了的檔案(基於修改時間和大小判斷)才會觸發同步動作。", + + "setting_syncdirection": "同步方向(實驗性質)", + "setting_syncdirection_desc": "

W外掛應該向哪裡同步?注意每個選項都是隻有修改了的檔案(基於修改時間和大小判斷)才會觸發同步動作。

  1. 雙向同步:一邊的變動會同步到另一邊。
  2. 增量推送:本地建立或修改的檔案會複製到遠端。
  3. 增量拉取:遠端建立或修改的檔案會複製到本地。
  4. 增量推送帶刪除:本地建立或修改的檔案會複製到遠端,本地的刪除也會在遠端執行。
  5. 增量拉取帶刪除:遠端建立或修改的檔案會複製到本地,遠端的刪除也會在本地執行。
", "setting_syncdirection_bidirectional_desc": "雙向同步(預設)", - "setting_syncdirection_incremental_push_only_desc": "只增量推送(也即:備份模式)", - "setting_syncdirection_incremental_pull_only_desc": "只增量拉取", + "setting_syncdirection_incremental_push_only_desc": "增量推送(也即:備份模式)", + "setting_syncdirection_incremental_pull_only_desc": "增量拉取", + "setting_syncdirection_incremental_push_and_delete_only_desc": "增量推送帶刪除", + "setting_syncdirection_incremental_pull_and_delete_only_desc": "增量拉取帶刪除", + "settings_enablemobilestatusbar": "手機的狀態列(實驗性質)", "settings_enablemobilestatusbar_desc": "Obsidian 手機版預設隱藏了狀態列。有些使用者希望展示它。這裡提供了設定選項。", "settings_importexport": "匯入匯出部分設定", diff --git a/src/settings.ts b/src/settings.ts index 9653040..ad5dc89 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -2369,7 +2369,7 @@ export class RemotelySaveSettingTab extends PluginSettingTab { new Setting(advDiv) .setName(t("setting_syncdirection")) - .setDesc(t("setting_syncdirection_desc")) + .setDesc(stringToFragment(t("setting_syncdirection_desc"))) .addDropdown((dropdown) => { dropdown.addOption( "bidirectional", @@ -2383,6 +2383,14 @@ export class RemotelySaveSettingTab extends PluginSettingTab { "incremental_pull_only", t("setting_syncdirection_incremental_pull_only_desc") ); + dropdown.addOption( + "incremental_push_and_delete_only", + t("setting_syncdirection_incremental_push_and_delete_only_desc") + ); + dropdown.addOption( + "incremental_pull_and_delete_only", + t("setting_syncdirection_incremental_pull_and_delete_only_desc") + ); dropdown .setValue(this.plugin.settings.syncDirection ?? "bidirectional")