implement modification api in git, svn and hg. implement the endpoint

This commit is contained in:
Mohamed Karray
2018-09-13 11:57:10 +02:00
parent 6b6b558823
commit 4697c55f96
52 changed files with 1231 additions and 426 deletions

View File

@@ -37,32 +37,25 @@ package sonia.scm.repository;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
//~--- JDK imports ------------------------------------------------------------
/**
*
* @author Sebastian Sdorra
@@ -224,13 +217,6 @@ public class GitChangesetConverter implements Closeable
changeset.setParents(parentList);
}
Modifications modifications = createModifications(treeWalk, commit);
if (modifications != null)
{
changeset.setModifications(modifications);
}
Collection<String> tagCollection = tags.get(commit.getId());
if (Util.isNotEmpty(tagCollection))
@@ -245,108 +231,7 @@ public class GitChangesetConverter implements Closeable
return changeset;
}
/**
* TODO: copy and rename
*
*
* @param modifications
* @param entry
*/
private void appendModification(Modifications modifications, DiffEntry entry)
{
switch (entry.getChangeType())
{
case ADD :
modifications.getAdded().add(entry.getNewPath());
break;
case MODIFY :
modifications.getModified().add(entry.getNewPath());
break;
case DELETE :
modifications.getRemoved().add(entry.getOldPath());
break;
}
}
/**
* Method description
*
*
* @param treeWalk
* @param commit
*
* @return
*
* @throws IOException
*/
private Modifications createModifications(TreeWalk treeWalk, RevCommit commit)
throws IOException
{
Modifications modifications = null;
treeWalk.reset();
treeWalk.setRecursive(true);
if (commit.getParentCount() > 0)
{
RevCommit parent = commit.getParent(0);
RevTree tree = parent.getTree();
if ((tree == null) && (revWalk != null))
{
revWalk.parseHeaders(parent);
tree = parent.getTree();
}
if (tree != null)
{
treeWalk.addTree(tree);
}
else
{
if (logger.isTraceEnabled())
{
logger.trace("no parent tree at position 0 for commit {}",
commit.getName());
}
treeWalk.addTree(new EmptyTreeIterator());
}
}
else
{
if (logger.isTraceEnabled())
{
logger.trace("no parent available for commit {}", commit.getName());
}
treeWalk.addTree(new EmptyTreeIterator());
}
treeWalk.addTree(commit.getTree());
List<DiffEntry> entries = DiffEntry.scan(treeWalk);
for (DiffEntry e : entries)
{
if (!e.getOldId().equals(e.getNewId()))
{
if (modifications == null)
{
modifications = new Modifications();
}
appendModification(modifications, e);
}
}
return modifications;
}
//~--- fields ---------------------------------------------------------------

View File

