/* Copyright (c) 2013-2014 Boundless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/edl-v10.html
*
* Contributors:
* Victor Olaya (Boundless) - initial implementation
*/
package org.locationtech.geogig.cli.plumbing;
import java.io.IOException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import jline.console.ConsoleReader;
import org.geotools.util.Range;
import org.locationtech.geogig.api.GeoGIG;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.RevCommit;
import org.locationtech.geogig.api.RevPerson;
import org.locationtech.geogig.api.plumbing.ParseTimestamp;
import org.locationtech.geogig.api.plumbing.RevParse;
import org.locationtech.geogig.api.plumbing.diff.DiffEntry;
import org.locationtech.geogig.api.porcelain.DiffOp;
import org.locationtech.geogig.api.porcelain.LogOp;
import org.locationtech.geogig.cli.AbstractCommand;
import org.locationtech.geogig.cli.CLICommand;
import org.locationtech.geogig.cli.GeogigCLI;
import org.locationtech.geogig.cli.annotation.ReadOnly;
import com.beust.jcommander.Parameters;
import com.beust.jcommander.ParametersDelegate;
import com.google.common.base.Optional;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
/**
* Shows list of commits.
*
* @see org.locationtech.geogig.api.porcelain.LogOp
*/
@ReadOnly
@Parameters(commandNames = "rev-list", commandDescription = "Show list of commits")
public class RevList extends AbstractCommand implements CLICommand {
@ParametersDelegate
public final RevListArgs args = new RevListArgs();
private GeoGIG geogig;
private ConsoleReader console;
/**
* Executes the revlist command using the provided options.
*/
@Override
public void runInternal(GeogigCLI cli) throws IOException {
checkParameter(!args.commits.isEmpty(), "No starting commit provided");
geogig = cli.getGeogig();
LogOp op = geogig.command(LogOp.class).setTopoOrder(args.topo)
.setFirstParentOnly(args.firstParent);
for (String commit : args.commits) {
if (commit.contains("..")) {
checkParameter(args.commits.size() == 1,
"Only one value accepted when using <since>..<until> syntax");
List<String> sinceUntil = ImmutableList.copyOf((Splitter.on("..").split(commit)));
checkParameter(sinceUntil.size() == 2 || sinceUntil.size() == 1,
"Invalid refSpec format, expected [<commit> ...]|[<since>..<until>]: %s",
commit);
String sinceRefSpec;
String untilRefSpec;
if (sinceUntil.size() == 1) {
// just until was given
sinceRefSpec = null;
untilRefSpec = sinceUntil.get(0);
} else {
sinceRefSpec = sinceUntil.get(0);
untilRefSpec = sinceUntil.get(1);
}
if (sinceRefSpec != null) {
Optional<ObjectId> since;
since = geogig.command(RevParse.class).setRefSpec(sinceRefSpec).call();
checkParameter(since.isPresent(), "Object not found '%s'", sinceRefSpec);
op.setSince(since.get());
}
if (untilRefSpec != null) {
Optional<ObjectId> until;
until = geogig.command(RevParse.class).setRefSpec(untilRefSpec).call();
checkParameter(until.isPresent(), "Object not found '%s'", sinceRefSpec);
op.setUntil(until.get());
}
} else {
Optional<ObjectId> commitId = geogig.command(RevParse.class).setRefSpec(commit)
.call();
checkParameter(commitId.isPresent(), "Object not found '%s'", commit);
checkParameter(geogig.getRepository().commitExists(commitId.get()),
"%s does not resolve to a commit", commit);
op.addCommit(commitId.get());
}
}
if (args.author != null && !args.author.isEmpty()) {
op.setAuthor(args.author);
}
if (args.committer != null && !args.committer.isEmpty()) {
op.setCommiter(args.committer);
}
if (args.skip != null) {
op.setSkip(args.skip.intValue());
}
if (args.limit != null) {
op.setLimit(args.limit.intValue());
}
if (args.since != null || args.until != null) {
Date since = new Date(0);
Date until = new Date();
if (args.since != null) {
since = new Date(geogig.command(ParseTimestamp.class).setString(args.since).call());
}
if (args.until != null) {
until = new Date(geogig.command(ParseTimestamp.class).setString(args.until).call());
}
op.setTimeRange(new Range<Date>(Date.class, since, until));
}
if (!args.pathNames.isEmpty()) {
for (String s : args.pathNames) {
op.addPath(s);
}
}
Iterator<RevCommit> log = op.call();
console = cli.getConsole();
RawPrinter printer = new RawPrinter(args.changed);
while (log.hasNext()) {
printer.print(log.next());
console.flush();
}
}
private class RawPrinter {
private boolean showChanges;
public RawPrinter(boolean showChanges) {
this.showChanges = showChanges;
}
public void print(RevCommit commit) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("commit ").append(commit.getId().toString()).append('\n');
sb.append("tree ").append(commit.getTreeId().toString()).append('\n');
sb.append("parent");
for (ObjectId parentId : commit.getParentIds()) {
sb.append(' ').append(parentId.toString());
}
sb.append('\n');
sb.append("author ").append(format(commit.getAuthor())).append('\n');
sb.append("committer ").append(format(commit.getCommitter())).append('\n');
if (commit.getMessage() != null) {
sb.append("message\n");
sb.append("\t" + commit.getMessage().replace("\n", "\n\t"));
sb.append('\n');
}
if (showChanges) {
Iterator<DiffEntry> diff = geogig.command(DiffOp.class)
.setOldVersion(commit.parentN(0).or(ObjectId.NULL))
.setNewVersion(commit.getId()).call();
DiffEntry diffEntry;
sb.append("changes\n");
while (diff.hasNext()) {
diffEntry = diff.next();
String path = diffEntry.newPath() != null ? diffEntry.newPath() : diffEntry
.oldPath();
sb.append('\t').append(path).append(' ')
.append(diffEntry.oldObjectId().toString()).append(' ')
.append(diffEntry.newObjectId().toString()).append('\n');
}
}
console.println(sb.toString());
}
private String format(RevPerson p) {
StringBuilder sb = new StringBuilder();
sb.append(p.getName().or("[unknown]")).append(' ');
sb.append(p.getEmail().or("[unknown]")).append(' ');
sb.append(p.getTimestamp()).append(' ').append(p.getTimeZoneOffset());
return sb.toString();
}
}
}