Commit c9f4d7b0 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'ph/281171/diffFileConflictAlert' into 'master'

Added warning to diff files when there are conflicts

See merge request gitlab-org/gitlab!67216
parents 29d770cd 30a6f52a
...@@ -14,9 +14,11 @@ import { ...@@ -14,9 +14,11 @@ import {
} from '~/behaviors/shortcuts/keybindings'; } from '~/behaviors/shortcuts/keybindings';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { isSingleViewStyle } from '~/helpers/diffs_helper'; import { isSingleViewStyle } from '~/helpers/diffs_helper';
import { helpPagePath } from '~/helpers/help_page_helper';
import { parseBoolean } from '~/lib/utils/common_utils'; import { parseBoolean } from '~/lib/utils/common_utils';
import { updateHistory } from '~/lib/utils/url_utility'; import { updateHistory } from '~/lib/utils/url_utility';
import { __ } from '~/locale'; import { __ } from '~/locale';
import MrWidgetHowToMergeModal from '~/vue_merge_request_widget/components/mr_widget_how_to_merge_modal.vue';
import PanelResizer from '~/vue_shared/components/panel_resizer.vue'; import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
import notesEventHub from '../../notes/event_hub'; import notesEventHub from '../../notes/event_hub';
...@@ -52,7 +54,6 @@ import CommitWidget from './commit_widget.vue'; ...@@ -52,7 +54,6 @@ import CommitWidget from './commit_widget.vue';
import CompareVersions from './compare_versions.vue'; import CompareVersions from './compare_versions.vue';
import DiffFile from './diff_file.vue'; import DiffFile from './diff_file.vue';
import HiddenFilesWarning from './hidden_files_warning.vue'; import HiddenFilesWarning from './hidden_files_warning.vue';
import MergeConflictWarning from './merge_conflict_warning.vue';
import NoChanges from './no_changes.vue'; import NoChanges from './no_changes.vue';
import PreRenderer from './pre_renderer.vue'; import PreRenderer from './pre_renderer.vue';
import TreeList from './tree_list.vue'; import TreeList from './tree_list.vue';
...@@ -65,7 +66,6 @@ export default { ...@@ -65,7 +66,6 @@ export default {
DiffFile, DiffFile,
NoChanges, NoChanges,
HiddenFilesWarning, HiddenFilesWarning,
MergeConflictWarning,
CollapsedFilesWarning, CollapsedFilesWarning,
CommitWidget, CommitWidget,
TreeList, TreeList,
...@@ -77,6 +77,7 @@ export default { ...@@ -77,6 +77,7 @@ export default {
DynamicScrollerItem, DynamicScrollerItem,
PreRenderer, PreRenderer,
VirtualScrollerScrollSync, VirtualScrollerScrollSync,
MrWidgetHowToMergeModal,
}, },
alerts: { alerts: {
ALERT_OVERFLOW_HIDDEN, ALERT_OVERFLOW_HIDDEN,
...@@ -164,6 +165,21 @@ export default { ...@@ -164,6 +165,21 @@ export default {
required: false, required: false,
default: () => ({}), default: () => ({}),
}, },
sourceProjectDefaultUrl: {
type: String,
required: false,
default: '',
},
sourceProjectFullPath: {
type: String,
required: false,
default: '',
},
isForked: {
type: Boolean,
required: false,
default: false,
},
}, },
data() { data() {
const treeWidth = const treeWidth =
...@@ -203,6 +219,8 @@ export default { ...@@ -203,6 +219,8 @@ export default {
'mrReviews', 'mrReviews',
'renderTreeList', 'renderTreeList',
'showWhitespace', 'showWhitespace',
'targetBranchName',
'branchName',
]), ]),
...mapGetters('diffs', [ ...mapGetters('diffs', [
'whichCollapsedTypes', 'whichCollapsedTypes',
...@@ -596,6 +614,9 @@ export default { ...@@ -596,6 +614,9 @@ export default {
}, },
minTreeWidth: MIN_TREE_WIDTH, minTreeWidth: MIN_TREE_WIDTH,
maxTreeWidth: MAX_TREE_WIDTH, maxTreeWidth: MAX_TREE_WIDTH,
howToMergeDocsPath: helpPagePath('user/project/merge_requests/reviews/index.md', {
anchor: 'checkout-merge-requests-locally-through-the-head-ref',
}),
}; };
</script> </script>
...@@ -615,12 +636,6 @@ export default { ...@@ -615,12 +636,6 @@ export default {
:plain-diff-path="plainDiffPath" :plain-diff-path="plainDiffPath"
:email-patch-path="emailPatchPath" :email-patch-path="emailPatchPath"
/> />
<merge-conflict-warning
v-if="visibleWarning == $options.alerts.ALERT_MERGE_CONFLICT"
:limited="isLimitedContainer"
:resolution-path="conflictResolutionPath"
:mergeable="canMerge"
/>
<collapsed-files-warning <collapsed-files-warning
v-if="visibleWarning == $options.alerts.ALERT_COLLAPSED_FILES" v-if="visibleWarning == $options.alerts.ALERT_COLLAPSED_FILES"
:limited="isLimitedContainer" :limited="isLimitedContainer"
...@@ -733,6 +748,15 @@ export default { ...@@ -733,6 +748,15 @@ export default {
<no-changes v-else :changes-empty-state-illustration="changesEmptyStateIllustration" /> <no-changes v-else :changes-empty-state-illustration="changesEmptyStateIllustration" />
</div> </div>
</div> </div>
<mr-widget-how-to-merge-modal
:is-fork="isForked"
:can-merge="canMerge"
:source-branch="branchName"
:source-project-path="sourceProjectFullPath"
:target-branch="targetBranchName"
:source-project-default-url="sourceProjectDefaultUrl"
:reviewing-docs-path="$options.howToMergeDocsPath"
/>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { GlButton, GlLoadingIcon, GlSafeHtmlDirective as SafeHtml, GlSprintf } from '@gitlab/ui'; import {
GlButton,
GlLoadingIcon,
GlSafeHtmlDirective as SafeHtml,
GlSprintf,
GlAlert,
GlModalDirective,
} from '@gitlab/ui';
import { escape } from 'lodash'; import { escape } from 'lodash';
import { mapActions, mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import { IdState } from 'vendor/vue-virtual-scroller'; import { IdState } from 'vendor/vue-virtual-scroller';
...@@ -19,7 +26,7 @@ import { ...@@ -19,7 +26,7 @@ import {
EVT_PERF_MARK_FIRST_DIFF_FILE_SHOWN, EVT_PERF_MARK_FIRST_DIFF_FILE_SHOWN,
} from '../constants'; } from '../constants';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import { DIFF_FILE, GENERIC_ERROR } from '../i18n'; import { DIFF_FILE, GENERIC_ERROR, CONFLICT_TEXT } from '../i18n';
import { collapsedType, getShortShaFromFile } from '../utils/diff_file'; import { collapsedType, getShortShaFromFile } from '../utils/diff_file';
import DiffContent from './diff_content.vue'; import DiffContent from './diff_content.vue';
import DiffFileHeader from './diff_file_header.vue'; import DiffFileHeader from './diff_file_header.vue';
...@@ -31,9 +38,11 @@ export default { ...@@ -31,9 +38,11 @@ export default {
GlButton, GlButton,
GlLoadingIcon, GlLoadingIcon,
GlSprintf, GlSprintf,
GlAlert,
}, },
directives: { directives: {
SafeHtml, SafeHtml,
GlModalDirective,
}, },
mixins: [glFeatureFlagsMixin(), IdState({ idProp: (vm) => vm.file.file_hash })], mixins: [glFeatureFlagsMixin(), IdState({ idProp: (vm) => vm.file.file_hash })],
props: { props: {
...@@ -93,7 +102,12 @@ export default { ...@@ -93,7 +102,12 @@ export default {
genericError: GENERIC_ERROR, genericError: GENERIC_ERROR,
}, },
computed: { computed: {
...mapState('diffs', ['currentDiffFileId', 'codequalityDiff']), ...mapState('diffs', [
'currentDiffFileId',
'codequalityDiff',
'conflictResolutionPath',
'canMerge',
]),
...mapGetters(['isNotesFetched']), ...mapGetters(['isNotesFetched']),
...mapGetters('diffs', ['getDiffFileDiscussions', 'isVirtualScrollingEnabled']), ...mapGetters('diffs', ['getDiffFileDiscussions', 'isVirtualScrollingEnabled']),
viewBlobHref() { viewBlobHref() {
...@@ -312,6 +326,7 @@ export default { ...@@ -312,6 +326,7 @@ export default {
this.idState.forkMessageVisible = false; this.idState.forkMessageVisible = false;
}, },
}, },
CONFLICT_TEXT,
}; };
</script> </script>
...@@ -390,6 +405,55 @@ export default { ...@@ -390,6 +405,55 @@ export default {
<div v-else v-safe-html="errorMessage" class="nothing-here-block"></div> <div v-else v-safe-html="errorMessage" class="nothing-here-block"></div>
</div> </div>
<template v-else> <template v-else>
<gl-alert
v-if="file.conflict_type"
variant="danger"
:dismissible="false"
data-testid="conflictsAlert"
>
{{ $options.CONFLICT_TEXT[file.conflict_type] }}
<template v-if="!canMerge">
{{ __('Ask someone with write access to resolve it.') }}
</template>
<gl-sprintf
v-else-if="conflictResolutionPath"
:message="
__(
'You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}.',
)
"
>
<template #gitlabLink="{ content }">
<gl-button
:href="conflictResolutionPath"
variant="link"
class="gl-vertical-align-text-bottom"
>{{ content }}</gl-button
>
</template>
<template #resolveLocally="{ content }">
<gl-button
v-gl-modal-directive="'modal-merge-info'"
variant="link"
class="gl-vertical-align-text-bottom"
>{{ content }}</gl-button
>
</template>
</gl-sprintf>
<gl-sprintf
v-else
:message="__('You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}.')"
>
<template #resolveLocally="{ content }">
<gl-button
v-gl-modal-directive="'modal-merge-info'"
variant="link"
class="gl-vertical-align-text-bottom"
>{{ content }}</gl-button
>
</template>
</gl-sprintf>
</gl-alert>
<div <div
v-if="showWarning" v-if="showWarning"
class="collapsed-file-warning gl-p-7 gl-bg-orange-50 gl-text-center gl-rounded-bottom-left-base gl-rounded-bottom-right-base" class="collapsed-file-warning gl-p-7 gl-bg-orange-50 gl-text-center gl-rounded-bottom-left-base gl-rounded-bottom-right-base"
......
...@@ -134,12 +134,6 @@ export default { ...@@ -134,12 +134,6 @@ export default {
interopRightAttributes(props) { interopRightAttributes(props) {
return getInteropNewSideAttributes(props.line.right); return getInteropNewSideAttributes(props.line.right);
}, },
conflictText: memoize(
(line) => {
return line.type === CONFLICT_MARKER_THEIR ? 'HEAD//our changes' : 'origin//their changes';
},
(line) => line.type,
),
lineContent: (line) => { lineContent: (line) => {
if (line.isConflictMarker) { if (line.isConflictMarker) {
return line.type === CONFLICT_MARKER_THEIR ? 'HEAD//our changes' : 'origin//their changes'; return line.type === CONFLICT_MARKER_THEIR ? 'HEAD//our changes' : 'origin//their changes';
......
...@@ -25,3 +25,25 @@ export const SETTINGS_DROPDOWN = { ...@@ -25,3 +25,25 @@ export const SETTINGS_DROPDOWN = {
fileByFile: __('Show one file at a time'), fileByFile: __('Show one file at a time'),
preferences: __('Preferences'), preferences: __('Preferences'),
}; };
export const CONFLICT_TEXT = {
both_modified: __('Conflict: This file was modified in both the source and target branches.'),
modified_source_removed_target: __(
'Conflict: This file was modified in the source branch, but removed in the target branch.',
),
modified_target_removed_source: __(
'Conflict: This file was removed in the source branch, but modified in the target branch.',
),
renamed_same_file: __(
'Conflict: This file was renamed differently in the source and target branches.',
),
removed_source_renamed_target: __(
'Conflict: This file was removed in the source branch, but renamed in the target branch.',
),
removed_target_renamed_source: __(
'Conflict: This file was renamed in the source branch, but removed in the target branch.',
),
both_added: __(
'Conflict: This file was added both in the source and target branches, but with different contents.',
),
};
...@@ -83,6 +83,9 @@ export default function initDiffsApp(store) { ...@@ -83,6 +83,9 @@ export default function initDiffsApp(store) {
showWhitespaceDefault: parseBoolean(dataset.showWhitespaceDefault), showWhitespaceDefault: parseBoolean(dataset.showWhitespaceDefault),
viewDiffsFileByFile: parseBoolean(dataset.fileByFileDefault), viewDiffsFileByFile: parseBoolean(dataset.fileByFileDefault),
defaultSuggestionCommitMessage: dataset.defaultSuggestionCommitMessage, defaultSuggestionCommitMessage: dataset.defaultSuggestionCommitMessage,
sourceProjectDefaultUrl: dataset.sourceProjectDefaultUrl,
sourceProjectFullPath: dataset.sourceProjectFullPath,
isForked: parseBoolean(dataset.isForked),
}; };
}, },
computed: { computed: {
...@@ -147,6 +150,9 @@ export default function initDiffsApp(store) { ...@@ -147,6 +150,9 @@ export default function initDiffsApp(store) {
fileByFileUserPreference: this.viewDiffsFileByFile, fileByFileUserPreference: this.viewDiffsFileByFile,
defaultSuggestionCommitMessage: this.defaultSuggestionCommitMessage, defaultSuggestionCommitMessage: this.defaultSuggestionCommitMessage,
rehydratedMrReviews: getReviewsForMergeRequest(mrPath), rehydratedMrReviews: getReviewsForMergeRequest(mrPath),
sourceProjectDefaultUrl: this.sourceProjectDefaultUrl,
sourceProjectFullPath: this.sourceProjectFullPath,
isForked: this.isForked,
}, },
}); });
}, },
......
...@@ -186,7 +186,10 @@ module MergeRequestsHelper ...@@ -186,7 +186,10 @@ module MergeRequestsHelper
show_suggest_popover: show_suggest_popover?.to_s, show_suggest_popover: show_suggest_popover?.to_s,
show_whitespace_default: @show_whitespace_default.to_s, show_whitespace_default: @show_whitespace_default.to_s,
file_by_file_default: @file_by_file_default.to_s, file_by_file_default: @file_by_file_default.to_s,
default_suggestion_commit_message: default_suggestion_commit_message default_suggestion_commit_message: default_suggestion_commit_message,
source_project_default_url: @merge_request.source_project && default_url_to_repo(@merge_request.source_project),
source_project_full_path: @merge_request.source_project&.full_path,
is_forked: @project.forked?.to_s
} }
end end
......
...@@ -4514,6 +4514,9 @@ msgstr "" ...@@ -4514,6 +4514,9 @@ msgstr ""
msgid "Ask again later" msgid "Ask again later"
msgstr "" msgstr ""
msgid "Ask someone with write access to resolve it."
msgstr ""
msgid "Ask your group maintainer to set up a group runner." msgid "Ask your group maintainer to set up a group runner."
msgstr "" msgstr ""
...@@ -8503,6 +8506,27 @@ msgstr "" ...@@ -8503,6 +8506,27 @@ msgstr ""
msgid "Confirmed:" msgid "Confirmed:"
msgstr "" msgstr ""
msgid "Conflict: This file was added both in the source and target branches, but with different contents."
msgstr ""
msgid "Conflict: This file was modified in both the source and target branches."
msgstr ""
msgid "Conflict: This file was modified in the source branch, but removed in the target branch."
msgstr ""
msgid "Conflict: This file was removed in the source branch, but modified in the target branch."
msgstr ""
msgid "Conflict: This file was removed in the source branch, but renamed in the target branch."
msgstr ""
msgid "Conflict: This file was renamed differently in the source and target branches."
msgstr ""
msgid "Conflict: This file was renamed in the source branch, but removed in the target branch."
msgstr ""
msgid "Confluence" msgid "Confluence"
msgstr "" msgstr ""
...@@ -37762,6 +37786,12 @@ msgstr "" ...@@ -37762,6 +37786,12 @@ msgstr ""
msgid "You are using PostgreSQL %{pg_version_current}, but PostgreSQL %{pg_version_minimum} is required for this version of GitLab. Please upgrade your environment to a supported PostgreSQL version, see %{pg_requirements_url} for details." msgid "You are using PostgreSQL %{pg_version_current}, but PostgreSQL %{pg_version_minimum} is required for this version of GitLab. Please upgrade your environment to a supported PostgreSQL version, see %{pg_requirements_url} for details."
msgstr "" msgstr ""
msgid "You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}."
msgstr ""
msgid "You can also create a project from the command line." msgid "You can also create a project from the command line."
msgstr "" msgstr ""
......
...@@ -17,7 +17,6 @@ import TreeList from '~/diffs/components/tree_list.vue'; ...@@ -17,7 +17,6 @@ import TreeList from '~/diffs/components/tree_list.vue';
/* You know what: sometimes alphabetical isn't the best order */ /* You know what: sometimes alphabetical isn't the best order */
import CollapsedFilesWarning from '~/diffs/components/collapsed_files_warning.vue'; import CollapsedFilesWarning from '~/diffs/components/collapsed_files_warning.vue';
import HiddenFilesWarning from '~/diffs/components/hidden_files_warning.vue'; import HiddenFilesWarning from '~/diffs/components/hidden_files_warning.vue';
import MergeConflictWarning from '~/diffs/components/merge_conflict_warning.vue';
/* eslint-enable import/order */ /* eslint-enable import/order */
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
...@@ -542,43 +541,6 @@ describe('diffs/components/app', () => { ...@@ -542,43 +541,6 @@ describe('diffs/components/app', () => {
expect(getCollapsedFilesWarning(wrapper).exists()).toBe(false); expect(getCollapsedFilesWarning(wrapper).exists()).toBe(false);
}); });
}); });
describe('merge conflicts', () => {
it('should render the merge conflicts banner if viewing the whole changeset and there are conflicts', () => {
createComponent({}, ({ state }) => {
Object.assign(state.diffs, {
latestDiff: true,
startVersion: null,
hasConflicts: true,
canMerge: false,
conflictResolutionPath: 'path',
});
});
expect(wrapper.find(MergeConflictWarning).exists()).toBe(true);
});
it.each`
prop | value
${'latestDiff'} | ${false}
${'startVersion'} | ${'notnull'}
${'hasConflicts'} | ${false}
`(
"should not render if any of the MR properties aren't correct - like $prop: $value",
({ prop, value }) => {
createComponent({}, ({ state }) => {
Object.assign(state.diffs, {
latestDiff: true,
startVersion: null,
hasConflicts: true,
[prop]: value,
});
});
expect(wrapper.find(MergeConflictWarning).exists()).toBe(false);
},
);
});
}); });
it('should display commit widget if store has a commit', () => { it('should display commit widget if store has a commit', () => {
......
...@@ -541,4 +541,34 @@ describe('DiffFile', () => { ...@@ -541,4 +541,34 @@ describe('DiffFile', () => {
expect(findLoader(wrapper).exists()).toBe(true); expect(findLoader(wrapper).exists()).toBe(true);
}); });
describe('merge conflicts', () => {
beforeEach(() => {
wrapper.destroy();
});
it('does not render conflict alert', () => {
const file = {
...getReadableFile(),
conflict_type: null,
renderIt: true,
};
({ wrapper, store } = createComponent({ file }));
expect(wrapper.find('[data-testid="conflictsAlert"]').exists()).toBe(false);
});
it('renders conflict alert when conflict_type is present', () => {
const file = {
...getReadableFile(),
conflict_type: 'both_modified',
renderIt: true,
};
({ wrapper, store } = createComponent({ file }));
expect(wrapper.find('[data-testid="conflictsAlert"]').exists()).toBe(true);
});
});
}); });
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