Modifications command between two revisions (#1761)

Adds the option to compute the modifications between two revisions unsing the modifications command.
This commit is contained in:
René Pfeuffer
2021-08-09 12:13:41 +02:00
committed by GitHub
parent ddd2fc1055
commit 8558572c99
22 changed files with 696 additions and 100 deletions

View File

@@ -27,6 +27,7 @@ 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.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -60,10 +61,65 @@ public class GitModificationsCommand extends AbstractGitCommand implements Modif
super(context);
}
private Modifications createModifications(TreeWalk treeWalk, RevCommit commit, RevWalk revWalk, String revision)
throws IOException {
@Override
public Modifications getModifications(String baseRevision, String revision) {
org.eclipse.jgit.lib.Repository gitRepository = null;
RevWalk revWalk = null;
try {
gitRepository = open();
if (!gitRepository.getAllRefs().isEmpty()) {
revWalk = new RevWalk(gitRepository);
RevCommit commit = getCommit(revision, gitRepository, revWalk);
TreeWalk treeWalk = createTreeWalk(gitRepository);
if (baseRevision == null) {
determineParentAsBase(treeWalk, commit, revWalk);
} else {
RevCommit baseCommit = getCommit(baseRevision, gitRepository, revWalk);
treeWalk.addTree(baseCommit.getTree());
}
return new Modifications(baseRevision, revision, createModifications(treeWalk, commit));
}
} catch (IOException ex) {
log.error("could not open repository", ex);
throw new InternalRepositoryException(entity(repository), "could not open repository", ex);
} finally {
GitUtil.release(revWalk);
GitUtil.close(gitRepository);
}
return null;
}
private RevCommit getCommit(String revision, Repository gitRepository, RevWalk revWalk) throws IOException {
ObjectId id = GitUtil.getRevisionId(gitRepository, revision);
return revWalk.parseCommit(id);
}
@Override
public Modifications getModifications(String revision) {
return getModifications(null, revision);
}
private TreeWalk createTreeWalk(Repository gitRepository) {
TreeWalk treeWalk = new TreeWalk(gitRepository);
treeWalk.reset();
treeWalk.setRecursive(true);
return treeWalk;
}
private Collection<Modification> createModifications(TreeWalk treeWalk, RevCommit commit)
throws IOException {
treeWalk.addTree(commit.getTree());
List<DiffEntry> entries = Differ.scanWithRename(context.open(), null, treeWalk);
Collection<Modification> modifications = new ArrayList<>();
for (DiffEntry e : entries) {
if (!e.getOldId().equals(e.getNewId()) || !e.getOldPath().equals(e.getNewPath())) {
modifications.add(asModification(e));
}
}
return modifications;
}
private void determineParentAsBase(TreeWalk treeWalk, RevCommit commit, RevWalk revWalk) throws IOException {
if (commit.getParentCount() > 0) {
RevCommit parent = commit.getParent(0);
RevTree tree = parent.getTree();
@@ -81,43 +137,6 @@ public class GitModificationsCommand extends AbstractGitCommand implements Modif
log.trace("no parent available for commit {}", commit.getName());
treeWalk.addTree(new EmptyTreeIterator());
}
treeWalk.addTree(commit.getTree());
List<DiffEntry> entries = Differ.scanWithRename(context.open(), null, treeWalk);
Collection<Modification> modifications = new ArrayList<>();
for (DiffEntry e : entries) {
if (!e.getOldId().equals(e.getNewId()) || !e.getOldPath().equals(e.getNewPath())) {
modifications.add(asModification(e));
}
}
return new Modifications(revision, 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(entity(repository), "could not open repository", ex);
} finally {
GitUtil.release(revWalk);
GitUtil.close(gitRepository);
}
return null;
}
@Override
public Modifications getModifications(ModificationsCommandRequest request) {
return getModifications(request.getRevision());
}
private Modification asModification(DiffEntry entry) throws UnsupportedModificationTypeException {

View File

@@ -60,7 +60,10 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider {
Command.MIRROR
);
protected static final Set<Feature> FEATURES = EnumSet.of(Feature.INCOMING_REVISION);
protected static final Set<Feature> FEATURES = EnumSet.of(
Feature.INCOMING_REVISION,
Feature.MODIFICATIONS_BETWEEN_REVISIONS
);
private final GitContext context;
private final Injector commandInjector;

View File

@@ -106,6 +106,36 @@ public class GitModificationsCommandTest extends AbstractRemoteCommandTestBase {
assertModifications.accept(incomingModificationsCommand.getModifications(revision));
}
@Test
public void shouldFindModificationsBetweenRevisions() throws Exception {
write(outgoing, outgoingDirectory, "a.txt", "bal bla");
write(outgoing, outgoingDirectory, "d.txt", "some file to be renamed");
RevCommit baseCommit = commit(outgoing, "add files");
write(outgoing, outgoingDirectory, "a.txt", "modified content");
commit(outgoing, "modify file");
write(outgoing, outgoingDirectory, "c.txt", "brand new file");
commit(outgoing, "add file");
write(outgoing, outgoingDirectory, "o.txt", "some file to be renamed");
outgoing.rm().addFilepattern("d.txt").call();
RevCommit targetCommit = commit(outgoing, "move/rename file");
outgoing.checkout().setName("some_branch").setCreateBranch(true).setStartPoint(baseCommit).call();
write(outgoing, outgoingDirectory, "x.txt", "bla bla");
RevCommit otherBranchCommit = commit(outgoing, "other branch");
Modifications modifications = outgoingModificationsCommand.getModifications(otherBranchCommit.getName(), targetCommit.getName());
assertThat(modifications.getModifications())
.hasSize(4)
.extracting("class.simpleName")
.contains("Modified") // File a.txt has been modified
.contains("Removed") // File x.txt from the other branch is not present
.contains("Added") // File c.txt has been created on the original branch
.contains("Renamed") // File d.txt has been renamed on the original branch
;
}
void pushOutgoingAndPullIncoming() throws IOException {
GitPushCommand cmd = new GitPushCommand(handler, new GitContext(outgoingDirectory, outgoingRepository, null, new GitConfig()));
PushCommandRequest request = new PushCommandRequest();