Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--app/assets/javascripts/diffs/components/diff_row.vue169
-rw-r--r--app/assets/javascripts/diffs/constants.js6
-rw-r--r--app/assets/javascripts/diffs/store/utils.js44
-rw-r--r--app/assets/stylesheets/highlight/common.scss42
-rw-r--r--app/assets/stylesheets/highlight/conflict_colors.scss119
-rw-r--r--app/assets/stylesheets/highlight/themes/dark.scss2
-rw-r--r--app/assets/stylesheets/highlight/themes/monokai.scss2
-rw-r--r--app/assets/stylesheets/highlight/themes/solarized-dark.scss2
-rw-r--r--app/assets/stylesheets/highlight/themes/solarized-light.scss2
-rw-r--r--app/assets/stylesheets/highlight/themes/white.scss2
-rw-r--r--app/assets/stylesheets/highlight/white_base.scss13
-rw-r--r--app/assets/stylesheets/page_bundles/merge_conflicts.scss180
-rw-r--r--config/feature_flags/development/ci_pipeline_open_merge_requests.yml1
-rw-r--r--config/feature_flags/development/value_stream_analytics_extended_form.yml8
-rw-r--r--doc/administration/postgresql/replication_and_failover.md10
-rw-r--r--doc/administration/reference_architectures/3k_users.md2
-rw-r--r--doc/ci/pipelines/index.md4
-rw-r--r--doc/development/feature_flags/development.md12
-rw-r--r--doc/user/packages/composer_repository/index.md7
-rw-r--r--doc/user/packages/npm_registry/index.md23
-rw-r--r--doc/user/packages/pypi_repository/index.md6
-rw-r--r--doc/user/search/advanced_search_syntax.md7
-rw-r--r--lib/gitlab/conflict/file.rb29
-rw-r--r--lib/gitlab/diff/line.rb4
-rw-r--r--locale/gitlab.pot6
-rw-r--r--spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb3
-rw-r--r--spec/frontend/diffs/store/utils_spec.js64
-rw-r--r--spec/lib/gitlab/conflict/file_spec.rb32
29 files changed, 563 insertions, 240 deletions
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index d9a01d25342..04f6473099c 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-13.14.0
+13.15.0
diff --git a/app/assets/javascripts/diffs/components/diff_row.vue b/app/assets/javascripts/diffs/components/diff_row.vue
index c0719e2a7d9..9a43a7405c6 100644
--- a/app/assets/javascripts/diffs/components/diff_row.vue
+++ b/app/assets/javascripts/diffs/components/diff_row.vue
@@ -1,7 +1,15 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import { GlTooltipDirective, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
-import { CONTEXT_LINE_CLASS_NAME, PARALLEL_DIFF_VIEW_TYPE } from '../constants';
+import {
+ CONTEXT_LINE_CLASS_NAME,
+ PARALLEL_DIFF_VIEW_TYPE,
+ CONFLICT_MARKER_OUR,
+ CONFLICT_MARKER_THEIR,
+ CONFLICT_OUR,
+ CONFLICT_THEIR,
+ CONFLICT_MARKER,
+} from '../constants';
import DiffGutterAvatars from './diff_gutter_avatars.vue';
import * as utils from './diff_row_utils';
@@ -71,6 +79,12 @@ export default {
addCommentTooltipRight() {
return utils.addCommentTooltip(this.line.right);
},
+ emptyCellRightClassMap() {
+ return { conflict_their: this.line.left?.type === CONFLICT_OUR };
+ },
+ emptyCellLeftClassMap() {
+ return { conflict_our: this.line.right?.type === CONFLICT_THEIR };
+ },
shouldRenderCommentButton() {
return (
this.isLoggedIn &&
@@ -80,6 +94,9 @@ export default {
!this.line.hasDiscussionsRight
);
},
+ isLeftConflictMarker() {
+ return [CONFLICT_MARKER_OUR, CONFLICT_MARKER_THEIR].includes(this.line.left?.type);
+ },
},
mounted() {
this.scrollToLineIfNeededParallel(this.line);
@@ -107,37 +124,50 @@ export default {
handleCommentButton(line) {
this.showCommentForm({ lineCode: line.line_code, fileHash: this.fileHash });
},
+ conflictText(line) {
+ return line.type === CONFLICT_MARKER_THEIR
+ ? this.$options.THEIR_CHANGES
+ : this.$options.OUR_CHANGES;
+ },
},
+ OUR_CHANGES: 'HEAD//our changes',
+ THEIR_CHANGES: 'origin//their changes',
+ CONFLICT_MARKER,
+ CONFLICT_MARKER_THEIR,
+ CONFLICT_OUR,
+ CONFLICT_THEIR,
};
</script>
<template>
<div :class="classNameMap" class="diff-grid-row diff-tr line_holder">
<div class="diff-grid-left left-side">
- <template v-if="line.left">
+ <template v-if="line.left && line.left.type !== $options.CONFLICT_MARKER">
<div
:class="classNameMapCellLeft"
data-testid="leftLineNumber"
class="diff-td diff-line-num old_line"
>
- <span
- v-if="shouldRenderCommentButton"
- v-gl-tooltip
- data-testid="leftCommentButton"
- class="add-diff-note tooltip-wrapper"
- :title="addCommentTooltipLeft"
- >
- <button
- type="button"
- class="add-diff-note note-button js-add-diff-note-button qa-diff-comment"
- :disabled="line.left.commentsDisabled"
- @click="handleCommentButton(line.left)"
+ <template v-if="!isLeftConflictMarker">
+ <span
+ v-if="shouldRenderCommentButton"
+ v-gl-tooltip
+ data-testid="leftCommentButton"
+ class="add-diff-note tooltip-wrapper"
+ :title="addCommentTooltipLeft"
>
- <gl-icon :size="12" name="comment" />
- </button>
- </span>
+ <button
+ type="button"
+ class="add-diff-note note-button js-add-diff-note-button qa-diff-comment"
+ :disabled="line.left.commentsDisabled"
+ @click="handleCommentButton(line.left)"
+ >
+ <gl-icon :size="12" name="comment" />
+ </button>
+ </span>
+ </template>
<a
- v-if="line.left.old_line"
+ v-if="line.left.old_line && line.left.type !== $options.CONFLICT_THEIR"
:data-linenumber="line.left.old_line"
:href="line.lineHrefOld"
@click="setHighlightedRow(line.lineCode)"
@@ -159,7 +189,7 @@ export default {
</div>
<div v-if="inline" :class="classNameMapCellLeft" class="diff-td diff-line-num old_line">
<a
- v-if="line.left.new_line"
+ v-if="line.left.new_line && line.left.type !== $options.CONFLICT_OUR"
:data-linenumber="line.left.new_line"
:href="line.lineHrefOld"
@click="setHighlightedRow(line.lineCode)"
@@ -170,39 +200,59 @@ export default {
<div
:id="line.left.line_code"
:key="line.left.line_code"
- v-safe-html="line.left.rich_text"
:class="parallelViewLeftLineType"
class="diff-td line_content with-coverage parallel left-side"
data-testid="leftContent"
@mousedown="handleParallelLineMouseDown"
- ></div>
+ >
+ <strong v-if="isLeftConflictMarker">{{ conflictText(line.left) }}</strong>
+ <span v-else v-safe-html="line.left.rich_text"></span>
+ </div>
</template>
- <template v-else>
- <div data-testid="leftEmptyCell" class="diff-td diff-line-num old_line empty-cell"></div>
- <div v-if="inline" class="diff-td diff-line-num old_line empty-cell"></div>
- <div class="diff-td line-coverage left-side empty-cell"></div>
- <div class="diff-td line_content with-coverage parallel left-side empty-cell"></div>
+ <template v-else-if="!inline || (line.left && line.left.type === $options.CONFLICT_MARKER)">
+ <div
+ data-testid="leftEmptyCell"
+ class="diff-td diff-line-num old_line empty-cell"
+ :class="emptyCellLeftClassMap"
+ >
+ &nbsp;
+ </div>
+ <div
+ v-if="inline"
+ class="diff-td diff-line-num old_line empty-cell"
+ :class="emptyCellLeftClassMap"
+ ></div>
+ <div
+ class="diff-td line-coverage left-side empty-cell"
+ :class="emptyCellLeftClassMap"
+ ></div>
+ <div
+ class="diff-td line_content with-coverage parallel left-side empty-cell"
+ :class="emptyCellLeftClassMap"
+ ></div>
</template>
</div>
<div v-if="!inline" class="diff-grid-right right-side">
<template v-if="line.right">
<div :class="classNameMapCellRight" class="diff-td diff-line-num new_line">
- <span
- v-if="shouldRenderCommentButton"
- v-gl-tooltip
- data-testid="rightCommentButton"
- class="add-diff-note tooltip-wrapper"
- :title="addCommentTooltipRight"
- >
- <button
- type="button"
- class="add-diff-note note-button js-add-diff-note-button qa-diff-comment"
- :disabled="line.right.commentsDisabled"
- @click="handleCommentButton(line.right)"
+ <template v-if="line.right.type !== $options.CONFLICT_MARKER_THEIR">
+ <span
+ v-if="shouldRenderCommentButton"
+ v-gl-tooltip
+ data-testid="rightCommentButton"
+ class="add-diff-note tooltip-wrapper"
+ :title="addCommentTooltipRight"
>
- <gl-icon :size="12" name="comment" />
- </button>
- </span>
+ <button
+ type="button"
+ class="add-diff-note note-button js-add-diff-note-button qa-diff-comment"
+ :disabled="line.right.commentsDisabled"
+ @click="handleCommentButton(line.right)"
+ >
+ <gl-icon :size="12" name="comment" />
+ </button>
+ </span>
+ </template>
<a
v-if="line.right.new_line"
:data-linenumber="line.right.new_line"
@@ -233,22 +283,35 @@ export default {
<div
:id="line.right.line_code"
:key="line.right.rich_text"
- v-safe-html="line.right.rich_text"
- :class="[
- line.right.type,
- {
- hll: isHighlighted,
- },
- ]"
+ :class="[line.right.type, { hll: isHighlighted }]"
class="diff-td line_content with-coverage parallel right-side"
@mousedown="handleParallelLineMouseDown"
- ></div>
+ >
+ <strong v-if="line.right.type === $options.CONFLICT_MARKER_THEIR">{{
+ conflictText(line.right)
+ }}</strong>
+ <span v-else v-safe-html="line.right.rich_text"></span>
+ </div>
</template>
<template v-else>
- <div data-testid="rightEmptyCell" class="diff-td diff-line-num old_line empty-cell"></div>
- <div class="diff-td diff-line-num old_line empty-cell"></div>
- <div class="diff-td line-coverage right-side empty-cell"></div>
- <div class="diff-td line_content with-coverage parallel right-side empty-cell"></div>
+ <div
+ data-testid="rightEmptyCell"
+ class="diff-td diff-line-num old_line empty-cell"
+ :class="emptyCellRightClassMap"
+ ></div>
+ <div
+ v-if="inline"
+ class="diff-td diff-line-num old_line empty-cell"
+ :class="emptyCellRightClassMap"
+ ></div>
+ <div
+ class="diff-td line-coverage right-side empty-cell"
+ :class="emptyCellRightClassMap"
+ ></div>
+ <div
+ class="diff-td line_content with-coverage parallel right-side empty-cell"
+ :class="emptyCellRightClassMap"
+ ></div>
</template>
</div>
</div>
diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js
index 07e27bd8e47..7080348ee7d 100644
--- a/app/assets/javascripts/diffs/constants.js
+++ b/app/assets/javascripts/diffs/constants.js
@@ -109,3 +109,9 @@ export const EVT_PERF_MARK_FILE_TREE_END = 'mr:diffs:perf:fileTreeEnd';
export const EVT_PERF_MARK_DIFF_FILES_START = 'mr:diffs:perf:filesStart';
export const EVT_PERF_MARK_FIRST_DIFF_FILE_SHOWN = 'mr:diffs:perf:firstFileShown';
export const EVT_PERF_MARK_DIFF_FILES_END = 'mr:diffs:perf:filesEnd';
+
+export const CONFLICT_OUR = 'conflict_our';
+export const CONFLICT_THEIR = 'conflict_their';
+export const CONFLICT_MARKER = 'conflict_marker';
+export const CONFLICT_MARKER_OUR = 'conflict_marker_our';
+export const CONFLICT_MARKER_THEIR = 'conflict_marker_their';
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index 1839df12c96..27702eaf91e 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -15,6 +15,11 @@ import {
INLINE_DIFF_LINES_KEY,
SHOW_WHITESPACE,
NO_SHOW_WHITESPACE,
+ CONFLICT_OUR,
+ CONFLICT_THEIR,
+ CONFLICT_MARKER,
+ CONFLICT_MARKER_OUR,
+ CONFLICT_MARKER_THEIR,
} from '../constants';
import { prepareRawDiffFile } from '../utils/diff_file';
@@ -22,6 +27,11 @@ export const isAdded = line => ['new', 'new-nonewline'].includes(line.type);
export const isRemoved = line => ['old', 'old-nonewline'].includes(line.type);
export const isUnchanged = line => !line.type;
export const isMeta = line => ['match', 'new-nonewline', 'old-nonewline'].includes(line.type);
+export const isConflictMarker = line =>
+ [CONFLICT_MARKER_OUR, CONFLICT_MARKER_THEIR].includes(line.type);
+export const isConflictSeperator = line => line.type === CONFLICT_MARKER;
+export const isConflictOur = line => line.type === CONFLICT_OUR;
+export const isConflictTheir = line => line.type === CONFLICT_THEIR;
/**
* Pass in the inline diff lines array which gets converted
@@ -42,12 +52,13 @@ export const isMeta = line => ['match', 'new-nonewline', 'old-nonewline'].includ
export const parallelizeDiffLines = (diffLines, inline) => {
let freeRightIndex = null;
+ let conflictStartIndex = -1;
const lines = [];
for (let i = 0, diffLinesLength = diffLines.length, index = 0; i < diffLinesLength; i += 1) {
const line = diffLines[i];
- if (isRemoved(line) || inline) {
+ if (isRemoved(line) || isConflictOur(line) || inline) {
lines.push({
[LINE_POSITION_LEFT]: line,
[LINE_POSITION_RIGHT]: null,
@@ -58,7 +69,7 @@ export const parallelizeDiffLines = (diffLines, inline) => {
freeRightIndex = index;
}
index += 1;
- } else if (isAdded(line)) {
+ } else if (isAdded(line) || isConflictTheir(line)) {
if (freeRightIndex !== null) {
// If an old line came before this without a line on the right, this
// line can be put to the right of it.
@@ -77,15 +88,28 @@ export const parallelizeDiffLines = (diffLines, inline) => {
freeRightIndex = null;
index += 1;
}
- } else if (isMeta(line) || isUnchanged(line)) {
- // line in the right panel is the same as in the left one
- lines.push({
- [LINE_POSITION_LEFT]: line,
- [LINE_POSITION_RIGHT]: line,
- });
+ } else if (
+ isMeta(line) ||
+ isUnchanged(line) ||
+ isConflictMarker(line) ||
+ (isConflictSeperator(line) && inline)
+ ) {
+ if (conflictStartIndex <= 0) {
+ // line in the right panel is the same as in the left one
+ lines.push({
+ [LINE_POSITION_LEFT]: line,
+ [LINE_POSITION_RIGHT]: !inline && line,
+ });
- freeRightIndex = null;
- index += 1;
+ if (!inline && isConflictMarker(line)) {
+ conflictStartIndex = index;
+ }
+ freeRightIndex = null;
+ index += 1;
+ } else {
+ lines[conflictStartIndex][LINE_POSITION_RIGHT] = line;
+ conflictStartIndex = -1;
+ }
}
}
diff --git a/app/assets/stylesheets/highlight/common.scss b/app/assets/stylesheets/highlight/common.scss
index d9b9f3694c1..5c6668814cb 100644
--- a/app/assets/stylesheets/highlight/common.scss
+++ b/app/assets/stylesheets/highlight/common.scss
@@ -1,4 +1,5 @@
@import '../framework/variables';
+@import './conflict_colors';
@mixin diff-background($background, $idiff, $border) {
background: $background;
@@ -51,3 +52,44 @@
color: darken($color, 15%);
}
}
+
+@mixin conflict-colors($theme) {
+ .diff-line-num {
+ &.conflict_marker_our,
+ &.conflict_our {
+ background-color: map-get($conflict-colors, #{$theme}-header-head-neutral);
+ border-color: map-get($conflict-colors, #{$theme}-header-head-neutral);
+ }
+
+ &.conflict_marker_their,
+ &.conflict_their {
+ background-color: map-get($conflict-colors, #{$theme}-header-origin-neutral);
+ border-color: map-get($conflict-colors, #{$theme}-header-origin-neutral);
+ }
+ }
+
+ .line_holder {
+ .line_content,
+ .line-coverage {
+ &.conflict_marker_our {
+ background-color: map-get($conflict-colors, #{$theme}-header-head-neutral);
+ border-color: map-get($conflict-colors, #{$theme}-header-head-neutral);
+ }
+
+ &.conflict_marker_their {
+ background-color: map-get($conflict-colors, #{$theme}-header-origin-neutral);
+ border-color: map-get($conflict-colors, #{$theme}-header-origin-neutral);
+ }
+
+ &.conflict_our {
+ background-color: map-get($conflict-colors, #{$theme}-line-head-neutral);
+ border-color: map-get($conflict-colors, #{$theme}-line-head-neutral);
+ }
+
+ &.conflict_their {
+ background-color: map-get($conflict-colors, #{$theme}-line-origin-neutral);
+ border-color: map-get($conflict-colors, #{$theme}-line-origin-neutral);
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/highlight/conflict_colors.scss b/app/assets/stylesheets/highlight/conflict_colors.scss
new file mode 100644
index 00000000000..98ca3775b72
--- /dev/null
+++ b/app/assets/stylesheets/highlight/conflict_colors.scss
@@ -0,0 +1,119 @@
+// Disabled to use the color map for creating color schemes
+// scss-lint:disable ColorVariable
+$conflict-colors: (
+ white-header-head-neutral : #e1fad7,
+ white-line-head-neutral : #effdec,
+ white-button-head-neutral : #9adb84,
+
+ white-header-head-chosen : #baf0a8,
+ white-line-head-chosen : #e1fad7,
+ white-button-head-chosen : #52c22d,
+
+ white-header-origin-neutral : #e0f0ff,
+ white-line-origin-neutral : #f2f9ff,
+ white-button-origin-neutral : #87c2fa,
+
+ white-header-origin-chosen : #add8ff,
+ white-line-origin-chosen : #e0f0ff,
+ white-button-origin-chosen : #268ced,
+
+ white-header-not-chosen : #f0f0f0,
+ white-line-not-chosen : $gray-light,
+
+ dark-header-head-neutral : rgba(#3f3, 0.2),
+ dark-line-head-neutral : rgba(#3f3, 0.1),
+ dark-button-head-neutral : #40874f,
+
+ dark-header-head-chosen : rgba(#3f3, 0.33),
+ dark-line-head-chosen : rgba(#3f3, 0.2),
+ dark-button-head-chosen : #258537,
+
+ dark-header-origin-neutral : rgba(#2878c9, 0.4),
+ dark-line-origin-neutral : rgba(#2878c9, 0.3),
+ dark-button-origin-neutral : #2a5c8c,
+
+ dark-header-origin-chosen : rgba(#2878c9, 0.6),
+ dark-line-origin-chosen : rgba(#2878c9, 0.4),
+ dark-button-origin-chosen : #1d6cbf,
+
+ dark-header-not-chosen : rgba(#fff, 0.25),
+ dark-line-not-chosen : rgba(#fff, 0.1),
+
+ monokai-header-head-neutral : rgba(#a6e22e, 0.25),
+ monokai-line-head-neutral : rgba(#a6e22e, 0.1),
+ monokai-button-head-neutral : #376b20,
+
+ monokai-header-head-chosen : rgba(#a6e22e, 0.4),
+ monokai-line-head-chosen : rgba(#a6e22e, 0.25),
+ monokai-button-head-chosen : #39800d,
+
+ monokai-header-origin-neutral : rgba(#60d9f1, 0.35),
+ monokai-line-origin-neutral : rgba(#60d9f1, 0.15),
+ monokai-button-origin-neutral : #38848c,
+
+ monokai-header-origin-chosen : rgba(#60d9f1, 0.5),
+ monokai-line-origin-chosen : rgba(#60d9f1, 0.35),
+ monokai-button-origin-chosen : #3ea4b2,
+
+ monokai-header-not-chosen : rgba(#76715d, 0.24),
+ monokai-line-not-chosen : rgba(#76715d, 0.1),
+
+ solarized-light-header-head-neutral : rgba(#859900, 0.37),
+ solarized-light-line-head-neutral : rgba(#859900, 0.2),
+ solarized-light-button-head-neutral : #afb262,
+
+ solarized-light-header-head-chosen : rgba(#859900, 0.5),
+ solarized-light-line-head-chosen : rgba(#859900, 0.37),
+ solarized-light-button-head-chosen : #94993d,
+
+ solarized-light-header-origin-neutral : rgba(#2878c9, 0.37),
+ solarized-light-line-origin-neutral : rgba(#2878c9, 0.15),
+ solarized-light-button-origin-neutral : #60a1bf,
+
+ solarized-light-header-origin-chosen : rgba(#2878c9, 0.6),
+ solarized-light-line-origin-chosen : rgba(#2878c9, 0.37),
+ solarized-light-button-origin-chosen : #2482b2,
+
+ solarized-light-header-not-chosen : rgba(#839496, 0.37),
+ solarized-light-line-not-chosen : rgba(#839496, 0.2),
+
+ solarized-dark-header-head-neutral : rgba(#859900, 0.35),
+ solarized-dark-line-head-neutral : rgba(#859900, 0.15),
+ solarized-dark-button-head-neutral : #376b20,
+
+ solarized-dark-header-head-chosen : rgba(#859900, 0.5),
+ solarized-dark-line-head-chosen : rgba(#859900, 0.35),
+ solarized-dark-button-head-chosen : #39800d,
+
+ solarized-dark-header-origin-neutral : rgba(#2878c9, 0.35),
+ solarized-dark-line-origin-neutral : rgba(#2878c9, 0.15),
+ solarized-dark-button-origin-neutral : #086799,
+
+ solarized-dark-header-origin-chosen : rgba(#2878c9, 0.6),
+ solarized-dark-line-origin-chosen : rgba(#2878c9, 0.35),
+ solarized-dark-button-origin-chosen : #0082cc,
+
+ solarized_dark_header_not_chosen : rgba(#839496, 0.25),
+ solarized_dark_line_not_chosen : rgba(#839496, 0.15),
+
+ none_header_head_neutral : $gray-normal,
+ none_line_head_neutral : $gray-normal,
+ none_button_head_neutral : $gray-normal,
+
+ none_header_head_chosen : $gray-darker,
+ none_line_head_chosen : $gray-darker,
+ none_button_head_chosen : $gray-darker,
+
+ none_header_origin_neutral : $gray-normal,
+ none_line_origin_neutral : $gray-normal,
+ none_button_origin_neutral : $gray-normal,
+
+ none_header_origin_chosen : $gray-darker,
+ none_line_origin_chosen : $gray-darker,
+ none_button_origin_chosen : $gray-darker,
+
+ none_header_not_chosen : $gray-light,
+ none_line_not_chosen : $gray-light
+
+);
+// scss-lint:enable ColorVariable
diff --git a/app/assets/stylesheets/highlight/themes/dark.scss b/app/assets/stylesheets/highlight/themes/dark.scss
index d51d5b7137d..ecfff50726a 100644
--- a/app/assets/stylesheets/highlight/themes/dark.scss
+++ b/app/assets/stylesheets/highlight/themes/dark.scss
@@ -198,6 +198,8 @@ $dark-il: #de935f;
}
}
+ @include conflict-colors('dark');
+
// highlight line via anchor
pre .hll {
background-color: $dark-pre-hll-bg !important;
diff --git a/app/assets/stylesheets/highlight/themes/monokai.scss b/app/assets/stylesheets/highlight/themes/monokai.scss
index e690f9c7c74..119908ffba8 100644
--- a/app/assets/stylesheets/highlight/themes/monokai.scss
+++ b/app/assets/stylesheets/highlight/themes/monokai.scss
@@ -198,6 +198,8 @@ $monokai-gi: #a6e22e;
}
}
+ @include conflict-colors('monokai');
+
// highlight line via anchor
pre .hll {
background-color: $monokai-hll !important;
diff --git a/app/assets/stylesheets/highlight/themes/solarized-dark.scss b/app/assets/stylesheets/highlight/themes/solarized-dark.scss
index 8c532f53182..f95f5393323 100644
--- a/app/assets/stylesheets/highlight/themes/solarized-dark.scss
+++ b/app/assets/stylesheets/highlight/themes/solarized-dark.scss
@@ -202,6 +202,8 @@ $solarized-dark-il: #2aa198;
}
}
+ @include conflict-colors('solarized-dark');
+
// highlight line via anchor
pre .hll {
background-color: $solarized-dark-hll-bg !important;
diff --git a/app/assets/stylesheets/highlight/themes/solarized-light.scss b/app/assets/stylesheets/highlight/themes/solarized-light.scss
index 1f9042a9534..dc4bc2f32c2 100644
--- a/app/assets/stylesheets/highlight/themes/solarized-light.scss
+++ b/app/assets/stylesheets/highlight/themes/solarized-light.scss
@@ -210,6 +210,8 @@ $solarized-light-il: #2aa198;
}
}
+ @include conflict-colors('solarized-light');
+
// highlight line via anchor
pre .hll {
background-color: $solarized-light-hll-bg !important;
diff --git a/app/assets/stylesheets/highlight/themes/white.scss b/app/assets/stylesheets/highlight/themes/white.scss
index 6362dd734f6..ed1d9c924c0 100644
--- a/app/assets/stylesheets/highlight/themes/white.scss
+++ b/app/assets/stylesheets/highlight/themes/white.scss
@@ -1,3 +1,5 @@
.code.white {
@import '../white_base';
+
+ @include conflict-colors('white');
}
diff --git a/app/assets/stylesheets/highlight/white_base.scss b/app/assets/stylesheets/highlight/white_base.scss
index bb5ca94af33..e54dc685515 100644
--- a/app/assets/stylesheets/highlight/white_base.scss
+++ b/app/assets/stylesheets/highlight/white_base.scss
@@ -81,6 +81,17 @@ $white-gc-bg: #eaf2f5;
.line-numbers,
.diff-line-num {
background-color: $gray-light;
+
+ &.conflict_marker,
+ &.conflict_our {
+ background-color: map-get($conflict-colors, 'white-header-head-neutral');
+ border-color: map-get($conflict-colors, 'white-header-head-neutral');
+ }
+
+ &.conflict_their {
+ background-color: map-get($conflict-colors, 'white-header-origin-neutral');
+ border-color: map-get($conflict-colors, 'white-header-origin-neutral');
+ }
}
.diff-line-num,
@@ -115,7 +126,7 @@ pre.code,
.diff-grid-left:hover,
.diff-grid-right:hover {
- .diff-line-num:not(.empty-cell) {
+ .diff-line-num:not(.empty-cell):not(.conflict_marker_their):not(.conflict_marker_our) {
@include line-number-hover($white-over-bg);
}
}
diff --git a/app/assets/stylesheets/page_bundles/merge_conflicts.scss b/app/assets/stylesheets/page_bundles/merge_conflicts.scss
index a26affb10a9..d7473d2c942 100644
--- a/app/assets/stylesheets/page_bundles/merge_conflicts.scss
+++ b/app/assets/stylesheets/page_bundles/merge_conflicts.scss
@@ -1,183 +1,65 @@
@import 'mixins_and_variables_and_functions';
-// Disabled to use the color map for creating color schemes
-// scss-lint:disable ColorVariable
-$colors: (
- white-header-head-neutral : #e1fad7,
- white-line-head-neutral : #effdec,
- white-button-head-neutral : #9adb84,
-
- white-header-head-chosen : #baf0a8,
- white-line-head-chosen : #e1fad7,
- white-button-head-chosen : #52c22d,
-
- white-header-origin-neutral : #e0f0ff,
- white-line-origin-neutral : #f2f9ff,
- white-button-origin-neutral : #87c2fa,
-
- white-header-origin-chosen : #add8ff,
- white-line-origin-chosen : #e0f0ff,
- white-button-origin-chosen : #268ced,
-
- white-header-not-chosen : #f0f0f0,
- white-line-not-chosen : $gray-light,
-
- dark-header-head-neutral : rgba(#3f3, 0.2),
- dark-line-head-neutral : rgba(#3f3, 0.1),
- dark-button-head-neutral : #40874f,
-
- dark-header-head-chosen : rgba(#3f3, 0.33),
- dark-line-head-chosen : rgba(#3f3, 0.2),
- dark-button-head-chosen : #258537,
-
- dark-header-origin-neutral : rgba(#2878c9, 0.4),
- dark-line-origin-neutral : rgba(#2878c9, 0.3),
- dark-button-origin-neutral : #2a5c8c,
-
- dark-header-origin-chosen : rgba(#2878c9, 0.6),
- dark-line-origin-chosen : rgba(#2878c9, 0.4),
- dark-button-origin-chosen : #1d6cbf,
-
- dark-header-not-chosen : rgba(#fff, 0.25),
- dark-line-not-chosen : rgba(#fff, 0.1),
-
- monokai-header-head-neutral : rgba(#a6e22e, 0.25),
- monokai-line-head-neutral : rgba(#a6e22e, 0.1),
- monokai-button-head-neutral : #376b20,
-
- monokai-header-head-chosen : rgba(#a6e22e, 0.4),
- monokai-line-head-chosen : rgba(#a6e22e, 0.25),
- monokai-button-head-chosen : #39800d,
-
- monokai-header-origin-neutral : rgba(#60d9f1, 0.35),
- monokai-line-origin-neutral : rgba(#60d9f1, 0.15),
- monokai-button-origin-neutral : #38848c,
-
- monokai-header-origin-chosen : rgba(#60d9f1, 0.5),
- monokai-line-origin-chosen : rgba(#60d9f1, 0.35),
- monokai-button-origin-chosen : #3ea4b2,
-
- monokai-header-not-chosen : rgba(#76715d, 0.24),
- monokai-line-not-chosen : rgba(#76715d, 0.1),
-
- solarized-light-header-head-neutral : rgba(#859900, 0.37),
- solarized-light-line-head-neutral : rgba(#859900, 0.2),
- solarized-light-button-head-neutral : #afb262,
-
- solarized-light-header-head-chosen : rgba(#859900, 0.5),
- solarized-light-line-head-chosen : rgba(#859900, 0.37),
- solarized-light-button-head-chosen : #94993d,
-
- solarized-light-header-origin-neutral : rgba(#2878c9, 0.37),
- solarized-light-line-origin-neutral : rgba(#2878c9, 0.15),
- solarized-light-button-origin-neutral : #60a1bf,
-
- solarized-light-header-origin-chosen : rgba(#2878c9, 0.6),
- solarized-light-line-origin-chosen : rgba(#2878c9, 0.37),
- solarized-light-button-origin-chosen : #2482b2,
-
- solarized-light-header-not-chosen : rgba(#839496, 0.37),
- solarized-light-line-not-chosen : rgba(#839496, 0.2),
-
- solarized-dark-header-head-neutral : rgba(#859900, 0.35),
- solarized-dark-line-head-neutral : rgba(#859900, 0.15),
- solarized-dark-button-head-neutral : #376b20,
-
- solarized-dark-header-head-chosen : rgba(#859900, 0.5),
- solarized-dark-line-head-chosen : rgba(#859900, 0.35),
- solarized-dark-button-head-chosen : #39800d,
-
- solarized-dark-header-origin-neutral : rgba(#2878c9, 0.35),
- solarized-dark-line-origin-neutral : rgba(#2878c9, 0.15),
- solarized-dark-button-origin-neutral : #086799,
-
- solarized-dark-header-origin-chosen : rgba(#2878c9, 0.6),
- solarized-dark-line-origin-chosen : rgba(#2878c9, 0.35),
- solarized-dark-button-origin-chosen : #0082cc,
-
- solarized_dark_header_not_chosen : rgba(#839496, 0.25),
- solarized_dark_line_not_chosen : rgba(#839496, 0.15),
-
- none_header_head_neutral : $gray-normal,
- none_line_head_neutral : $gray-normal,
- none_button_head_neutral : $gray-normal,
-
- none_header_head_chosen : $gray-darker,
- none_line_head_chosen : $gray-darker,
- none_button_head_chosen : $gray-darker,
-
- none_header_origin_neutral : $gray-normal,
- none_line_origin_neutral : $gray-normal,
- none_button_origin_neutral : $gray-normal,
-
- none_header_origin_chosen : $gray-darker,
- none_line_origin_chosen : $gray-darker,
- none_button_origin_chosen : $gray-darker,
-
- none_header_not_chosen : $gray-light,
- none_line_not_chosen : $gray-light
-
-);
-// scss-lint:enable ColorVariable
+@import '../highlight/conflict_colors';
@mixin color-scheme($color) {
.header.line_content,
.diff-line-num {
&.origin {
- background-color: map-get($colors, #{$color}-header-origin-neutral);
- border-color: map-get($colors, #{$color}-header-origin-neutral);
+ background-color: map-get($conflict-colors, #{$color}-header-origin-neutral);
+ border-color: map-get($conflict-colors, #{$color}-header-origin-neutral);
button {
- background-color: map-get($colors, #{$color}-button-origin-neutral);
- border-color: darken(map-get($colors, #{$color}-button-origin-neutral), 15);
+ background-color: map-get($conflict-colors, #{$color}-button-origin-neutral);
+ border-color: darken(map-get($conflict-colors, #{$color}-button-origin-neutral), 15);
}
&.selected {
- background-color: map-get($colors, #{$color}-header-origin-chosen);
- border-color: map-get($colors, #{$color}-header-origin-chosen);
+ background-color: map-get($conflict-colors, #{$color}-header-origin-chosen);
+ border-color: map-get($conflict-colors, #{$color}-header-origin-chosen);
button {
- background-color: map-get($colors, #{$color}-button-origin-chosen);
- border-color: darken(map-get($colors, #{$color}-button-origin-chosen), 15);
+ background-color: map-get($conflict-colors, #{$color}-button-origin-chosen);
+ border-color: darken(map-get($conflict-colors, #{$color}-button-origin-chosen), 15);
}
}
&.unselected {
- background-color: map-get($colors, #{$color}-header-not-chosen);
- border-color: map-get($colors, #{$color}-header-not-chosen);
+ background-color: map-get($conflict-colors, #{$color}-header-not-chosen);
+ border-color: map-get($conflict-colors, #{$color}-header-not-chosen);
button {
- background-color: lighten(map-get($colors, #{$color}-button-origin-neutral), 15);
- border-color: map-get($colors, #{$color}-button-origin-neutral);
+ background-color: lighten(map-get($conflict-colors, #{$color}-button-origin-neutral), 15);
+ border-color: map-get($conflict-colors, #{$color}-button-origin-neutral);
}
}
}
&.head {
- background-color: map-get($colors, #{$color}-header-head-neutral);
- border-color: map-get($colors, #{$color}-header-head-neutral);
+ background-color: map-get($conflict-colors, #{$color}-header-head-neutral);
+ border-color: map-get($conflict-colors, #{$color}-header-head-neutral);
button {
- background-color: map-get($colors, #{$color}-button-head-neutral);
- border-color: darken(map-get($colors, #{$color}-button-head-neutral), 15);
+ background-color: map-get($conflict-colors, #{$color}-button-head-neutral);
+ border-color: darken(map-get($conflict-colors, #{$color}-button-head-neutral), 15);
}
&.selected {
- background-color: map-get($colors, #{$color}-header-head-chosen);
- border-color: map-get($colors, #{$color}-header-head-chosen);
+ background-color: map-get($conflict-colors, #{$color}-header-head-chosen);
+ border-color: map-get($conflict-colors, #{$color}-header-head-chosen);
button {
- background-color: map-get($colors, #{$color}-button-head-chosen);
- border-color: darken(map-get($colors, #{$color}-button-head-chosen), 15);
+ background-color: map-get($conflict-colors, #{$color}-button-head-chosen);
+ border-color: darken(map-get($conflict-colors, #{$color}-button-head-chosen), 15);
}
}
&.unselected {
- background-color: map-get($colors, #{$color}-header-not-chosen);
- border-color: map-get($colors, #{$color}-header-not-chosen);
+ background-color: map-get($conflict-colors, #{$color}-header-not-chosen);
+ border-color: map-get($conflict-colors, #{$color}-header-not-chosen);
button {
- background-color: lighten(map-get($colors, #{$color}-button-head-neutral), 15);
- border-color: map-get($colors, #{$color}-button-head-neutral);
+ background-color: lighten(map-get($conflict-colors, #{$color}-button-head-neutral), 15);
+ border-color: map-get($conflict-colors, #{$color}-button-head-neutral);
}
}
}
@@ -185,26 +67,26 @@ $colors: (
.line_content {
&.origin {
- background-color: map-get($colors, #{$color}-line-origin-neutral);
+ background-color: map-get($conflict-colors, #{$color}-line-origin-neutral);
&.selected {
- background-color: map-get($colors, #{$color}-line-origin-chosen);
+ background-color: map-get($conflict-colors, #{$color}-line-origin-chosen);
}
&.unselected {
- background-color: map-get($colors, #{$color}-line-not-chosen);
+ background-color: map-get($conflict-colors, #{$color}-line-not-chosen);
}
}
&.head {
- background-color: map-get($colors, #{$color}-line-head-neutral);
+ background-color: map-get($conflict-colors, #{$color}-line-head-neutral);
&.selected {
- background-color: map-get($colors, #{$color}-line-head-chosen);
+ background-color: map-get($conflict-colors, #{$color}-line-head-chosen);
}
&.unselected {
- background-color: map-get($colors, #{$color}-line-not-chosen);
+ background-color: map-get($conflict-colors, #{$color}-line-not-chosen);
}
}
}
diff --git a/config/feature_flags/development/ci_pipeline_open_merge_requests.yml b/config/feature_flags/development/ci_pipeline_open_merge_requests.yml
index e3be9e7323d..d50e49b6aa1 100644
--- a/config/feature_flags/development/ci_pipeline_open_merge_requests.yml
+++ b/config/feature_flags/development/ci_pipeline_open_merge_requests.yml
@@ -2,6 +2,7 @@
name: ci_pipeline_open_merge_requests
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38673
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/292727
+milestone: '13.7'
group: group::memory
type: development
default_enabled: false
diff --git a/config/feature_flags/development/value_stream_analytics_extended_form.yml b/config/feature_flags/development/value_stream_analytics_extended_form.yml
new file mode 100644
index 00000000000..1cdb9111af4
--- /dev/null
+++ b/config/feature_flags/development/value_stream_analytics_extended_form.yml
@@ -0,0 +1,8 @@
+---
+name: value_stream_analytics_extended_form
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50229
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/294190
+milestone: '13.7'
+type: development
+group: group::optimize
+default_enabled: false
diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md
index 303246c9c82..702891fab65 100644
--- a/doc/administration/postgresql/replication_and_failover.md
+++ b/doc/administration/postgresql/replication_and_failover.md
@@ -943,6 +943,16 @@ standby nodes.
gitlab-ctl repmgr standby follow NEW_MASTER
```
+#### Geo secondary site considerations
+
+When a Geo secondary site is replicating from a primary site that uses `repmgr` and `PgBouncer`, [replicating through PgBouncer is not supported](https://github.com/pgbouncer/pgbouncer/issues/382#issuecomment-517911529) and the secondary must replicate directly from the leader node in the `repmgr` cluster. Therefore, when there is a failover in the `repmgr` cluster, you will need to manually re-point your secondary site to replicate from the new leader with:
+
+```shell
+sudo gitlab-ctl replicate-geo-database --host=<new_leader_ip> --replication-slot=<slot_name>
+```
+
+Otherwise, the replication will not happen anymore, even if the original node gets re-added as a follower node. This will re-sync your secondary site database and may take a long time depending on the amount of data to sync.
+
### Restore procedure
If a node fails, it can be removed from the cluster, or added back as a standby
diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md
index 370247ed856..d9eab28bfd6 100644
--- a/doc/administration/reference_architectures/3k_users.md
+++ b/doc/administration/reference_architectures/3k_users.md
@@ -17,6 +17,8 @@ highly-available GitLab deployment. If you do not have the expertise or need to
maintain a highly-available environment, you can have a simpler and less
costly-to-operate environment by using the
[2,000-user reference architecture](2k_users.md).
+If you have fewer than 3,000 users and still want a highly-available GitLab deployment,
+you should still use this reference architecture but scale down the node sizes.
> - **Supported users (approximate):** 3,000
> - **High Availability:** Yes
diff --git a/doc/ci/pipelines/index.md b/doc/ci/pipelines/index.md
index 22e331f2de0..160b10aefa2 100644
--- a/doc/ci/pipelines/index.md
+++ b/doc/ci/pipelines/index.md
@@ -132,6 +132,10 @@ Pipelines can be manually executed, with predefined or manually-specified [varia
You might do this if the results of a pipeline (for example, a code build) are required outside the normal
operation of the pipeline.
+[In GitLab 13.7 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/30101),
+the variables fields are pre-filled with any global variables defined in the
+`.gitlab-ci.yml` file.
+
To execute a pipeline manually:
1. Navigate to your project's **CI/CD > Pipelines**.
diff --git a/doc/development/feature_flags/development.md b/doc/development/feature_flags/development.md
index 7c5333c9aa6..f0667cdbcd5 100644
--- a/doc/development/feature_flags/development.md
+++ b/doc/development/feature_flags/development.md
@@ -378,6 +378,18 @@ You can also enable a feature flag for a given gate:
Feature.enable(:feature_flag_name, Project.find_by_full_path("root/my-project"))
```
+### Removing a feature flag locally (in development)
+
+When manually enabling or disabling a feature flag from the Rails console, its default value gets overwritten.
+This can cause confusion when changing the flag's `default_enabled` attribute.
+
+To reset the feature flag to the default status, you can remove it in the rails console (`rails c`)
+as follows:
+
+```ruby
+Feature.remove(:feature_flag_name)
+```
+
## Feature flags in tests
Introducing a feature flag into the codebase creates an additional code path that should be tested.
diff --git a/doc/user/packages/composer_repository/index.md b/doc/user/packages/composer_repository/index.md
index 751915f84a0..b6d47b56bce 100644
--- a/doc/user/packages/composer_repository/index.md
+++ b/doc/user/packages/composer_repository/index.md
@@ -132,6 +132,13 @@ A more detailed Composer CI/CD file is also available as a `.gitlab-ci.yml` temp
WARNING:
Do not save unless you want to overwrite the existing CI/CD file.
+## Publishing packages with the same name or version
+
+When you publish:
+
+- The same package with different data, it overwrites the existing package.
+- The same package with the same data, a `404 Bad request` error occurs.
+
## Install a Composer package
Install a package from the Package Registry so you can use it as a dependency.
diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md
index 51b41b842fa..c411fd556df 100644
--- a/doc/user/packages/npm_registry/index.md
+++ b/doc/user/packages/npm_registry/index.md
@@ -465,3 +465,26 @@ If you get this error, your package name may not meet the
Ensure the name meets the convention exactly, including the case.
Then try to publish again.
+
+### `npm publish` returns `npm ERR! 500 Internal Server Error - PUT`
+
+This is a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/238950) in GitLab
+13.3.x and later. The error in the logs will appear as:
+
+```plaintext
+>NoMethodError - undefined method `preferred_language' for #<Rack::Response
+```
+
+This might be accompanied by another error:
+
+```plaintext
+>Errno::EACCES","exception.message":"Permission denied
+```
+
+This is usually a permissions issue with either:
+
+- `'packages_storage_path'` default `/var/opt/gitlab/gitlab-rails/shared/packages/`.
+- The remote bucket if [object storage](../../../administration/packages/#using-object-storage)
+ is used.
+
+In the latter case, ensure the bucket exists and the GitLab has write access to it.
diff --git a/doc/user/packages/pypi_repository/index.md b/doc/user/packages/pypi_repository/index.md
index e78224f89d1..954ea91ba28 100644
--- a/doc/user/packages/pypi_repository/index.md
+++ b/doc/user/packages/pypi_repository/index.md
@@ -301,6 +301,12 @@ python -m twine upload --repository <source_name> dist/<package_file>
- `<package_file>` is your package filename, ending in `.tar.gz` or `.whl`.
- `<source_name>` is the [source name used during setup](#authenticate-with-the-package-registry).
+### Publishing packages with the same name or version
+
+You cannot publish a package if a package of the same name and version already exists.
+You must delete the existing package first. If you attempt to publish the same package
+more than once, a `404 Bad request` error occurs.
+
## Install a PyPI package
To install the latest version of a package, use the following command:
diff --git a/doc/user/search/advanced_search_syntax.md b/doc/user/search/advanced_search_syntax.md
index fe2371947b4..e3501be8e8e 100644
--- a/doc/user/search/advanced_search_syntax.md
+++ b/doc/user/search/advanced_search_syntax.md
@@ -52,14 +52,17 @@ The Advanced Search Syntax also supports the use of filters. The available filte
- blob: Filters by Git `object ID`. Exact match only.
To use them, add them to your keyword in the format `<filter_name>:<value>` without
-any spaces between the colon (`:`) and the value. A keyword or an asterisk (`*`) is required for filter searches and has to be added in front of the filter separated by a space.
+any spaces between the colon (`:`) and the value. When no keyword is provided, an asterisk (`*`) will be used as the keyword.
Examples:
- Finding a file with any content named `search_results.rb`: [`* filename:search_results.rb`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=*+filename%3Asearch_results.rb&group_id=9970&project_id=278964)
-- Finding a file named `found_blob_spec.rb` with the text `CHANGELOG` inside of it: [`CHANGELOG filename:found_blob_spec.rb](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=CHANGELOG+filename%3Afound_blob_spec.rb&group_id=9970&project_id=278964)
+- The leading asterisk (`*`) can be ignored in the case above: [`filename:search_results.rb`](https://gitlab.com/search?group_id=9970&project_id=278964&scope=blobs&search=filename%3Asearch_results.rb)
+- Finding a file named `found_blob_spec.rb` with the text `CHANGELOG` inside of it: [`CHANGELOG filename:found_blob_spec.rb`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=CHANGELOG+filename%3Afound_blob_spec.rb&group_id=9970&project_id=278964)
- Finding the text `EpicLinks` inside files with the `.rb` extension: [`EpicLinks extension:rb`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=EpicLinks+extension%3Arb&group_id=9970&project_id=278964)
+- Finding any file with the `.yaml` extension: [`extension:yaml`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=extension%3Ayaml&group_id=9970&project_id=278964)
- Finding the text `Sidekiq` in a file, when that file is in a path that includes `elastic`: [`Sidekiq path:elastic`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=Sidekiq+path%3Aelastic&group_id=9970&project_id=278964)
+- Finding any file in a path that includes `elasticsearch`: [`path:elasticsearch`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=path%3Aelasticsearch&group_id=9970&project_id=278964)
- Finding the files represented by the Git object ID `998707b421c89bd9a3063333f9f728ef3e43d101`: [`* blob:998707b421c89bd9a3063333f9f728ef3e43d101`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=false&scope=blobs&repository_ref=&search=*+blob%3A998707b421c89bd9a3063333f9f728ef3e43d101&group_id=9970)
- Syntax filters can be combined for complex filtering. Finding any file starting with `search` containing `eventHub` and with the `.js` extension: [`eventHub filename:search* extension:js`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=eventHub+filename%3Asearch*+extension%3Ajs&group_id=9970&project_id=278964)
diff --git a/lib/gitlab/conflict/file.rb b/lib/gitlab/conflict/file.rb
index 4d7590a8e38..fbf021345ca 100644
--- a/lib/gitlab/conflict/file.rb
+++ b/lib/gitlab/conflict/file.rb
@@ -9,9 +9,13 @@ module Gitlab
CONTEXT_LINES = 3
+ CONFLICT_MARKER_OUR = 'conflict_marker_our'
+ CONFLICT_MARKER_THEIR = 'conflict_marker_their'
+ CONFLICT_MARKER_SEPARATOR = 'conflict_marker'
+
CONFLICT_TYPES = {
- "old" => "conflict_marker_their",
- "new" => "conflict_marker_our"
+ "old" => "conflict_their",
+ "new" => "conflict_our"
}.freeze
attr_reader :merge_request
@@ -59,18 +63,25 @@ module Gitlab
if section[:conflict]
lines = []
- initial_type = nil
+ lines << create_separator_line(section[:lines].first, CONFLICT_MARKER_OUR)
+
+ current_type = section[:lines].first.type
section[:lines].each do |line|
- if line.type != initial_type
- lines << create_separator_line(line)
- initial_type = line.type
+ if line.type != current_type # insert a separator between our changes and theirs
+ lines << create_separator_line(line, CONFLICT_MARKER_SEPARATOR)
+ current_type = line.type
end
line.type = CONFLICT_TYPES[line.type]
+
+ # Swap the positions around due to conflicts/diffs display inconsistency
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/291989
+ line.old_pos, line.new_pos = line.new_pos, line.old_pos
+
lines << line
end
- lines << create_separator_line(lines.last)
+ lines << create_separator_line(lines.last, CONFLICT_MARKER_THEIR)
lines
else
@@ -156,8 +167,8 @@ module Gitlab
Gitlab::Diff::Line.new('', 'match', line.index, line.old_pos, line.new_pos)
end
- def create_separator_line(line)
- Gitlab::Diff::Line.new('', 'conflict_marker', line.index, nil, nil)
+ def create_separator_line(line, type)
+ Gitlab::Diff::Line.new('', type, line.index, nil, nil)
end
# Any line beginning with a letter, an underscore, or a dollar can be used in a
diff --git a/lib/gitlab/diff/line.rb b/lib/gitlab/diff/line.rb
index af9140215f0..98ed2400d82 100644
--- a/lib/gitlab/diff/line.rb
+++ b/lib/gitlab/diff/line.rb
@@ -8,9 +8,9 @@ module Gitlab
#
SERIALIZE_KEYS = %i(line_code rich_text text type index old_pos new_pos).freeze
- attr_reader :line_code, :old_pos, :new_pos
+ attr_reader :line_code
attr_writer :rich_text
- attr_accessor :text, :index, :type
+ attr_accessor :text, :index, :type, :old_pos, :new_pos
def initialize(text, type, index, old_pos, new_pos, parent_file: nil, line_code: nil, rich_text: nil)
@text, @type, @index = text, type, index
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 176e2c4d29d..0fd057100b0 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8617,6 +8617,9 @@ msgstr ""
msgid "DastProfiles|Active"
msgstr ""
+msgid "DastProfiles|Additional request headers (Optional)"
+msgstr ""
+
msgid "DastProfiles|Are you sure you want to delete this profile?"
msgstr ""
@@ -8686,6 +8689,9 @@ msgstr ""
msgid "DastProfiles|Excluded URLs"
msgstr ""
+msgid "DastProfiles|Excluded URLs (Optional)"
+msgstr ""
+
msgid "DastProfiles|Hide debug messages"
msgstr ""
diff --git a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
index 782a7e3bfb6..ac0c66524f0 100644
--- a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
+++ b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
@@ -69,7 +69,8 @@ RSpec.describe 'Batch diffs', :js do
end
context 'which is in at least page 2 of the batched pages of diffs' do
- it 'scrolls to the correct discussion' do
+ it 'scrolls to the correct discussion',
+ quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/293814' } do
page.within('.diff-file.file-holder:last-of-type') do
click_link('just now')
end
diff --git a/spec/frontend/diffs/store/utils_spec.js b/spec/frontend/diffs/store/utils_spec.js
index 7ee97224707..3c44ba3fc07 100644
--- a/spec/frontend/diffs/store/utils_spec.js
+++ b/spec/frontend/diffs/store/utils_spec.js
@@ -1119,6 +1119,42 @@ describe('DiffsStoreUtils', () => {
});
});
+ describe('isConflictMarker', () => {
+ it.each`
+ type | expected
+ ${'conflict_marker_our'} | ${true}
+ ${'conflict_marker_their'} | ${true}
+ ${'conflict_their'} | ${false}
+ ${'conflict_our'} | ${false}
+ `('returns $expected when type is $type', ({ type, expected }) => {
+ expect(utils.isConflictMarker({ type })).toBe(expected);
+ });
+ });
+
+ describe('isConflictOur', () => {
+ it.each`
+ type | expected
+ ${'conflict_marker_our'} | ${false}
+ ${'conflict_marker_their'} | ${false}
+ ${'conflict_their'} | ${false}
+ ${'conflict_our'} | ${true}
+ `('returns $expected when type is $type', ({ type, expected }) => {
+ expect(utils.isConflictOur({ type })).toBe(expected);
+ });
+ });
+
+ describe('isConflictTheir', () => {
+ it.each`
+ type | expected
+ ${'conflict_marker_our'} | ${false}
+ ${'conflict_marker_their'} | ${false}
+ ${'conflict_their'} | ${true}
+ ${'conflict_our'} | ${false}
+ `('returns $expected when type is $type', ({ type, expected }) => {
+ expect(utils.isConflictTheir({ type })).toBe(expected);
+ });
+ });
+
describe('parallelizeDiffLines', () => {
it('converts inline diff lines to parallel diff lines', () => {
const file = getDiffFileMock();
@@ -1128,6 +1164,34 @@ describe('DiffsStoreUtils', () => {
);
});
+ it('converts conflicted diffs line', () => {
+ const lines = [
+ { type: 'new' },
+ { type: 'conflict_marker_our' },
+ { type: 'conflict_our' },
+ { type: 'conflict_marker' },
+ { type: 'conflict_their' },
+ { type: 'conflict_marker_their' },
+ ];
+
+ expect(utils.parallelizeDiffLines(lines)).toEqual([
+ {
+ left: null,
+ right: {
+ type: 'new',
+ },
+ },
+ {
+ left: { type: 'conflict_marker_our' },
+ right: { type: 'conflict_marker_their' },
+ },
+ {
+ left: { type: 'conflict_our' },
+ right: { type: 'conflict_their' },
+ },
+ ]);
+ });
+
it('converts inline diff lines', () => {
const file = getDiffFileMock();
const files = utils.parallelizeDiffLines(file.highlighted_diff_lines, true);
diff --git a/spec/lib/gitlab/conflict/file_spec.rb b/spec/lib/gitlab/conflict/file_spec.rb
index 0de944d3f8a..bb9bee763d8 100644
--- a/spec/lib/gitlab/conflict/file_spec.rb
+++ b/spec/lib/gitlab/conflict/file_spec.rb
@@ -97,19 +97,27 @@ RSpec.describe Gitlab::Conflict::File do
let(:diff_line_types) { conflict_file.diff_lines_for_serializer.map(&:type) }
it 'assigns conflict types to the diff lines' do
- expect(diff_line_types[4]).to eq('conflict_marker')
- expect(diff_line_types[5..10]).to eq(['conflict_marker_our'] * 6)
+ expect(diff_line_types[4]).to eq('conflict_marker_our')
+ expect(diff_line_types[5..10]).to eq(['conflict_our'] * 6)
expect(diff_line_types[11]).to eq('conflict_marker')
- expect(diff_line_types[12..17]).to eq(['conflict_marker_their'] * 6)
- expect(diff_line_types[18]).to eq('conflict_marker')
+ expect(diff_line_types[12..17]).to eq(['conflict_their'] * 6)
+ expect(diff_line_types[18]).to eq('conflict_marker_their')
expect(diff_line_types[19..24]).to eq([nil] * 6)
- expect(diff_line_types[25]).to eq('conflict_marker')
- expect(diff_line_types[26..27]).to eq(['conflict_marker_our'] * 2)
+ expect(diff_line_types[25]).to eq('conflict_marker_our')
+ expect(diff_line_types[26..27]).to eq(['conflict_our'] * 2)
expect(diff_line_types[28]).to eq('conflict_marker')
- expect(diff_line_types[29..30]).to eq(['conflict_marker_their'] * 2)
- expect(diff_line_types[31]).to eq('conflict_marker')
+ expect(diff_line_types[29..30]).to eq(['conflict_their'] * 2)
+ expect(diff_line_types[31]).to eq('conflict_marker_their')
+ end
+
+ # Swap the positions around due to conflicts/diffs display inconsistency
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/291989
+ it 'swaps the new and old positions around' do
+ lines = conflict_file.diff_lines_for_serializer
+ expect(lines.map(&:old_pos)[26..27]).to eq([21, 22])
+ expect(lines.map(&:new_pos)[29..30]).to eq([21, 22])
end
it 'does not add a match line to the end of the section' do
@@ -124,13 +132,13 @@ RSpec.describe Gitlab::Conflict::File do
expect(diff_line_types).to eq([
'match',
nil, nil, nil,
- "conflict_marker",
"conflict_marker_our",
+ "conflict_our",
"conflict_marker",
+ "conflict_their",
+ "conflict_their",
+ "conflict_their",
"conflict_marker_their",
- "conflict_marker_their",
- "conflict_marker_their",
- "conflict_marker",
nil, nil, nil,
"match"
])