Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
gitlab-ce
Commits
26f658de
Commit
26f658de
authored
Sep 08, 2016
by
Alfredo Sumaran
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement editor to manually resolve merge conflicts
parent
6af52d7d
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
300 additions
and
73 deletions
+300
-73
app/assets/javascripts/merge_conflict_data_provider.js.es6
app/assets/javascripts/merge_conflict_data_provider.js.es6
+126
-39
app/assets/javascripts/merge_conflict_resolver.js.es6
app/assets/javascripts/merge_conflict_resolver.js.es6
+45
-15
app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6
...cripts/merge_conflicts/components/diff_file_editor.js.es6
+63
-0
app/assets/stylesheets/pages/merge_conflicts.scss
app/assets/stylesheets/pages/merge_conflicts.scss
+26
-0
app/views/projects/merge_requests/conflicts.html.haml
app/views/projects/merge_requests/conflicts.html.haml
+3
-0
app/views/projects/merge_requests/conflicts/_diff_file_editor.html.haml
...ects/merge_requests/conflicts/_diff_file_editor.html.haml
+9
-0
app/views/projects/merge_requests/conflicts/_file_actions.html.haml
...projects/merge_requests/conflicts/_file_actions.html.haml
+12
-0
app/views/projects/merge_requests/conflicts/_inline_view.html.haml
.../projects/merge_requests/conflicts/_inline_view.html.haml
+4
-6
app/views/projects/merge_requests/conflicts/_parallel_view.html.haml
...rojects/merge_requests/conflicts/_parallel_view.html.haml
+4
-6
app/views/projects/merge_requests/conflicts/_submit_form.html.haml
.../projects/merge_requests/conflicts/_submit_form.html.haml
+2
-7
app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml
...requests/conflicts/components/_diff_file_editor.html.haml
+6
-0
No files found.
app/assets/javascripts/merge_conflict_data_provider.js.es6
View file @
26f658de
...
@@ -2,7 +2,9 @@ const HEAD_HEADER_TEXT = 'HEAD//our changes';
...
@@ -2,7 +2,9 @@ const HEAD_HEADER_TEXT = 'HEAD//our changes';
const ORIGIN_HEADER_TEXT = 'origin//their changes';
const ORIGIN_HEADER_TEXT = 'origin//their changes';
const HEAD_BUTTON_TITLE = 'Use ours';
const HEAD_BUTTON_TITLE = 'Use ours';
const ORIGIN_BUTTON_TITLE = 'Use theirs';
const ORIGIN_BUTTON_TITLE = 'Use theirs';
const INTERACTIVE_RESOLVE_MODE = 'interactive';
const EDIT_RESOLVE_MODE = 'edit';
const DEFAULT_RESOLVE_MODE = INTERACTIVE_RESOLVE_MODE;
class MergeConflictDataProvider {
class MergeConflictDataProvider {
...
@@ -18,8 +20,7 @@ class MergeConflictDataProvider {
...
@@ -18,8 +20,7 @@ class MergeConflictDataProvider {
diffViewType : diffViewType,
diffViewType : diffViewType,
fixedLayout : fixedLayout,
fixedLayout : fixedLayout,
isSubmitting : false,
isSubmitting : false,
conflictsData : {},
conflictsData : {}
resolutionData : {}
}
}
}
}
...
@@ -35,9 +36,9 @@ class MergeConflictDataProvider {
...
@@ -35,9 +36,9 @@ class MergeConflictDataProvider {
data.shortCommitSha = data.commit_sha.slice(0, 7);
data.shortCommitSha = data.commit_sha.slice(0, 7);
data.commitMessage = data.commit_message;
data.commitMessage = data.commit_message;
this.decorateFiles(data);
this.setParallelLines(data);
this.setParallelLines(data);
this.setInlineLines(data);
this.setInlineLines(data);
this.updateResolutionsData(data);
}
}
vueInstance.conflictsData = data;
vueInstance.conflictsData = data;
...
@@ -47,16 +48,12 @@ class MergeConflictDataProvider {
...
@@ -47,16 +48,12 @@ class MergeConflictDataProvider {
vueInstance.conflictsData.conflictsText = conflictsText;
vueInstance.conflictsData.conflictsText = conflictsText;
}
}
decorateFiles(data) {
updateResolutionsData(data) {
data.files.forEach((file) => {
const vi = this.vueInstance;
file.content = '';
file.resolutionData = {};
data.files.forEach( (file) => {
file.promptDiscardConfirmation = false;
file.sections.forEach( (section) => {
file.resolveMode = DEFAULT_RESOLVE_MODE;
if (section.conflict) {
vi.$set(`resolutionData['${section.id}']`, false);
}
});
});
});
}
}
...
@@ -165,11 +162,14 @@ class MergeConflictDataProvider {
...
@@ -165,11 +162,14 @@ class MergeConflictDataProvider {
}
}
handleSelected(sectionId, selection) {
handleSelected(
file,
sectionId, selection) {
const vi = this.vueInstance;
const vi = this.vueInstance;
let files = vi.conflictsData.files;
vi.$set(`conflictsData.files[${files.indexOf(file)}].resolutionData['${sectionId}']`, selection);
vi.resolutionData[sectionId] = selection;
files.forEach( (file) => {
vi.conflictsData.files.forEach( (file) => {
file.inlineLines.forEach( (line) => {
file.inlineLines.forEach( (line) => {
if (line.id === sectionId && (line.hasConflict || line.isHeader)) {
if (line.id === sectionId && (line.hasConflict || line.isHeader)) {
this.markLine(line, selection);
this.markLine(line, selection);
...
@@ -208,6 +208,48 @@ class MergeConflictDataProvider {
...
@@ -208,6 +208,48 @@ class MergeConflictDataProvider {
.toggleClass('container-limited', !vi.isParallel && vi.fixedLayout);
.toggleClass('container-limited', !vi.isParallel && vi.fixedLayout);
}
}
setFileResolveMode(file, mode) {
const vi = this.vueInstance;
// Restore Interactive mode when switching to Edit mode
if (mode === EDIT_RESOLVE_MODE) {
file.resolutionData = {};
this.restoreFileLinesState(file);
}
file.resolveMode = mode;
}
restoreFileLinesState(file) {
file.inlineLines.forEach((line) => {
if (line.hasConflict || line.isHeader) {
line.isSelected = false;
line.isUnselected = false;
}
});
file.parallelLines.forEach((lines) => {
const left = lines[0];
const right = lines[1];
const isLeftMatch = left.hasConflict || left.isHeader;
const isRightMatch = right.hasConflict || right.isHeader;
if (isLeftMatch || isRightMatch) {
left.isSelected = false;
left.isUnselected = false;
right.isSelected = false;
right.isUnselected = false;
}
});
}
setPromptConfirmationState(file, state) {
file.promptDiscardConfirmation = state;
}
markLine(line, selection) {
markLine(line, selection) {
if (selection === 'head' && line.isHead) {
if (selection === 'head' && line.isHead) {
...
@@ -226,31 +268,54 @@ class MergeConflictDataProvider {
...
@@ -226,31 +268,54 @@ class MergeConflictDataProvider {
getConflictsCount() {
getConflictsCount() {
return Object.keys(this.vueInstance.resolutionData).length;
const files = this.vueInstance.conflictsData.files;
}
let count = 0;
getResolvedCount() {
let count = 0;
const data = this.vueInstance.resolutionData;
for (const id in data) {
files.forEach((file) => {
const resolution = data[id];
file.sections.forEach((section) => {
if (resolution) {
if (section.conflict) {
count++;
count++;
}
}
}
});
});
return count;
return count;
}
}
isReadyToCommit() {
isReadyToCommit() {
const { conflictsData, isSubmitting } = this.vueInstance
const vi = this.vueInstance;
const allResolved = this.getConflictsCount() === this.getResolvedCount();
const files = this.vueInstance.conflictsData.files;
const hasCommitMessage = $.trim(conflictsData.commitMessage).length;
const hasCommitMessage = $.trim(this.vueInstance.conflictsData.commitMessage).length;
let unresolved = 0;
for (let i = 0, l = files.length; i < l; i++) {
let file = files[i];
if (file.resolveMode === INTERACTIVE_RESOLVE_MODE) {
let numberConflicts = 0;
let resolvedConflicts = Object.keys(file.resolutionData).length
for (let j = 0, k = file.sections.length; j < k; j++) {
if (file.sections[j].conflict) {
numberConflicts++;
}
}
if (resolvedConflicts !== numberConflicts) {
unresolved++;
}
} else if (file.resolveMode === EDIT_RESOLVE_MODE) {
// Unlikely to happen since switching to Edit mode saves content automatically.
// Checking anyway in case the save strategy changes in the future
if (!file.content) {
unresolved++;
continue;
}
}
}
return !
isSubmitting && hasCommitMessage && allR
esolved;
return !
vi.isSubmitting && hasCommitMessage && !unr
esolved;
}
}
...
@@ -332,10 +397,33 @@ class MergeConflictDataProvider {
...
@@ -332,10 +397,33 @@ class MergeConflictDataProvider {
getCommitData() {
getCommitData() {
return {
let conflictsData = this.vueInstance.conflictsData;
commit_message: this.vueInstance.conflictsData.commitMessage,
let commitData = {};
sections: this.vueInstance.resolutionData
}
commitData = {
commitMessage: conflictsData.commitMessage,
files: []
};
conflictsData.files.forEach((file) => {
let addFile;
addFile = {
old_path: file.old_path,
new_path: file.new_path
};
// Submit only one data for type of editing
if (file.resolveMode === INTERACTIVE_RESOLVE_MODE) {
addFile.sections = file.resolutionData;
} else if (file.resolveMode === EDIT_RESOLVE_MODE) {
addFile.content = file.content;
}
commitData.files.push(addFile);
});
return commitData;
}
}
...
@@ -343,5 +431,4 @@ class MergeConflictDataProvider {
...
@@ -343,5 +431,4 @@ class MergeConflictDataProvider {
const { old_path, new_path } = file;
const { old_path, new_path } = file;
return old_path === new_path ? new_path : `${old_path} → ${new_path}`;
return old_path === new_path ? new_path : `${old_path} → ${new_path}`;
}
}
}
}
app/assets/javascripts/merge_conflict_resolver.js.es6
View file @
26f658de
//= require vue
//= require vue
//= require ./merge_conflicts/components/diff_file_editor
const INTERACTIVE_RESOLVE_MODE = 'interactive';
const EDIT_RESOLVE_MODE = 'edit';
class MergeConflictResolver {
class MergeConflictResolver {
constructor() {
constructor() {
this.dataProvider = new MergeConflictDataProvider()
this.dataProvider = new MergeConflictDataProvider()
;
this.initVue()
this.initVue()
;
}
}
initVue() {
initVue() {
const that = this;
const that = this;
this.vue = new Vue({
this.vue = new Vue({
...
@@ -17,15 +20,28 @@ class MergeConflictResolver {
...
@@ -17,15 +20,28 @@ class MergeConflictResolver {
created : this.fetchData(),
created : this.fetchData(),
computed : this.setComputedProperties(),
computed : this.setComputedProperties(),
methods : {
methods : {
handleSelected(sectionId, selection) {
handleSelected(
file,
sectionId, selection) {
that.dataProvider.handleSelected(sectionId, selection);
that.dataProvider.handleSelected(
file,
sectionId, selection);
},
},
handleViewTypeChange(newType) {
handleViewTypeChange(newType) {
that.dataProvider.updateViewType(newType);
that.dataProvider.updateViewType(newType);
},
},
commit() {
commit() {
that.commit();
that.commit();
}
},
onClickResolveModeButton(file, mode) {
that.toggleResolveMode(file, mode);
},
acceptDiscardConfirmation(file) {
that.dataProvider.setPromptConfirmationState(file, false);
that.dataProvider.setFileResolveMode(file, INTERACTIVE_RESOLVE_MODE);
},
cancelDiscardConfirmation(file) {
that.dataProvider.setPromptConfirmationState(file, false);
},
},
components: {
'diff-file-editor': window.gl.diffFileEditor
}
}
})
})
}
}
...
@@ -36,7 +52,6 @@ class MergeConflictResolver {
...
@@ -36,7 +52,6 @@ class MergeConflictResolver {
return {
return {
conflictsCount() { return dp.getConflictsCount() },
conflictsCount() { return dp.getConflictsCount() },
resolvedCount() { return dp.getResolvedCount() },
readyToCommit() { return dp.isReadyToCommit() },
readyToCommit() { return dp.isReadyToCommit() },
commitButtonText() { return dp.getCommitButtonText() }
commitButtonText() { return dp.getCommitButtonText() }
}
}
...
@@ -69,14 +84,29 @@ class MergeConflictResolver {
...
@@ -69,14 +84,29 @@ class MergeConflictResolver {
commit() {
commit() {
this.vue.isSubmitting = true;
this.vue.isSubmitting = true;
$.post($('#conflicts').data('resolveConflictsPath'), this.dataProvider.getCommitData())
$.ajax({
.done((data) => {
url: $('#conflicts').data('resolveConflictsPath'),
window.location.href = data.redirect_to;
data: JSON.stringify(this.dataProvider.getCommitData()),
})
contentType: "application/json",
.error(() => {
dataType: 'json',
this.vue.isSubmitting = false;
method: 'POST'
new Flash('Something went wrong!');
})
});
.done((data) => {
window.location.href = data.redirect_to;
})
.error(() => {
this.vue.isSubmitting = false;
new Flash('Something went wrong!');
});
}
}
toggleResolveMode(file, mode) {
if (mode === INTERACTIVE_RESOLVE_MODE && file.resolveEditChanged) {
this.dataProvider.setPromptConfirmationState(file, true);
return;
}
this.dataProvider.setFileResolveMode(file, mode);
}
}
}
app/assets/javascripts/merge_conflicts/components/diff_file_editor.js.es6
0 → 100644
View file @
26f658de
((global) => {
global.diffFileEditor = Vue.extend({
props: ['file', 'loadFile'],
template: '#diff-file-editor',
data() {
return {
originalState: '',
saved: false,
loading: false,
fileLoaded: false
}
},
computed: {
classObject() {
return {
'load-file': this.loadFile,
'saved': this.saved,
'is-loading': this.loading
};
}
},
watch: {
loadFile(val) {
const self = this;
if (!val || this.fileLoaded || this.loading) {
return
}
this.loading = true;
$.get(this.file.content_path)
.done((file) => {
$(self.$el).find('textarea').val(file.content);
self.originalState = file.content;
self.fileLoaded = true;
self.saveDiffResolution();
})
.fail(() => {
console.log('error');
})
.always(() => {
self.loading = false;
});
}
},
methods: {
saveDiffResolution() {
this.saved = true;
// This probably be better placed in the data provider
this.file.content = this.$el.querySelector('textarea').value;
this.file.resolveEditChanged = this.file.content !== this.originalState;
this.file.promptDiscardConfirmation = false;
},
onInput() {
this.saveDiffResolution();
}
}
});
})(window.gl || (window.gl = {}));
app/assets/stylesheets/pages/merge_conflicts.scss
View file @
26f658de
...
@@ -235,4 +235,30 @@ $colors: (
...
@@ -235,4 +235,30 @@ $colors: (
.btn-success
.fa-spinner
{
.btn-success
.fa-spinner
{
color
:
#fff
;
color
:
#fff
;
}
}
.editor-wrap
{
&
.is-loading
{
.editor
{
display
:
none
;
}
.loading-text
{
display
:
block
;
}
}
&
.saved
{
.editor
{
border-top
:
solid
1px
green
;
}
}
.editor
{
border-top
:
solid
1px
yellow
;
}
.loading-text
{
display
:
none
;
}
}
}
}
app/views/projects/merge_requests/conflicts.html.haml
View file @
26f658de
...
@@ -27,3 +27,6 @@
...
@@ -27,3 +27,6 @@
=
render
partial:
"projects/merge_requests/conflicts/parallel_view"
,
locals:
{
class_bindings:
class_bindings
}
=
render
partial:
"projects/merge_requests/conflicts/parallel_view"
,
locals:
{
class_bindings:
class_bindings
}
=
render
partial:
"projects/merge_requests/conflicts/inline_view"
,
locals:
{
class_bindings:
class_bindings
}
=
render
partial:
"projects/merge_requests/conflicts/inline_view"
,
locals:
{
class_bindings:
class_bindings
}
=
render
partial:
"projects/merge_requests/conflicts/submit_form"
=
render
partial:
"projects/merge_requests/conflicts/submit_form"
-# Components
=
render
partial:
'projects/merge_requests/conflicts/components/diff_file_editor'
app/views/projects/merge_requests/conflicts/_diff_file_editor.html.haml
0 → 100644
View file @
26f658de
-
if_condition
=
local_assigns
.
fetch
(
:if_condition
,
''
)
.diff-editor-wrap
{
"v-show"
=>
if_condition
}
.discard-changes-alert-wrap
{
"v-if"
=>
"file.promptDiscardConfirmation"
}
%p
Are you sure to discard your changes?
%button
.btn.btn-sm.btn-close
{
"@click"
=>
"acceptDiscardConfirmation(file)"
}
Discard changes
%button
.btn.btn-sm
{
"@click"
=>
"cancelDiscardConfirmation(file)"
}
Cancel
%diff-file-editor
{
":file"
=>
"file"
,
":load-file"
=>
if_condition
}
app/views/projects/merge_requests/conflicts/_file_actions.html.haml
0 → 100644
View file @
26f658de
.file-actions
.btn-group
%button
.btn
{
":class"
=>
"{ 'active': file.resolveMode == 'interactive' }"
,
'@click'
=>
"onClickResolveModeButton(file, 'interactive')"
,
type:
'button'
}
Interactive mode
%button
.btn
{
':class'
=>
"{ 'active': file.resolveMode == 'edit' }"
,
'@click'
=>
"onClickResolveModeButton(file, 'edit')"
,
type:
'button'
}
Edit inline
%a
.btn.view-file.btn-file-option
{
":href"
=>
"file.blobPath"
}
View file @{{conflictsData.shortCommitSha}}
app/views/projects/merge_requests/conflicts/_inline_view.html.haml
View file @
26f658de
...
@@ -3,12 +3,9 @@
...
@@ -3,12 +3,9 @@
.file-title
.file-title
%i
.fa.fa-fw
{
":class"
=>
"file.iconClass"
}
%i
.fa.fa-fw
{
":class"
=>
"file.iconClass"
}
%strong
{{file.filePath}}
%strong
{{file.filePath}}
.file-actions
=
render
partial:
'projects/merge_requests/conflicts/file_actions'
%a
.btn.view-file.btn-file-option
{
":href"
=>
"file.blobPath"
}
View file @{{conflictsData.shortCommitSha}}
.diff-content.diff-wrap-lines
.diff-content.diff-wrap-lines
.diff-wrap-lines.code.file-content.js-syntax-highlight
.diff-wrap-lines.code.file-content.js-syntax-highlight
{
'v-show'
=>
"file.resolveMode === 'interactive'"
}
%table
%table
%tr
.line_holder.diff-inline
{
"v-for"
=>
"line in file.inlineLines"
}
%tr
.line_holder.diff-inline
{
"v-for"
=>
"line in file.inlineLines"
}
%template
{
"v-if"
=>
"!line.isHeader"
}
%template
{
"v-if"
=>
"!line.isHeader"
}
...
@@ -24,5 +21,6 @@
...
@@ -24,5 +21,6 @@
%td
.diff-line-num.header
{
":class"
=>
class_bindings
}
%td
.diff-line-num.header
{
":class"
=>
class_bindings
}
%td
.line_content.header
{
":class"
=>
class_bindings
}
%td
.line_content.header
{
":class"
=>
class_bindings
}
%strong
{{{line.richText}}}
%strong
{{{line.richText}}}
%button
.btn
{
"@click"
=>
"handleSelected(line.id, line.section)"
}
%button
.btn
{
"@click"
=>
"handleSelected(file, line.id, line.section)"
}
{{line.buttonTitle}}
{{line.buttonTitle}}
=
render
partial:
'projects/merge_requests/conflicts/diff_file_editor'
,
locals:
{
if_condition:
"file.resolveMode === 'edit' && !isParallel"
}
app/views/projects/merge_requests/conflicts/_parallel_view.html.haml
View file @
26f658de
...
@@ -3,12 +3,9 @@
...
@@ -3,12 +3,9 @@
.file-title
.file-title
%i
.fa.fa-fw
{
":class"
=>
"file.iconClass"
}
%i
.fa.fa-fw
{
":class"
=>
"file.iconClass"
}
%strong
{{file.filePath}}
%strong
{{file.filePath}}
.file-actions
=
render
partial:
'projects/merge_requests/conflicts/file_actions'
%a
.btn.view-file.btn-file-option
{
":href"
=>
"file.blobPath"
}
View file @{{conflictsData.shortCommitSha}}
.diff-content.diff-wrap-lines
.diff-content.diff-wrap-lines
.diff-wrap-lines.code.file-content.js-syntax-highlight
.diff-wrap-lines.code.file-content.js-syntax-highlight
{
'v-show'
=>
"file.resolveMode === 'interactive'"
}
%table
%table
%tr
.line_holder.parallel
{
"v-for"
=>
"section in file.parallelLines"
}
%tr
.line_holder.parallel
{
"v-for"
=>
"section in file.parallelLines"
}
%template
{
"v-for"
=>
"line in section"
}
%template
{
"v-for"
=>
"line in section"
}
...
@@ -17,7 +14,7 @@
...
@@ -17,7 +14,7 @@
%td
.diff-line-num.header
{
":class"
=>
class_bindings
}
%td
.diff-line-num.header
{
":class"
=>
class_bindings
}
%td
.line_content.header
{
":class"
=>
class_bindings
}
%td
.line_content.header
{
":class"
=>
class_bindings
}
%strong
{{line.richText}}
%strong
{{line.richText}}
%button
.btn
{
"@click"
=>
"handleSelected(line.id, line.section)"
}
%button
.btn
{
"@click"
=>
"handleSelected(
file,
line.id, line.section)"
}
{{line.buttonTitle}}
{{line.buttonTitle}}
%template
{
"v-if"
=>
"!line.isHeader"
}
%template
{
"v-if"
=>
"!line.isHeader"
}
...
@@ -25,3 +22,4 @@
...
@@ -25,3 +22,4 @@
{{line.lineNumber}}
{{line.lineNumber}}
%td
.line_content.parallel
{
":class"
=>
class_bindings
}
%td
.line_content.parallel
{
":class"
=>
class_bindings
}
{{{line.richText}}}
{{{line.richText}}}
=
render
partial:
'projects/merge_requests/conflicts/diff_file_editor'
,
locals:
{
if_condition:
"file.resolveMode === 'edit' && isParallel"
}
app/views/projects/merge_requests/conflicts/_submit_form.html.haml
View file @
26f658de
.content-block.oneline-block.files-changed
.content-block
%strong
.resolved-count
{{resolvedCount}}
of
%strong
.total-count
{{conflictsCount}}
conflicts have been resolved
.commit-message-container.form-group
.commit-message-container.form-group
.max-width-marker
.max-width-marker
%textarea
.form-control.js-commit-message
{
"v-model"
=>
"conflictsData.commitMessage"
}
%textarea
.form-control.js-commit-message
{
"v-model"
=>
"conflictsData.commitMessage"
}
{{{conflictsData.commitMessage}}}
{{{conflictsData.commitMessage}}}
%button
{
type:
"button"
,
class:
"btn btn-success js-submit-button"
,
"
:disabled"
=>
"!readyToCommit"
,
"@click"
=>
"commit()"
}
%button
{
type:
"button"
,
class:
"btn btn-success js-submit-button"
,
"
@click"
=>
"commit()"
,
":disabled"
=>
"!readyToCommit"
}
%span
{{commitButtonText}}
%span
{{commitButtonText}}
=
link_to
"Cancel"
,
namespace_project_merge_request_path
(
@merge_request
.
project
.
namespace
,
@merge_request
.
project
,
@merge_request
),
class:
"btn btn-cancel"
=
link_to
"Cancel"
,
namespace_project_merge_request_path
(
@merge_request
.
project
.
namespace
,
@merge_request
.
project
,
@merge_request
),
class:
"btn btn-cancel"
app/views/projects/merge_requests/conflicts/components/_diff_file_editor.html.haml
0 → 100644
View file @
26f658de
%template
{
id:
"diff-file-editor"
}
%div
.editor-wrap
{
":class"
=>
"classObject"
}
%p
.loading-text
Loading...
.editor
%textarea
{
"@input"
=>
"onInput"
,
cols:
'80'
,
rows:
'20'
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment