Skip to content

Commit c2eec5e

Browse files
committed
[SCM-914] Introduce properly typed last modified date
Populate it with svnexe, gitexe and JGit providers
1 parent f5d8bb4 commit c2eec5e

File tree

15 files changed

+476
-105
lines changed

15 files changed

+476
-105
lines changed

maven-scm-api/src/main/java/org/apache/maven/scm/CommandParameter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public class CommandParameter implements Serializable {
7474
public static final CommandParameter SCM_MKDIR_CREATE_IN_LOCAL = new CommandParameter("createInLocal");
7575

7676
/**
77-
* Parameter used only for Git SCM and simulate the <code>git rev-parse --short=lenght</code> command.
77+
* Parameter used only for Git SCM to truncate the emitted hash to the given character length, simulates <code>git rev-parse --short=length</code> command.
7878
*
7979
* @since 1.7
8080
*/

maven-scm-api/src/main/java/org/apache/maven/scm/command/info/InfoItem.java

+34
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,14 @@
1818
*/
1919
package org.apache.maven.scm.command.info;
2020

21+
import java.time.OffsetDateTime;
22+
import java.time.temporal.TemporalAccessor;
23+
2124
/**
25+
* Encapsulates meta information about a file (or directory) being managed with an SCM.
26+
*
27+
* For historical reasons the field/method names are inspired from (and sometimes only applicable to) the <a href="https://svnbook.red-bean.com/">Subversion SCM</a>.
28+
*
2229
* @author <a href="mailto:[email protected]">Kenney Westerhof</a>
2330
* @author Olivier Lamy
2431
*
@@ -45,6 +52,8 @@ public class InfoItem {
4552

4653
private String lastChangedDate;
4754

55+
private OffsetDateTime lastChangedDateTime;
56+
4857
public String getPath() {
4958
return path;
5059
}
@@ -117,11 +126,36 @@ public void setLastChangedRevision(String lastChangedRevision) {
117126
this.lastChangedRevision = lastChangedRevision;
118127
}
119128

129+
/**
130+
* @deprecated Use {@link #getLastModifiedDate()} instead
131+
*/
132+
@Deprecated
120133
public String getLastChangedDate() {
121134
return lastChangedDate;
122135
}
123136

137+
/**
138+
* @deprecated Use {@link #setLastModifiedDate(TemporalAccessor)} instead
139+
*/
140+
@Deprecated
124141
public void setLastChangedDate(String lastChangedDate) {
125142
this.lastChangedDate = lastChangedDate;
126143
}
144+
145+
/**
146+
*
147+
* @return the date when the file indicated via {@link #getPath()} has been changed in the SCM for the last time
148+
* @since 2.1.0
149+
*/
150+
public OffsetDateTime getLastChangedDateTime() {
151+
return lastChangedDateTime;
152+
}
153+
154+
/**
155+
* @param accessor temporal accessor from which to populate the last changed date
156+
* @since 2.1.0
157+
*/
158+
public void setLastChangedDateTime(TemporalAccessor accessor) {
159+
this.lastChangedDateTime = OffsetDateTime.from(accessor);
160+
}
127161
}

