From 32735792973e22f9aaccc59eeaf8358e5059e6ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Wed, 25 Jan 2023 21:49:22 +0100 Subject: [PATCH] Initial commit --- .envrc | 4 ++ .gitignore | 1 + fix-conflicts.ts | 134 +++++++++++++++++++++++++++++++++++++++++++++++ flake.lock | 67 ++++++++++++++++++++++++ flake.nix | 27 ++++++++++ 5 files changed, 233 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 fix-conflicts.ts create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..11b05b8 --- /dev/null +++ b/.envrc @@ -0,0 +1,4 @@ +# reload when these files change +export TRITON_DONT_SOURCE_PROFILE=1 +watch_file flake.nix +use_flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..92b2793 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.direnv diff --git a/fix-conflicts.ts b/fix-conflicts.ts new file mode 100644 index 0000000..d257abd --- /dev/null +++ b/fix-conflicts.ts @@ -0,0 +1,134 @@ +(async () => { + const dir = '/home/ben/Nextcloud'; + + const DRY_RUN = false; + const BATCH_SIZE = 16; + const ENCRYPTED_CONTENT_STRING = 'HBEGIN:oc_encryption_module:OC_DEFAULT_MODULE:cipher'; + const CONFLICTED_COPY_REGEX = /.*( \(conflicted copy \d{4}-\d{2}-\d{2} \d+\))(\.[^\.]*)?/; + + const p = Deno.run({ + cmd: ['bash', '-c', `find ${dir} -type f | grep conflicted\\ copy`], + stdout: 'piped', + }); + + const output = await p.output(); + p.close(); + + const outputStr = (new TextDecoder()).decode(output); + const fileNames = [...new Set(outputStr.split('\n').filter(s => !!s))]; + + const totalFiles = fileNames.length; + let filesSuccess = 0; + let fileData = []; + + for (let i = 0; i < totalFiles; i += BATCH_SIZE) { + fileData = [ + ...fileData, + ...(await Promise.all(fileNames.slice(i, i + BATCH_SIZE).map(async (conflictedFileName, index) => { + const conflictedNameMatch = conflictedFileName.match(CONFLICTED_COPY_REGEX); + if (!conflictedNameMatch) { + return `[${conflictedFileName}] encountered regex error`; + } + const conflictString = conflictedNameMatch[1]; + const nonConflictedFileName = conflictedFileName.replace(conflictString, ''); + + const conflictedFile = await Deno.open(conflictedFileName, { read: true }); + const conflictedFileInfo = await Deno.fstat(conflictedFile.rid); + const normalFile = await Deno.open(nonConflictedFileName, { read: true }).catch(() => null); + if (!normalFile) { + if (!DRY_RUN) { + // Only the conflicted file exists, just move it to the non-conflict filename + await Deno.rename(conflictedFileName, nonConflictedFileName); + } + return `[${conflictedFileName}] only conflicted exists. Move to non-conflict filename`; + } + const normalFileInfo = await Deno.fstat(normalFile.rid); + conflictedFile.close(); + normalFile.close(); + + const shaP = Deno.run({ + cmd: ['sha256sum', nonConflictedFileName, conflictedFileName], + stdout: 'piped', + }); + + const shaOutputStr = (new TextDecoder()).decode(await shaP.output()); + const shaOutputs = shaOutputStr.split('\n'); + const normalSha = shaOutputs[0].split(' ')[0]; + const conflictedSha = shaOutputs[1].split(' ')[0]; + + shaP.close(); + + if (normalSha === conflictedSha) { + if (!DRY_RUN) { + // They're the same, remove the conflicted file + await Deno.remove(conflictedFileName); + } + return `[${conflictedFileName}] conflict and non-conflict have the same contents, remove the conflict`; + } + + const headP = Deno.run({ + cmd: ['head', '-c', '64', nonConflictedFileName, conflictedFileName], + stdout: 'piped', + }); + + const headOutputStr = (new TextDecoder()).decode(await headP.output()); + const headOutputs = headOutputStr.split('\n'); + const normalHead = headOutputs[1].split(' ')[0]; + const conflictedHead = headOutputs[3].split(' ')[0]; + + headP.close(); + + const normalEncrypted = normalHead.startsWith(ENCRYPTED_CONTENT_STRING); + const conflictedEncrypted = conflictedHead.startsWith(ENCRYPTED_CONTENT_STRING); + + if (normalEncrypted && conflictedEncrypted) { + console.error("Both files encrypted differently!"); + console.error(nonConflictedFileName, conflictedFileName); + if (!DRY_RUN) { + // Whatever, deleted the conflicted one + await Deno.remove(conflictedFileName); + } + return `[${conflictedFileName}] both are encrypted but with different contents. We'll remove the conflict`; + } + + if (normalEncrypted) { + if (!DRY_RUN) { + // delete normal + await Deno.remove(nonConflictedFileName); + await Deno.rename(conflictedFileName, nonConflictedFileName); + } + return `[${conflictedFileName}] the non-conflict is encrypted. Remove it and move the conflicted file in its place`; + } + + if (conflictedEncrypted) { + if (!DRY_RUN) { + // delete conflicted + await Deno.remove(conflictedFileName); + } + return `[${conflictedFileName}] the conflict is encrypted. Remove it and keep the normal file`; + } + + if (normalFileInfo.mtime > conflictedFileInfo.mtime) { + if (!DRY_RUN) { + // delete conflicted + await Deno.remove(conflictedFileName); + } + return `[${conflictedFileName}] conflict is older than the non-conflict. Remove the conflict`; + } + + if (!DRY_RUN) { + // delete normal + await Deno.remove(nonConflictedFileName); + await Deno.rename(conflictedFileName, nonConflictedFileName); + } + return `[${conflictedFileName}] non-conflict is older than the conflict. Remove the non-conflict`; + }).map(p => p.then((msg) => { + filesSuccess++; + console.log(`[${filesSuccess}/${totalFiles}] ${msg}`); + return msg; + })))), + ]; + } + + // console.log(fileData); +})(); diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..68c350c --- /dev/null +++ b/flake.lock @@ -0,0 +1,67 @@ +{ + "nodes": { + "devshell": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1671489820, + "narHash": "sha256-qoei5HDJ8psd1YUPD7DhbHdhLIT9L2nadscp4Qk37uk=", + "owner": "numtide", + "repo": "devshell", + "rev": "5aa3a8039c68b4bf869327446590f4cdf90bb634", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "devshell", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1673796341, + "narHash": "sha256-1kZi9OkukpNmOaPY7S5/+SlCDOuYnP3HkXHvNDyLQcc=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "6dccdc458512abce8d19f74195bb20fdb067df50", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devshell": "devshell", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..8db69c1 --- /dev/null +++ b/flake.nix @@ -0,0 +1,27 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + + flake-utils.url = "github:numtide/flake-utils"; + + devshell.url = "github:numtide/devshell"; + devshell.inputs.flake-utils.follows = "flake-utils"; + devshell.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = { self, devshell, flake-utils, nixpkgs }: + flake-utils.lib.simpleFlake { + inherit self nixpkgs; + name = "fix-nc-encrypted-file-conflicts"; + preOverlays = [ devshell.overlay ]; + shell = { pkgs }: + pkgs.devshell.mkShell { + devshell.packages = with pkgs; [ + deno + ]; + bash.extra = '' + export NVIM_USE_DENOLS=1 + ''; + }; + }; +}