2020-06-23 00:09:42 +05:30
|
|
|
/* 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
|
|
|
|
*/
|
2021-03-08 18:12:59 +05:30
|
|
|
const cleanTwoFilesPatch = (text) => text.replace(/^(=+\s*)/, '');
|
2020-06-23 00:09:42 +05:30
|
|
|
|
2021-03-08 18:12:59 +05:30
|
|
|
const endsWithNewLine = (val) => !val || val[val.length - 1] === NEW_LINE;
|
2020-06-23 00:09:42 +05:30
|
|
|
|
2021-03-08 18:12:59 +05:30
|
|
|
const addEndingNewLine = (val) => (endsWithNewLine(val) ? val : val + NEW_LINE);
|
2020-06-23 00:09:42 +05:30
|
|
|
|
2021-03-08 18:12:59 +05:30
|
|
|
const removeEndingNewLine = (val) => (endsWithNewLine(val) ? val.substr(0, val.length - 1) : val);
|
2020-06-23 00:09:42 +05:30
|
|
|
|
|
|
|
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
|
2021-03-08 18:12:59 +05:30
|
|
|
.map((line) => `${prefix}${line}`)
|
2020-06-23 00:09:42 +05:30
|
|
|
.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;
|