@@ -0,0 +1,106 @@
package sonia.scm.repository.spi;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;
import sonia.scm.repository.GitUtil;
import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.Modifications;
import sonia.scm.repository.Repository;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.List;
@Slf4j
public class GitModificationsCommand extends AbstractGitCommand implements ModificationsCommand {
protected GitModificationsCommand(GitContext context, Repository repository) {
super(context, repository);
}
private Modifications createModifications(TreeWalk treeWalk, RevCommit commit, RevWalk revWalk, String revision)
throws IOException, UnsupportedModificationTypeException {
treeWalk.reset();
treeWalk.setRecursive(true);
if (commit.getParentCount() > 0) {
RevCommit parent = commit.getParent(0);
RevTree tree = parent.getTree();
if ((tree == null) && (revWalk != null)) {
revWalk.parseHeaders(parent);
tree = parent.getTree();
}
if (tree != null) {
treeWalk.addTree(tree);
} else {
log.trace("no parent tree at position 0 for commit {}", commit.getName());
treeWalk.addTree(new EmptyTreeIterator());
}
} else {
log.trace("no parent available for commit {}", commit.getName());
treeWalk.addTree(new EmptyTreeIterator());
}
treeWalk.addTree(commit.getTree());
List<DiffEntry> entries = DiffEntry.scan(treeWalk);
Modifications modifications = new Modifications();
for (DiffEntry e : entries) {
if (!e.getOldId().equals(e.getNewId())) {
appendModification(modifications, e);
}
}
modifications.setRevision(revision);
return modifications;
}
@Override
public Modifications getModifications(String revision) {
org.eclipse.jgit.lib.Repository gitRepository = null;
RevWalk revWalk = null;
try {
gitRepository = open();
if (!gitRepository.getAllRefs().isEmpty()) {
revWalk = new RevWalk(gitRepository);
ObjectId id = GitUtil.getRevisionId(gitRepository, revision);
RevCommit commit = revWalk.parseCommit(id);
TreeWalk treeWalk = new TreeWalk(gitRepository);
return createModifications(treeWalk, commit, revWalk, revision);
}
} catch (IOException ex) {
log.error("could not open repository", ex);
throw new InternalRepositoryException(ex);
} catch (UnsupportedModificationTypeException ex) {
log.error("Unsupported modification type", ex);
throw new InternalRepositoryException(ex);
} finally {
GitUtil.release(revWalk);
GitUtil.close(gitRepository);
}
return null;
}
@Override
public Modifications getModifications(ModificationsCommandRequest request) {
return getModifications(request.getRevision());
}
private void appendModification(Modifications modifications, DiffEntry entry) throws UnsupportedModificationTypeException {
DiffEntry.ChangeType type = entry.getChangeType();
if (type == DiffEntry.ChangeType.ADD) {
modifications.getAdded().add(entry.getNewPath());
} else if (type == DiffEntry.ChangeType.MODIFY) {
modifications.getModified().add(entry.getNewPath());
} else if (type == DiffEntry.ChangeType.DELETE) {
modifications.getRemoved().add(entry.getOldPath());
} else {
throw new UnsupportedModificationTypeException(MessageFormat.format("The modification type: {0} is not supported.", type));
}
}
}

View File

@@ -36,17 +36,15 @@ package sonia.scm.repository.spi;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.collect.ImmutableSet;
import sonia.scm.repository.GitRepositoryHandler;
import sonia.scm.repository.Repository;
import sonia.scm.repository.api.Command;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.Set;
//~--- JDK imports ------------------------------------------------------------
/**
*
* @author Sebastian Sdorra
@@ -188,6 +186,11 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
return new GitLogCommand(context, repository);
}
@Override
public ModificationsCommand getModificationsCommand() {
return new GitModificationsCommand(context,repository);
}
/**
* Method description
*

View File

@@ -0,0 +1,9 @@
package sonia.scm.repository.spi;
import sonia.scm.repository.InternalRepositoryException;
public class UnsupportedModificationTypeException extends InternalRepositoryException {
public UnsupportedModificationTypeException(String message) {
super(message);
}
}

View File

@@ -85,14 +85,14 @@ public class AbstractRemoteCommandTestBase
outgoingDirectory = tempFolder.newFile("outgoing");
outgoingDirectory.delete();
incomgingRepository = new Repository("1", "git", "space", "incoming");
incomingRepository = new Repository("1", "git", "space", "incoming");
outgoingRepository = new Repository("2", "git", "space", "outgoing");
incoming = Git.init().setDirectory(incomingDirectory).setBare(false).call();
outgoing = Git.init().setDirectory(outgoingDirectory).setBare(false).call();
handler = mock(GitRepositoryHandler.class);
when(handler.getDirectory(incomgingRepository)).thenReturn(
when(handler.getDirectory(incomingRepository)).thenReturn(
incomingDirectory);
when(handler.getDirectory(outgoingRepository)).thenReturn(
outgoingDirectory);
@@ -211,7 +211,7 @@ public class AbstractRemoteCommandTestBase
protected GitRepositoryHandler handler;
/** Field description */
protected Repository incomgingRepository;
protected Repository incomingRepository;
/** Field description */
protected Git incoming;

View File

@@ -105,7 +105,7 @@ public class GitIncomingCommandTest
commit(outgoing, "added a");
GitPullCommand pull = new GitPullCommand(handler, new GitContext(incomingDirectory), incomgingRepository);
GitPullCommand pull = new GitPullCommand(handler, new GitContext(incomingDirectory), incomingRepository);
PullCommandRequest req = new PullCommandRequest();
req.setRemoteRepository(outgoingRepository);
pull.pull(req);
@@ -192,6 +192,6 @@ public class GitIncomingCommandTest
private GitIncomingCommand createCommand()
{
return new GitIncomingCommand(handler, new GitContext(incomingDirectory),
incomgingRepository);
incomingRepository);
}
}

