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