From 36fd88ab60aba832da367325110367164f6a5bdd Mon Sep 17 00:00:00 2001
From: Kai Reinhard <K.Reinhard@micromata.de>
Date: Thu, 20 Dec 2018 00:41:23 +0000
Subject: [PATCH] Difftool...
---
borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgFilesystemItem.java | 108 +++++++++++++++++++++
borgbutler-core/src/main/java/de/micromata/borgbutler/DiffTool.java | 72 ++++++++++++++
borgbutler-core/src/test/java/de/micromata/borgbutler/DiffToolTest.java | 115 +++++++++++++++++++++++
3 files changed, 292 insertions(+), 3 deletions(-)
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/DiffTool.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/DiffTool.java
new file mode 100644
index 0000000..7709ba5
--- /dev/null
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/DiffTool.java
@@ -0,0 +1,72 @@
+package de.micromata.borgbutler;
+
+import de.micromata.borgbutler.json.borg.BorgFilesystemItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Extracts the differences between two archives of one repo.
+ */
+public class DiffTool {
+ private static Logger log = LoggerFactory.getLogger(DiffTool.class);
+
+ /**
+ * @param items Sorted list of items from the current archive.
+ * @param otherItems Sorted list of items of the archive to extract differences.
+ * @return A list of differing items (new, removed and modified ones).
+ */
+ public static List<BorgFilesystemItem> extractDifferences(List<BorgFilesystemItem> items, List<BorgFilesystemItem> otherItems) {
+ List<BorgFilesystemItem> currentList = items != null ? items : new ArrayList<>();
+ List<BorgFilesystemItem> otherList = otherItems != null ? otherItems : new ArrayList<>();
+ List<BorgFilesystemItem> result = new ArrayList<>();
+ Iterator<BorgFilesystemItem> currentIt = currentList.iterator();
+ Iterator<BorgFilesystemItem> otherIt = otherList.iterator();
+ BorgFilesystemItem current = null;
+ BorgFilesystemItem other = null;
+ while (true) {
+ if (current == null && currentIt.hasNext())
+ current = currentIt.next();
+ if (other == null && otherIt.hasNext())
+ other = otherIt.next();
+ if (current == null || other == null) {
+ break;
+ }
+ int cmp = current.compareTo(other);
+ if (cmp == 0) { // Items represents both the same file system item.
+ if (current.equals(other)) {
+ current = other = null; // increment both iterators.
+ continue;
+ }
+ // Current entry differs:
+ current.setDiffStatus(BorgFilesystemItem.DiffStatus.MODIFIED);
+ current.setDiffItem(other);
+ current.buildDifferencesString();
+ result.add(current);
+ current = other = null; // increment both iterators.
+ } else if (cmp < 0) {
+ result.add(current.setDiffStatus(BorgFilesystemItem.DiffStatus.NEW));
+ current = currentIt.hasNext() ? currentIt.next() : null;
+ } else {
+ result.add(other.setDiffStatus(BorgFilesystemItem.DiffStatus.REMOVED));
+ other = otherIt.hasNext() ? otherIt.next() : null;
+ }
+ }
+ while (currentIt.hasNext()) {
+ if (current == null)
+ current = currentIt.next();
+ result.add(current.setDiffStatus(BorgFilesystemItem.DiffStatus.NEW));
+ current = null;
+ }
+ while (otherIt.hasNext()) {
+ if (other == null)
+ other = otherIt.next();
+ result.add(other.setDiffStatus(BorgFilesystemItem.DiffStatus.REMOVED));
+ other = null;
+ }
+ return result;
+ }
+}
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgFilesystemItem.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgFilesystemItem.java
index b4d8c24..b751326 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgFilesystemItem.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/json/borg/BorgFilesystemItem.java
@@ -1,19 +1,24 @@
package de.micromata.borgbutler.json.borg;
-import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.Serializable;
public class BorgFilesystemItem implements Serializable, Comparable<BorgFilesystemItem> {
+ private transient static Logger log = LoggerFactory.getLogger(BorgFilesystemItem.class);
private static final long serialVersionUID = -5545350851640655468L;
/**
* If running in diff mode, this flag specifies the type of difference. Null represents unmodified.
*/
public enum DiffStatus {NEW, REMOVED, MODIFIED}
+
/**
* d (directory), - (file)
*/
@@ -24,6 +29,7 @@
* Unix mode, e. g. <tt>drwxr-xr-x</tt>
*/
@Getter
+ @Setter
private String mode;
@Getter
private String user;
@@ -51,6 +57,7 @@
@Setter
private String mtime;
@Getter
+ @Setter
private long size;
/**
* Represents the number of the file in the archive (for downloading). This field is created and only known by BorgButler.
@@ -75,7 +82,6 @@
* This String may used for displaying.
*/
@Getter
- @Setter
private String differences;
@Override
@@ -87,8 +93,104 @@
return -1;
}
if (o.path == null) {
- return 1;
+ return 1;
}
return path.compareToIgnoreCase(o.path);
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (obj == this) {
+ return true;
+ }
+ if (obj.getClass() != getClass()) {
+ return false;
+ }
+ BorgFilesystemItem rhs = (BorgFilesystemItem) obj;
+ return new EqualsBuilder()
+ .append(path, rhs.path)
+ .append(type, rhs.type)
+ .append(mode, rhs.mode)
+ .append(user, rhs.user)
+ .append(group, rhs.group)
+ .append(uid, rhs.uid)
+ .append(gid, rhs.gid)
+ .append(mtime, rhs.mtime)
+ .append(size, rhs.size)
+ .append(flags, rhs.flags)
+ .isEquals();
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder()
+ .append(path)
+ .append(type)
+ .append(mode)
+ .append(user)
+ .append(group)
+ .append(uid)
+ .append(gid)
+ .append(mtime)
+ .append(size)
+ .append(flags)
+ .toHashCode();
+ }
+
+ /**
+ * Compares all fields and creates human readable string with differences.
+ */
+ public void buildDifferencesString() {
+ if (diffItem == null) {
+ // Nothing to do.
+ return;
+ }
+ if (!StringUtils.equals(this.path, diffItem.path)) {
+ log.error("*** Internal error: Differences should only be made on same path object: current='" + path + "', other='" + diffItem.path + "'.");
+ return;
+ }
+ StringBuilder sb = new StringBuilder();
+ appendDiff(sb, "type", this.type, diffItem.type);
+ appendDiff(sb, "mode", this.mode, diffItem.mode);
+ appendDiff(sb, "user", this.user, diffItem.user);
+ appendDiff(sb, "group", this.group, diffItem.group);
+ appendDiff(sb, "uid", this.uid, diffItem.uid);
+ appendDiff(sb, "gid", this.gid, diffItem.gid);
+ appendDiff(sb, "mtime", this.mtime, diffItem.mtime);
+ appendDiff(sb, "size", this.size, diffItem.size);
+ if (sb.length() > 0) {
+ diffStatus = DiffStatus.MODIFIED;
+ this.differences = sb.toString();
+ }
+ }
+
+ private void appendDiff(StringBuilder sb, String field, String current, String other) {
+ if (StringUtils.equals(current, other)) {
+ // Not modified.
+ return;
+ }
+ if (sb.length() > 0) {
+ sb.append(", ");
+ }
+ sb.append(field + ":['" + other + "'->'" + current + "']");
+ }
+
+ private void appendDiff(StringBuilder sb, String field, long current, long other) {
+ if (current == other) {
+ // Not modified.
+ return;
+ }
+ if (sb.length() > 0) {
+ sb.append(", ");
+ }
+ sb.append(field + ": ['" + current + "' -> '" + other + "']");
+ }
+
+ @Override
+ public String toString() {
+ return path;
+ }
}
diff --git a/borgbutler-core/src/test/java/de/micromata/borgbutler/DiffToolTest.java b/borgbutler-core/src/test/java/de/micromata/borgbutler/DiffToolTest.java
new file mode 100644
index 0000000..975667b
--- /dev/null
+++ b/borgbutler-core/src/test/java/de/micromata/borgbutler/DiffToolTest.java
@@ -0,0 +1,115 @@
+package de.micromata.borgbutler;
+
+import de.micromata.borgbutler.json.borg.BorgFilesystemItem;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class DiffToolTest {
+ @Test
+ void differencesTest() {
+ BorgFilesystemItem i1 = create("etc", true, "drwx------", 0, "2018-11-21");
+ BorgFilesystemItem i2 = create("etc", true, "drwx------", 0, "2018-11-21");
+ assertTrue(i1.equals(i2));
+ i1.setType("-").setMode("drwxrwxrwx").setMtime("2018-11-22");
+ assertFalse(i1.equals(i2));
+ i1.setDiffItem(i2).buildDifferencesString();
+ assertEquals("type:['d'->'-'], mode:['drwx------'->'drwxrwxrwx'], mtime:['2018-11-21'->'2018-11-22']", i1.getDifferences());
+ }
+
+ @Test
+ void diffToolTest() {
+ List<BorgFilesystemItem> l1 = null;
+ List<BorgFilesystemItem> l2 = null;
+ List<BorgFilesystemItem> result;
+ assertEquals(0, DiffTool.extractDifferences(l1, l2).size());
+ l1 = create();
+ result = DiffTool.extractDifferences(l1, l2);
+ assertEquals(7, result.size());
+ assertEquals(BorgFilesystemItem.DiffStatus.NEW, result.get(0).getDiffStatus());
+ assertEquals(BorgFilesystemItem.DiffStatus.NEW, result.get(1).getDiffStatus());
+ result = DiffTool.extractDifferences(l2, l1);
+ assertEquals(7, result.size());
+ assertEquals(BorgFilesystemItem.DiffStatus.REMOVED, result.get(0).getDiffStatus());
+ assertEquals(BorgFilesystemItem.DiffStatus.REMOVED, result.get(1).getDiffStatus());
+
+ l1 = create();
+ l2 = create();
+ result = DiffTool.extractDifferences(l2, l1);
+ assertEquals(0, result.size());
+ remove(l2, "etc"); // 0
+ remove(l2, "etc/passwd"); // 1
+ remove(l1, "home/kai/.borgbutler/borgbutler-config-bak.json"); // 2
+ get(l1, "home/kai/.borgbutler/borgbutler-config.json").setSize(712).setMtime("2018-11-22"); // 3
+ result = DiffTool.extractDifferences(l1, l2);
+ assertEquals(4, result.size());
+ assertEquals(BorgFilesystemItem.DiffStatus.NEW, result.get(0).getDiffStatus());
+ assertEquals(BorgFilesystemItem.DiffStatus.NEW, result.get(1).getDiffStatus());
+ assertEquals(BorgFilesystemItem.DiffStatus.REMOVED, result.get(2).getDiffStatus());
+ assertEquals(BorgFilesystemItem.DiffStatus.MODIFIED, result.get(3).getDiffStatus());
+
+ result = DiffTool.extractDifferences(l2, l1);
+ assertEquals(4, result.size());
+ assertEquals(BorgFilesystemItem.DiffStatus.REMOVED, result.get(0).getDiffStatus());
+ assertEquals(BorgFilesystemItem.DiffStatus.REMOVED, result.get(1).getDiffStatus());
+ assertEquals(BorgFilesystemItem.DiffStatus.NEW, result.get(2).getDiffStatus());
+ assertEquals(BorgFilesystemItem.DiffStatus.MODIFIED, result.get(3).getDiffStatus());
+
+ l1 = create();
+ l2 = create();
+ remove(l2, "etc"); // 0
+ remove(l2, "etc/passwd"); // 1
+ remove(l1, "home/kai/.borgbutler/borgbutler-config.json"); // 2
+ result = DiffTool.extractDifferences(l1, l2);
+ assertEquals(3, result.size());
+ assertEquals(BorgFilesystemItem.DiffStatus.NEW, result.get(0).getDiffStatus());
+ assertEquals(BorgFilesystemItem.DiffStatus.NEW, result.get(1).getDiffStatus());
+ assertEquals(BorgFilesystemItem.DiffStatus.REMOVED, result.get(2).getDiffStatus());
+ result = DiffTool.extractDifferences(l2, l1);
+ assertEquals(3, result.size());
+ assertEquals(BorgFilesystemItem.DiffStatus.REMOVED, result.get(0).getDiffStatus());
+ assertEquals(BorgFilesystemItem.DiffStatus.REMOVED, result.get(1).getDiffStatus());
+ assertEquals(BorgFilesystemItem.DiffStatus.NEW, result.get(2).getDiffStatus());
+ }
+
+ private BorgFilesystemItem create(String path, boolean directory, String mode, long size, String mtime) {
+ return new BorgFilesystemItem()
+ .setPath(path)
+ .setType(directory ? "d" : "-")
+ .setMode(mode)
+ .setSize(size)
+ .setMtime(mtime);
+ }
+
+ private List<BorgFilesystemItem> create() {
+ List<BorgFilesystemItem> list = new ArrayList<>();
+ list.add(create("etc", true, "drwx------", 0, "2018-11-21"));
+ list.add(create("etc/passwd", false, "-rwx------", 100, "2018-11-21"));
+ list.add(create("home", true, "drwx------", 0, "2018-11-21"));
+ list.add(create("home/kai", true, "-rwx------", 0, "2018-11-21"));
+ list.add(create("home/kai/.borgbutler", true, "-rwx------", 0, "2018-11-21"));
+ list.add(create("home/kai/.borgbutler/borgbutler-config-bak.json", false, "drwxr-xr-x", 666, "2018-11-19"));
+ list.add(create("home/kai/.borgbutler/borgbutler-config.json", false, "drwxr-xr-x", 666, "2018-11-21"));
+ Collections.sort(list);
+ return list;
+ }
+
+ private void remove(List<BorgFilesystemItem> list, String path) {
+ BorgFilesystemItem item = get(list, path);
+ list.remove(item);
+ }
+
+ private BorgFilesystemItem get(List<BorgFilesystemItem> list, String path) {
+ for (BorgFilesystemItem item : list) {
+ if (item.getPath().equals(path)) {
+ return item;
+ }
+ }
+ fail();
+ return null;
+ }
+}
--
Gitblit v1.10.0