Commit 5a7834e5 authored by Denys Mishunov's avatar Denys Mishunov Committed by Phil Hughes

Allow adding new entries to existing path

If an entry has been removed from the tree and later, during the same
session, a user is trying to add a new item with the same name/path we
should allow for that as long as the original entry is marked with
`deleted`.
parent c2118657
...@@ -62,7 +62,7 @@ export const createTempEntry = ( ...@@ -62,7 +62,7 @@ export const createTempEntry = (
new Promise(resolve => { new Promise(resolve => {
const fullName = name.slice(-1) !== '/' && type === 'tree' ? `${name}/` : name; const fullName = name.slice(-1) !== '/' && type === 'tree' ? `${name}/` : name;
if (state.entries[name]) { if (state.entries[name] && !state.entries[name].deleted) {
flash( flash(
`The name "${name.split('/').pop()}" is already taken in this directory.`, `The name "${name.split('/').pop()}" is already taken in this directory.`,
'alert', 'alert',
...@@ -208,6 +208,7 @@ export const deleteEntry = ({ commit, dispatch, state }, path) => { ...@@ -208,6 +208,7 @@ export const deleteEntry = ({ commit, dispatch, state }, path) => {
} }
commit(types.DELETE_ENTRY, path); commit(types.DELETE_ENTRY, path);
dispatch('stageChange', path);
dispatch('triggerFilesChange'); dispatch('triggerFilesChange');
}; };
......
...@@ -186,6 +186,8 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo ...@@ -186,6 +186,8 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
commit(rootTypes.CLEAR_STAGED_CHANGES, null, { root: true }); commit(rootTypes.CLEAR_STAGED_CHANGES, null, { root: true });
commit(rootTypes.CLEAR_REPLACED_FILES, null, { root: true });
setTimeout(() => { setTimeout(() => {
commit(rootTypes.SET_LAST_COMMIT_MSG, '', { root: true }); commit(rootTypes.SET_LAST_COMMIT_MSG, '', { root: true });
}, 5000); }, 5000);
......
...@@ -60,6 +60,8 @@ export const CLEAR_STAGED_CHANGES = 'CLEAR_STAGED_CHANGES'; ...@@ -60,6 +60,8 @@ export const CLEAR_STAGED_CHANGES = 'CLEAR_STAGED_CHANGES';
export const STAGE_CHANGE = 'STAGE_CHANGE'; export const STAGE_CHANGE = 'STAGE_CHANGE';
export const UNSTAGE_CHANGE = 'UNSTAGE_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 UPDATE_FILE_AFTER_COMMIT = 'UPDATE_FILE_AFTER_COMMIT';
export const ADD_PENDING_TAB = 'ADD_PENDING_TAB'; export const ADD_PENDING_TAB = 'ADD_PENDING_TAB';
export const REMOVE_PENDING_TAB = 'REMOVE_PENDING_TAB'; export const REMOVE_PENDING_TAB = 'REMOVE_PENDING_TAB';
......
...@@ -56,6 +56,11 @@ export default { ...@@ -56,6 +56,11 @@ export default {
stagedFiles: [], stagedFiles: [],
}); });
}, },
[types.CLEAR_REPLACED_FILES](state) {
Object.assign(state, {
replacedFiles: [],
});
},
[types.SET_ENTRIES](state, entries) { [types.SET_ENTRIES](state, entries) {
Object.assign(state, { Object.assign(state, {
entries, entries,
...@@ -70,6 +75,13 @@ export default { ...@@ -70,6 +75,13 @@ export default {
Object.assign(state.entries, { Object.assign(state.entries, {
[key]: entry, [key]: entry,
}); });
} else if (foundEntry.deleted) {
Object.assign(state.entries, {
[key]: {
...entry,
replaces: true,
},
});
} else { } else {
const tree = entry.tree.filter( const tree = entry.tree.filter(
f => foundEntry.tree.find(e => e.path === f.path) === undefined, f => foundEntry.tree.find(e => e.path === f.path) === undefined,
...@@ -144,6 +156,7 @@ export default { ...@@ -144,6 +156,7 @@ export default {
raw: file.content, raw: file.content,
changed: Boolean(changedFile), changed: Boolean(changedFile),
staged: false, staged: false,
replaces: false,
prevPath: '', prevPath: '',
moved: false, moved: false,
lastCommitSha: lastCommit.commit.id, lastCommitSha: lastCommit.commit.id,
......
...@@ -170,12 +170,16 @@ export default { ...@@ -170,12 +170,16 @@ export default {
entries: Object.assign(state.entries, { entries: Object.assign(state.entries, {
[path]: Object.assign(state.entries[path], { [path]: Object.assign(state.entries[path], {
staged: true, staged: true,
changed: false,
}), }),
}), }),
}); });
if (stagedFile) { if (stagedFile) {
Object.assign(state, {
replacedFiles: state.replacedFiles.concat({
...stagedFile,
}),
});
Object.assign(stagedFile, { Object.assign(stagedFile, {
...state.entries[path], ...state.entries[path],
}); });
......
...@@ -6,6 +6,7 @@ export default () => ({ ...@@ -6,6 +6,7 @@ export default () => ({
currentMergeRequestId: '', currentMergeRequestId: '',
changedFiles: [], changedFiles: [],
stagedFiles: [], stagedFiles: [],
replacedFiles: [],
endpoints: {}, endpoints: {},
lastCommitMsg: '', lastCommitMsg: '',
lastCommitPath: '', lastCommitPath: '',
......
...@@ -18,6 +18,7 @@ export const dataStructure = () => ({ ...@@ -18,6 +18,7 @@ export const dataStructure = () => ({
active: false, active: false,
changed: false, changed: false,
staged: false, staged: false,
replaces: false,
lastCommitPath: '', lastCommitPath: '',
lastCommitSha: '', lastCommitSha: '',
lastCommit: { lastCommit: {
...@@ -119,7 +120,7 @@ export const commitActionForFile = file => { ...@@ -119,7 +120,7 @@ export const commitActionForFile = file => {
return commitActionTypes.move; return commitActionTypes.move;
} else if (file.deleted) { } else if (file.deleted) {
return commitActionTypes.delete; return commitActionTypes.delete;
} else if (file.tempFile) { } else if (file.tempFile && !file.replaces) {
return commitActionTypes.create; return commitActionTypes.create;
} }
...@@ -151,7 +152,8 @@ export const createCommitPayload = ({ ...@@ -151,7 +152,8 @@ export const createCommitPayload = ({
previous_path: f.prevPath === '' ? undefined : f.prevPath, previous_path: f.prevPath === '' ? undefined : f.prevPath,
content: f.prevPath ? null : f.content || undefined, content: f.prevPath ? null : f.content || undefined,
encoding: f.base64 ? 'base64' : 'text', 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, start_sha: newBranch ? rootGetters.lastCommit.short_id : undefined,
}); });
......
---
title: In WebIDE allow adding new entries of the same name as deleted entry
merge_request: 30239
author:
type: fixed
...@@ -10,6 +10,7 @@ import actions, { ...@@ -10,6 +10,7 @@ import actions, {
deleteEntry, deleteEntry,
renameEntry, renameEntry,
getBranchData, getBranchData,
createTempEntry,
} from '~/ide/stores/actions'; } from '~/ide/stores/actions';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import store from '~/ide/stores'; import store from '~/ide/stores';
...@@ -247,18 +248,30 @@ describe('Multi-file store actions', () => { ...@@ -247,18 +248,30 @@ describe('Multi-file store actions', () => {
}); });
it('sets tmp file as active', done => { it('sets tmp file as active', done => {
store testAction(
.dispatch('createTempEntry', { createTempEntry,
{
name: 'test', name: 'test',
branchId: 'mybranch', branchId: 'mybranch',
type: 'blob', type: 'blob',
}) },
.then(f => { store.state,
expect(f.active).toBeTruthy(); [
{ type: types.CREATE_TMP_ENTRY, payload: jasmine.any(Object) },
done(); { type: types.TOGGLE_FILE_OPEN, payload: 'test' },
}) { type: types.ADD_FILE_TO_CHANGED, payload: 'test' },
.catch(done.fail); ],
[
{
type: 'setFileActive',
payload: 'test',
},
{
type: 'triggerFilesChange',
},
],
done,
);
}); });
it('creates flash message if file already exists', done => { it('creates flash message if file already exists', done => {
...@@ -488,7 +501,11 @@ describe('Multi-file store actions', () => { ...@@ -488,7 +501,11 @@ describe('Multi-file store actions', () => {
'path', 'path',
store.state, store.state,
[{ type: types.DELETE_ENTRY, payload: 'path' }], [{ type: types.DELETE_ENTRY, payload: 'path' }],
[{ type: 'burstUnusedSeal' }, { type: 'triggerFilesChange' }], [
{ type: 'burstUnusedSeal' },
{ type: 'stageChange', payload: 'path' },
{ type: 'triggerFilesChange' },
],
done, done,
); );
}); });
...@@ -515,7 +532,11 @@ describe('Multi-file store actions', () => { ...@@ -515,7 +532,11 @@ describe('Multi-file store actions', () => {
'testFolder/entry-to-delete', 'testFolder/entry-to-delete',
store.state, store.state,
[{ type: types.DELETE_ENTRY, payload: 'testFolder/entry-to-delete' }], [{ 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, done,
); );
}); });
......
...@@ -315,6 +315,19 @@ describe('IDE store file mutations', () => { ...@@ -315,6 +315,19 @@ describe('IDE store file mutations', () => {
expect(localState.stagedFiles.length).toBe(1); expect(localState.stagedFiles.length).toBe(1);
expect(localState.stagedFiles[0].raw).toEqual('testing 123'); 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', () => { describe('UNSTAGE_CHANGE', () => {
......
...@@ -79,6 +79,16 @@ describe('Multi-file store mutations', () => { ...@@ -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', () => { describe('UPDATE_VIEWER', () => {
it('sets viewer state', () => { it('sets viewer state', () => {
mutations.UPDATE_VIEWER(localState, 'diff'); mutations.UPDATE_VIEWER(localState, 'diff');
...@@ -109,6 +119,62 @@ describe('Multi-file store mutations', () => { ...@@ -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', () => { describe('UPDATE_TEMP_FLAG', () => {
beforeEach(() => { beforeEach(() => {
localState.entries.test = { localState.entries.test = {
...@@ -252,6 +318,7 @@ describe('Multi-file store mutations', () => { ...@@ -252,6 +318,7 @@ describe('Multi-file store mutations', () => {
permalink: `${gl.TEST_HOST}/testing-123`, permalink: `${gl.TEST_HOST}/testing-123`,
commitsPath: `${gl.TEST_HOST}/testing-123`, commitsPath: `${gl.TEST_HOST}/testing-123`,
blamePath: `${gl.TEST_HOST}/testing-123`, blamePath: `${gl.TEST_HOST}/testing-123`,
replaces: true,
}; };
localState.entries.test = f; localState.entries.test = f;
localState.changedFiles.push(f); localState.changedFiles.push(f);
...@@ -262,6 +329,7 @@ describe('Multi-file store mutations', () => { ...@@ -262,6 +329,7 @@ describe('Multi-file store mutations', () => {
expect(f.permalink).toBe(`${gl.TEST_HOST}/test`); expect(f.permalink).toBe(`${gl.TEST_HOST}/test`);
expect(f.commitsPath).toBe(`${gl.TEST_HOST}/test`); expect(f.commitsPath).toBe(`${gl.TEST_HOST}/test`);
expect(f.blamePath).toBe(`${gl.TEST_HOST}/test`); expect(f.blamePath).toBe(`${gl.TEST_HOST}/test`);
expect(f.replaces).toBe(false);
}); });
}); });
......
...@@ -92,6 +92,16 @@ describe('Multi-file store utils', () => { ...@@ -92,6 +92,16 @@ describe('Multi-file store utils', () => {
path: 'deletedFile', path: 'deletedFile',
deleted: true, deleted: true,
}, },
{
...file('renamedFile'),
path: 'renamedFile',
prevPath: 'prevPath',
},
{
...file('replacingFile'),
path: 'replacingFile',
replaces: true,
},
], ],
currentBranchId: 'master', currentBranchId: 'master',
}; };
...@@ -131,6 +141,22 @@ describe('Multi-file store utils', () => { ...@@ -131,6 +141,22 @@ describe('Multi-file store utils', () => {
last_commit_id: undefined, last_commit_id: undefined,
previous_path: 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, start_sha: undefined,
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment