001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.scm.provider.git.jgit.command.diff;
020
021import java.io.ByteArrayOutputStream;
022import java.io.IOException;
023import java.io.OutputStream;
024
025import org.apache.commons.lang3.StringUtils;
026import org.apache.maven.scm.ScmException;
027import org.apache.maven.scm.ScmFileSet;
028import org.apache.maven.scm.ScmVersion;
029import org.apache.maven.scm.command.diff.AbstractDiffCommand;
030import org.apache.maven.scm.command.diff.DiffScmResult;
031import org.apache.maven.scm.provider.ScmProviderRepository;
032import org.apache.maven.scm.provider.git.command.GitCommand;
033import org.apache.maven.scm.provider.git.command.diff.GitDiffConsumer;
034import org.apache.maven.scm.provider.git.jgit.command.JGitUtils;
035import org.eclipse.jgit.api.Git;
036import org.eclipse.jgit.api.errors.GitAPIException;
037import org.eclipse.jgit.lib.ObjectId;
038import org.eclipse.jgit.lib.ObjectReader;
039import org.eclipse.jgit.lib.Repository;
040import org.eclipse.jgit.revwalk.RevWalk;
041import org.eclipse.jgit.treewalk.AbstractTreeIterator;
042import org.eclipse.jgit.treewalk.CanonicalTreeParser;
043
044/**
045 * @author Dominik Bartholdi (imod)
046 * @since 1.9
047 */
048public class JGitDiffCommand extends AbstractDiffCommand implements GitCommand {
049
050    @Override
051    protected DiffScmResult executeDiffCommand(
052            ScmProviderRepository repository, ScmFileSet fileSet, ScmVersion startRevision, ScmVersion endRevision)
053            throws ScmException {
054
055        Git git = null;
056        try {
057            git = JGitUtils.openRepo(fileSet.getBasedir());
058            DiffScmResult diff = callDiff(git, startRevision, endRevision);
059            git.getRepository().close();
060            return diff;
061        } catch (Exception e) {
062            throw new ScmException("JGit diff failure!", e);
063        } finally {
064            JGitUtils.closeRepo(git);
065        }
066    }
067
068    public DiffScmResult callDiff(Git git, ScmVersion startRevision, ScmVersion endRevision)
069            throws IOException, GitAPIException, ScmException {
070
071        AbstractTreeIterator oldTree = null;
072        if (startRevision != null
073                && StringUtils.isNotEmpty(startRevision.getName().trim())) {
074            String startRev = startRevision.getName().trim();
075            oldTree = getTreeIterator(git.getRepository(), startRev);
076        }
077
078        AbstractTreeIterator newTree = null;
079        if (endRevision != null && StringUtils.isNotEmpty(endRevision.getName().trim())) {
080            String endRev = endRevision.getName().trim();
081            newTree = getTreeIterator(git.getRepository(), endRev);
082        }
083
084        OutputStream out = new ByteArrayOutputStream();
085
086        git.diff()
087                .setOutputStream(out)
088                .setOldTree(oldTree)
089                .setNewTree(newTree)
090                .setCached(false)
091                .call();
092        git.diff()
093                .setOutputStream(out)
094                .setOldTree(oldTree)
095                .setNewTree(newTree)
096                .setCached(true)
097                .call();
098
099        out.flush();
100
101        GitDiffConsumer consumer = new GitDiffConsumer(null);
102        String fullDiff = out.toString();
103        out.close();
104
105        String[] lines = fullDiff.split("\n");
106        for (String aLine : lines) {
107            consumer.consumeLine(aLine);
108        }
109
110        return new DiffScmResult(
111                "JGit diff", consumer.getChangedFiles(), consumer.getDifferences(), consumer.getPatch());
112    }
113
114    private AbstractTreeIterator getTreeIterator(Repository repo, String name) throws IOException {
115        final ObjectId id = repo.resolve(name);
116        if (id == null) {
117            throw new IllegalArgumentException(name);
118        }
119        final CanonicalTreeParser p = new CanonicalTreeParser();
120
121        try (ObjectReader or = repo.newObjectReader();
122                RevWalk revWalk = new RevWalk(repo)) {
123            p.reset(or, revWalk.parseTree(id));
124            return p;
125        }
126    }
127}