View File

@@ -168,21 +168,23 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase
Changeset c = command.getChangeset("435df2f061add3589cb3");
assertNotNull(c);
assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", c.getId());
String revision = "435df2f061add3589cb326cc64be9b9c3897ceca";
assertEquals(revision, c.getId());
assertEquals("added a and b files", c.getDescription());
checkDate(c.getDate());
assertEquals("Douglas Adams", c.getAuthor().getName());
assertEquals("douglas.adams@hitchhiker.com", c.getAuthor().getMail());
assertEquals("added a and b files", c.getDescription());
Modifications mods = c.getModifications();
GitModificationsCommand gitModificationsCommand = new GitModificationsCommand(createContext(), repository);
Modifications modifications = gitModificationsCommand.getModifications(revision);
assertNotNull(mods);
assertTrue("modified list should be empty", mods.getModified().isEmpty());
assertTrue("removed list should be empty", mods.getRemoved().isEmpty());
assertFalse("added list should not be empty", mods.getAdded().isEmpty());
assertEquals(2, mods.getAdded().size());
assertThat(mods.getAdded(), contains("a.txt", "b.txt"));
assertNotNull(modifications);
assertTrue("modified list should be empty", modifications.getModified().isEmpty());
assertTrue("removed list should be empty", modifications.getRemoved().isEmpty());
assertFalse("added list should not be empty", modifications.getAdded().isEmpty());
assertEquals(2, modifications.getAdded().size());
assertThat(modifications.getAdded(), contains("a.txt", "b.txt"));
}
@Test

View File

@@ -0,0 +1,126 @@
package sonia.scm.repository.spi;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
import org.junit.Test;
import sonia.scm.repository.Modifications;
import java.io.File;
import java.io.IOException;
import java.util.function.Consumer;
import static org.assertj.core.api.Java6Assertions.assertThat;
public class GitModificationsCommandTest extends AbstractRemoteCommandTestBase {
private GitModificationsCommand incomingModificationsCommand;
private GitModificationsCommand outgoingModificationsCommand;
@Before
public void init() {
incomingModificationsCommand = new GitModificationsCommand(new GitContext(incomingDirectory), incomingRepository);
outgoingModificationsCommand = new GitModificationsCommand(new GitContext(outgoingDirectory), outgoingRepository);
}
@Test
public void shouldReadAddedFiles() throws Exception {
write(outgoing, outgoingDirectory, "a.txt", "bal bla");
RevCommit addedFileCommit = commit(outgoing, "add file");
String revision = addedFileCommit.getName();
Consumer<Modifications> assertModifications = assertAddedFiles("a.txt");
assertModifications.accept(outgoingModificationsCommand.getModifications(revision));
pushOutgoingAndPullIncoming();
assertModifications.accept(incomingModificationsCommand.getModifications(revision));
}
@Test
public void shouldReadModifiedFiles() throws Exception {
write(outgoing, outgoingDirectory, "a.txt", "bal bla");
commit(outgoing, "add file");
write(outgoing, outgoingDirectory, "a.txt", "modified content");
RevCommit modifiedFileCommit = commit(outgoing, "modify file");
String revision = modifiedFileCommit.getName();
Consumer<Modifications> assertModifications = assertModifiedFiles("a.txt");
assertModifications.accept(outgoingModificationsCommand.getModifications(revision));
pushOutgoingAndPullIncoming();
assertModifications.accept(incomingModificationsCommand.getModifications(revision));
}
@Test
public void shouldReadRemovedFiles() throws Exception {
String fileName = "a.txt";
write(outgoing, outgoingDirectory, fileName, "bal bla");
commit(outgoing, "add file");
File file = new File(outgoingDirectory, fileName);
file.delete();
outgoing.add().setUpdate(true).addFilepattern(".").call();
RevCommit removedFileCommit = commit(outgoing, "remove file");
String revision = removedFileCommit.getName();
Consumer<Modifications> assertModifications = assertRemovedFiles(fileName);
pushOutgoingAndPullIncoming();
assertModifications.accept(incomingModificationsCommand.getModifications(revision));
assertModifications.accept(outgoingModificationsCommand.getModifications(revision));
}
void pushOutgoingAndPullIncoming() throws IOException {
GitPushCommand cmd = new GitPushCommand(handler, new GitContext(outgoingDirectory),
outgoingRepository);
PushCommandRequest request = new PushCommandRequest();
request.setRemoteRepository(incomingRepository);
cmd.push(request);
GitPullCommand pullCommand = new GitPullCommand(handler, new GitContext(incomingDirectory),
incomingRepository);
PullCommandRequest pullRequest = new PullCommandRequest();
pullRequest.setRemoteRepository(incomingRepository);
pullCommand.pull(pullRequest);
}
Consumer<Modifications> assertRemovedFiles(String fileName) {
return (modifications) -> {
assertThat(modifications).isNotNull();
assertThat(modifications.getAdded())
.as("added files modifications")
.hasSize(0);
assertThat(modifications.getModified())
.as("modified files modifications")
.hasSize(0);
assertThat(modifications.getRemoved())
.as("removed files modifications")
.hasSize(1)
.containsOnly(fileName);
};
}
Consumer<Modifications> assertModifiedFiles(String file) {
return (modifications) -> {
assertThat(modifications).isNotNull();
assertThat(modifications.getAdded())
.as("added files modifications")
.hasSize(0);
assertThat(modifications.getModified())
.as("modified files modifications")
.hasSize(1)
.containsOnly(file);
assertThat(modifications.getRemoved())
.as("removed files modifications")
.hasSize(0);
};
}
Consumer<Modifications> assertAddedFiles(String file) {
return (modifications) -> {
assertThat(modifications).isNotNull();
assertThat(modifications.getAdded())
.as("added files modifications")
.hasSize(1)
.containsOnly(file);
assertThat(modifications.getModified())
.as("modified files modifications")
.hasSize(0);
assertThat(modifications.getRemoved())
.as("removed files modifications")
.hasSize(0);
};
}
}

