Fix initial mirror sync if empty (#1842)

Fixes an error, when the initial synchronization of a git mirror had no branches.
This commit is contained in:
René Pfeuffer
2021-11-03 10:51:06 +01:00
committed by GitHub
parent b896df5046
commit cab09680f8
3 changed files with 149 additions and 55 deletions

View File

@@ -0,0 +1,2 @@
- type: fixed
descripion: Initial mirror with no accepted branch ([#1842](https://github.com/scm-manager/scm-manager/pull/1842))

View File

@@ -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<TrackingRefUpdate, Result> 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<String> remainingBranches;
private final Set<String> newBranches = new HashSet<>();
private boolean changed = false;
DefaultBranchSelector(String initialDefaultBranch, Collection<String> 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<String> newDefault() {
public boolean isChanged() {
return changed;
}
public Optional<String> 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;
}
}
}

View File

@@ -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");
}
}