Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/ide/lib/create_file_diff.js')
-rw-r--r--app/assets/javascripts/ide/lib/create_file_diff.js112
1 files changed, 112 insertions, 0 deletions
diff --git a/app/assets/javascripts/ide/lib/create_file_diff.js b/app/assets/javascripts/ide/lib/create_file_diff.js
new file mode 100644
index 00000000000..5ae4993321c
--- /dev/null
+++ b/app/assets/javascripts/ide/lib/create_file_diff.js
@@ -0,0 +1,112 @@
+/* eslint-disable @gitlab/require-i18n-strings */
+import { createTwoFilesPatch } from 'diff';
+import { commitActionTypes } from '~/ide/constants';
+
+const DEV_NULL = '/dev/null';
+const DEFAULT_MODE = '100644';
+const NO_NEW_LINE = '\\ No newline at end of file';
+const NEW_LINE = '\n';
+
+/**
+ * Cleans patch generated by `diff` package.
+ *
+ * - Removes "=======" separator added at the beginning
+ */
+const cleanTwoFilesPatch = text => text.replace(/^(=+\s*)/, '');
+
+const endsWithNewLine = val => !val || val[val.length - 1] === NEW_LINE;
+
+const addEndingNewLine = val => (endsWithNewLine(val) ? val : val + NEW_LINE);
+
+const removeEndingNewLine = val => (endsWithNewLine(val) ? val.substr(0, val.length - 1) : val);
+
+const diffHead = (prevPath, newPath = '') =>
+ `diff --git "a/${prevPath}" "b/${newPath || prevPath}"`;
+
+const createDiffBody = (path, content, isCreate) => {
+ if (!content) {
+ return '';
+ }
+
+ const prefix = isCreate ? '+' : '-';
+ const fromPath = isCreate ? DEV_NULL : `a/${path}`;
+ const toPath = isCreate ? `b/${path}` : DEV_NULL;
+
+ const hasNewLine = endsWithNewLine(content);
+ const lines = removeEndingNewLine(content).split(NEW_LINE);
+
+ const chunkHead = isCreate ? `@@ -0,0 +1,${lines.length} @@` : `@@ -1,${lines.length} +0,0 @@`;
+ const chunk = lines
+ .map(line => `${prefix}${line}`)
+ .concat(!hasNewLine ? [NO_NEW_LINE] : [])
+ .join(NEW_LINE);
+
+ return `--- ${fromPath}
++++ ${toPath}
+${chunkHead}
+${chunk}`;
+};
+
+const createMoveFileDiff = (prevPath, newPath) => `${diffHead(prevPath, newPath)}
+rename from ${prevPath}
+rename to ${newPath}`;
+
+const createNewFileDiff = (path, content) => {
+ const diff = createDiffBody(path, content, true);
+
+ return `${diffHead(path)}
+new file mode ${DEFAULT_MODE}
+${diff}`;
+};
+
+const createDeleteFileDiff = (path, content) => {
+ const diff = createDiffBody(path, content, false);
+
+ return `${diffHead(path)}
+deleted file mode ${DEFAULT_MODE}
+${diff}`;
+};
+
+const createUpdateFileDiff = (path, oldContent, newContent) => {
+ const patch = createTwoFilesPatch(`a/${path}`, `b/${path}`, oldContent, newContent);
+
+ return `${diffHead(path)}
+${cleanTwoFilesPatch(patch)}`;
+};
+
+const createFileDiffRaw = (file, action) => {
+ switch (action) {
+ case commitActionTypes.move:
+ return createMoveFileDiff(file.prevPath, file.path);
+ case commitActionTypes.create:
+ return createNewFileDiff(file.path, file.content);
+ case commitActionTypes.delete:
+ return createDeleteFileDiff(file.path, file.content);
+ case commitActionTypes.update:
+ return createUpdateFileDiff(file.path, file.raw || '', file.content);
+ default:
+ return '';
+ }
+};
+
+/**
+ * Create a git diff for a single IDE file.
+ *
+ * ## Notes:
+ * When called with `commitActionType.move`, it assumes that the move
+ * is a 100% similarity move. No diff will be generated. This is because
+ * generating a move with changes is not support by the current IDE, since
+ * the source file might not have it's content loaded yet.
+ *
+ * When called with `commitActionType.delete`, it does not support
+ * deleting files with a mode different than 100644. For the IDE mirror, this
+ * isn't needed because deleting is handled outside the unified patch.
+ *
+ * ## References:
+ * - https://git-scm.com/docs/git-diff#_generating_patches_with_p
+ */
+const createFileDiff = (file, action) =>
+ // It's important that the file diff ends in a new line - git expects this.
+ addEndingNewLine(createFileDiffRaw(file, action));
+
+export default createFileDiff;