Merge "Fix label votes not showing up when not logged in"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 40721f5..5ed8ccf 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -5523,6 +5523,49 @@
trustFolderStat = false
----
+[[jgit-gc]]
+=== Section gc
+
+Options in section gc are used when command link:cmd-gc.html[gerrit gc] is used
+or scheduled via options link:cmd-gc.html#gc.startTime[gc.startTime] and
+link:cmd-gc.html#gc.interval[gc.interval].
+
+[[gc.auto]]gc.auto::
++
+When there are approximately more than this many loose objects in the repository,
+auto gc will pack them. Some commands use this command to perform a light-weight
+garbage collection from time to time. The default value is 6700.
++
+Setting this to 0 disables not only automatic packing based on the number of
+loose objects, but any other heuristic auto gc will otherwise use to determine
+if there’s work to do, such as link:#gc.autoPackLimit[gc.autoPackLimit].
+
+[[gc.autodetach]]gc.autodetach::
++
+Makes auto gc run in a background thread. Default is `true`.
+
+[[gc.autopacklimit]]gc.autopacklimit::
++
+When there are more than this many packs that are not marked with `*.keep` file
+in the repository, auto gc consolidates them into one larger pack. The
+default value is 50. Setting this to 0 disables it. Setting `gc.auto` to 0 will
+also disable this.
+
+[[gc.packRefs]]gc.packRefs::
++
+This variable determines whether gc runs git pack-refs. The default is `true`.
+
+[[gc.reflogExpire]]gc.reflogExpire::
++
+Removes reflog entries older than this time; defaults to 90 days. The value "now"
+expires all entries immediately, and "never" suppresses expiration altogether.
+
+[[gc.reflogExpireUnreachable]]gc.reflogExpireUnreachable::
++
+Removes reflog entries older than this time and not reachable from the
+current tip; defaults to 30 days. The value "now" expires all entries immediately,
+and "never" suppresses expiration altogether.
+
[[jgit-protocol]]
=== Section protocol
@@ -5540,6 +5583,16 @@
2:: wire protocol version 2. Speeds up fetches from repositories with many refs by allowing the client
to specify which refs to list before the server lists them.
+[[jgit-receive]]
+=== Section receive
+
+[[receive.autogc]]receive.autogc::
++
+By default, `git-receive-pack` will run auto gc after receiving data from git-push and updating refs.
+You can stop it by setting this variable to `false`. This is recommended in gerrit to avoid the
+additional load this creates. Instead schedule gc using link:cmd-gc.html#gc.startTime[gc.startTime]
+and link:cmd-gc.html#gc.interval[gc.interval] or e.g. in a cron job that runs gc in a separate process.
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index 086e836..96cc67f 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -61,8 +61,7 @@
in future gerrit releases. To build Gerrit with Java 8 language level, run:
```
- $ bazel build --java_toolchain //tools:error_prone_warnings_toolchain_java8
- :release
+ $ bazel build --java_toolchain //tools:error_prone_warnings_toolchain :release
```
[[java-11]]
diff --git a/Documentation/dev-core-plugins.txt b/Documentation/dev-core-plugins.txt
index aa519806..6b777d3 100644
--- a/Documentation/dev-core-plugins.txt
+++ b/Documentation/dev-core-plugins.txt
@@ -170,7 +170,7 @@
The plugin functionality has gone outside the Gerrit-related scope,
has a clear scope or conflict with other core plugins or existing and
planned Gerrit core features.
-
++
NOTE: The plugin would need to remain core until the planned replacement gets
implemented. Otherwise the feature is likely missing between the removal and
planned implementation times.
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.ts b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.ts
index bb7b518..6d81e9b 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.ts
@@ -150,6 +150,9 @@
@property({type: Array})
coverageRanges: CoverageRange[] = [];
+ @property({type: Boolean})
+ useNewContextControls = false;
+
@property({
type: Array,
computed: '_computeLeftCoverageRanges(coverageRanges)',
@@ -405,14 +408,16 @@
diff,
localPrefs,
this.diffElement,
- this._layers
+ this._layers,
+ this.useNewContextControls
);
} else if (this.viewMode === DiffViewMode.UNIFIED) {
builder = new GrDiffBuilderUnified(
diff,
localPrefs,
this.diffElement,
- this._layers
+ this._layers,
+ this.useNewContextControls
);
}
if (!builder) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.js
index 7cbbdb9..b10b251 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element_test.js
@@ -96,47 +96,113 @@
return [new GrDiffGroup(GrDiffGroupType.BOTH, lines)];
}
- test('no +10 buttons for 10 or less lines', () => {
- const contextGroups = createContextGroups({count: 10});
- const td = builder._createContextControl({}, contextGroups);
- const buttons = td.querySelectorAll('gr-button.showContext');
+ function createContextSectionForGroups(options) {
+ const section = document.createElement('div');
+ builder._createContextControls(
+ section, createContextGroups(options), DiffViewMode.UNIFIED);
+ return section;
+ }
- assert.equal(buttons.length, 1);
- assert.equal(buttons[0].textContent, 'Show 10 common lines');
+ suite('old style', () => {
+ setup(() => {
+ builder = new GrDiffBuilder(
+ {content: []}, prefs, null, [], false /* useNewContextControls */);
+ });
+
+ test('no +10 buttons for 10 or less lines', () => {
+ const section = createContextSectionForGroups({count: 10});
+ const buttons = section.querySelectorAll('gr-button.showContext');
+
+ assert.equal(buttons.length, 1);
+ assert.equal(buttons[0].textContent, 'Show 10 common lines');
+ });
+
+ test('context control at the top', () => {
+ builder._numLinesLeft = 50;
+ const section = createContextSectionForGroups({offset: 0, count: 20});
+ const buttons = section.querySelectorAll('gr-button.showContext');
+
+ assert.equal(buttons.length, 2);
+ assert.equal(buttons[0].textContent, 'Show 20 common lines');
+ assert.equal(buttons[1].textContent, '+10 below');
+ });
+
+ test('context control in the middle', () => {
+ builder._numLinesLeft = 50;
+ const section = createContextSectionForGroups({offset: 10, count: 20});
+ const buttons = section.querySelectorAll('gr-button.showContext');
+
+ assert.equal(buttons.length, 3);
+ assert.equal(buttons[0].textContent, '+10 above');
+ assert.equal(buttons[1].textContent, 'Show 20 common lines');
+ assert.equal(buttons[2].textContent, '+10 below');
+ });
+
+ test('context control at the bottom', () => {
+ builder._numLinesLeft = 50;
+ const section = createContextSectionForGroups({offset: 30, count: 20});
+ const buttons = section.querySelectorAll('gr-button.showContext');
+
+ assert.equal(buttons.length, 2);
+ assert.equal(buttons[0].textContent, '+10 above');
+ assert.equal(buttons[1].textContent, 'Show 20 common lines');
+ });
});
- test('context control at the top', () => {
- const contextGroups = createContextGroups({offset: 0, count: 20});
- builder._numLinesLeft = 50;
- const td = builder._createContextControl({}, contextGroups);
- const buttons = td.querySelectorAll('gr-button.showContext');
+ suite('new style', () => {
+ setup(() => {
+ builder = new GrDiffBuilder(
+ {content: []}, prefs, null, [], true /* useNewContextControls */);
+ });
- assert.equal(buttons.length, 2);
- assert.equal(buttons[0].textContent, 'Show 20 common lines');
- assert.equal(buttons[1].textContent, '+10 below');
- });
+ test('no +10 buttons for 10 or less lines', () => {
+ const section = createContextSectionForGroups({count: 10});
+ const buttons = section.querySelectorAll('gr-button.showContext');
- test('context control in the middle', () => {
- const contextGroups = createContextGroups({offset: 10, count: 20});
- builder._numLinesLeft = 50;
- const td = builder._createContextControl({}, contextGroups);
- const buttons = td.querySelectorAll('gr-button.showContext');
+ assert.equal(buttons.length, 1);
+ assert.equal(buttons[0].textContent, '+10 common lines');
+ });
- assert.equal(buttons.length, 3);
- assert.equal(buttons[0].textContent, '+10 above');
- assert.equal(buttons[1].textContent, 'Show 20 common lines');
- assert.equal(buttons[2].textContent, '+10 below');
- });
+ test('context control at the top', () => {
+ builder._numLinesLeft = 50;
+ const section = createContextSectionForGroups({offset: 0, count: 20});
+ const buttons = section.querySelectorAll('gr-button.showContext');
- test('context control at the top', () => {
- const contextGroups = createContextGroups({offset: 30, count: 20});
- builder._numLinesLeft = 50;
- const td = builder._createContextControl({}, contextGroups);
- const buttons = td.querySelectorAll('gr-button.showContext');
+ assert.equal(buttons.length, 2);
+ assert.equal(buttons[0].textContent, '+20 common lines');
+ assert.equal(buttons[1].textContent, '+10');
- assert.equal(buttons.length, 2);
- assert.equal(buttons[0].textContent, '+10 above');
- assert.equal(buttons[1].textContent, 'Show 20 common lines');
+ assert.include([...buttons[0].classList.values()], 'belowButton');
+ assert.include([...buttons[1].classList.values()], 'belowButton');
+ });
+
+ test('context control in the middle', () => {
+ builder._numLinesLeft = 50;
+ const section = createContextSectionForGroups({offset: 10, count: 20});
+ const buttons = section.querySelectorAll('gr-button.showContext');
+
+ assert.equal(buttons.length, 3);
+ assert.equal(buttons[0].textContent, '+20 common lines');
+ assert.equal(buttons[1].textContent, '+10');
+ assert.equal(buttons[2].textContent, '+10');
+
+ assert.include([...buttons[0].classList.values()], 'centeredButton');
+ assert.include([...buttons[1].classList.values()], 'aboveButton');
+ assert.include([...buttons[2].classList.values()], 'belowButton');
+ });
+
+ test('context control at the bottom', () => {
+ builder._numLinesLeft = 50;
+ const section = createContextSectionForGroups({offset: 30, count: 20});
+ const buttons = section.querySelectorAll('gr-button.showContext');
+
+ assert.equal(buttons.length, 2);
+ assert.equal(buttons[0].textContent, '+20 common lines');
+ assert.equal(buttons[1].textContent, '+10');
+
+ assert.include([...buttons[0].classList.values()], 'aboveButton');
+ assert.include([...buttons[1].classList.values()], 'aboveButton');
+ });
});
});
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-side-by-side.ts b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-side-by-side.ts
index 0f7eb43..657dfa2 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-side-by-side.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-side-by-side.ts
@@ -19,7 +19,7 @@
import {GrDiffGroup, GrDiffGroupType} from '../gr-diff/gr-diff-group';
import {DiffInfo, DiffPreferencesInfo} from '../../../types/common';
import {GrDiffLine, LineNumber} from '../gr-diff/gr-diff-line';
-import {Side} from '../../../constants/constants';
+import {DiffViewMode, Side} from '../../../constants/constants';
export class GrDiffBuilderSideBySide extends GrDiffBuilder {
constructor(
@@ -27,9 +27,10 @@
prefs: DiffPreferencesInfo,
outputEl: HTMLElement,
// TODO(TS): Replace any by a layer interface.
- readonly layers: any[] = []
+ readonly layers: any[] = [],
+ useNewContextControls = false
) {
- super(diff, prefs, outputEl, layers);
+ super(diff, prefs, outputEl, layers, useNewContextControls);
}
_getMoveControlsConfig() {
@@ -57,8 +58,10 @@
sectionEl.classList.add('ignoredWhitespaceOnly');
}
if (group.type === GrDiffGroupType.CONTEXT_CONTROL) {
- sectionEl.appendChild(
- this._createContextRow(sectionEl, group.contextGroups)
+ this._createContextControls(
+ sectionEl,
+ group.contextGroups,
+ DiffViewMode.SIDE_BY_SIDE
);
return sectionEl;
}
@@ -122,21 +125,6 @@
row.appendChild(this._createTextEl(lineNumberEl, line, side));
}
- _createContextRow(section: HTMLElement, contextGroups: GrDiffGroup[]) {
- const row = this._createElement('tr');
- row.classList.add('diff-row', 'side-by-side');
- row.setAttribute('left-type', GrDiffGroupType.CONTEXT_CONTROL);
- row.setAttribute('right-type', GrDiffGroupType.CONTEXT_CONTROL);
- row.tabIndex = -1;
-
- row.appendChild(this._createBlameCell(0));
- row.appendChild(this._createElement('td', 'contextLineNum'));
- row.appendChild(this._createContextControl(section, contextGroups));
- row.appendChild(this._createElement('td', 'contextLineNum'));
- row.appendChild(this._createContextControl(section, contextGroups));
- return row;
- }
-
_getNextContentOnSide(content: HTMLElement, side: Side): HTMLElement | null {
let tr: HTMLElement = content.parentElement!.parentElement!;
while ((tr = tr.nextSibling as HTMLElement)) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified.ts b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified.ts
index 7c070e5..2028b0c 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified.ts
@@ -18,7 +18,7 @@
import {GrDiffBuilder} from './gr-diff-builder';
import {GrDiffGroup, GrDiffGroupType} from '../gr-diff/gr-diff-group';
import {DiffInfo, DiffPreferencesInfo} from '../../../types/common';
-import {Side} from '../../../constants/constants';
+import {DiffViewMode, Side} from '../../../constants/constants';
export class GrDiffBuilderUnified extends GrDiffBuilder {
constructor(
@@ -26,9 +26,10 @@
prefs: DiffPreferencesInfo,
outputEl: HTMLElement,
// TODO(TS): Replace any by a layer interface.
- readonly layers: any[] = []
+ readonly layers: any[] = [],
+ useNewContextControls = false
) {
- super(diff, prefs, outputEl, layers);
+ super(diff, prefs, outputEl, layers, useNewContextControls);
}
_getMoveControlsConfig() {
@@ -56,8 +57,10 @@
sectionEl.classList.add('ignoredWhitespaceOnly');
}
if (group.type === GrDiffGroupType.CONTEXT_CONTROL) {
- sectionEl.appendChild(
- this._createContextRow(sectionEl, group.contextGroups)
+ this._createContextControls(
+ sectionEl,
+ group.contextGroups,
+ DiffViewMode.UNIFIED
);
return sectionEl;
}
@@ -121,17 +124,6 @@
return row;
}
- _createContextRow(section: HTMLElement, contextGroups: GrDiffGroup[]) {
- const row = this._createElement('tr', GrDiffGroupType.CONTEXT_CONTROL);
- row.classList.add('diff-row', 'unified');
- row.tabIndex = -1;
- row.appendChild(this._createBlameCell(0));
- row.appendChild(this._createElement('td', 'contextLineNum'));
- row.appendChild(this._createElement('td', 'contextLineNum'));
- row.appendChild(this._createContextControl(section, contextGroups));
- return row;
- }
-
_getNextContentOnSide(content: HTMLElement, side: Side): HTMLElement | null {
let tr: HTMLElement = content.parentElement!.parentElement!;
while ((tr = tr.nextSibling as HTMLElement)) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.ts b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.ts
index 91fd137..29af31c 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.ts
@@ -24,7 +24,7 @@
rangeBySide,
} from '../gr-diff/gr-diff-group';
import {BlameInfo, DiffInfo, DiffPreferencesInfo} from '../../../types/common';
-import {Side} from '../../../constants/constants';
+import {DiffViewMode, Side} from '../../../constants/constants';
import {DiffLayer} from '../../../types/types';
/**
@@ -92,7 +92,8 @@
diff: DiffInfo,
prefs: DiffPreferencesInfo,
outputEl: HTMLElement,
- readonly layers: DiffLayer[] = []
+ readonly layers: DiffLayer[] = [],
+ protected readonly useNewContextControls: boolean = false
) {
this._diff = diff;
this._numLinesLeft = this._diff.content
@@ -304,25 +305,116 @@
);
}
- _createContextControl(
+ _createContextControls(
section: HTMLElement,
- contextGroups: GrDiffGroup[]
- ): HTMLElement {
+ contextGroups: GrDiffGroup[],
+ viewMode: DiffViewMode
+ ) {
const leftStart = contextGroups[0].lineRange.left.start!;
const leftEnd = contextGroups[contextGroups.length - 1].lineRange.left.end!;
const numLines = leftEnd - leftStart + 1;
if (numLines === 0) console.error('context group without lines');
- const td = this._createElement('td');
- const showPartialLinks = numLines > PARTIAL_CONTEXT_AMOUNT;
const firstGroupIsSkipped = !!contextGroups[0].skip;
const lastGroupIsSkipped = !!contextGroups[contextGroups.length - 1].skip;
- const showAboveButton =
- showPartialLinks && leftStart > 1 && !firstGroupIsSkipped;
- const showBelowButton =
- showPartialLinks && leftEnd < this._numLinesLeft && !lastGroupIsSkipped;
- if (showAboveButton) {
+
+ const showPartialLinks = numLines > PARTIAL_CONTEXT_AMOUNT;
+ const showAbove = leftStart > 1 && !firstGroupIsSkipped;
+ const showBelow = leftEnd < this._numLinesLeft && !lastGroupIsSkipped;
+
+ if (this.useNewContextControls) {
+ section.classList.add('newStyle');
+ if (showAbove) {
+ const paddingRow = this._createContextControlPaddingRow(viewMode);
+ paddingRow.classList.add('above');
+ section.appendChild(paddingRow);
+ }
+ section.appendChild(
+ this._createNewContextControlRow(
+ section,
+ contextGroups,
+ showAbove,
+ showBelow,
+ numLines
+ )
+ );
+ if (showBelow) {
+ const paddingRow = this._createContextControlPaddingRow(viewMode);
+ paddingRow.classList.add('below');
+ section.appendChild(paddingRow);
+ }
+ } else {
+ section.appendChild(
+ this._createOldContextControlRow(
+ section,
+ contextGroups,
+ viewMode,
+ showAbove && showPartialLinks,
+ showBelow && showPartialLinks,
+ numLines
+ )
+ );
+ }
+ }
+
+ /**
+ * Creates old-style context controls: a single row of "+X above" and
+ * "+X below" buttons.
+ */
+ _createOldContextControlRow(
+ section: HTMLElement,
+ contextGroups: GrDiffGroup[],
+ viewMode: DiffViewMode,
+ showAbove: boolean,
+ showBelow: boolean,
+ numLines: number
+ ) {
+ const row = this._createElement('tr', GrDiffGroupType.CONTEXT_CONTROL);
+
+ row.classList.add('diff-row');
+ row.classList.add(
+ viewMode === DiffViewMode.SIDE_BY_SIDE ? 'side-by-side' : 'unified'
+ );
+
+ row.tabIndex = -1;
+ row.appendChild(this._createBlameCell(0));
+ row.appendChild(this._createElement('td', 'contextLineNum'));
+ if (viewMode === DiffViewMode.SIDE_BY_SIDE) {
+ row.appendChild(
+ this._createOldContextControlButtons(
+ section,
+ contextGroups,
+ showAbove,
+ showBelow,
+ numLines
+ )
+ );
+ }
+ row.appendChild(this._createElement('td', 'contextLineNum'));
+ row.appendChild(
+ this._createOldContextControlButtons(
+ section,
+ contextGroups,
+ showAbove,
+ showBelow,
+ numLines
+ )
+ );
+
+ return row;
+ }
+
+ _createOldContextControlButtons(
+ section: HTMLElement,
+ contextGroups: GrDiffGroup[],
+ showAbove: boolean,
+ showBelow: boolean,
+ numLines: number
+ ): HTMLElement {
+ const td = this._createElement('td');
+
+ if (showAbove) {
td.appendChild(
this._createContextButton(
ContextButtonType.ABOVE,
@@ -332,6 +424,7 @@
)
);
}
+
td.appendChild(
this._createContextButton(
ContextButtonType.ALL,
@@ -340,7 +433,8 @@
numLines
)
);
- if (showBelowButton) {
+
+ if (showBelow) {
td.appendChild(
this._createContextButton(
ContextButtonType.BELOW,
@@ -350,9 +444,104 @@
)
);
}
+
return td;
}
+ /**
+ * Creates new-style context controls: buttons extend from the gap created by
+ * this method up or down into the area of code that they affect.
+ */
+ _createNewContextControlRow(
+ section: HTMLElement,
+ contextGroups: GrDiffGroup[],
+ showAbove: boolean,
+ showBelow: boolean,
+ numLines: number
+ ): HTMLElement {
+ const row = this._createElement('tr', 'contextDivider');
+ if (!(showAbove && showBelow)) {
+ row.classList.add('collapsed');
+ }
+
+ const element = this._createElement('td', 'dividerCell');
+ row.appendChild(element);
+
+ const showAllContainer = this._createElement('div', 'aboveBelowButtons');
+ element.appendChild(showAllContainer);
+
+ const showAllButton = this._createContextButton(
+ ContextButtonType.ALL,
+ section,
+ contextGroups,
+ numLines
+ );
+ showAllButton.classList.add(
+ showAbove && showBelow
+ ? 'centeredButton'
+ : showAbove
+ ? 'aboveButton'
+ : 'belowButton'
+ );
+ showAllContainer.appendChild(showAllButton);
+
+ const showPartialLinks = numLines > PARTIAL_CONTEXT_AMOUNT;
+ if (showPartialLinks) {
+ const container = this._createElement('div', 'aboveBelowButtons');
+ if (showAbove) {
+ container.appendChild(
+ this._createContextButton(
+ ContextButtonType.ABOVE,
+ section,
+ contextGroups,
+ numLines
+ )
+ );
+ }
+ if (showBelow) {
+ container.appendChild(
+ this._createContextButton(
+ ContextButtonType.BELOW,
+ section,
+ contextGroups,
+ numLines
+ )
+ );
+ }
+ element.appendChild(container);
+ }
+
+ return row;
+ }
+
+ /**
+ * Creates a table row to serve as padding between code and context controls.
+ * Blame column, line gutters, and content area will continue visually, but
+ * context controls can render over this background to map more clearly to
+ * the area of code they expand.
+ */
+ _createContextControlPaddingRow(viewMode: DiffViewMode) {
+ const row = this._createElement('tr', 'contextBackground');
+
+ if (viewMode === DiffViewMode.SIDE_BY_SIDE) {
+ row.classList.add('side-by-side');
+ row.setAttribute('left-type', GrDiffGroupType.CONTEXT_CONTROL);
+ row.setAttribute('right-type', GrDiffGroupType.CONTEXT_CONTROL);
+ } else {
+ row.classList.add('unified');
+ }
+
+ row.appendChild(this._createBlameCell(0));
+ row.appendChild(this._createElement('td', 'contextLineNum'));
+ if (viewMode === DiffViewMode.SIDE_BY_SIDE) {
+ row.appendChild(this._createElement('td'));
+ }
+ row.appendChild(this._createElement('td', 'contextLineNum'));
+ row.appendChild(this._createElement('td'));
+
+ return row;
+ }
+
_createContextButton(
type: ContextButtonType,
section: HTMLElement,
@@ -361,6 +550,9 @@
) {
const context = PARTIAL_CONTEXT_AMOUNT;
const button = this._createElement('gr-button', 'showContext');
+ if (this.useNewContextControls) {
+ button.classList.add('contextControlButton');
+ }
button.setAttribute('link', 'true');
button.setAttribute('no-uppercase', 'true');
@@ -368,26 +560,39 @@
let groups: GrDiffGroup[] = []; // The groups that replace this one if tapped.
let requiresLoad = false;
if (type === GrDiffBuilder.ContextButtonType.ALL) {
- requiresLoad = contextGroups.find(c => !!c.skip) !== undefined;
- const icon = this._createElement('iron-icon', 'showContext');
- icon.setAttribute('icon', 'gr-icons:unfold-more');
- button.appendChild(icon);
-
- text = `Show ${numLines} common line`;
+ if (this.useNewContextControls) {
+ text = `+${numLines} common line`;
+ } else {
+ text = `Show ${numLines} common line`;
+ const icon = this._createElement('iron-icon', 'showContext');
+ icon.setAttribute('icon', 'gr-icons:unfold-more');
+ button.appendChild(icon);
+ }
if (numLines > 1) {
text += 's';
}
+ requiresLoad = contextGroups.find(c => !!c.skip) !== undefined;
if (requiresLoad) {
// Expanding content would require load of more data
text += ' (too large)';
}
groups.push(...contextGroups);
} else if (type === GrDiffBuilder.ContextButtonType.ABOVE) {
- text = `+${context} above`;
groups = hideInContextControl(contextGroups, context, numLines);
+ if (this.useNewContextControls) {
+ text = `+${context}`;
+ button.classList.add('aboveButton');
+ } else {
+ text = `+${context} above`;
+ }
} else if (type === GrDiffBuilder.ContextButtonType.BELOW) {
- text = `+${context} below`;
groups = hideInContextControl(contextGroups, 0, numLines - context);
+ if (this.useNewContextControls) {
+ text = `+${context}`;
+ button.classList.add('belowButton');
+ } else {
+ text = `+${context} below`;
+ }
}
const textSpan = this._createElement('span', 'showContext');
textSpan.textContent = text;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.ts b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.ts
index e7fb30e..e42eb84 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.ts
@@ -20,6 +20,7 @@
AbortStop,
CursorMoveResult,
GrCursorManager,
+ Stop,
isTargetable,
} from '../../shared/gr-cursor-manager/gr-cursor-manager';
import {afterNextRender} from '@polymer/polymer/lib/utils/render-status';
@@ -525,7 +526,7 @@
_updateStops() {
this.$.cursorManager.stops = this.diffs.reduce(
- (stops: HTMLElement[], diff) => stops.concat(diff.getCursorStops()),
+ (stops: Stop[], diff) => stops.concat(diff.getCursorStops()),
[]
);
}
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.js b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.js
index 8e95f3d..5619acc 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.js
@@ -19,8 +19,9 @@
import '../gr-diff/gr-diff.js';
import './gr-diff-cursor.js';
import '../../shared/gr-rest-api-interface/gr-rest-api-interface.js';
-import {getMockDiffResponse} from '../../../test/mocks/diff-response.js';
import {html} from '@polymer/polymer/lib/utils/html-tag.js';
+import {listenOnce} from '../../../test/test-utils.js';
+import {getMockDiffResponse} from '../../../test/mocks/diff-response.js';
const basicFixture = fixtureFromTemplate(html`
<gr-diff></gr-diff>
@@ -490,5 +491,78 @@
someEmptyDiv.appendChild(cursorElement);
});
});
+
+ suite('multi diff', () => {
+ const multiDiffFixture = fixtureFromTemplate(html`
+ <gr-diff></gr-diff>
+ <gr-diff></gr-diff>
+ <gr-diff></gr-diff>
+ <gr-diff-cursor></gr-diff-cursor>
+ <gr-rest-api-interface></gr-rest-api-interface>
+ `);
+
+ let diffElements;
+
+ setup(async () => {
+ const fixtureElems = multiDiffFixture.instantiate();
+ diffElements = fixtureElems.slice(0, 3);
+ cursorElement = fixtureElems[3];
+ const restAPI = fixtureElems[4];
+
+ // Register the diff with the cursor.
+ cursorElement.push('diffs', ...diffElements);
+
+ await restAPI.getDiffPreferences().then(prefs => {
+ for (const el of diffElements) {
+ el.prefs = prefs;
+ }
+ });
+ });
+
+ function getTargetDiffIndex() {
+ // Mocha has a bug where when `assert.equals` fails, it will try to
+ // JSON.stringify the operands, which fails when they are cyclic structures
+ // like GrDiffElement. The failure is difficult to attribute to a specific
+ // assertion because of the async nature assertion errors are handled and
+ // can cause the test simply timing out, causing a lot of debugging headache.
+ // Working with indices circumvents the problem.
+ return diffElements.indexOf(cursorElement.getTargetDiffElement());
+ }
+
+ test('do not skip loading diffs', async () => {
+ const diffRenderedPromises =
+ diffElements.map(diffEl => listenOnce(diffEl, 'render'));
+
+ diffElements[0].diff = getMockDiffResponse();
+ diffElements[2].diff = getMockDiffResponse();
+ await Promise.all([diffRenderedPromises[0], diffRenderedPromises[2]]);
+
+ const lastLine = diffElements[0].diff.meta_b.lines;
+
+ // Goto second last line of the first diff
+ cursorElement.moveToLineNumber(lastLine - 1, 'right');
+ assert.equal(
+ cursorElement.getTargetLineElement().textContent, lastLine - 1);
+
+ // Can move down until we reach the loading file
+ cursorElement.moveDown();
+ assert.equal(getTargetDiffIndex(), 0);
+ assert.equal(cursorElement.getTargetLineElement().textContent, lastLine);
+
+ // Cannot move down while still loading the diff we would switch to
+ cursorElement.moveDown();
+ assert.equal(getTargetDiffIndex(), 0);
+ assert.equal(cursorElement.getTargetLineElement().textContent, lastLine);
+
+ // Diff 1 finishing to load
+ diffElements[1].diff = getMockDiffResponse();
+ await diffRenderedPromises[1];
+
+ // Now we can go down
+ cursorElement.moveDown();
+ assert.equal(getTargetDiffIndex(), 1);
+ assert.equal(cursorElement.getTargetLineElement().textContent, 'File');
+ });
+ });
});
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
index 50112aa..c6f5d21 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
@@ -71,6 +71,7 @@
import {LineNumber} from '../gr-diff/gr-diff-line';
import {GrCommentThread} from '../../shared/gr-comment-thread/gr-comment-thread';
import {PatchSetFile} from '../../../types/types';
+import {KnownExperimentId} from '../../../services/flags/flags';
const MSG_EMPTY_BLAME = 'No blame information for this diff.';
@@ -273,6 +274,8 @@
private readonly reporting = appContext.reportingService;
+ private readonly flags = appContext.flagsService;
+
/** @override */
created() {
super.created();
@@ -1197,6 +1200,10 @@
_showNewlineWarningRight(diff?: DiffInfo) {
return this._hasTrailingNewlines(diff, false) === false;
}
+
+ _useNewContextControls() {
+ return this.flags.isEnabled(KnownExperimentId.NEW_CONTEXT_CONTROLS);
+ }
}
declare global {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_html.ts b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_html.ts
index d1564b0..9921dd6 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_html.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_html.ts
@@ -41,6 +41,7 @@
diff="[[diff]]"
show-newline-warning-left="[[_showNewlineWarningLeft(diff)]]"
show-newline-warning-right="[[_showNewlineWarningRight(diff)]]"
+ use-new-context-controls="[[_useNewContextControls()]]"
>
</gr-diff>
<gr-syntax-layer
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.ts b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.ts
index 08ea1a6..ab7ab8a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.ts
@@ -307,8 +307,10 @@
state.lineNums.right + 1
);
- if (this.context !== WHOLE_FILE) {
- const hiddenStart = state.chunkIndex === 0 ? 0 : this.context;
+ const hasSkippedGroup = !!groups.find(g => g.skip);
+ if (this.context !== WHOLE_FILE || hasSkippedGroup) {
+ const contextNumLines = this.context > 0 ? this.context : 0;
+ const hiddenStart = state.chunkIndex === 0 ? 0 : contextNumLines;
const hiddenEnd =
lineCount -
(firstUncollapsibleChunkIndex === chunks.length ? 0 : this.context);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.js b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.js
index eed5900..ce7a3c4 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.js
@@ -770,6 +770,51 @@
state.lineNums.right + rows.length);
});
+ test('WHOLE_FILE with skip chunks still get collapsed', () => {
+ element.context = WHOLE_FILE;
+ const lineNums = {left: 10, right: 100};
+ const state = {
+ lineNums,
+ chunkIndex: 1,
+ };
+ const skip = 10000;
+ const chunks = [
+ {a: ['foo']},
+ {skip},
+ {ab: rows},
+ {a: ['bar']},
+ ];
+ const result = element._processNext(state, chunks);
+ // Results in one, uncollapsed group with all rows.
+ assert.equal(result.groups.length, 1);
+ assert.equal(result.groups[0].type, GrDiffGroupType.CONTEXT_CONTROL);
+
+ // Skip and ab group are hidden in the same context control
+ assert.equal(result.groups[0].contextGroups.length, 2);
+ const [skippedGroup, abGroup] = result.groups[0].contextGroups;
+
+ // Line numbers are set correctly.
+ assert.deepEqual(
+ skippedGroup.lineRange,
+ {
+ left: {start: lineNums.left + 1, end: lineNums.left + skip},
+ right: {start: lineNums.right + 1, end: lineNums.right + skip},
+ });
+
+ assert.deepEqual(
+ abGroup.lineRange,
+ {
+ left: {
+ start: lineNums.left + skip + 1,
+ end: lineNums.left + skip + rows.length,
+ },
+ right: {
+ start: lineNums.right + skip + 1,
+ end: lineNums.right + skip + rows.length,
+ },
+ });
+ });
+
test('with context', () => {
element.context = 10;
const state = {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts
index 7c317bb..14b8666 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts
@@ -54,6 +54,7 @@
import {KeyLocations} from '../gr-diff-processor/gr-diff-processor';
import {FlattenedNodesObserver} from '@polymer/polymer/lib/utils/flattened-nodes-observer';
import {PolymerDeepPropertyChange} from '@polymer/polymer/interfaces';
+import {AbortStop} from '../../shared/gr-cursor-manager/gr-cursor-manager';
const NO_NEWLINE_BASE = 'No newline at end of base file.';
const NO_NEWLINE_REVISION = 'No newline at end of revision file.';
@@ -193,7 +194,7 @@
/** True when diff is changed, until the content is done rendering. */
@property({type: Boolean})
- _loading = false;
+ _loading = true;
@property({type: Boolean})
loggedIn = false;
@@ -240,6 +241,9 @@
@property({type: Boolean})
showNewlineWarningRight = false;
+ @property({type: Boolean})
+ useNewContextControls = false;
+
@property({
type: String,
computed:
@@ -456,14 +460,17 @@
this.cancelDebouncer(RENDER_DIFF_TABLE_DEBOUNCE_NAME);
}
- getCursorStops(): HTMLElement[] {
+ getCursorStops(): Array<HTMLElement | AbortStop> {
if (this.hidden && this.noAutoRender) return [];
- if (!this.root) return [];
+
+ if (this._loading) {
+ return [new AbortStop()];
+ }
return Array.from(
- this.root.querySelectorAll<HTMLElement>(
+ this.root?.querySelectorAll<HTMLElement>(
':not(.contextControl) > .diff-row'
- )
+ ) || []
).filter(tr => tr.querySelector('button'));
}
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts
index e9de9e7..48a4596 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts
@@ -46,9 +46,38 @@
}
table {
border-collapse: collapse;
- border-right: 1px solid var(--border-color);
table-layout: fixed;
}
+
+ /*
+ Context controls break up the table visually, so we set the right border
+ on individual sections to leave a gap for the divider.
+ */
+ .section {
+ border-right: 1px solid var(--border-color);
+ }
+ .section.contextControl.newStyle {
+ /*
+ * Divider inside this section must not have border; we set borders on
+ * the padding rows below.
+ */
+ border-right-width: 0;
+ }
+ /*
+ * Padding rows behind new style context controls. The diff is styled to be
+ * cut into two halves by the negative space of the divider on which the
+ * context control buttons are anchored.
+ */
+ .contextBackground {
+ border-right: 1px solid var(--border-color);
+ }
+ .contextBackground.above {
+ border-bottom: 1px solid var(--border-color);
+ }
+ .contextBackground.below {
+ border-top: 1px solid var(--border-color);
+ }
+
.lineNumButton {
display: block;
width: 100%;
@@ -206,12 +235,22 @@
/* Newline, to ensure empty lines are one line-height tall. */
content: '\\A';
}
+
+ /* Context controls */
.contextControl {
background-color: var(--diff-context-control-background-color);
border: 1px solid var(--diff-context-control-border-color);
color: var(--diff-context-control-color);
+ --divider-height: var(--spacing-s);
+ --divider-border: 1px;
}
- .contextControl gr-button {
+ .contextControl.newStyle {
+ background-color: transparent;
+ border: none;
+ /* Change to --diff-context-control-color once only new style exists. */
+ --diff-context-control-color: var(--default-button-text-color);
+ }
+ .contextControl:not(.newStyle) gr-button {
display: inline-block;
text-decoration: none;
vertical-align: top;
@@ -229,6 +268,97 @@
.contextControl td:not(.lineNumButton) {
text-align: center;
}
+
+ /*
+ * Padding rows behind new style context controls. Styled as a continuation
+ * of the line gutters and code area.
+ */
+ .contextBackground > .contextLineNum {
+ background-color: var(--diff-blank-background-color);
+ }
+ .contextBackground > td:not(.contextLineNum) {
+ background-color: var(--view-background-color);
+ }
+ .contextBackground {
+ /*
+ * One line of background behind the context expanders which they can
+ * render on top of, plus some padding.
+ */
+ height: calc(var(--line-height-normal) + var(--spacing-s));
+ }
+
+ .contextDivider {
+ height: var(--divider-height);
+ /* Create a positioning context. */
+ transform: translateX(0px);
+ }
+ .contextDivider.collapsed {
+ /* Hide divider gap, but still show child elements (expansion buttons). */
+ height: 0;
+ }
+ .dividerCell {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+ .contextControlButton {
+ background-color: var(--default-button-background-color);
+ font: var(--context-control-button-font, inherit);
+ /* All position is relative to container, so ignore sibling buttons. */
+ position: absolute;
+ }
+ .contextControlButton:first-child {
+ /* First button needs to claim width to display without text wrapping. */
+ position: relative;
+ }
+ .centeredButton {
+ /* Center over divider. */
+ top: 50%;
+ transform: translateY(-50%);
+ --gr-button: {
+ color: var(--diff-context-control-color);
+ border: solid var(--border-color);
+ border-width: 1px;
+ border-radius: var(--border-radius);
+ padding: var(--spacing-s) var(--spacing-l);
+ }
+ }
+ .aboveBelowButtons {
+ display: flex;
+ flex-direction: column;
+ margin-left: var(--spacing-m);
+ position: relative;
+ }
+ .aboveBelowButtons:first-child {
+ margin-left: 0;
+ }
+ .aboveButton {
+ /* Display over preceding content / background placeholder. */
+ transform: translateY(-100%);
+ --gr-button: {
+ color: var(--diff-context-control-color);
+ border: solid var(--border-color);
+ border-width: 1px 1px 0 1px;
+ border-radius: var(--border-radius) var(--border-radius) 0 0;
+ padding: var(--spacing-xxs) var(--spacing-l);
+ }
+ }
+ .belowButton {
+ /* Display over following content / background placeholder. */
+ top: calc(100% + var(--divider-border));
+ --gr-button: {
+ color: var(--diff-context-control-color);
+ border: solid var(--border-color);
+ border-width: 0 1px 1px 1px;
+ border-radius: 0 0 var(--border-radius) var(--border-radius);
+ padding: var(--spacing-xxs) var(--spacing-l);
+ }
+ }
+
.displayLine .diff-row.target-row td {
box-shadow: inset 0 -1px var(--border-color);
}
@@ -437,6 +567,7 @@
base-image="[[baseImage]]"
layers="[[layers]]"
revision-image="[[revisionImage]]"
+ use-new-context-controls="[[useNewContextControls]]"
>
<table
id="diffTable"
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
index 36b3b8f..5e95d83 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
@@ -585,7 +585,7 @@
});
suite('getCursorStops', () => {
- const setupDiff = function() {
+ function setupDiff() {
element.diff = getMockDiffResponse();
element.prefs = {
context: 10,
@@ -605,8 +605,9 @@
};
element._renderDiffTable();
+ element._loading = false;
flush();
- };
+ }
test('getCursorStops returns [] when hidden and noAutoRender', () => {
element.noAutoRender = true;
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_html.ts b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_html.ts
index ed44807..52465b3 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_html.ts
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_html.ts
@@ -45,9 +45,6 @@
--native-select-style: {
max-width: 5.25em;
}
- --dropdown-content-stype: {
- max-width: 300px;
- }
}
}
</style>
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.ts b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.ts
index 30e0143..26a6b3f 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.ts
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list_html.ts
@@ -35,9 +35,7 @@
background-color: var(--dropdown-background-color);
box-shadow: var(--elevation-level-2);
max-height: 70vh;
- margin-top: var(--spacing-xxl);
min-width: 266px;
- @apply --dropdown-content-style;
}
paper-listbox {
--paper-listbox: {
@@ -136,6 +134,9 @@
<iron-dropdown
id="dropdown"
vertical-align="top"
+ horizontal-align="left"
+ dynamic-align
+ no-overlap
allow-outside-scroll="true"
on-click="_handleDropdownClick"
>
diff --git a/polygerrit-ui/app/styles/themes/app-theme.ts b/polygerrit-ui/app/styles/themes/app-theme.ts
index 9b62718..3641815 100644
--- a/polygerrit-ui/app/styles/themes/app-theme.ts
+++ b/polygerrit-ui/app/styles/themes/app-theme.ts
@@ -134,6 +134,7 @@
--font-weight-h1: 400;
--font-weight-h2: 400;
--font-weight-h3: 400;
+ --context-control-button-font: var(--font-weight-normal) var(--font-size-normal) var(--font-family);
/* spacing */
--spacing-xxs: 1px;
diff --git a/tools/release_noter/release_noter.py b/tools/release_noter/release_noter.py
index 4b10620..05fa023 100644
--- a/tools/release_noter/release_noter.py
+++ b/tools/release_noter/release_noter.py
@@ -187,12 +187,14 @@
class Commit:
sha1 = None
subject = None
+ component = None
issues = set()
def reset(self, signature, task):
if signature is not None:
self.sha1 = signature.group(1)
self.subject = None
+ self.component = None
self.issues = set()
return Task.finish_headers
return task
@@ -238,24 +240,25 @@
if noted_commit.subject == commit.subject:
return Commit()
set_component(commit, commits, cwd)
- link_subject(commit, gerrit, options)
+ link_subject(commit, gerrit, options, cwd)
escape_these(commit)
return Commit()
def set_component(commit, commits, cwd):
- component_found = False
+ component_found = None
for component in Components:
for sentinel in component.value.sentinels:
- if not component_found:
+ if component_found is None:
if re.match(f"{GIT_PATH}/{PLUGINS}{component.value.name.lower()}", cwd):
- component_found = True
+ component_found = component
elif sentinel.lower() in commit.subject.lower():
- component_found = True
- if component_found:
+ component_found = component
+ if component_found is not None:
commits[component].append(commit)
- if not component_found:
+ if component_found is None:
commits[Components.otherwise].append(commit)
+ commit.component = component_found
def init_components():
@@ -265,13 +268,17 @@
return components
-def link_subject(commit, gerrit, options):
+def link_subject(commit, gerrit, options, cwd):
if options.link:
gerrit_change = gerrit.get(f"{COMMIT_URL}{commit.sha1}")
if not gerrit_change:
return
change_number = gerrit_change[0]["_number"]
- change_address = f"{GERRIT_URL}{CHANGE_URL}{change_number}"
+ plugin_wd = re.search(f"{GIT_PATH}/({PLUGINS}.+)", cwd)
+ if plugin_wd is not None:
+ change_address = f"{GERRIT_URL}/c/{plugin_wd.group(1)}/+/{change_number}"
+ else:
+ change_address = f"{GERRIT_URL}{CHANGE_URL}{change_number}"
short_sha1 = commit.sha1[0:7]
commit.subject = f"[{short_sha1}]({change_address})\n {commit.subject}"