Add parameter to check current revision to prevent conflicts

This commit is contained in:
Rene Pfeuffer
2019-09-03 10:36:23 +02:00
parent 695457e949
commit 379c58d3a9
5 changed files with 48 additions and 11 deletions

View File

@@ -57,15 +57,6 @@ public class ModifyCommandBuilder {
this.workdir = workdirProvider.createNewWorkdir(); this.workdir = workdirProvider.createNewWorkdir();
} }
/**
* Set the branch that should be modified. The new commit will be made for this branch.
* @param branchToModify The branch to modify.
* @return This builder instance.
*/
public ModifyCommandBuilder setBranchToModify(String branchToModify) {
return this;
}
/** /**
* Create a new file. The content of the file will be specified in a subsequent call to * Create a new file. The content of the file will be specified in a subsequent call to
* {@link ContentLoader#withData(ByteSource)} or {@link ContentLoader#withData(InputStream)}. * {@link ContentLoader#withData(ByteSource)} or {@link ContentLoader#withData(InputStream)}.
@@ -158,6 +149,17 @@ public class ModifyCommandBuilder {
return this; return this;
} }
/**
* Set the expected revision of the branch, before the changes are applied. If the branch does not have the
* expected revision, a concurrent modification exception will be thrown when the command is executed and no
* changes will be applied.
* @return This builder instance.
*/
public ModifyCommandBuilder setExpectedRevision(String expectedRevision) {
request.setExpectedRevision(expectedRevision);
return this;
}
public interface ContentLoader { public interface ContentLoader {
/** /**
* Specify the data of the file using a {@link ByteSource}. * Specify the data of the file using a {@link ByteSource}.

View File

@@ -22,6 +22,7 @@ public class ModifyCommandRequest implements Resetable, Validateable {
private Person author; private Person author;
private String commitMessage; private String commitMessage;
private String branch; private String branch;
private String expectedRevision;
@Override @Override
public void reset() { public void reset() {
@@ -63,11 +64,19 @@ public class ModifyCommandRequest implements Resetable, Validateable {
return branch; return branch;
} }
public String getExpectedRevision() {
return expectedRevision;
}
@Override @Override
public boolean isValid() { public boolean isValid() {
return StringUtils.isNotEmpty(commitMessage) && !requests.isEmpty(); return StringUtils.isNotEmpty(commitMessage) && !requests.isEmpty();
} }
public void setExpectedRevision(String expectedRevision) {
this.expectedRevision = expectedRevision;
}
public interface PartialRequest { public interface PartialRequest {
void execute(ModifyCommand.Worker worker) throws IOException; void execute(ModifyCommand.Worker worker) throws IOException;
} }

View File

@@ -240,6 +240,10 @@ class AbstractGitCommand
logger.debug("pushed changes"); logger.debug("pushed changes");
} }
Ref getCurrentRevision() throws IOException {
return getClone().getRepository().getRefDatabase().findRef("HEAD");
}
private Person determineAuthor(Person author) { private Person determineAuthor(Person author) {
if (author == null) { if (author == null) {
Subject subject = SecurityUtils.getSubject(); Subject subject = SecurityUtils.getSubject();

View File

@@ -5,6 +5,7 @@ import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import sonia.scm.BadRequestException; import sonia.scm.BadRequestException;
import sonia.scm.ConcurrentModificationException;
import sonia.scm.ContextEntry; import sonia.scm.ContextEntry;
import sonia.scm.repository.GitWorkdirFactory; import sonia.scm.repository.GitWorkdirFactory;
import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.InternalRepositoryException;
@@ -51,6 +52,11 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman
if (!StringUtils.isEmpty(request.getBranch())) { if (!StringUtils.isEmpty(request.getBranch())) {
checkOutBranch(request.getBranch()); checkOutBranch(request.getBranch());
} }
if (!StringUtils.isEmpty(request.getExpectedRevision())) {
if (!request.getExpectedRevision().equals(getCurrentRevision().getName())) {
throw new ConcurrentModificationException("branch", request.getBranch() == null? "default": request.getBranch());
}
}
for (ModifyCommandRequest.PartialRequest r : request.getRequests()) { for (ModifyCommandRequest.PartialRequest r : request.getRequests()) {
r.execute(this); r.execute(this);
} }

View File

@@ -15,6 +15,7 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
import sonia.scm.AlreadyExistsException; import sonia.scm.AlreadyExistsException;
import sonia.scm.BadRequestException; import sonia.scm.BadRequestException;
import sonia.scm.ConcurrentModificationException;
import sonia.scm.repository.Person; import sonia.scm.repository.Person;
import sonia.scm.repository.util.WorkdirProvider; import sonia.scm.repository.util.WorkdirProvider;
@@ -56,7 +57,7 @@ public class GitModifyCommandTest extends AbstractGitCommandTestBase {
} }
@Test @Test
public void shouldCreateCommitOnSelectedBranch() throws IOException, GitAPIException { public void shouldCreateCommitOnSelectedBranch() throws IOException {
File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile();
GitModifyCommand command = createCommand(); GitModifyCommand command = createCommand();
@@ -95,7 +96,7 @@ public class GitModifyCommandTest extends AbstractGitCommandTestBase {
} }
@Test(expected = AlreadyExistsException.class) @Test(expected = AlreadyExistsException.class)
public void shouldFailIfOverwritingExistingFileWithoutOverwriteFlag() throws IOException, GitAPIException { public void shouldFailIfOverwritingExistingFileWithoutOverwriteFlag() throws IOException {
File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile();
GitModifyCommand command = createCommand(); GitModifyCommand command = createCommand();
@@ -140,6 +141,21 @@ public class GitModifyCommandTest extends AbstractGitCommandTestBase {
command.execute(request); command.execute(request);
} }
@Test(expected = ConcurrentModificationException.class)
public void shouldFailBranchDoesNotHaveExpectedRevision() throws IOException {
File newFile = Files.write(temporaryFolder.newFile().toPath(), "irrelevant\n".getBytes()).toFile();
GitModifyCommand command = createCommand();
ModifyCommandRequest request = new ModifyCommandRequest();
request.setCommitMessage("test commit");
request.addRequest(new ModifyCommandRequest.CreateFileRequest("irrelevant", newFile, true));
request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det"));
request.setExpectedRevision("abc");
command.execute(request);
}
private void assertInTree(TreeAssertions assertions) throws IOException, GitAPIException { private void assertInTree(TreeAssertions assertions) throws IOException, GitAPIException {
try (Git git = new Git(createContext().open())) { try (Git git = new Git(createContext().open())) {
RevCommit lastCommit = getLastCommit(git); RevCommit lastCommit = getLastCommit(git);