View File

@@ -78,7 +78,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase
GitOutgoingCommand cmd = createCommand();
OutgoingCommandRequest request = new OutgoingCommandRequest();
request.setRemoteRepository(incomgingRepository);
request.setRemoteRepository(incomingRepository);
ChangesetPagingResult cpr = cmd.getOutgoingChangesets(request);
@@ -98,7 +98,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase
* @throws RepositoryException
*/
@Test
public void testGetOutgoingChangesetsWithAllreadyPushedChanges()
public void testGetOutgoingChangesetsWithAlreadyPushedChanges()
throws IOException, GitAPIException
{
write(outgoing, outgoingDirectory, "a.txt", "content of a.txt");
@@ -110,7 +110,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase
outgoingRepository);
PushCommandRequest req = new PushCommandRequest();
req.setRemoteRepository(incomgingRepository);
req.setRemoteRepository(incomingRepository);
push.push(req);
write(outgoing, outgoingDirectory, "b.txt", "content of b.txt");
@@ -120,7 +120,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase
GitOutgoingCommand cmd = createCommand();
OutgoingCommandRequest request = new OutgoingCommandRequest();
request.setRemoteRepository(incomgingRepository);
request.setRemoteRepository(incomingRepository);
ChangesetPagingResult cpr = cmd.getOutgoingChangesets(request);
@@ -144,7 +144,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase
GitOutgoingCommand cmd = createCommand();
OutgoingCommandRequest request = new OutgoingCommandRequest();
request.setRemoteRepository(incomgingRepository);
request.setRemoteRepository(incomingRepository);
ChangesetPagingResult cpr = cmd.getOutgoingChangesets(request);

View File

@@ -78,7 +78,7 @@ public class GitPushCommandTest extends AbstractRemoteCommandTestBase
GitPushCommand cmd = createCommand();
PushCommandRequest request = new PushCommandRequest();
request.setRemoteRepository(incomgingRepository);
request.setRemoteRepository(incomingRepository);
PushResponse response = cmd.push(request);