mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-13 00:45:44 +01:00
Ignore duplicate contributors for single changeset
Committed-by: Konstantin Schaper <konstantin.schaper@cloudogu.com>
This commit is contained in:
2
gradle/changelog/duplicate_contributors.yaml
Normal file
2
gradle/changelog/duplicate_contributors.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
- type: fixed
|
||||||
|
description: Duplicate contributors for single changeset
|
||||||
@@ -240,6 +240,9 @@ public class Changeset extends BasicPropertiesAware implements ModelObject {
|
|||||||
* @since 2.1.0
|
* @since 2.1.0
|
||||||
*/
|
*/
|
||||||
public Collection<Contributor> getContributors() {
|
public Collection<Contributor> getContributors() {
|
||||||
|
if (contributors == null) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
return contributors;
|
return contributors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ import {
|
|||||||
Level,
|
Level,
|
||||||
SignatureIcon,
|
SignatureIcon,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
SubSubtitle
|
SubSubtitle,
|
||||||
} from "@scm-manager/ui-components";
|
} from "@scm-manager/ui-components";
|
||||||
import ContributorTable from "./ContributorTable";
|
import ContributorTable from "./ContributorTable";
|
||||||
import { Link as ReactLink } from "react-router-dom";
|
import { Link as ReactLink } from "react-router-dom";
|
||||||
@@ -57,7 +57,19 @@ type Props = {
|
|||||||
|
|
||||||
const countContributors = (changeset: Changeset) => {
|
const countContributors = (changeset: Changeset) => {
|
||||||
if (changeset.contributors) {
|
if (changeset.contributors) {
|
||||||
return changeset.contributors.length + 1;
|
const uniqueContributors: string[] = [];
|
||||||
|
changeset.contributors
|
||||||
|
.map((p) => p.person)
|
||||||
|
.forEach((c) => {
|
||||||
|
if (c.mail) {
|
||||||
|
if (!uniqueContributors.includes(c.mail)) {
|
||||||
|
uniqueContributors.push(c.mail);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uniqueContributors.push(c.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return uniqueContributors.length + 1;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
@@ -93,7 +105,7 @@ const Contributors: FC<{ changeset: Changeset }> = ({ changeset }) => {
|
|||||||
return (
|
return (
|
||||||
<div className="is-flex is-flex-direction-column mb-4">
|
<div className="is-flex is-flex-direction-column mb-4">
|
||||||
<div className="is-flex">
|
<div className="is-flex">
|
||||||
<p className="is-ellipsis-overflow is-clickable mb-2" onClick={e => setOpen(!open)}>
|
<p className="is-ellipsis-overflow is-clickable mb-2" onClick={(e) => setOpen(!open)}>
|
||||||
<Icon name="angle-down" alt={t("changeset.contributors.hideList")} /> {t("changeset.contributors.list")}
|
<Icon name="angle-down" alt={t("changeset.contributors.hideList")} /> {t("changeset.contributors.list")}
|
||||||
</p>
|
</p>
|
||||||
{signatureIcon}
|
{signatureIcon}
|
||||||
@@ -105,7 +117,7 @@ const Contributors: FC<{ changeset: Changeset }> = ({ changeset }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="is-flex is-clickable" onClick={e => setOpen(!open)}>
|
<div className="is-flex is-clickable" onClick={(e) => setOpen(!open)}>
|
||||||
<ContributorColumn className="is-ellipsis-overflow">
|
<ContributorColumn className="is-ellipsis-overflow">
|
||||||
<Icon name="angle-right" alt={t("changeset.contributors.showList")} />{" "}
|
<Icon name="angle-right" alt={t("changeset.contributors.showList")} />{" "}
|
||||||
<ChangesetAuthor changeset={changeset} />
|
<ChangesetAuthor changeset={changeset} />
|
||||||
@@ -150,7 +162,7 @@ const ChangesetDetails: FC<Props> = ({ changeset, repository, fileControlFactory
|
|||||||
name="changeset.description"
|
name="changeset.description"
|
||||||
props={{
|
props={{
|
||||||
changeset,
|
changeset,
|
||||||
value: description.title
|
value: description.title,
|
||||||
}}
|
}}
|
||||||
renderAll={false}
|
renderAll={false}
|
||||||
>
|
>
|
||||||
@@ -210,7 +222,7 @@ const ChangesetDetails: FC<Props> = ({ changeset, repository, fileControlFactory
|
|||||||
name="changeset.description"
|
name="changeset.description"
|
||||||
props={{
|
props={{
|
||||||
changeset,
|
changeset,
|
||||||
value: item
|
value: item,
|
||||||
}}
|
}}
|
||||||
renderAll={false}
|
renderAll={false}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ public class ChangesetDescriptionContributorProvider implements ChangesetPreProc
|
|||||||
private Worker(Changeset changeset) {
|
private Worker(Changeset changeset) {
|
||||||
this.changeset = changeset;
|
this.changeset = changeset;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void process() {
|
private void process() {
|
||||||
try (Scanner scanner = new Scanner(changeset.getDescription())) {
|
try (Scanner scanner = new Scanner(changeset.getDescription())) {
|
||||||
while (scanner.hasNextLine()) {
|
while (scanner.hasNextLine()) {
|
||||||
@@ -76,13 +77,34 @@ public class ChangesetDescriptionContributorProvider implements ChangesetPreProc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkForContributor(String line) {
|
private boolean checkForContributor(String line) {
|
||||||
Optional<Contributor> contributor = Contributor.fromCommitLine(line);
|
Optional<Contributor> optionalContributor = Contributor.fromCommitLine(line);
|
||||||
if (contributor.isPresent()) {
|
if (optionalContributor.isPresent()) {
|
||||||
changeset.addContributor(contributor.get());
|
Contributor contributor = optionalContributor.get();
|
||||||
|
if (acceptContributor(contributor)) {
|
||||||
|
changeset.addContributor(contributor);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
} else{
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean acceptContributor(Contributor contributor) {
|
||||||
|
if (isCoAuthorEqualToAuthor(contributor)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
changeset.getContributors().stream()
|
||||||
|
.noneMatch(c ->
|
||||||
|
c.getType().equals(contributor.getType())
|
||||||
|
&& c.getPerson().getMail().equals(contributor.getPerson().getMail()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCoAuthorEqualToAuthor(Contributor contributor) {
|
||||||
|
return contributor.getType().equals(Contributor.CO_AUTHORED_BY)
|
||||||
|
&& contributor.getPerson().getMail().equals(changeset.getAuthor().getMail());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleEmptyLine(Scanner scanner, String line) {
|
private void handleEmptyLine(Scanner scanner, String line) {
|
||||||
|
|||||||
@@ -53,15 +53,28 @@ class ChangesetDescriptionContributorProviderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldConvertTrailerWithCoAuthors() {
|
void shouldConvertTrailerWithCoAuthors() {
|
||||||
Person person = createPerson("Arthur Dent", "dent@hitchhiker.org");
|
Person person = createPerson("Tricia McMillan", "trillian@hitchhiker.org");
|
||||||
Changeset changeset = createChangeset("zaphod beeblebrox\n\nCo-authored-by: Arthur Dent <dent@hitchhiker.org>");
|
Changeset changeset = createChangeset("zaphod beeblebrox\n\nCo-authored-by: Arthur Dent <dent@hitchhiker.org>");
|
||||||
|
changeset.setAuthor(person);
|
||||||
changesetDescriptionContributors.createPreProcessor(REPOSITORY).process(changeset);
|
changesetDescriptionContributors.createPreProcessor(REPOSITORY).process(changeset);
|
||||||
Collection<Contributor> contributors = changeset.getContributors();
|
Collection<Contributor> contributors = changeset.getContributors();
|
||||||
|
|
||||||
Contributor contributor = contributors.iterator().next();
|
Contributor contributor = contributors.iterator().next();
|
||||||
assertThat(contributor.getType()).isEqualTo("Co-authored-by");
|
assertThat(contributor.getType()).isEqualTo("Co-authored-by");
|
||||||
assertThat(contributor.getPerson()).isEqualTo(person);
|
assertThat(contributor.getPerson()).isEqualTo(createPerson("Arthur Dent", "dent@hitchhiker.org"));
|
||||||
|
assertThat(changeset.getDescription()).isEqualTo("zaphod beeblebrox\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldIgnoreCoAuthorIfEqualToAuthor() {
|
||||||
|
Person person = createPerson("Arthur Dent", "dent@hitchhiker.org");
|
||||||
|
Changeset changeset = createChangeset("zaphod beeblebrox\n\nCo-authored-by: Arthur Dent <dent@hitchhiker.org>");
|
||||||
|
changeset.setAuthor(person);
|
||||||
|
|
||||||
|
changesetDescriptionContributors.createPreProcessor(REPOSITORY).process(changeset);
|
||||||
|
Collection<Contributor> contributors = changeset.getContributors();
|
||||||
|
|
||||||
|
assertThat(contributors).isEmpty();
|
||||||
assertThat(changeset.getDescription()).isEqualTo("zaphod beeblebrox\n\n");
|
assertThat(changeset.getDescription()).isEqualTo("zaphod beeblebrox\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +123,21 @@ class ChangesetDescriptionContributorProviderTest {
|
|||||||
assertThat(changeset.getDescription()).isEqualTo("zaphod beeblebrox\n\n");
|
assertThat(changeset.getDescription()).isEqualTo("zaphod beeblebrox\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldIgnoreDuplicateContributorWithSameTypeAndMail() {
|
||||||
|
Person person = createPerson("Tricia McMillan", "trillian@hitchhiker.org");
|
||||||
|
Changeset changeset = createChangeset("zaphod beeblebrox\n\nCommitted-by: Tricia McMillan <trillian@hitchhiker.org>\nCommitted-by: Tricia McMillan <trillian@hitchhiker.org>");
|
||||||
|
|
||||||
|
changesetDescriptionContributors.createPreProcessor(REPOSITORY).process(changeset);
|
||||||
|
Collection<Contributor> contributors = changeset.getContributors();
|
||||||
|
|
||||||
|
Contributor contributor = contributors.iterator().next();
|
||||||
|
|
||||||
|
assertThat(contributor.getType()).isEqualTo("Committed-by");
|
||||||
|
assertThat(contributor.getPerson()).isEqualTo(person);
|
||||||
|
assertThat(changeset.getDescription()).isEqualTo("zaphod beeblebrox\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldConvertMixedTrailers() {
|
void shouldConvertMixedTrailers() {
|
||||||
Changeset changeset = createChangeset("zaphod beeblebrox\n\n" +
|
Changeset changeset = createChangeset("zaphod beeblebrox\n\n" +
|
||||||
|
|||||||
Reference in New Issue
Block a user