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.changelog;
020
021import java.io.File;
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.Date;
025import java.util.List;
026
027import org.apache.maven.scm.ChangeSet;
028import org.apache.maven.scm.ScmBranch;
029import org.apache.maven.scm.ScmException;
030import org.apache.maven.scm.ScmFileSet;
031import org.apache.maven.scm.ScmVersion;
032import org.apache.maven.scm.command.changelog.AbstractChangeLogCommand;
033import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
034import org.apache.maven.scm.command.changelog.ChangeLogSet;
035import org.apache.maven.scm.provider.ScmProviderRepository;
036import org.apache.maven.scm.provider.git.command.GitCommand;
037import org.apache.maven.scm.provider.git.jgit.command.JGitUtils;
038import org.eclipse.jgit.api.Git;
039import org.eclipse.jgit.errors.IncorrectObjectTypeException;
040import org.eclipse.jgit.errors.MissingObjectException;
041import org.eclipse.jgit.lib.Repository;
042import org.eclipse.jgit.revwalk.RevCommit;
043import org.eclipse.jgit.revwalk.RevSort;
044
045import static org.apache.maven.scm.provider.git.jgit.command.JGitUtils.getTags;
046
047/**
048 * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
049 * @author Dominik Bartholdi (imod)
050 * @since 1.9
051 */
052public class JGitChangeLogCommand extends AbstractChangeLogCommand implements GitCommand {
053
054    /**
055     * {@inheritDoc}
056     */
057    protected ChangeLogScmResult executeChangeLogCommand(
058            ScmProviderRepository repo,
059            ScmFileSet fileSet,
060            ScmVersion startVersion,
061            ScmVersion endVersion,
062            String datePattern)
063            throws ScmException {
064        return executeChangeLogCommand(repo, fileSet, null, null, null, datePattern, startVersion, endVersion);
065    }
066
067    @Override
068    protected ChangeLogScmResult executeChangeLogCommand(
069            ScmProviderRepository repository, ScmFileSet fileSet, ScmVersion version, String datePattern)
070            throws ScmException {
071        return executeChangeLogCommand(repository, fileSet, null, null, null, datePattern, null, null, version);
072    }
073
074    /**
075     * {@inheritDoc}
076     */
077    protected ChangeLogScmResult executeChangeLogCommand(
078            ScmProviderRepository repo,
079            ScmFileSet fileSet,
080            Date startDate,
081            Date endDate,
082            ScmBranch branch,
083            String datePattern)
084            throws ScmException {
085        return executeChangeLogCommand(repo, fileSet, startDate, endDate, branch, datePattern, null, null);
086    }
087
088    protected ChangeLogScmResult executeChangeLogCommand(
089            ScmProviderRepository repo,
090            ScmFileSet fileSet,
091            Date startDate,
092            Date endDate,
093            ScmBranch branch,
094            String datePattern,
095            ScmVersion startVersion,
096            ScmVersion endVersion)
097            throws ScmException {
098        return executeChangeLogCommand(
099                repo, fileSet, startDate, endDate, branch, datePattern, startVersion, endVersion, null);
100    }
101
102    protected ChangeLogScmResult executeChangeLogCommand(
103            ScmProviderRepository repo,
104            ScmFileSet fileSet,
105            Date startDate,
106            Date endDate,
107            ScmBranch branch,
108            String datePattern,
109            ScmVersion startVersion,
110            ScmVersion endVersion,
111            ScmVersion version)
112            throws ScmException {
113        Git git = null;
114        boolean isARangeChangeLog = startVersion != null || endVersion != null;
115
116        try {
117            git = JGitUtils.openRepo(fileSet.getBasedir());
118
119            boolean versionOnly = startVersion == null && endVersion == null && version != null;
120
121            String startRev;
122            String endRev;
123
124            if (versionOnly) {
125                startRev = null;
126                endRev = version.getName();
127            } else {
128                startRev = startVersion != null ? startVersion.getName() : (isARangeChangeLog ? "HEAD" : null);
129                endRev = endVersion != null ? endVersion.getName() : (isARangeChangeLog ? "HEAD" : null);
130            }
131
132            List<ChangeEntry> gitChanges =
133                    this.whatchanged(git.getRepository(), null, startRev, endRev, startDate, endDate, -1);
134
135            List<ChangeSet> modifications = new ArrayList<>(gitChanges.size());
136
137            for (ChangeEntry change : gitChanges) {
138                ChangeSet scmChange = new ChangeSet();
139
140                scmChange.setAuthor(change.getAuthorName());
141                scmChange.setComment(change.getBody());
142                scmChange.setDate(change.getAuthorDate());
143                scmChange.setRevision(change.getCommitHash());
144                scmChange.setTags(change.getTags());
145                // X TODO scmChange.setFiles( change.get )
146
147                modifications.add(scmChange);
148            }
149
150            ChangeLogSet changeLogSet = new ChangeLogSet(modifications, startDate, endDate);
151            changeLogSet.setStartVersion(startVersion);
152            changeLogSet.setEndVersion(endVersion);
153
154            return new ChangeLogScmResult("JGit changelog", changeLogSet);
155        } catch (Exception e) {
156            throw new ScmException("JGit changelog failure!", e);
157        } finally {
158            JGitUtils.closeRepo(git);
159        }
160    }
161
162    public List<ChangeEntry> whatchanged(
163            Repository repo, RevSort[] sortings, String fromRev, String toRev, Date fromDate, Date toDate, int maxLines)
164            throws MissingObjectException, IncorrectObjectTypeException, IOException {
165        List<RevCommit> revs = JGitUtils.getRevCommits(repo, sortings, fromRev, toRev, fromDate, toDate, maxLines);
166        List<ChangeEntry> changes = new ArrayList<>(revs.size());
167
168        if (fromRev != null && fromRev.equals(toRev)) {
169            // there are no changes between 2 identical versions
170            return changes;
171        }
172
173        for (RevCommit c : revs) {
174            ChangeEntry ce = new ChangeEntry();
175
176            ce.setAuthorDate(c.getAuthorIdent().getWhen());
177            ce.setAuthorEmail(c.getAuthorIdent().getEmailAddress());
178            ce.setAuthorName(c.getAuthorIdent().getName());
179            ce.setCommitterDate(c.getCommitterIdent().getWhen());
180            ce.setCommitterEmail(c.getCommitterIdent().getEmailAddress());
181            ce.setCommitterName(c.getCommitterIdent().getName());
182
183            ce.setSubject(c.getShortMessage());
184            ce.setBody(c.getFullMessage());
185
186            ce.setCommitHash(c.getId().name());
187            ce.setTreeHash(c.getTree().getId().name());
188
189            ce.setTags(getTags(repo, c));
190            // X TODO missing: file list
191
192            changes.add(ce);
193        }
194
195        return changes;
196    }
197
198    /**
199     *
200     */
201    public static final class ChangeEntry {
202        private String commitHash;
203
204        private String treeHash;
205
206        private String authorName;
207
208        private String authorEmail;
209
210        private Date authorDate;
211
212        private String committerName;
213
214        private String committerEmail;
215
216        private Date committerDate;
217
218        private String subject;
219
220        private String body;
221
222        private List<File> files;
223
224        private List<String> tags;
225
226        public String getCommitHash() {
227            return commitHash;
228        }
229
230        public void setCommitHash(String commitHash) {
231            this.commitHash = commitHash;
232        }
233
234        public String getTreeHash() {
235            return treeHash;
236        }
237
238        public void setTreeHash(String treeHash) {
239            this.treeHash = treeHash;
240        }
241
242        public String getAuthorName() {
243            return authorName;
244        }
245
246        public void setAuthorName(String authorName) {
247            this.authorName = authorName;
248        }
249
250        public String getAuthorEmail() {
251            return authorEmail;
252        }
253
254        public void setAuthorEmail(String authorEmail) {
255            this.authorEmail = authorEmail;
256        }
257
258        public Date getAuthorDate() {
259            return authorDate;
260        }
261
262        public void setAuthorDate(Date authorDate) {
263            this.authorDate = authorDate;
264        }
265
266        public String getCommitterName() {
267            return committerName;
268        }
269
270        public void setCommitterName(String committerName) {
271            this.committerName = committerName;
272        }
273
274        public String getCommitterEmail() {
275            return committerEmail;
276        }
277
278        public void setCommitterEmail(String committerEmail) {
279            this.committerEmail = committerEmail;
280        }
281
282        public Date getCommitterDate() {
283            return committerDate;
284        }
285
286        public void setCommitterDate(Date committerDate) {
287            this.committerDate = committerDate;
288        }
289
290        public String getSubject() {
291            return subject;
292        }
293
294        public void setSubject(String subject) {
295            this.subject = subject;
296        }
297
298        public String getBody() {
299            return body;
300        }
301
302        public void setBody(String body) {
303            this.body = body;
304        }
305
306        public List<File> getFiles() {
307            return files;
308        }
309
310        public void setFiles(List<File> files) {
311            this.files = files;
312        }
313
314        public List<String> getTags() {
315            return tags;
316        }
317
318        public void setTags(List<String> tags) {
319            this.tags = tags;
320        }
321    }
322}