summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2019-07-02 11:12:17 +0000
committerPhil Hughes <me@iamphill.com>2019-07-02 11:12:17 +0000
commite2648d1130f7498aeaa935c4311dcf6511eea94f (patch)
treeb65ffc957ffc3024734bff752e426cd8757c1f6c
parentc2118657dbe7728a751f29767cc89b4a80f39b92 (diff)
parent5a7834e5a91549b5ecb8890a718efa93dd61ae7a (diff)
downloadgitlab-ce-e2648d1130f7498aeaa935c4311dcf6511eea94f.tar.gz
Merge branch '60859-upload-after-delete' into 'master'
Resolve "Cannot delete and upload file of the same name in the Web IDE" Closes #60859 See merge request gitlab-org/gitlab-ce!30239
-rw-r--r--app/assets/javascripts/ide/stores/actions.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/actions.js2
-rw-r--r--app/assets/javascripts/ide/stores/mutation_types.js2
-rw-r--r--app/assets/javascripts/ide/stores/mutations.js13
-rw-r--r--app/assets/javascripts/ide/stores/mutations/file.js6
-rw-r--r--app/assets/javascripts/ide/stores/state.js1
-rw-r--r--app/assets/javascripts/ide/stores/utils.js6
-rw-r--r--changelogs/unreleased/60859-upload-after-delete.yml5
-rw-r--r--spec/javascripts/ide/stores/actions_spec.js43
-rw-r--r--spec/javascripts/ide/stores/mutations/file_spec.js13
-rw-r--r--spec/javascripts/ide/stores/mutations_spec.js68
-rw-r--r--spec/javascripts/ide/stores/utils_spec.js26
12 files changed, 173 insertions, 15 deletions
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index 507dc363529..8c0119a1fed 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -62,7 +62,7 @@ export const createTempEntry = (
new Promise(resolve => {
const fullName = name.slice(-1) !== '/' && type === 'tree' ? `${name}/` : name;
- if (state.entries[name]) {
+ if (state.entries[name] && !state.entries[name].deleted) {
flash(
`The name "${name.split('/').pop()}" is already taken in this directory.`,
'alert',
@@ -208,6 +208,7 @@ export const deleteEntry = ({ commit, dispatch, state }, path) => {
}
commit(types.DELETE_ENTRY, path);
+ dispatch('stageChange', path);
dispatch('triggerFilesChange');
};
diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js
index 01ca6a6b12f..ac34491c1ad 100644
--- a/app/assets/javascripts/ide/stores/modules/commit/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js
@@ -186,6 +186,8 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
commit(rootTypes.CLEAR_STAGED_CHANGES, null, { root: true });
+ commit(rootTypes.CLEAR_REPLACED_FILES, null, { root: true });
+
setTimeout(() => {
commit(rootTypes.SET_LAST_COMMIT_MSG, '', { root: true });
}, 5000);
diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js
index 86ab76136df..f021729c451 100644
--- a/app/assets/javascripts/ide/stores/mutation_types.js
+++ b/app/assets/javascripts/ide/stores/mutation_types.js
@@ -60,6 +60,8 @@ export const CLEAR_STAGED_CHANGES = 'CLEAR_STAGED_CHANGES';
export const STAGE_CHANGE = 'STAGE_CHANGE';
export const UNSTAGE_CHANGE = 'UNSTAGE_CHANGE';
+export const CLEAR_REPLACED_FILES = 'CLEAR_REPLACED_FILES';
+
export const UPDATE_FILE_AFTER_COMMIT = 'UPDATE_FILE_AFTER_COMMIT';
export const ADD_PENDING_TAB = 'ADD_PENDING_TAB';
export const REMOVE_PENDING_TAB = 'REMOVE_PENDING_TAB';
diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js
index ec4c2fdcde2..ea125214ebb 100644
--- a/app/assets/javascripts/ide/stores/mutations.js
+++ b/app/assets/javascripts/ide/stores/mutations.js
@@ -56,6 +56,11 @@ export default {
stagedFiles: [],
});
},
+ [types.CLEAR_REPLACED_FILES](state) {
+ Object.assign(state, {
+ replacedFiles: [],
+ });
+ },
[types.SET_ENTRIES](state, entries) {
Object.assign(state, {
entries,
@@ -70,6 +75,13 @@ export default {
Object.assign(state.entries, {
[key]: entry,
});
+ } else if (foundEntry.deleted) {
+ Object.assign(state.entries, {
+ [key]: {
+ ...entry,
+ replaces: true,
+ },
+ });
} else {
const tree = entry.tree.filter(
f => foundEntry.tree.find(e => e.path === f.path) === undefined,
@@ -144,6 +156,7 @@ export default {
raw: file.content,
changed: Boolean(changedFile),
staged: false,
+ replaces: false,
prevPath: '',
moved: false,
lastCommitSha: lastCommit.commit.id,
diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js
index 6ca246c1d63..c88244492e0 100644
--- a/app/assets/javascripts/ide/stores/mutations/file.js
+++ b/app/assets/javascripts/ide/stores/mutations/file.js
@@ -170,12 +170,16 @@ export default {
entries: Object.assign(state.entries, {
[path]: Object.assign(state.entries[path], {
staged: true,
- changed: false,
}),
}),
});
if (stagedFile) {
+ Object.assign(state, {
+ replacedFiles: state.replacedFiles.concat({
+ ...stagedFile,
+ }),
+ });
Object.assign(stagedFile, {
...state.entries[path],
});
diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js
index d400b9831a9..c4da482bf0a 100644
--- a/app/assets/javascripts/ide/stores/state.js
+++ b/app/assets/javascripts/ide/stores/state.js
@@ -6,6 +6,7 @@ export default () => ({
currentMergeRequestId: '',
changedFiles: [],
stagedFiles: [],
+ replacedFiles: [],
endpoints: {},
lastCommitMsg: '',
lastCommitPath: '',
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
index fb132c1afc1..01f78a29cf6 100644
--- a/app/assets/javascripts/ide/stores/utils.js
+++ b/app/assets/javascripts/ide/stores/utils.js
@@ -18,6 +18,7 @@ export const dataStructure = () => ({
active: false,
changed: false,
staged: false,
+ replaces: false,
lastCommitPath: '',
lastCommitSha: '',
lastCommit: {
@@ -119,7 +120,7 @@ export const commitActionForFile = file => {
return commitActionTypes.move;
} else if (file.deleted) {
return commitActionTypes.delete;
- } else if (file.tempFile) {
+ } else if (file.tempFile && !file.replaces) {
return commitActionTypes.create;
}
@@ -151,7 +152,8 @@ export const createCommitPayload = ({
previous_path: f.prevPath === '' ? undefined : f.prevPath,
content: f.prevPath ? null : f.content || undefined,
encoding: f.base64 ? 'base64' : 'text',
- last_commit_id: newBranch || f.deleted || f.prevPath ? undefined : f.lastCommitSha,
+ last_commit_id:
+ newBranch || f.deleted || f.prevPath || f.replaces ? undefined : f.lastCommitSha,
})),
start_sha: newBranch ? rootGetters.lastCommit.short_id : undefined,
});
diff --git a/changelogs/unreleased/60859-upload-after-delete.yml b/changelogs/unreleased/60859-upload-after-delete.yml
new file mode 100644
index 00000000000..c36dfb84bfe
--- /dev/null
+++ b/changelogs/unreleased/60859-upload-after-delete.yml
@@ -0,0 +1,5 @@
+---
+title: In WebIDE allow adding new entries of the same name as deleted entry
+merge_request: 30239
+author:
+type: fixed
diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js
index 2d105103c1c..8504fb3f42b 100644
--- a/spec/javascripts/ide/stores/actions_spec.js
+++ b/spec/javascripts/ide/stores/actions_spec.js
@@ -10,6 +10,7 @@ import actions, {
deleteEntry,
renameEntry,
getBranchData,
+ createTempEntry,
} from '~/ide/stores/actions';
import axios from '~/lib/utils/axios_utils';
import store from '~/ide/stores';
@@ -247,18 +248,30 @@ describe('Multi-file store actions', () => {
});
it('sets tmp file as active', done => {
- store
- .dispatch('createTempEntry', {
+ testAction(
+ createTempEntry,
+ {
name: 'test',
branchId: 'mybranch',
type: 'blob',
- })
- .then(f => {
- expect(f.active).toBeTruthy();
-
- done();
- })
- .catch(done.fail);
+ },
+ store.state,
+ [
+ { type: types.CREATE_TMP_ENTRY, payload: jasmine.any(Object) },
+ { type: types.TOGGLE_FILE_OPEN, payload: 'test' },
+ { type: types.ADD_FILE_TO_CHANGED, payload: 'test' },
+ ],
+ [
+ {
+ type: 'setFileActive',
+ payload: 'test',
+ },
+ {
+ type: 'triggerFilesChange',
+ },
+ ],
+ done,
+ );
});
it('creates flash message if file already exists', done => {
@@ -488,7 +501,11 @@ describe('Multi-file store actions', () => {
'path',
store.state,
[{ type: types.DELETE_ENTRY, payload: 'path' }],
- [{ type: 'burstUnusedSeal' }, { type: 'triggerFilesChange' }],
+ [
+ { type: 'burstUnusedSeal' },
+ { type: 'stageChange', payload: 'path' },
+ { type: 'triggerFilesChange' },
+ ],
done,
);
});
@@ -515,7 +532,11 @@ describe('Multi-file store actions', () => {
'testFolder/entry-to-delete',
store.state,
[{ type: types.DELETE_ENTRY, payload: 'testFolder/entry-to-delete' }],
- [{ type: 'burstUnusedSeal' }, { type: 'triggerFilesChange' }],
+ [
+ { type: 'burstUnusedSeal' },
+ { type: 'stageChange', payload: 'testFolder/entry-to-delete' },
+ { type: 'triggerFilesChange' },
+ ],
done,
);
});
diff --git a/spec/javascripts/ide/stores/mutations/file_spec.js b/spec/javascripts/ide/stores/mutations/file_spec.js
index efd0d86552b..18ee4330f69 100644
--- a/spec/javascripts/ide/stores/mutations/file_spec.js
+++ b/spec/javascripts/ide/stores/mutations/file_spec.js
@@ -315,6 +315,19 @@ describe('IDE store file mutations', () => {
expect(localState.stagedFiles.length).toBe(1);
expect(localState.stagedFiles[0].raw).toEqual('testing 123');
});
+
+ it('adds already-staged file to `replacedFiles`', () => {
+ localFile.raw = 'already-staged';
+
+ mutations.STAGE_CHANGE(localState, localFile.path);
+
+ localFile.raw = 'testing 123';
+
+ mutations.STAGE_CHANGE(localState, localFile.path);
+
+ expect(localState.replacedFiles.length).toBe(1);
+ expect(localState.replacedFiles[0].raw).toEqual('already-staged');
+ });
});
describe('UNSTAGE_CHANGE', () => {
diff --git a/spec/javascripts/ide/stores/mutations_spec.js b/spec/javascripts/ide/stores/mutations_spec.js
index 460c5b01081..2470c99e300 100644
--- a/spec/javascripts/ide/stores/mutations_spec.js
+++ b/spec/javascripts/ide/stores/mutations_spec.js
@@ -79,6 +79,16 @@ describe('Multi-file store mutations', () => {
});
});
+ describe('CLEAR_REPLACED_FILES', () => {
+ it('clears replacedFiles array', () => {
+ localState.replacedFiles.push('a');
+
+ mutations.CLEAR_REPLACED_FILES(localState);
+
+ expect(localState.replacedFiles.length).toBe(0);
+ });
+ });
+
describe('UPDATE_VIEWER', () => {
it('sets viewer state', () => {
mutations.UPDATE_VIEWER(localState, 'diff');
@@ -109,6 +119,62 @@ describe('Multi-file store mutations', () => {
});
});
+ describe('CREATE_TMP_ENTRY', () => {
+ beforeEach(() => {
+ localState.currentProjectId = 'gitlab-ce';
+ localState.currentBranchId = 'master';
+ localState.trees['gitlab-ce/master'] = {
+ tree: [],
+ };
+ });
+
+ it('creates temp entry in the tree', () => {
+ const tmpFile = file('test');
+ mutations.CREATE_TMP_ENTRY(localState, {
+ data: {
+ entries: {
+ test: {
+ ...tmpFile,
+ tempFile: true,
+ changed: true,
+ },
+ },
+ treeList: [tmpFile],
+ },
+ projectId: 'gitlab-ce',
+ branchId: 'master',
+ });
+
+ expect(localState.trees['gitlab-ce/master'].tree.length).toEqual(1);
+ expect(localState.entries.test.tempFile).toEqual(true);
+ });
+
+ it('marks entry as replacing previous entry if the old one has been deleted', () => {
+ const tmpFile = file('test');
+ localState.entries.test = {
+ ...tmpFile,
+ deleted: true,
+ };
+ mutations.CREATE_TMP_ENTRY(localState, {
+ data: {
+ entries: {
+ test: {
+ ...tmpFile,
+ tempFile: true,
+ changed: true,
+ },
+ },
+ treeList: [tmpFile],
+ },
+ projectId: 'gitlab-ce',
+ branchId: 'master',
+ });
+
+ expect(localState.trees['gitlab-ce/master'].tree.length).toEqual(1);
+ expect(localState.entries.test.replaces).toEqual(true);
+ });
+ });
+
describe('UPDATE_TEMP_FLAG', () => {
beforeEach(() => {
localState.entries.test = {
@@ -252,6 +318,7 @@ describe('Multi-file store mutations', () => {
permalink: `${gl.TEST_HOST}/testing-123`,
commitsPath: `${gl.TEST_HOST}/testing-123`,
blamePath: `${gl.TEST_HOST}/testing-123`,
+ replaces: true,
};
localState.entries.test = f;
localState.changedFiles.push(f);
@@ -262,6 +329,7 @@ describe('Multi-file store mutations', () => {
expect(f.permalink).toBe(`${gl.TEST_HOST}/test`);
expect(f.commitsPath).toBe(`${gl.TEST_HOST}/test`);
expect(f.blamePath).toBe(`${gl.TEST_HOST}/test`);
+ expect(f.replaces).toBe(false);
});
});
diff --git a/spec/javascripts/ide/stores/utils_spec.js b/spec/javascripts/ide/stores/utils_spec.js
index e3bf6d40245..bceb3a8db91 100644
--- a/spec/javascripts/ide/stores/utils_spec.js
+++ b/spec/javascripts/ide/stores/utils_spec.js
@@ -92,6 +92,16 @@ describe('Multi-file store utils', () => {
path: 'deletedFile',
deleted: true,
},
+ {
+ ...file('renamedFile'),
+ path: 'renamedFile',
+ prevPath: 'prevPath',
+ },
+ {
+ ...file('replacingFile'),
+ path: 'replacingFile',
+ replaces: true,
+ },
],
currentBranchId: 'master',
};
@@ -131,6 +141,22 @@ describe('Multi-file store utils', () => {
last_commit_id: undefined,
previous_path: undefined,
},
+ {
+ action: commitActionTypes.move,
+ file_path: 'renamedFile',
+ content: null,
+ encoding: 'text',
+ last_commit_id: undefined,
+ previous_path: 'prevPath',
+ },
+ {
+ action: commitActionTypes.update,
+ file_path: 'replacingFile',
+ content: undefined,
+ encoding: 'text',
+ last_commit_id: undefined,
+ previous_path: undefined,
+ },
],
start_sha: undefined,
});