diff --git a/gradle/changelog/initial_empty_mirror.yaml b/gradle/changelog/initial_empty_mirror.yaml new file mode 100644 index 0000000000..ad060c43f0 --- /dev/null +++ b/gradle/changelog/initial_empty_mirror.yaml @@ -0,0 +1,2 @@ +- type: fixed + descripion: Initial mirror with no accepted branch ([#1842](https://github.com/scm-manager/scm-manager/pull/1842)) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMirrorCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMirrorCommand.java index 0575c0b1f6..ae513fc923 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMirrorCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMirrorCommand.java @@ -198,12 +198,18 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman if (fetchResult.getTrackingRefUpdates().isEmpty()) { LOG.trace("No updates found for mirror repository {}", repository); mirrorLog.add("No updates found"); + return new MirrorCommandResult(result, mirrorLog, stopwatch.stop().elapsed()); } else { handleBranches(); handleTags(); } - defaultBranchSelector.newDefault().ifPresent(this::setNewDefaultBranch); + if (!defaultBranchSelector.isChanged()) { + mirrorLog.add("No effective changes detected"); + return new MirrorCommandResult(result, mirrorLog, stopwatch.stop().elapsed()); + } + + defaultBranchSelector.newDefaultBranch().ifPresent(this::setNewDefaultBranch); String[] pushRefSpecs = generatePushRefSpecs().toArray(new String[0]); push(pushRefSpecs); @@ -272,7 +278,7 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman } private void handleBranch(LoggerWithHeader logger, TrackingRefUpdate ref) { - MirrorReferenceUpdateHandler refHandler = new MirrorReferenceUpdateHandler(logger, ref, "heads/", "branch"); + MirrorReferenceUpdateHandler refHandler = new MirrorReferenceUpdateHandler(logger, ref, RefType.BRANCH); refHandler.handleRef(ref1 -> refHandler.testFilterForBranch()); } @@ -282,28 +288,26 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman } private void handleTag(LoggerWithHeader logger, TrackingRefUpdate ref) { - MirrorReferenceUpdateHandler refHandler = new MirrorReferenceUpdateHandler(logger, ref, "tags/", "tag"); + MirrorReferenceUpdateHandler refHandler = new MirrorReferenceUpdateHandler(logger, ref, RefType.TAG); refHandler.handleRef(ref1 -> refHandler.testFilterForTag()); } private class MirrorReferenceUpdateHandler { private final LoggerWithHeader logger; private final TrackingRefUpdate ref; - private final String refType; - private final String typeForLog; + private final RefType refType; - public MirrorReferenceUpdateHandler(LoggerWithHeader logger, TrackingRefUpdate ref, String refType, String typeForLog) { + public MirrorReferenceUpdateHandler(LoggerWithHeader logger, TrackingRefUpdate ref, RefType refType) { this.logger = logger; this.ref = ref; this.refType = refType; - this.typeForLog = typeForLog; } private void handleRef(Function filter) { LOG.trace("Handling {}", ref.getLocalName()); Result filterResult = filter.apply(ref); try { - String referenceName = ref.getLocalName().substring("refs/".length() + refType.length()); + String referenceName = ref.getLocalName().substring("refs/".length() + refType.refPath.length()); if (filterResult.isAccepted()) { LOG.trace("Accepted ref {}", ref.getLocalName()); handleAcceptedReference(referenceName); @@ -332,7 +336,7 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman private void handleRejectedRef(String referenceName, Result filterResult) throws IOException { result = REJECTED_UPDATES; - LOG.trace("{} ref rejected in {}: {}", typeForLog, GitMirrorCommand.this.repository, ref.getLocalName()); + LOG.trace("{} ref rejected in {}: {}", refType.typeForLog, GitMirrorCommand.this.repository, ref.getLocalName()); if (ref.getResult() == NEW) { deleteReference(ref.getLocalName()); } else { @@ -342,16 +346,16 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman } private void handleAcceptedReference(String referenceName) throws IOException { - String targetRef = "refs/" + refType + referenceName; + String targetRef = "refs/" + refType.refPath + referenceName; if (isDeletedReference(ref)) { - LOG.trace("deleting {} ref in {}: {}", typeForLog, GitMirrorCommand.this.repository, targetRef); - defaultBranchSelector.deleted(referenceName); + LOG.trace("deleting {} ref in {}: {}", refType.typeForLog, GitMirrorCommand.this.repository, targetRef); + defaultBranchSelector.deleted(refType, referenceName); logger.logChange(ref, referenceName, "deleted"); deleteReference(targetRef); deletedRefs.add(targetRef); } else { - LOG.trace("updating {} ref in {}: {}", typeForLog, GitMirrorCommand.this.repository, targetRef); - defaultBranchSelector.accepted(referenceName); + LOG.trace("updating {} ref in {}: {}", refType.typeForLog, GitMirrorCommand.this.repository, targetRef); + defaultBranchSelector.accepted(refType, referenceName); logger.logChange(ref, referenceName, getUpdateType(ref)); } } @@ -665,6 +669,8 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman private final Set remainingBranches; private final Set newBranches = new HashSet<>(); + private boolean changed = false; + DefaultBranchSelector(String initialDefaultBranch, Collection initialBranches) { this.initialDefaultBranch = initialBranches.isEmpty() ? null : initialDefaultBranch; this.initialBranches = new HashSet<>(initialBranches); @@ -706,25 +712,50 @@ public class GitMirrorCommand extends AbstractGitCommand implements MirrorComman } } - public void accepted(String branch) { - newBranches.add(branch); + public void accepted(RefType refType, String ref) { + changed = true; + if (refType == RefType.BRANCH) { + newBranches.add(ref); + } } - public void deleted(String branch) { - remainingBranches.remove(branch); + public void deleted(RefType refType, String ref) { + changed = true; + if (refType == RefType.BRANCH) { + remainingBranches.remove(ref); + } } - public Optional newDefault() { + public boolean isChanged() { + return changed; + } + + public Optional newDefaultBranch() { if (initialDefaultBranch == null && newBranches.contains("master") || remainingBranches.contains(initialDefaultBranch)) { return empty(); } else if (!newBranches.isEmpty() && initialBranches.isEmpty()) { return of(newBranches.iterator().next()); + } else if (initialDefaultBranch == null && newBranches.isEmpty()) { + LOG.info("Could not mirror repository with tags only."); + throw new IllegalStateException("No branch could be mirrored. Initial mirror without accepted branch is not supported. Make sure that at least one branch can be mirrored."); } else if (remainingBranches.isEmpty()) { - LOG.warn("Could not compute new default branch."); + LOG.info("Could not compute new default branch."); throw new IllegalStateException("Deleting all existing branches is not supported. Please restore branch '" + initialDefaultBranch + "' or recreate the mirror."); } else { return of(remainingBranches.iterator().next()); } } } + + enum RefType { + BRANCH("heads/", "branch"), TAG("tags/", "tag"); + + private final String refPath; + private final String typeForLog; + + RefType(String refPath, String typeForLog) { + this.refPath = refPath; + this.typeForLog = typeForLog; + } + } } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMirrorCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMirrorCommandTest.java index 0e5fa6977a..6668666240 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMirrorCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMirrorCommandTest.java @@ -82,6 +82,7 @@ import static org.mockito.Mockito.when; import static sonia.scm.repository.api.MirrorCommandResult.ResultType.FAILED; import static sonia.scm.repository.api.MirrorCommandResult.ResultType.OK; import static sonia.scm.repository.api.MirrorCommandResult.ResultType.REJECTED_UPDATES; +import static sonia.scm.repository.spi.GitMirrorCommand.RefType.BRANCH; public class GitMirrorCommandTest extends AbstractGitCommandTestBase { @@ -164,6 +165,58 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { } } + @Test + public void shouldAcceptEmptyInitialMirror() throws IOException, GitAPIException { + MirrorCommandResult result = callMirrorCommand(repositoryDirectory.getAbsolutePath(), c -> { + c.setFilter(new MirrorFilter() { + @Override + public Filter getFilter(FilterContext context) { + return new Filter() { + @Override + public Result acceptBranch(BranchUpdate branch) { + return Result.reject("nothing accepted"); + } + + @Override + public Result acceptTag(TagUpdate tag) { + return Result.reject("nothing accepted"); + } + }; + } + }); + }); + + assertThat(result.getResult()).isEqualTo(REJECTED_UPDATES); + assertThat(result.getLog()).contains("Branches:") + .contains("- 000000000..fcd0ef183 master (nothing accepted)") + .contains("- 000000000..3f76a12f0 test-branch (nothing accepted)") + .contains("Tags:") + .contains("- 000000000..86a6645ec test-tag (nothing accepted)"); + + try (Git createdMirror = Git.open(clone)) { + assertThat(createdMirror.branchList().call()).isEmpty(); + assertThat(createdMirror.tagList().call()).isEmpty(); + } + } + + @Test + public void shouldAcceptOnlyTagInInitialMirror() { + assertThrows(IllegalStateException.class, () -> + callMirrorCommand(repositoryDirectory.getAbsolutePath(), c -> { + c.setFilter(new MirrorFilter() { + @Override + public Filter getFilter(FilterContext context) { + return new Filter() { + @Override + public Result acceptBranch(BranchUpdate branch) { + return Result.reject("nothing accepted"); + } + }; + } + }); + })); + } + @Test public void shouldFilterMasterBranchWhenFilteredOnInitialMirror() throws IOException, GitAPIException { MirrorCommandResult result = callMirrorCommand(repositoryDirectory.getAbsolutePath(), c -> { @@ -356,7 +409,8 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { assertThat(result.getResult()).isEqualTo(REJECTED_UPDATES); assertThat(result.getLog()).containsExactly( "Branches:", - "- 000000000..fcd0ef183 added-branch (rejected due to filter)" + "- 000000000..fcd0ef183 added-branch (rejected due to filter)", + "No effective changes detected" ); try (Git updatedMirror = Git.open(clone)) { @@ -378,7 +432,8 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { assertThat(result.getResult()).isEqualTo(REJECTED_UPDATES); assertThat(result.getLog()).containsExactly( "Branches:", - "- 3f76a12f0..9e93d8631 test-branch (rejected due to filter)" + "- 3f76a12f0..9e93d8631 test-branch (rejected due to filter)", + "No effective changes detected" ); try (Git updatedMirror = Git.open(clone)) { @@ -400,7 +455,8 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { assertThat(result.getResult()).isEqualTo(REJECTED_UPDATES); assertThat(result.getLog()).containsExactly( "Branches:", - "- 3f76a12f0..000000000 test-branch (rejected due to filter)" + "- 3f76a12f0..000000000 test-branch (rejected due to filter)", + "No effective changes detected" ); try (Git updatedMirror = Git.open(clone)) { @@ -423,7 +479,8 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { assertThat(result.getResult()).isEqualTo(REJECTED_UPDATES); assertThat(result.getLog()).containsExactly( "Tags:", - "- 000000000..9e93d8631 added-tag (rejected due to filter)" + "- 000000000..9e93d8631 added-tag (rejected due to filter)", + "No effective changes detected" ); try (Git updatedMirror = Git.open(clone)) { @@ -446,7 +503,8 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { assertThat(result.getResult()).isEqualTo(REJECTED_UPDATES); assertThat(result.getLog()).containsExactly( "Tags:", - "- 86a6645ec..9e93d8631 test-tag (rejected due to filter)" + "- 86a6645ec..9e93d8631 test-tag (rejected due to filter)", + "No effective changes detected" ); try (Git updatedMirror = Git.open(clone)) { @@ -468,7 +526,8 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { assertThat(result.getResult()).isEqualTo(REJECTED_UPDATES); assertThat(result.getLog()).containsExactly( "Tags:", - "- 86a6645ec..000000000 test-tag (rejected due to filter)" + "- 86a6645ec..000000000 test-tag (rejected due to filter)", + "No effective changes detected" ); try (Git updatedMirror = Git.open(clone)) { @@ -490,7 +549,8 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { assertThat(result.getResult()).isEqualTo(REJECTED_UPDATES); assertThat(result.getLog()).containsExactly( "Tags:", - "- 86a6645ec..000000000 test-tag (thou shalt not pass)" + "- 86a6645ec..000000000 test-tag (thou shalt not pass)", + "No effective changes detected" ); } @@ -508,7 +568,8 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { assertThat(result.getLog()).containsExactly( "! got error checking filter for update: this tag creates an exception", "Tags:", - "- 86a6645ec..000000000 test-tag (exception in filter)" + "- 86a6645ec..000000000 test-tag (exception in filter)", + "No effective changes detected" ); } @@ -777,10 +838,10 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { GitMirrorCommand.DefaultBranchSelector selector = new GitMirrorCommand.DefaultBranchSelector("master", emptyList()); - selector.accepted("master"); - selector.accepted("something"); + selector.accepted(BRANCH, "master"); + selector.accepted(BRANCH, "something"); - assertThat(selector.newDefault()).isEmpty(); + assertThat(selector.newDefaultBranch()).isEmpty(); } @Test @@ -788,10 +849,10 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { GitMirrorCommand.DefaultBranchSelector selector = new GitMirrorCommand.DefaultBranchSelector("master", BRANCHES); - selector.accepted("new"); - selector.deleted("two"); + selector.accepted(BRANCH, "new"); + selector.deleted(BRANCH, "two"); - assertThat(selector.newDefault()).isEmpty(); + assertThat(selector.newDefaultBranch()).isEmpty(); } @Test @@ -799,9 +860,9 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { GitMirrorCommand.DefaultBranchSelector selector = new GitMirrorCommand.DefaultBranchSelector("master", BRANCHES); - selector.deleted("master"); + selector.deleted(BRANCH, "master"); - assertThat(selector.newDefault()).get().isIn("one", "two", "three"); + assertThat(selector.newDefaultBranch()).get().isIn("one", "two", "three"); } @Test @@ -809,11 +870,11 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { GitMirrorCommand.DefaultBranchSelector selector = new GitMirrorCommand.DefaultBranchSelector("master", BRANCHES); - selector.deleted("master"); - selector.deleted("one"); - selector.deleted("three"); + selector.deleted(BRANCH, "master"); + selector.deleted(BRANCH, "one"); + selector.deleted(BRANCH, "three"); - assertThat(selector.newDefault()).get().isEqualTo("two"); + assertThat(selector.newDefaultBranch()).get().isEqualTo("two"); } @Test @@ -821,11 +882,11 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { GitMirrorCommand.DefaultBranchSelector selector = new GitMirrorCommand.DefaultBranchSelector("master", BRANCHES); - selector.deleted("master"); - selector.deleted("one"); - selector.deleted("three"); + selector.deleted(BRANCH, "master"); + selector.deleted(BRANCH, "one"); + selector.deleted(BRANCH, "three"); - assertThat(selector.newDefault()).get().isEqualTo("two"); + assertThat(selector.newDefaultBranch()).get().isEqualTo("two"); } @Test @@ -833,13 +894,13 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { GitMirrorCommand.DefaultBranchSelector selector = new GitMirrorCommand.DefaultBranchSelector("master", BRANCHES); - selector.deleted("master"); - selector.deleted("one"); - selector.deleted("two"); - selector.accepted("new"); - selector.deleted("three"); + selector.deleted(BRANCH, "master"); + selector.deleted(BRANCH, "one"); + selector.deleted(BRANCH, "two"); + selector.accepted(BRANCH, "new"); + selector.deleted(BRANCH, "three"); - assertThrows(IllegalStateException.class, selector::newDefault); + assertThrows(IllegalStateException.class, selector::newDefaultBranch); } @Test @@ -847,10 +908,10 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { GitMirrorCommand.DefaultBranchSelector selector = new GitMirrorCommand.DefaultBranchSelector("master", emptyList()); - selector.accepted("main"); - selector.deleted("master"); + selector.accepted(BRANCH, "main"); + selector.deleted(BRANCH, "master"); - assertThat(selector.newDefault()).get().isEqualTo("main"); + assertThat(selector.newDefaultBranch()).get().isEqualTo("main"); } @Test @@ -858,9 +919,9 @@ public class GitMirrorCommandTest extends AbstractGitCommandTestBase { GitMirrorCommand.DefaultBranchSelector selector = new GitMirrorCommand.DefaultBranchSelector("master", emptyList()); - selector.accepted("main"); + selector.accepted(BRANCH, "main"); - assertThat(selector.newDefault()).get().isEqualTo("main"); + assertThat(selector.newDefaultBranch()).get().isEqualTo("main"); } }