You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by fr...@apache.org on 2016/05/27 09:23:02 UTC
svn commit: r1745725 - in
/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak:
plugins/segment/FileStoreDiff.java run/FileStoreDiffCommand.java
run/PrintingDiff.java run/SegmentTarUtils.java run/SegmentUtils.java
Author: frm
Date: Fri May 27 09:23:02 2016
New Revision: 1745725
URL: http://svn.apache.org/viewvc?rev=1745725&view=rev
Log:
OAK-4340 - Add a flag to choose between segment store implementations in the "tarmkdiff" command
Added:
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/PrintingDiff.java (with props)
Removed:
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/plugins/segment/FileStoreDiff.java
Modified:
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FileStoreDiffCommand.java
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentTarUtils.java
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentUtils.java
Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FileStoreDiffCommand.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FileStoreDiffCommand.java?rev=1745725&r1=1745724&r2=1745725&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FileStoreDiffCommand.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FileStoreDiffCommand.java Fri May 27 09:23:02 2016
@@ -17,13 +17,59 @@
package org.apache.jackrabbit.oak.run;
-import org.apache.jackrabbit.oak.plugins.segment.FileStoreDiff;
+import static java.util.Arrays.asList;
+
+import java.io.File;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
class FileStoreDiffCommand implements Command {
@Override
public void execute(String... args) throws Exception {
- FileStoreDiff.main(args);
+ OptionParser parser = new OptionParser();
+ OptionSpec<?> help = parser.acceptsAll(asList("h", "?", "help"), "show help").forHelp();
+ OptionSpec<File> storeO = parser.nonOptions("Path to segment store (required)").ofType(File.class);
+ OptionSpec<File> outO = parser.accepts("output", "Output file").withRequiredArg().ofType(File.class).defaultsTo(defaultOutFile());
+ OptionSpec<?> listOnlyO = parser.accepts("list", "Lists available revisions");
+ OptionSpec<String> intervalO = parser.accepts("diff", "Revision diff interval. Ex '--diff=R0..R1'. 'HEAD' can be used to reference the latest head revision, ie. '--diff=R0..HEAD'").withRequiredArg().ofType(String.class);
+ OptionSpec<?> incrementalO = parser.accepts("incremental", "Runs diffs between each subsequent revisions in the provided interval");
+ OptionSpec<String> pathO = parser.accepts("path", "Filter diff by given path").withRequiredArg().ofType(String.class).defaultsTo("/");
+ OptionSpec<?> ignoreSNFEsO = parser.accepts("ignore-snfes", "Ignores SegmentNotFoundExceptions and continues running the diff (experimental)");
+ OptionSpec segmentTar = parser.accepts("segment-tar", "Use oak-segment-tar instead of oak-segment");
+ OptionSet options = parser.parse(args);
+
+ if (options.has(help)) {
+ parser.printHelpOn(System.out);
+ System.exit(0);
+ }
+
+ File store = storeO.value(options);
+
+ if (store == null) {
+ parser.printHelpOn(System.out);
+ System.exit(1);
+ }
+
+ File out = outO.value(options);
+
+ boolean listOnly = options.has(listOnlyO);
+ String interval = intervalO.value(options);
+ boolean incremental = options.has(incrementalO);
+ String path = pathO.value(options);
+ boolean ignoreSNFEs = options.has(ignoreSNFEsO);
+
+ if (options.has(segmentTar)) {
+ SegmentTarUtils.diff(store, out, listOnly, interval, incremental, path, ignoreSNFEs);
+ } else {
+ SegmentUtils.diff(store, out, listOnly, interval, incremental, path, ignoreSNFEs);
+ }
+ }
+
+ private File defaultOutFile() {
+ return new File("diff_" + System.currentTimeMillis() + ".log");
}
}
Added: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/PrintingDiff.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/PrintingDiff.java?rev=1745725&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/PrintingDiff.java (added)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/PrintingDiff.java Fri May 27 09:23:02 2016
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.run;
+
+import static com.google.common.collect.Iterables.transform;
+import static org.apache.commons.io.FileUtils.byteCountToDisplaySize;
+import static org.apache.jackrabbit.oak.api.Type.BINARIES;
+import static org.apache.jackrabbit.oak.api.Type.BINARY;
+import static org.apache.jackrabbit.oak.api.Type.STRING;
+import static org.apache.jackrabbit.oak.api.Type.STRINGS;
+import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.MISSING_NODE;
+
+import java.io.PrintWriter;
+
+import com.google.common.base.Function;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+
+final class PrintingDiff implements NodeStateDiff {
+
+ private static final Function<Blob, String> BLOB_LENGTH = new Function<Blob, String>() {
+
+ @Override
+ public String apply(Blob b) {
+ return safeGetLength(b);
+ }
+
+ private String safeGetLength(Blob b) {
+ try {
+ return byteCountToDisplaySize(b.length());
+ } catch (IllegalStateException e) {
+ // missing BlobStore probably
+ }
+ return "[N/A]";
+ }
+
+ };
+
+ private final PrintWriter pw;
+
+ private final String path;
+
+ private final boolean skipProps;
+
+ PrintingDiff(PrintWriter pw, String path) {
+ this(pw, path, false);
+ }
+
+ private PrintingDiff(PrintWriter pw, String path, boolean skipProps) {
+ this.pw = pw;
+ this.path = path;
+ this.skipProps = skipProps;
+ }
+
+ @Override
+ public boolean propertyAdded(PropertyState after) {
+ if (!skipProps) {
+ pw.println(" + " + toString(after));
+ }
+ return true;
+ }
+
+ @Override
+ public boolean propertyChanged(PropertyState before, PropertyState after) {
+ if (!skipProps) {
+ pw.println(" ^ " + before.getName());
+ pw.println(" - " + toString(before));
+ pw.println(" + " + toString(after));
+ }
+ return true;
+ }
+
+ @Override
+ public boolean propertyDeleted(PropertyState before) {
+ if (!skipProps) {
+ pw.println(" - " + toString(before));
+ }
+ return true;
+ }
+
+ @Override
+ public boolean childNodeAdded(String name, NodeState after) {
+ String p = concat(path, name);
+ pw.println("+ " + p);
+ return after.compareAgainstBaseState(EMPTY_NODE, new PrintingDiff(
+ pw, p));
+ }
+
+ @Override
+ public boolean childNodeChanged(String name, NodeState before, NodeState after) {
+ String p = concat(path, name);
+ pw.println("^ " + p);
+ return after.compareAgainstBaseState(before,
+ new PrintingDiff(pw, p));
+ }
+
+ @Override
+ public boolean childNodeDeleted(String name, NodeState before) {
+ String p = concat(path, name);
+ pw.println("- " + p);
+ return MISSING_NODE.compareAgainstBaseState(before, new PrintingDiff(pw, p, true));
+ }
+
+ private static String toString(PropertyState ps) {
+ StringBuilder val = new StringBuilder();
+ val.append(ps.getName()).append("<").append(ps.getType()).append(">");
+ if (ps.getType() == BINARY) {
+ String v = BLOB_LENGTH.apply(ps.getValue(BINARY));
+ val.append(" = {").append(v).append("}");
+ } else if (ps.getType() == BINARIES) {
+ String v = transform(ps.getValue(BINARIES), BLOB_LENGTH).toString();
+ val.append("[").append(ps.count()).append("] = ").append(v);
+ } else if (ps.isArray()) {
+ val.append("[").append(ps.count()).append("] = ").append(ps.getValue(STRINGS));
+ } else {
+ val.append(" = ").append(ps.getValue(STRING));
+ }
+ return ps.getName() + "<" + ps.getType() + ">" + val.toString();
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/PrintingDiff.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentTarUtils.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentTarUtils.java?rev=1745725&r1=1745724&r2=1745725&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentTarUtils.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentTarUtils.java Fri May 27 09:23:02 2016
@@ -17,13 +17,17 @@
package org.apache.jackrabbit.oak.run;
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Lists.reverse;
import static com.google.common.collect.Sets.newHashSet;
import static com.google.common.collect.Sets.newTreeSet;
import static com.google.common.escape.Escapers.builder;
import static org.apache.commons.io.FileUtils.byteCountToDisplaySize;
+import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.checkFileStoreVersionOrFail;
import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.isValidFileStoreOrFail;
import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.newBasicReadOnlyBlobStore;
+import static org.apache.jackrabbit.oak.segment.RecordId.fromString;
import static org.apache.jackrabbit.oak.segment.RecordType.NODE;
import static org.apache.jackrabbit.oak.segment.SegmentGraph.writeGCGraph;
import static org.apache.jackrabbit.oak.segment.SegmentGraph.writeSegmentGraph;
@@ -35,12 +39,14 @@ import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
@@ -73,6 +79,7 @@ import org.apache.jackrabbit.oak.segment
import org.apache.jackrabbit.oak.segment.SegmentId;
import org.apache.jackrabbit.oak.segment.SegmentNodeState;
import org.apache.jackrabbit.oak.segment.SegmentNodeStore;
+import org.apache.jackrabbit.oak.segment.SegmentNotFoundException;
import org.apache.jackrabbit.oak.segment.SegmentPropertyState;
import org.apache.jackrabbit.oak.segment.SegmentTracker;
import org.apache.jackrabbit.oak.segment.file.FileStore;
@@ -209,6 +216,150 @@ final class SegmentTarUtils {
}
}
+ static void diff(File store, File out, boolean listOnly, String interval, boolean incremental, String path, boolean ignoreSNFEs) throws IOException {
+ if (listOnly) {
+ listRevs(store, out);
+ } else {
+ diff(store, interval, incremental, out, path, ignoreSNFEs);
+ }
+ }
+
+ private static void listRevs(File store, File out) throws IOException {
+ System.out.println("Store " + store);
+ System.out.println("Writing revisions to " + out);
+ List<String> revs = readRevisions(store);
+ if (revs.isEmpty()) {
+ System.out.println("No revisions found.");
+ return;
+ }
+ PrintWriter pw = new PrintWriter(out);
+ try {
+ for (String r : revs) {
+ pw.println(r);
+ }
+ } finally {
+ pw.close();
+ }
+ }
+
+ private static List<String> readRevisions(File store) {
+ File journal = new File(store, "journal.log");
+ if (!journal.exists()) {
+ return newArrayList();
+ }
+
+ List<String> revs = newArrayList();
+ JournalReader journalReader = null;
+ try {
+ journalReader = new JournalReader(journal);
+ try {
+ revs = newArrayList(journalReader.iterator());
+ } finally {
+ journalReader.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (journalReader != null) {
+ journalReader.close();
+ }
+ } catch (IOException e) {
+ }
+ }
+ return revs;
+ }
+
+ private static void diff(File dir, String interval, boolean incremental, File out, String filter, boolean ignoreSNFEs) throws IOException {
+ System.out.println("Store " + dir);
+ System.out.println("Writing diff to " + out);
+ String[] tokens = interval.trim().split("\\.\\.");
+ if (tokens.length != 2) {
+ System.out.println("Error parsing revision interval '" + interval
+ + "'.");
+ return;
+ }
+ ReadOnlyStore store = FileStore.builder(dir).withBlobStore(newBasicReadOnlyBlobStore()).buildReadOnly();
+ RecordId idL = null;
+ RecordId idR = null;
+ try {
+ if (tokens[0].equalsIgnoreCase("head")) {
+ idL = store.getHead().getRecordId();
+ } else {
+ idL = fromString(store.getTracker(), tokens[0]);
+ }
+ if (tokens[1].equalsIgnoreCase("head")) {
+ idR = store.getHead().getRecordId();
+ } else {
+ idR = fromString(store.getTracker(), tokens[1]);
+ }
+ } catch (IllegalArgumentException ex) {
+ System.out.println("Error parsing revision interval '" + interval + "': " + ex.getMessage());
+ ex.printStackTrace();
+ return;
+ }
+
+ long start = System.currentTimeMillis();
+ PrintWriter pw = new PrintWriter(out);
+ try {
+ if (incremental) {
+ List<String> revs = readRevisions(dir);
+ System.out.println("Generating diff between " + idL + " and " + idR + " incrementally. Found " + revs.size() + " revisions.");
+
+ int s = revs.indexOf(idL.toString10());
+ int e = revs.indexOf(idR.toString10());
+ if (s == -1 || e == -1) {
+ System.out.println("Unable to match input revisions with FileStore.");
+ return;
+ }
+ List<String> revDiffs = revs.subList(Math.min(s, e), Math.max(s, e) + 1);
+ if (s > e) {
+ // reverse list
+ revDiffs = reverse(revDiffs);
+ }
+ if (revDiffs.size() < 2) {
+ System.out.println("Nothing to diff: " + revDiffs);
+ return;
+ }
+ Iterator<String> revDiffsIt = revDiffs.iterator();
+ RecordId idLt = fromString(store.getTracker(), revDiffsIt.next());
+ while (revDiffsIt.hasNext()) {
+ RecordId idRt = fromString(store.getTracker(), revDiffsIt.next());
+ boolean good = diff(store, idLt, idRt, filter, pw);
+ idLt = idRt;
+ if (!good && !ignoreSNFEs) {
+ break;
+ }
+ }
+ } else {
+ System.out.println("Generating diff between " + idL + " and " + idR);
+ diff(store, idL, idR, filter, pw);
+ }
+ } finally {
+ pw.close();
+ }
+ long dur = System.currentTimeMillis() - start;
+ System.out.println("Finished in " + dur + " ms.");
+ }
+
+ private static boolean diff(ReadOnlyStore store, RecordId idL, RecordId idR, String filter, PrintWriter pw) throws IOException {
+ pw.println("rev " + idL + ".." + idR);
+ try {
+ NodeState before = new SegmentNodeState(store, idL).getChildNode("root");
+ NodeState after = new SegmentNodeState(store, idR).getChildNode("root");
+ for (String name : elements(filter)) {
+ before = before.getChildNode(name);
+ after = after.getChildNode(name);
+ }
+ after.compareAgainstBaseState(before, new PrintingDiff(pw, filter));
+ return true;
+ } catch (SegmentNotFoundException ex) {
+ System.out.println(ex.getMessage());
+ pw.println("#SNFE " + ex.getSegmentId());
+ return false;
+ }
+ }
+
private static void debugFileStore(FileStore store) {
Map<SegmentId, List<SegmentId>> idmap = Maps.newHashMap();
int dataCount = 0;
@@ -420,10 +571,10 @@ final class SegmentTarUtils {
RecordId id1 = store.getHead().getRecordId();
RecordId id2 = null;
if (matcher.group(2) != null) {
- id1 = RecordId.fromString(store.getTracker(),
+ id1 = fromString(store.getTracker(),
matcher.group(3));
if (matcher.group(4) != null) {
- id2 = RecordId.fromString(store.getTracker(),
+ id2 = fromString(store.getTracker(),
matcher.group(5));
}
}
Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentUtils.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentUtils.java?rev=1745725&r1=1745724&r2=1745725&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentUtils.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentUtils.java Fri May 27 09:23:02 2016
@@ -17,15 +17,19 @@
package org.apache.jackrabbit.oak.run;
+import static com.google.common.collect.Lists.reverse;
import static com.google.common.collect.Sets.newHashSet;
import static com.google.common.collect.Sets.newTreeSet;
import static com.google.common.escape.Escapers.builder;
import static javax.jcr.PropertyType.BINARY;
import static javax.jcr.PropertyType.STRING;
import static org.apache.commons.io.FileUtils.byteCountToDisplaySize;
+import static org.apache.jackrabbit.oak.commons.PathUtils.elements;
import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.newBasicReadOnlyBlobStore;
import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.openFileStore;
import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.openReadOnlyFileStore;
+import static org.apache.jackrabbit.oak.plugins.segment.FileStoreHelper.readRevisions;
+import static org.apache.jackrabbit.oak.plugins.segment.RecordId.fromString;
import static org.apache.jackrabbit.oak.plugins.segment.RecordType.NODE;
import static org.apache.jackrabbit.oak.plugins.segment.SegmentGraph.writeGCGraph;
import static org.apache.jackrabbit.oak.plugins.segment.SegmentGraph.writeSegmentGraph;
@@ -37,12 +41,14 @@ import static org.slf4j.LoggerFactory.ge
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
@@ -75,10 +81,12 @@ import org.apache.jackrabbit.oak.plugins
import org.apache.jackrabbit.oak.plugins.segment.SegmentId;
import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeState;
import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNotFoundException;
import org.apache.jackrabbit.oak.plugins.segment.SegmentPropertyState;
import org.apache.jackrabbit.oak.plugins.segment.SegmentTracker;
import org.apache.jackrabbit.oak.plugins.segment.compaction.CompactionStrategy;
import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
+import org.apache.jackrabbit.oak.plugins.segment.file.FileStore.ReadOnlyStore;
import org.apache.jackrabbit.oak.plugins.segment.file.JournalReader;
import org.apache.jackrabbit.oak.plugins.segment.file.tooling.RevisionHistory;
import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
@@ -225,6 +233,122 @@ class SegmentUtils {
}
}
+ static void diff(File store, File out, boolean listOnly, String interval, boolean incremental, String path, boolean ignoreSNFEs) throws IOException {
+ if (listOnly) {
+ listRevs(store, out);
+ } else {
+ diff(store, interval, incremental, out, path, ignoreSNFEs);
+ }
+ }
+
+ private static void listRevs(File store, File out) throws IOException {
+ System.out.println("Store " + store);
+ System.out.println("Writing revisions to " + out);
+ List<String> revs = readRevisions(store);
+ if (revs.isEmpty()) {
+ System.out.println("No revisions found.");
+ return;
+ }
+ PrintWriter pw = new PrintWriter(out);
+ try {
+ for (String r : revs) {
+ pw.println(r);
+ }
+ } finally {
+ pw.close();
+ }
+ }
+
+ private static void diff(File dir, String interval, boolean incremental, File out, String filter, boolean ignoreSNFEs) throws IOException {
+ System.out.println("Store " + dir);
+ System.out.println("Writing diff to " + out);
+ String[] tokens = interval.trim().split("\\.\\.");
+ if (tokens.length != 2) {
+ System.out.println("Error parsing revision interval '" + interval
+ + "'.");
+ return;
+ }
+ ReadOnlyStore store = FileStore.builder(dir).withBlobStore(newBasicReadOnlyBlobStore()).buildReadOnly();
+ RecordId idL = null;
+ RecordId idR = null;
+ try {
+ if (tokens[0].equalsIgnoreCase("head")) {
+ idL = store.getHead().getRecordId();
+ } else {
+ idL = fromString(store.getTracker(), tokens[0]);
+ }
+ if (tokens[1].equalsIgnoreCase("head")) {
+ idR = store.getHead().getRecordId();
+ } else {
+ idR = fromString(store.getTracker(), tokens[1]);
+ }
+ } catch (IllegalArgumentException ex) {
+ System.out.println("Error parsing revision interval '" + interval + "': " + ex.getMessage());
+ ex.printStackTrace();
+ return;
+ }
+
+ long start = System.currentTimeMillis();
+ PrintWriter pw = new PrintWriter(out);
+ try {
+ if (incremental) {
+ List<String> revs = readRevisions(dir);
+ System.out.println("Generating diff between " + idL + " and " + idR + " incrementally. Found " + revs.size() + " revisions.");
+
+ int s = revs.indexOf(idL.toString10());
+ int e = revs.indexOf(idR.toString10());
+ if (s == -1 || e == -1) {
+ System.out.println("Unable to match input revisions with FileStore.");
+ return;
+ }
+ List<String> revDiffs = revs.subList(Math.min(s, e), Math.max(s, e) + 1);
+ if (s > e) {
+ // reverse list
+ revDiffs = reverse(revDiffs);
+ }
+ if (revDiffs.size() < 2) {
+ System.out.println("Nothing to diff: " + revDiffs);
+ return;
+ }
+ Iterator<String> revDiffsIt = revDiffs.iterator();
+ RecordId idLt = fromString(store.getTracker(), revDiffsIt.next());
+ while (revDiffsIt.hasNext()) {
+ RecordId idRt = fromString(store.getTracker(), revDiffsIt.next());
+ boolean good = diff(store, idLt, idRt, filter, pw);
+ idLt = idRt;
+ if (!good && !ignoreSNFEs) {
+ break;
+ }
+ }
+ } else {
+ System.out.println("Generating diff between " + idL + " and " + idR);
+ diff(store, idL, idR, filter, pw);
+ }
+ } finally {
+ pw.close();
+ }
+ long dur = System.currentTimeMillis() - start;
+ System.out.println("Finished in " + dur + " ms.");
+ }
+
+ private static boolean diff(ReadOnlyStore store, RecordId idL, RecordId idR, String filter, PrintWriter pw) throws IOException {
+ pw.println("rev " + idL + ".." + idR);
+ try {
+ NodeState before = new SegmentNodeState(idL).getChildNode("root");
+ NodeState after = new SegmentNodeState(idR).getChildNode("root");
+ for (String name : elements(filter)) {
+ before = before.getChildNode(name);
+ after = after.getChildNode(name);
+ }
+ after.compareAgainstBaseState(before, new PrintingDiff(pw, filter));
+ return true;
+ } catch (SegmentNotFoundException ex) {
+ System.out.println(ex.getMessage());
+ pw.println("#SNFE " + ex.getSegmentId());
+ return false;
+ }
+ }
+
private static void debugFileStore(FileStore store) {
Map<SegmentId, List<SegmentId>> idmap = Maps.newHashMap();
int dataCount = 0;