mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-05-09 01:01:51 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b02596dfa1 | ||
|
|
02c69b202e | ||
|
|
6b2c7b56a5 | ||
|
|
820168a5ab | ||
|
|
40015642e4 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.18.0",
|
"version": "0.18.2",
|
||||||
"minAppVersion": "0.9.12",
|
"minAppVersion": "0.9.12",
|
||||||
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"author": "vorotamoroz",
|
"author": "vorotamoroz",
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.18.0",
|
"version": "0.18.2",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.18.0",
|
"version": "0.18.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.18.0",
|
"version": "0.18.2",
|
||||||
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Tidy up. Even though it is experimental feature, So dirty...
|
//TODO: Tidy up. Even though it is experimental feature, So dirty...
|
||||||
async syncInternalFilesAndDatabase(direction: "push" | "pull" | "safe", showMessage: boolean, files: InternalFileInfo[] | false = false, targetFiles: string[] | false = false) {
|
async syncInternalFilesAndDatabase(direction: "push" | "pull" | "safe" | "pullForce" | "pushForce", showMessage: boolean, files: InternalFileInfo[] | false = false, targetFiles: string[] | false = false) {
|
||||||
await this.resolveConflictOnInternalFiles();
|
await this.resolveConflictOnInternalFiles();
|
||||||
const logLevel = showMessage ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO;
|
const logLevel = showMessage ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO;
|
||||||
Logger("Scanning hidden files.", logLevel, "sync_internal");
|
Logger("Scanning hidden files.", logLevel, "sync_internal");
|
||||||
@@ -289,43 +289,43 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
|
|
||||||
p.push(addProc(async () => {
|
p.push(addProc(async () => {
|
||||||
const xFileOnStorage = fileOnStorage;
|
const xFileOnStorage = fileOnStorage;
|
||||||
const xfileOnDatabase = fileOnDatabase;
|
const xFileOnDatabase = fileOnDatabase;
|
||||||
if (xFileOnStorage && xfileOnDatabase) {
|
if (xFileOnStorage && xFileOnDatabase) {
|
||||||
// Both => Synchronize
|
// Both => Synchronize
|
||||||
if (xfileOnDatabase.mtime == cache.docMtime && xFileOnStorage.mtime == cache.storageMtime) {
|
if ((direction != "pullForce" && direction != "pushForce") && xFileOnDatabase.mtime == cache.docMtime && xFileOnStorage.mtime == cache.storageMtime) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const nw = compareMTime(xFileOnStorage.mtime, xfileOnDatabase.mtime);
|
const nw = compareMTime(xFileOnStorage.mtime, xFileOnDatabase.mtime);
|
||||||
if (nw > 0) {
|
if (nw > 0 || direction == "pushForce") {
|
||||||
await this.storeInternalFileToDatabase(xFileOnStorage);
|
await this.storeInternalFileToDatabase(xFileOnStorage);
|
||||||
}
|
}
|
||||||
if (nw < 0) {
|
if (nw < 0 || direction == "pullForce") {
|
||||||
// skip if not extraction performed.
|
// skip if not extraction performed.
|
||||||
if (!await this.extractInternalFileFromDatabase(filename))
|
if (!await this.extractInternalFileFromDatabase(filename))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If process successfully updated or file contents are same, update cache.
|
// If process successfully updated or file contents are same, update cache.
|
||||||
cache.docMtime = xfileOnDatabase.mtime;
|
cache.docMtime = xFileOnDatabase.mtime;
|
||||||
cache.storageMtime = xFileOnStorage.mtime;
|
cache.storageMtime = xFileOnStorage.mtime;
|
||||||
caches[filename] = cache;
|
caches[filename] = cache;
|
||||||
countUpdatedFolder(filename);
|
countUpdatedFolder(filename);
|
||||||
} else if (!xFileOnStorage && xfileOnDatabase) {
|
} else if (!xFileOnStorage && xFileOnDatabase) {
|
||||||
if (direction == "push") {
|
if (direction == "push" || direction == "pushForce") {
|
||||||
if (xfileOnDatabase.deleted)
|
if (xFileOnDatabase.deleted)
|
||||||
return;
|
return;
|
||||||
await this.deleteInternalFileOnDatabase(filename, false);
|
await this.deleteInternalFileOnDatabase(filename, false);
|
||||||
} else if (direction == "pull") {
|
} else if (direction == "pull" || direction == "pullForce") {
|
||||||
if (await this.extractInternalFileFromDatabase(filename)) {
|
if (await this.extractInternalFileFromDatabase(filename)) {
|
||||||
countUpdatedFolder(filename);
|
countUpdatedFolder(filename);
|
||||||
}
|
}
|
||||||
} else if (direction == "safe") {
|
} else if (direction == "safe") {
|
||||||
if (xfileOnDatabase.deleted)
|
if (xFileOnDatabase.deleted)
|
||||||
return;
|
return;
|
||||||
if (await this.extractInternalFileFromDatabase(filename)) {
|
if (await this.extractInternalFileFromDatabase(filename)) {
|
||||||
countUpdatedFolder(filename);
|
countUpdatedFolder(filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (xFileOnStorage && !xfileOnDatabase) {
|
} else if (xFileOnStorage && !xFileOnDatabase) {
|
||||||
await this.storeInternalFileToDatabase(xFileOnStorage);
|
await this.storeInternalFileToDatabase(xFileOnStorage);
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Invalid state on hidden file sync");
|
throw new Error("Invalid state on hidden file sync");
|
||||||
@@ -337,7 +337,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
await this.kvDB.set("diff-caches-internal", caches);
|
await this.kvDB.set("diff-caches-internal", caches);
|
||||||
|
|
||||||
// When files has been retrieved from the database. they must be reloaded.
|
// When files has been retrieved from the database. they must be reloaded.
|
||||||
if (direction == "pull" && filesChanged != 0) {
|
if (direction == "pull" || direction == "pullForce" && filesChanged != 0) {
|
||||||
const configDir = normalizePath(this.app.vault.configDir);
|
const configDir = normalizePath(this.app.vault.configDir);
|
||||||
// Show notification to restart obsidian when something has been changed in configDir.
|
// Show notification to restart obsidian when something has been changed in configDir.
|
||||||
if (configDir in updatedFolders) {
|
if (configDir in updatedFolders) {
|
||||||
@@ -468,7 +468,7 @@ export class HiddenFileSync extends LiveSyncCommands {
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
if (isDocContentSame(old.data, content) && !forceWrite) {
|
if (isDocContentSame(old.data, content) && !forceWrite) {
|
||||||
// Logger(`internal files STORAGE --> DB:${file.path}: Not changed`);
|
// Logger(`STORAGE --> DB:${file.path}: (hidden) Not changed`, LOG_LEVEL.VERBOSE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
saveData =
|
saveData =
|
||||||
|
|||||||
@@ -966,7 +966,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
});
|
});
|
||||||
c.addClass("op-warn");
|
c.addClass("op-warn");
|
||||||
}
|
}
|
||||||
|
containerSyncSettingEl.createEl("h3", { text: "Synchronization Methods" });
|
||||||
const syncLive: Setting[] = [];
|
const syncLive: Setting[] = [];
|
||||||
const syncNonLive: Setting[] = [];
|
const syncNonLive: Setting[] = [];
|
||||||
syncLive.push(
|
syncLive.push(
|
||||||
@@ -1019,6 +1019,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
text.inputEl.setAttribute("type", "number");
|
text.inputEl.setAttribute("type", "number");
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
||||||
new Setting(containerSyncSettingEl)
|
new Setting(containerSyncSettingEl)
|
||||||
.setName("Sync on Save")
|
.setName("Sync on Save")
|
||||||
.setDesc("When you save file, sync automatically")
|
.setDesc("When you save file, sync automatically")
|
||||||
@@ -1060,7 +1061,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
})
|
})
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
containerSyncSettingEl.createEl("h3", { text: "File deletion" });
|
||||||
new Setting(containerSyncSettingEl)
|
new Setting(containerSyncSettingEl)
|
||||||
.setName("Use Trash for deleted files")
|
.setName("Use Trash for deleted files")
|
||||||
.setDesc("Do not delete files that are deleted in remote, just move to trash.")
|
.setDesc("Do not delete files that are deleted in remote, just move to trash.")
|
||||||
@@ -1081,6 +1082,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
containerSyncSettingEl.createEl("h3", { text: "Conflict resolution" });
|
||||||
new Setting(containerSyncSettingEl)
|
new Setting(containerSyncSettingEl)
|
||||||
.setName("Use newer file if conflicted (beta)")
|
.setName("Use newer file if conflicted (beta)")
|
||||||
.setDesc("Resolve conflicts by newer files automatically.")
|
.setDesc("Resolve conflicts by newer files automatically.")
|
||||||
@@ -1119,14 +1121,63 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
new Setting(containerSyncSettingEl)
|
containerSyncSettingEl.createEl("h3", { text: "Hidden files" });
|
||||||
.setName("Sync hidden files")
|
const LABEL_ENABLED = "🔁 : Enabled";
|
||||||
.addToggle((toggle) =>
|
const LABEL_DISABLED = "⏹️ : Disabled"
|
||||||
toggle.setValue(this.plugin.settings.syncInternalFiles).onChange(async (value) => {
|
|
||||||
this.plugin.settings.syncInternalFiles = value;
|
const hiddenFileSyncSetting = new Setting(containerSyncSettingEl)
|
||||||
await this.plugin.saveSettings();
|
.setName("Hidden file synchronization")
|
||||||
|
const hiddenFileSyncSettingEl = hiddenFileSyncSetting.settingEl
|
||||||
|
const hiddenFileSyncSettingDiv = hiddenFileSyncSettingEl.createDiv("");
|
||||||
|
hiddenFileSyncSettingDiv.innerText = this.plugin.settings.syncInternalFiles ? LABEL_ENABLED : LABEL_DISABLED;
|
||||||
|
|
||||||
|
if (this.plugin.settings.syncInternalFiles) {
|
||||||
|
new Setting(containerSyncSettingEl)
|
||||||
|
.setName("Disable Hidden files sync")
|
||||||
|
.addButton((button) => {
|
||||||
|
button.setButtonText("Disable")
|
||||||
|
.onClick(async () => {
|
||||||
|
this.plugin.settings.syncInternalFiles = false;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
this.display();
|
||||||
|
})
|
||||||
})
|
})
|
||||||
);
|
} else {
|
||||||
|
|
||||||
|
new Setting(containerSyncSettingEl)
|
||||||
|
.setName("Enable Hidden files sync")
|
||||||
|
.addButton((button) => {
|
||||||
|
button.setButtonText("Merge")
|
||||||
|
.onClick(async () => {
|
||||||
|
this.plugin.settings.syncInternalFiles = true;
|
||||||
|
this.display();
|
||||||
|
await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("safe", true);
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
this.display();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.addButton((button) => {
|
||||||
|
button.setButtonText("Fetch")
|
||||||
|
.onClick(async () => {
|
||||||
|
this.plugin.settings.syncInternalFiles = true;
|
||||||
|
this.display();
|
||||||
|
await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("pullForce", true);
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
Logger(`Restarting the app is strongly recommended!`, LOG_LEVEL.NOTICE);
|
||||||
|
this.display();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.addButton((button) => {
|
||||||
|
button.setButtonText("Overwrite")
|
||||||
|
.onClick(async () => {
|
||||||
|
this.plugin.settings.syncInternalFiles = true;
|
||||||
|
this.display();
|
||||||
|
await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("pushForce", true);
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
this.display();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
new Setting(containerSyncSettingEl)
|
new Setting(containerSyncSettingEl)
|
||||||
.setName("Scan for hidden files before replication")
|
.setName("Scan for hidden files before replication")
|
||||||
@@ -1191,31 +1242,9 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
new Setting(containerSyncSettingEl)
|
|
||||||
.setName("Touch hidden files")
|
|
||||||
.setDesc("Update the modified time of all hidden files to the current time.")
|
|
||||||
.addButton((button) =>
|
|
||||||
button
|
|
||||||
.setButtonText("Touch")
|
|
||||||
.setWarning()
|
|
||||||
.setDisabled(false)
|
|
||||||
.onClick(async () => {
|
|
||||||
const filesAll = await this.plugin.addOnHiddenFileSync.scanInternalFiles();
|
|
||||||
const targetFiles = await this.plugin.filterTargetFiles(filesAll);
|
|
||||||
const now = Date.now();
|
|
||||||
const newFiles = targetFiles.map(e => ({ ...e, mtime: now }));
|
|
||||||
let i = 0;
|
|
||||||
const maxFiles = newFiles.length;
|
|
||||||
for (const file of newFiles) {
|
|
||||||
i++;
|
|
||||||
Logger(`Touched:${file.path} (${i}/${maxFiles})`, LOG_LEVEL.NOTICE, "touch-files");
|
|
||||||
await this.plugin.applyMTimeToFile(file);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
containerSyncSettingEl.createEl("h3", {
|
containerSyncSettingEl.createEl("h3", {
|
||||||
text: sanitizeHTMLToDom(`Experimental`),
|
text: sanitizeHTMLToDom(`Synchronization filters`),
|
||||||
});
|
});
|
||||||
new Setting(containerSyncSettingEl)
|
new Setting(containerSyncSettingEl)
|
||||||
.setName("Regular expression to ignore files")
|
.setName("Regular expression to ignore files")
|
||||||
@@ -1263,7 +1292,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
containerSyncSettingEl.createEl("h3", { text: "Efficiency" });
|
||||||
new Setting(containerSyncSettingEl)
|
new Setting(containerSyncSettingEl)
|
||||||
.setName("Chunk size")
|
.setName("Chunk size")
|
||||||
.setDesc("Customize chunk size for binary files (0.1MBytes). This cannot be increased when using IBM Cloudant.")
|
.setDesc("Customize chunk size for binary files (0.1MBytes). This cannot be increased when using IBM Cloudant.")
|
||||||
@@ -1282,7 +1311,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
});
|
});
|
||||||
|
|
||||||
new Setting(containerSyncSettingEl)
|
new Setting(containerSyncSettingEl)
|
||||||
.setName("Read chunks online.")
|
.setName("Read chunks online")
|
||||||
.setDesc("If this option is enabled, LiveSync reads chunks online directly instead of replicating them locally. Increasing Custom chunk size is recommended.")
|
.setDesc("If this option is enabled, LiveSync reads chunks online directly instead of replicating them locally. Increasing Custom chunk size is recommended.")
|
||||||
.addToggle((toggle) => {
|
.addToggle((toggle) => {
|
||||||
toggle
|
toggle
|
||||||
@@ -1292,8 +1321,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
})
|
})
|
||||||
return toggle;
|
return toggle;
|
||||||
}
|
});
|
||||||
);
|
|
||||||
containerSyncSettingEl.createEl("h3", {
|
containerSyncSettingEl.createEl("h3", {
|
||||||
text: sanitizeHTMLToDom(`Advanced settings`),
|
text: sanitizeHTMLToDom(`Advanced settings`),
|
||||||
});
|
});
|
||||||
@@ -1487,8 +1515,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.plugin.saveSettings();
|
this.plugin.saveSettings();
|
||||||
await this.plugin.realizeSettingSyncMode();
|
|
||||||
this.display();
|
this.display();
|
||||||
|
await this.plugin.realizeSettingSyncMode();
|
||||||
if (inWizard) {
|
if (inWizard) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.plugin.app.setting.close()
|
this.plugin.app.setting.close()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Plugin_2, TAbstractFile, TFile, TFolder } from "./deps";
|
import { Plugin_2, TAbstractFile, TFile, TFolder } from "./deps";
|
||||||
import { isPlainText, shouldBeIgnored } from "./lib/src/path";
|
import { isPlainText, shouldBeIgnored } from "./lib/src/path";
|
||||||
import { getGlobalStore } from "./lib/src/store";
|
import { getGlobalStore } from "./lib/src/store";
|
||||||
import { ObsidianLiveSyncSettings } from "./lib/src/types";
|
import { FilePath, ObsidianLiveSyncSettings } from "./lib/src/types";
|
||||||
import { FileEventItem, FileEventType, FileInfo, InternalFileInfo, queueItem } from "./types";
|
import { FileEventItem, FileEventType, FileInfo, InternalFileInfo, queueItem } from "./types";
|
||||||
import { recentlyTouched } from "./utils";
|
import { recentlyTouched } from "./utils";
|
||||||
|
|
||||||
@@ -58,12 +58,12 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
|||||||
if (file instanceof TFile) {
|
if (file instanceof TFile) {
|
||||||
this.appendWatchEvent([
|
this.appendWatchEvent([
|
||||||
{ type: "CREATE", file },
|
{ type: "CREATE", file },
|
||||||
{ type: "DELETE", file: { path: oldFile, mtime: file.stat.mtime, ctime: file.stat.ctime, size: file.stat.size, deleted: true } }
|
{ type: "DELETE", file: { path: oldFile as FilePath, mtime: file.stat.mtime, ctime: file.stat.ctime, size: file.stat.size, deleted: true } },
|
||||||
], ctx);
|
], ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Watch raw events (Internal API)
|
// Watch raw events (Internal API)
|
||||||
watchVaultRawEvents(path: string) {
|
watchVaultRawEvents(path: FilePath) {
|
||||||
if (!this.plugin.settings.syncInternalFiles) return;
|
if (!this.plugin.settings.syncInternalFiles) return;
|
||||||
if (!this.plugin.settings.watchInternalFileChanges) return;
|
if (!this.plugin.settings.watchInternalFileChanges) return;
|
||||||
if (!path.startsWith(app.vault.configDir)) return;
|
if (!path.startsWith(app.vault.configDir)) return;
|
||||||
|
|||||||
2
src/lib
2
src/lib
Submodule src/lib updated: f644c8dfc3...f5db618612
11
updates.md
11
updates.md
@@ -8,6 +8,17 @@ Since v0.18.0, they can be obfuscated. so it is no longer possible to decipher t
|
|||||||
We can configure the `Path Obfuscation` in the `Remote database configuration` pane.
|
We can configure the `Path Obfuscation` in the `Remote database configuration` pane.
|
||||||
Note: **When changing this configuration, we need to rebuild both of the local and the remote databases**.
|
Note: **When changing this configuration, we need to rebuild both of the local and the remote databases**.
|
||||||
|
|
||||||
|
#### Minors
|
||||||
|
- 0.18.1
|
||||||
|
- Fixed:
|
||||||
|
- Some messages are fixed (Typo)
|
||||||
|
- File type detection now works fine!
|
||||||
|
- 0.18.2
|
||||||
|
- Improved:
|
||||||
|
- The setting pane has been refined.
|
||||||
|
- We can enable `hidden files sync` with several initial behaviours; `Merge`, `Fetch` remote, and `Overwrite` remote.
|
||||||
|
- No longer `Touch hidden files`.
|
||||||
|
|
||||||
### 0.17.0
|
### 0.17.0
|
||||||
- 0.17.0 has no surfaced changes but the design of saving chunks has been changed. They have compatibility but changing files after upgrading makes different chunks than before 0.16.x.
|
- 0.17.0 has no surfaced changes but the design of saving chunks has been changed. They have compatibility but changing files after upgrading makes different chunks than before 0.16.x.
|
||||||
Please rebuild databases once if you have been worried about storage usage.
|
Please rebuild databases once if you have been worried about storage usage.
|
||||||
|
|||||||
Reference in New Issue
Block a user