maven-scm-api/src/main/java/org/apache/maven/scm/util/FilenameUtils.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public final class FilenameUtils {
2929
private FilenameUtils() {}
3030

3131
public static String normalizeFilename(File file) {
32-
return normalizeFilename(file.getName());
32+
return normalizeFilename(file.getPath());
3333
}
3434

3535
/**

maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/info/GitInfoCommand.java

+29-20
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,17 @@
1818
*/
1919
package org.apache.maven.scm.provider.git.gitexe.command.info;
2020

21+
import java.io.File;
22+
import java.util.LinkedList;
23+
import java.util.List;
24+
2125
import org.apache.maven.scm.CommandParameter;
2226
import org.apache.maven.scm.CommandParameters;
2327
import org.apache.maven.scm.ScmException;
2428
import org.apache.maven.scm.ScmFileSet;
2529
import org.apache.maven.scm.ScmResult;
2630
import org.apache.maven.scm.command.AbstractCommand;
31+
import org.apache.maven.scm.command.info.InfoItem;
2732
import org.apache.maven.scm.command.info.InfoScmResult;
2833
import org.apache.maven.scm.provider.ScmProviderRepository;
2934
import org.apache.maven.scm.provider.git.command.GitCommand;
@@ -32,6 +37,7 @@
3237
import org.codehaus.plexus.util.cli.Commandline;
3338

3439
/**
40+
* Uses {@code git log} command to retrieve info about the most recent commits related to specific files.
3541
* @author Olivier Lamy
3642
* @since 1.5
3743
*/
@@ -43,31 +49,34 @@ public class GitInfoCommand extends AbstractCommand implements GitCommand {
4349
protected ScmResult executeCommand(
4450
ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters) throws ScmException {
4551

46-
GitInfoConsumer consumer = new GitInfoConsumer(fileSet);
47-
CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
48-
49-
Commandline cli = createCommandLine(repository, fileSet, parameters);
52+
Commandline baseCli = GitCommandLineUtils.getBaseGitCommandLine(fileSet.getBasedir(), "log");
53+
baseCli.createArg().setValue("-1"); // only most recent commit matters
54+
baseCli.createArg().setValue("--no-merges"); // skip merge commits
55+
baseCli.addArg(GitInfoConsumer.getFormatArgument());
5056

51-
int exitCode = GitCommandLineUtils.execute(cli, consumer, stderr);
52-
if (exitCode != 0) {
53-
return new InfoScmResult(cli.toString(), "The git rev-parse command failed.", stderr.getOutput(), false);
57+
List<InfoItem> infoItems = new LinkedList<>();
58+
if (fileSet.getFileList().isEmpty()) {
59+
infoItems.add(executeInfoCommand(baseCli, parameters, fileSet.getBasedir()));
60+
} else {
61+
// iterate over files
62+
for (File scmFile : fileSet.getFileList()) {
63+
Commandline cliClone = (Commandline) baseCli.clone();
64+
cliClone.createArg().setFile(scmFile);
65+
infoItems.add(executeInfoCommand(cliClone, parameters, scmFile));
66+
}
5467
}
55-
return new InfoScmResult(cli.toString(), consumer.getInfoItems());
68+
return new InfoScmResult(baseCli.toString(), infoItems);
5669
}
5770

58-
public static Commandline createCommandLine(
59-
ScmProviderRepository repository, ScmFileSet fileSet, CommandParameters parameters) throws ScmException {
60-
Commandline cli = GitCommandLineUtils.getBaseGitCommandLine(fileSet.getBasedir(), "rev-parse");
61-
cli.createArg().setValue("--verify");
62-
final int revLength = getRevisionLength(parameters);
63-
if (revLength > NO_REVISION_LENGTH) // set the --short key only if revision length parameter is passed and
64-
// different from -1
65-
{
66-
cli.createArg().setValue("--short=" + revLength);
71+
protected InfoItem executeInfoCommand(Commandline cli, CommandParameters parameters, File scmFile)
72+
throws ScmException {
73+
GitInfoConsumer consumer = new GitInfoConsumer(scmFile.toPath(), getRevisionLength(parameters));
74+
CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
75+
int exitCode = GitCommandLineUtils.execute(cli, consumer, stderr);
76+
if (exitCode != 0) {
77+
throw new ScmException("The git log command failed:" + cli.toString() + " returned " + stderr.getOutput());
6778
}
68-
cli.createArg().setValue("HEAD");
69-
70-
return cli;
79+
return consumer.getInfoItem();
7180
}
7281

7382
/**

maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/info/GitInfoConsumer.java

+59-19
Original file line numberDiff line numberDiff line change
@@ -18,50 +18,90 @@
1818
*/
1919
package org.apache.maven.scm.provider.git.gitexe.command.info;
2020

21-
import java.util.ArrayList;
22-
import java.util.List;
21+
import java.nio.file.Path;
22+
import java.time.format.DateTimeFormatter;
2323

2424
import org.apache.commons.lang3.StringUtils;
25-
import org.apache.maven.scm.ScmFileSet;
2625
import org.apache.maven.scm.command.info.InfoItem;
2726
import org.apache.maven.scm.util.AbstractConsumer;
27+
import org.codehaus.plexus.util.cli.Arg;
28+
import org.codehaus.plexus.util.cli.Commandline;
2829

2930
/**
31+
* Parses output of {@code git log} with a particular format and populates a {@link InfoItem}.
32+
*
3033
* @author Olivier Lamy
3134
* @since 1.5
35+
* @see <a href="https://git-scm.com/docs/git-log#_pretty_formats">Pretty Formats</a>
3236
*/
3337
public class GitInfoConsumer extends AbstractConsumer {
3438

35-
// $ git show
36-
// commit cd3c0dfacb65955e6fbb35c56cc5b1bf8ce4f767
39+
private final InfoItem infoItem;
40+
private final int revisionLength;
41+
42+
public GitInfoConsumer(Path path, int revisionLength) {
43+
infoItem = new InfoItem();
44+
infoItem.setPath(path.toString());
45+
infoItem.setURL(path.toUri().toASCIIString());
46+
this.revisionLength = revisionLength;
47+
}
48+
49+
enum LineParts {
50+
HASH(0),
51+
AUTHOR_NAME(3),
52+
AUTHOR_EMAIL(2),
53+
AUTHOR_LAST_MODIFIED(1);
3754

38-
private final List<InfoItem> infoItems = new ArrayList<>(1);
55+
private final int index;
3956

40-
private final ScmFileSet scmFileSet;
57+
LineParts(int index) {
58+
this.index = index;
59+
}
4160

42-
public GitInfoConsumer(ScmFileSet scmFileSet) {
43-
this.scmFileSet = scmFileSet;
61+
public int getIndex() {
62+
return index;
63+
}
4464
}
4565

4666
/**
67+
* @param line the line which is supposed to have the format as specified by {@link #getFormatArgument()}.
4768
* @see org.codehaus.plexus.util.cli.StreamConsumer#consumeLine(java.lang.String)
4869
*/
4970
public void consumeLine(String line) {
5071
if (logger.isDebugEnabled()) {
51-
logger.debug("consume line " + line);
72+
logger.debug("consume line {}", line);
5273
}
5374

54-
if (infoItems.isEmpty()) {
55-
if (!(line == null || line.isEmpty())) {
56-
InfoItem infoItem = new InfoItem();
57-
infoItem.setRevision(StringUtils.trim(line));
58-
infoItem.setURL(scmFileSet.getBasedir().toPath().toUri().toASCIIString());
59-
infoItems.add(infoItem);
60-
}
75+
// name must be last token as it may contain separators
76+
String[] parts = line.split("\\s", 4);
77+
if (parts.length != 4) {
78+
throw new IllegalArgumentException(
79+
"Unexpected line: expecting 4 tokens separated by whitespace but got " + line);
6180
}
81+
infoItem.setLastChangedAuthor(
82+
parts[LineParts.AUTHOR_NAME.getIndex()] + " <" + parts[LineParts.AUTHOR_EMAIL.getIndex()] + ">");
83+
String revision = parts[LineParts.HASH.getIndex()];
84+
if (revisionLength > -1) {
85+
// do not truncate below 4 characters
86+
revision = StringUtils.truncate(revision, Integer.max(4, revisionLength));
87+
}
88+
infoItem.setRevision(revision);
89+
infoItem.setLastChangedDateTime(
90+
DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(parts[LineParts.AUTHOR_LAST_MODIFIED.getIndex()]));
91+
}
92+
93+
public InfoItem getInfoItem() {
94+
return infoItem;
6295
}
6396

64-
public List<InfoItem> getInfoItems() {
65-
return infoItems;
97+
/**
98+
* The format argument to use with {@code git log}
99+
* @return the format argument to use {@code git log} command
100+
* @see <a href="https://git-scm.com/docs/git-log#_pretty_formats">Pretty Formats</a>
101+
*/
102+
public static Arg getFormatArgument() {
103+
Commandline.Argument arg = new Commandline.Argument();
104+
arg.setValue("--format=format:%H %aI %aE %aN");
105+
return arg;
66106
}
67107
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.scm.provider.git.gitexe.command.info;
20+
21+
import org.apache.maven.scm.provider.git.GitScmTestUtils;
22+
import org.apache.maven.scm.provider.git.command.info.GitInfoCommandTckTest;
23+
24+
public class GitExeInfoCommandTckTest extends GitInfoCommandTckTest {
25+
26+
public String getScmUrl() throws Exception {
27+
return GitScmTestUtils.getScmUrl(getRepositoryRoot(), "git");
28+
}
29+
}

maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/test/java/org/apache/maven/scm/provider/git/gitexe/command/info/GitInfoCommandTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public void testInfoCommandWithZeroShortRevision() throws Exception {
109109
InfoScmResult result = provider.info(repository, new ScmFileSet(getRepositoryRoot()), commandParameters);
110110
assertNotNull(result);
111111
assertTrue(
112-
"revision should be not empty, minimum 4 (see git help rev-parse --short)",
112+
"revision should be not empty, minimum 4 (similar to git help rev-parse --short)",
113113
result.getInfoItems().get(0).getRevision().length() >= 4);
114114
}
115115

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.scm.provider.git.command.info;
20+
21+
import org.apache.maven.scm.provider.git.GitScmTestUtils;
22+
import org.apache.maven.scm.tck.command.info.InfoCommandTckTest;
23+
24+
/**
25+
* @author <a href="mailto:[email protected]">Mark Struberg</a>
26+
*
27+
*/
28+
public abstract class GitInfoCommandTckTest extends InfoCommandTckTest {
29+
/** {@inheritDoc} */
30+
public void initRepo() throws Exception {
31+
GitScmTestUtils.initRepo("src/test/resources/repository/", getRepositoryRoot(), getWorkingDirectory());
32+
}
33+
}

0 commit comments

Comments
 (0)