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.gitexe.command.changelog;
020
021import java.io.File;
022import java.text.SimpleDateFormat;
023import java.util.Date;
024import java.util.TimeZone;
025
026import org.apache.commons.text.StringEscapeUtils;
027import org.apache.maven.scm.CommandParameter;
028import org.apache.maven.scm.CommandParameters;
029import org.apache.maven.scm.ScmBranch;
030import org.apache.maven.scm.ScmException;
031import org.apache.maven.scm.ScmFileSet;
032import org.apache.maven.scm.ScmResult;
033import org.apache.maven.scm.ScmVersion;
034import org.apache.maven.scm.command.changelog.AbstractChangeLogCommand;
035import org.apache.maven.scm.command.changelog.ChangeLogScmRequest;
036import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
037import org.apache.maven.scm.command.changelog.ChangeLogSet;
038import org.apache.maven.scm.provider.ScmProviderRepository;
039import org.apache.maven.scm.provider.git.command.GitCommand;
040import org.apache.maven.scm.provider.git.gitexe.command.GitCommandLineUtils;
041import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
042import org.codehaus.plexus.util.cli.CommandLineUtils;
043import org.codehaus.plexus.util.cli.Commandline;
044
045/**
046 * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
047 * @author Olivier Lamy
048 *
049 */
050public class GitChangeLogCommand extends AbstractChangeLogCommand implements GitCommand {
051    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss Z";
052
053    @Override
054    public ScmResult executeCommand(ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters)
055            throws ScmException {
056        return executeChangeLogCommand(
057                repository,
058                fileSet,
059                parameters.getDate(CommandParameter.START_DATE, null),
060                parameters.getDate(CommandParameter.END_DATE, null),
061                (ScmBranch) parameters.getScmVersion(CommandParameter.BRANCH, null),
062                parameters.getString(CommandParameter.CHANGELOG_DATE_PATTERN, null),
063                parameters.getScmVersion(CommandParameter.START_SCM_VERSION, null),
064                parameters.getScmVersion(CommandParameter.END_SCM_VERSION, null),
065                parameters.getInt(CommandParameter.LIMIT, -1),
066                parameters.getScmVersion(CommandParameter.SCM_VERSION, null));
067    }
068
069    /** {@inheritDoc} */
070    @Override
071    protected ChangeLogScmResult executeChangeLogCommand(
072            ScmProviderRepository repo,
073            ScmFileSet fileSet,
074            ScmVersion startVersion,
075            ScmVersion endVersion,
076            String datePattern)
077            throws ScmException {
078        return executeChangeLogCommand(repo, fileSet, null, null, null, datePattern, startVersion, endVersion);
079    }
080
081    /** {@inheritDoc} */
082    @Override
083    protected ChangeLogScmResult executeChangeLogCommand(
084            ScmProviderRepository repo,
085            ScmFileSet fileSet,
086            Date startDate,
087            Date endDate,
088            ScmBranch branch,
089            String datePattern)
090            throws ScmException {
091        return executeChangeLogCommand(repo, fileSet, startDate, endDate, branch, datePattern, null, null);
092    }
093
094    @Override
095    protected ChangeLogScmResult executeChangeLogCommand(
096            ScmProviderRepository repository, ScmFileSet fileSet, ScmVersion version, String datePattern)
097            throws ScmException {
098        return executeChangeLogCommand(repository, fileSet, null, null, null, datePattern, null, null, null, version);
099    }
100
101    protected ChangeLogScmResult executeChangeLogCommand(
102            ScmProviderRepository repo,
103            ScmFileSet fileSet,
104            Date startDate,
105            Date endDate,
106            ScmBranch branch,
107            String datePattern,
108            ScmVersion startVersion,
109            ScmVersion endVersion)
110            throws ScmException {
111        return executeChangeLogCommand(
112                repo, fileSet, startDate, endDate, branch, datePattern, startVersion, endVersion, null, null);
113    }
114
115    @Override
116    protected ChangeLogScmResult executeChangeLogCommand(ChangeLogScmRequest request) throws ScmException {
117        final ScmVersion startVersion = request.getStartRevision();
118        final ScmVersion endVersion = request.getEndRevision();
119        final ScmVersion revision = request.getRevision();
120        final ScmFileSet fileSet = request.getScmFileSet();
121        final String datePattern = request.getDatePattern();
122        final ScmProviderRepository providerRepository =
123                request.getScmRepository().getProviderRepository();
124        final Date startDate = request.getStartDate();
125        final Date endDate = request.getEndDate();
126        final ScmBranch branch = request.getScmBranch();
127        final Integer limit = request.getLimit();
128
129        return executeChangeLogCommand(
130                providerRepository,
131                fileSet,
132                startDate,
133                endDate,
134                branch,
135                datePattern,
136                startVersion,
137                endVersion,
138                limit,
139                revision);
140    }
141
142    protected ChangeLogScmResult executeChangeLogCommand(
143            ScmProviderRepository repo,
144            ScmFileSet fileSet,
145            Date startDate,
146            Date endDate,
147            ScmBranch branch,
148            String datePattern,
149            ScmVersion startVersion,
150            ScmVersion endVersion,
151            Integer limit)
152            throws ScmException {
153        return executeChangeLogCommand(
154                repo, fileSet, startDate, endDate, branch, datePattern, startVersion, endVersion, limit, null);
155    }
156
157    protected ChangeLogScmResult executeChangeLogCommand(
158            ScmProviderRepository repo,
159            ScmFileSet fileSet,
160            Date startDate,
161            Date endDate,
162            ScmBranch branch,
163            String datePattern,
164            ScmVersion startVersion,
165            ScmVersion endVersion,
166            Integer limit,
167            ScmVersion version)
168            throws ScmException {
169        Commandline cl = createCommandLine(
170                (GitScmProviderRepository) repo,
171                fileSet.getBasedir(),
172                branch,
173                startDate,
174                endDate,
175                startVersion,
176                endVersion,
177                limit,
178                version);
179
180        GitChangeLogConsumer consumer = new GitChangeLogConsumer(datePattern);
181
182        CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
183
184        int exitCode;
185
186        exitCode = GitCommandLineUtils.execute(cl, consumer, stderr);
187        if (exitCode != 0) {
188            return new ChangeLogScmResult(cl.toString(), "The git-log command failed.", stderr.getOutput(), false);
189        }
190        ChangeLogSet changeLogSet = new ChangeLogSet(consumer.getModifications(), startDate, endDate);
191        changeLogSet.setStartVersion(startVersion);
192        changeLogSet.setEndVersion(endVersion);
193
194        return new ChangeLogScmResult(cl.toString(), changeLogSet);
195    }
196
197    // ----------------------------------------------------------------------
198    //
199    // ----------------------------------------------------------------------
200
201    /**
202     * This method creates the commandline for the git-whatchanged command.
203     * <p>
204     * Since it uses --since and --until for the start and end date, the branch
205     * and version parameters can be used simultanously.
206     *
207     * @param repository Provider repositry to use.
208     * @param workingDirectory Working copy directory.
209     * @param branch Branch to run command on.
210     * @param startDate Start date of log entries.
211     * @param endDate End date of log entries.
212     * @param startVersion Start version of log entries.
213     * @param endVersion End version of log entries.
214     * @return Command line.
215     */
216    public static Commandline createCommandLine(
217            GitScmProviderRepository repository,
218            File workingDirectory,
219            ScmBranch branch,
220            Date startDate,
221            Date endDate,
222            ScmVersion startVersion,
223            ScmVersion endVersion) {
224        return createCommandLine(
225                repository, workingDirectory, branch, startDate, endDate, startVersion, endVersion, null);
226    }
227
228    static Commandline createCommandLine(
229            GitScmProviderRepository repository,
230            File workingDirectory,
231            ScmBranch branch,
232            Date startDate,
233            Date endDate,
234            ScmVersion startVersion,
235            ScmVersion endVersion,
236            Integer limit) {
237        return createCommandLine(
238                repository, workingDirectory, branch, startDate, endDate, startVersion, endVersion, limit, null);
239    }
240
241    static Commandline createCommandLine(
242            GitScmProviderRepository repository,
243            File workingDirectory,
244            ScmBranch branch,
245            Date startDate,
246            Date endDate,
247            ScmVersion startVersion,
248            ScmVersion endVersion,
249            Integer limit,
250            ScmVersion version) {
251        SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
252        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
253
254        Commandline cl = GitCommandLineUtils.getBaseGitCommandLine(workingDirectory, "whatchanged");
255        cl.createArg().setValue("--format=medium");
256        cl.createArg().setValue("--decorate=short");
257        cl.createArg().setValue("--raw");
258        cl.createArg().setValue("--no-merges");
259
260        if (startDate != null || endDate != null) {
261            if (startDate != null) {
262                cl.createArg().setValue("--since=" + StringEscapeUtils.escapeJava(dateFormat.format(startDate)));
263            }
264
265            if (endDate != null) {
266                cl.createArg().setValue("--until=" + StringEscapeUtils.escapeJava(dateFormat.format(endDate)));
267            }
268        }
269
270        // since this parameter is also used for the output formatting, we need it also if no start nor end date is
271        // given
272        cl.createArg().setValue("--date=iso");
273
274        if (startVersion != null || endVersion != null) {
275            StringBuilder versionRange = new StringBuilder();
276
277            if (startVersion != null) {
278                versionRange.append(StringEscapeUtils.escapeJava(startVersion.getName()));
279            }
280
281            versionRange.append("..");
282
283            if (endVersion != null) {
284                versionRange.append(StringEscapeUtils.escapeJava(endVersion.getName()));
285            }
286
287            cl.createArg().setValue(versionRange.toString());
288
289        } else if (version != null) {
290            cl.createArg().setValue(StringEscapeUtils.escapeJava(version.getName()));
291        }
292
293        if (limit != null && limit > 0) {
294            cl.createArg().setValue("--max-count=" + limit);
295        }
296
297        if (branch != null && branch.getName() != null && branch.getName().length() > 0) {
298            cl.createArg().setValue(branch.getName());
299        }
300
301        // Insert a separator to make sure that files aren't interpreted as part of the version spec
302        cl.createArg().setValue("--");
303
304        // We have to report only the changes of the current project.
305        // This is needed for child projects, otherwise we would get the changelog of the
306        // whole parent-project including all childs.
307        cl.createArg().setValue(".");
308
309        return cl;
310    }
311}