diff --git a/CHANGELOG.md b/CHANGELOG.md
index 13a20975fb3cea4ebe6a120188e7999e5b59ee25..b70b2c179d9c340df77b3b42a103cbfd926b601f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
   by setting the server property `EXT_ENTITY_STATE=ENABLED`. See
   [!62](https://gitlab.com/caosdb/caosdb-server/-/merge_requests/62) for more
   information.
+* New version history feature. The "H" container flag retrieves the full
+  version history during a transaction (e.g. during Retrievals) and constructs
+  a tree of successors and predecessors of the requested entity version.
 * New query functionality: `ANY VERSION OF` modifier. E.g. `FIND ANY VERSION OF
   RECORD WITH pname=val` returns all current and old versions of records where
   `pname=val`. For further information, examples and limitations see the wiki
diff --git a/conf/core/cache.ccf b/conf/core/cache.ccf
index b4e1f93596a6170ef04ec373dc7a8d70e51fedcc..ad73cb86d259a1de8ecd836f8e72de8aa254793e 100644
--- a/conf/core/cache.ccf
+++ b/conf/core/cache.ccf
@@ -28,8 +28,8 @@ jcs.region.BACKEND_JobRules.cacheattributes.MaxObjects=103
 jcs.region.BACKEND_SparseEntities
 jcs.region.BACKEND_SparseEntities.cacheattributes.MaxObjects=1002
 
-jcs.region.BACKEND_RetrieveFullVersionInfo
-jcs.region.BACKEND_RetrieveFullVersionInfo.cacheattributes.MaxObjects=1006
+jcs.region.BACKEND_RetrieveVersionHistory
+jcs.region.BACKEND_RetrieveVersionHistory.cacheattributes.MaxObjects=1006
 
 # PAM UserSource Caching: Cached Items expire after 60 seconds if they are not requested (idle) and after 600 seconds max.
 # PAM_UnixUserGroups
diff --git a/conf/core/server.conf b/conf/core/server.conf
index 81ae7c2c43232024549c523636cfad090996d0fd..81b35a2a7498b0ad0f6feae7a20ce44dbe570865 100644
--- a/conf/core/server.conf
+++ b/conf/core/server.conf
@@ -68,7 +68,7 @@ MYSQL_USER_NAME=caosdb
 # Password for the user
 MYSQL_USER_PASSWORD=caosdb
 # Schema of mysql procedures and tables which is required by this CaosDB instance
-MYSQL_SCHEMA_VERSION=v4.0.0-rc1
+MYSQL_SCHEMA_VERSION=v4.0.0-rc2
 
 
 # --------------------------------------------------
diff --git a/src/main/java/org/caosdb/server/database/BackendTransaction.java b/src/main/java/org/caosdb/server/database/BackendTransaction.java
index 20ec5b71a7ae6f4dd908dc38145df1c41f035e8e..5bf978343d72272309d4f4ab0f039770419c87b8 100644
--- a/src/main/java/org/caosdb/server/database/BackendTransaction.java
+++ b/src/main/java/org/caosdb/server/database/BackendTransaction.java
@@ -59,7 +59,6 @@ import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveProp
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveQueryTemplateDefinition;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveRole;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveSparseEntity;
-import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveTransactionHistory;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveUser;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveVersionHistory;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRuleLoader;
@@ -116,7 +115,6 @@ import org.caosdb.server.database.backend.interfaces.RetrievePropertiesImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveQueryTemplateDefinitionImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveRoleImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveSparseEntityImpl;
-import org.caosdb.server.database.backend.interfaces.RetrieveTransactionHistoryImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveUserImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveVersionHistoryImpl;
 import org.caosdb.server.database.backend.interfaces.RuleLoaderImpl;
@@ -177,7 +175,6 @@ public abstract class BackendTransaction implements Undoable {
       setImpl(RetrieveAllImpl.class, MySQLRetrieveAll.class);
       setImpl(RegisterSubDomainImpl.class, MySQLRegisterSubDomain.class);
       setImpl(RetrieveDatatypesImpl.class, MySQLRetrieveDatatypes.class);
-      setImpl(RetrieveTransactionHistoryImpl.class, MySQLRetrieveTransactionHistory.class);
       setImpl(RetrieveUserImpl.class, MySQLRetrieveUser.class);
       setImpl(RetrieveParentsImpl.class, MySQLRetrieveParents.class);
       setImpl(GetFileRecordByPathImpl.class, MySQLGetFileRecordByPath.class);
diff --git a/src/main/java/org/caosdb/server/database/DatabaseUtils.java b/src/main/java/org/caosdb/server/database/DatabaseUtils.java
index d5910eb9a93003106369f87f50bec1c0013a61bf..4f6a58a4fc50fa91170fad6139384bccb413fa81 100644
--- a/src/main/java/org/caosdb/server/database/DatabaseUtils.java
+++ b/src/main/java/org/caosdb/server/database/DatabaseUtils.java
@@ -216,10 +216,7 @@ public class DatabaseUtils {
     ret.fileSize = rs.getLong("FileSize");
     ret.fileHash = bytes2UTF8(rs.getBytes("FileHash"));
 
-    ret.version = bytes2UTF8(rs.getBytes("Version"));
-    ret.versionSeconds = rs.getLong("VersionSeconds");
-    ret.versionNanos = rs.getInt("VersionNanos");
-
+    ret.versionId = bytes2UTF8(rs.getBytes("Version"));
     return ret;
   }
 
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java
index 1f1f32c69a260d374ea930ba4e1be63e34cf9ca9..1b945acf566db938f12847f758636dbfddeac3d1 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java
@@ -57,7 +57,7 @@ public class MySQLInsertSparseEntity extends MySQLTransaction implements InsertS
       try (final ResultSet rs = insertEntityStmt.executeQuery()) {
         if (rs.next()) {
           entity.id = rs.getInt("EntityID");
-          entity.version = DatabaseUtils.bytes2UTF8(rs.getBytes("Version"));
+          entity.versionId = DatabaseUtils.bytes2UTF8(rs.getBytes("Version"));
         } else {
           throw new TransactionException("Didn't get new EntityID back.");
         }
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveTransactionHistory.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveTransactionHistory.java
deleted file mode 100644
index 3273f49aa85776c4fd323ed4c027f4d1fcd3fbb0..0000000000000000000000000000000000000000
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveTransactionHistory.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package org.caosdb.server.database.backend.implementation.MySQL;
-
-import static org.caosdb.server.database.DatabaseUtils.bytes2UTF8;
-
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import org.caosdb.server.database.access.Access;
-import org.caosdb.server.database.backend.interfaces.RetrieveTransactionHistoryImpl;
-import org.caosdb.server.database.exceptions.TransactionException;
-import org.caosdb.server.database.proto.ProtoTransactionLogMessage;
-
-public class MySQLRetrieveTransactionHistory extends MySQLTransaction
-    implements RetrieveTransactionHistoryImpl {
-
-  public MySQLRetrieveTransactionHistory(final Access access) {
-    super(access);
-  }
-
-  public static final String STMT_RETRIEVE_HISTORY =
-      "SELECT transaction, realm, username, seconds, nanos FROM transaction_log WHERE entity_id=? ";
-
-  @Override
-  public ArrayList<ProtoTransactionLogMessage> execute(final Integer id)
-      throws TransactionException {
-    try {
-      final PreparedStatement stmt = prepareStatement(STMT_RETRIEVE_HISTORY);
-
-      final ArrayList<ProtoTransactionLogMessage> ret = new ArrayList<ProtoTransactionLogMessage>();
-      stmt.setInt(1, id);
-      final ResultSet rs = stmt.executeQuery();
-      try {
-        while (rs.next()) {
-          final String transaction = bytes2UTF8(rs.getBytes("transaction"));
-          final String realm = bytes2UTF8(rs.getBytes("realm"));
-          final String username = bytes2UTF8(rs.getBytes("username"));
-          final Integer seconds = rs.getInt("seconds");
-          final Integer nanos = rs.getInt("nanos");
-
-          ret.add(new ProtoTransactionLogMessage(transaction, realm, username, seconds, nanos));
-        }
-        return ret;
-      } finally {
-        rs.close();
-      }
-    } catch (final SQLException e) {
-      throw new TransactionException(e);
-    } catch (final ConnectionException e) {
-      throw new TransactionException(e);
-    }
-  }
-}
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveVersionHistory.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveVersionHistory.java
index a3c87ae6e97d3b26a99126b61d1d29db1ef6206a..7ecfbfb7ac70a4a5ec248eb0e2be2c590f0676aa 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveVersionHistory.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveVersionHistory.java
@@ -61,12 +61,16 @@ public class MySQLRetrieveVersionHistory extends MySQLTransaction
         String parentId = DatabaseUtils.bytes2UTF8(rs.getBytes("parent"));
         Long childSeconds = rs.getLong("child_seconds");
         Integer childNanos = rs.getInt("child_nanos");
+        String childUsername = DatabaseUtils.bytes2UTF8(rs.getBytes("child_username"));
+        String childRealm = DatabaseUtils.bytes2UTF8(rs.getBytes("child_realm"));
         VersionHistoryItem v = result.get(childId);
         if (v == null) {
           v = new VersionHistoryItem();
           v.id = childId;
           v.seconds = childSeconds;
           v.nanos = childNanos;
+          v.username = childUsername;
+          v.realm = childRealm;
           result.put(childId, v);
         }
 
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java
index 18b59ef9121bd2f488efcb657a3bf158b6e14308..1c8039253b4c5e74a8054dfa937fec23c55440e8 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateSparseEntity.java
@@ -80,7 +80,7 @@ public class MySQLUpdateSparseEntity extends MySQLTransaction implements UpdateS
       ResultSet rs = updateEntityStmt.executeQuery();
 
       if (rs.next()) {
-        spe.version = DatabaseUtils.bytes2UTF8(rs.getBytes("Version"));
+        spe.versionId = DatabaseUtils.bytes2UTF8(rs.getBytes("Version"));
       }
 
     } catch (final SQLIntegrityConstraintViolationException e) {
diff --git a/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveTransactionHistoryImpl.java b/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveTransactionHistoryImpl.java
deleted file mode 100644
index b02e4d9347e572941ab31e3a1a371470e1d8ee5c..0000000000000000000000000000000000000000
--- a/src/main/java/org/caosdb/server/database/backend/interfaces/RetrieveTransactionHistoryImpl.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package org.caosdb.server.database.backend.interfaces;
-
-import java.util.ArrayList;
-import org.caosdb.server.database.exceptions.TransactionException;
-import org.caosdb.server.database.proto.ProtoTransactionLogMessage;
-
-public interface RetrieveTransactionHistoryImpl extends BackendTransactionImpl {
-
-  public ArrayList<ProtoTransactionLogMessage> execute(Integer id) throws TransactionException;
-}
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/InsertSparseEntity.java b/src/main/java/org/caosdb/server/database/backend/transaction/InsertSparseEntity.java
index 1800168ebd2cd6ef4180e23046f95066597086e5..22720f836e5cbd4912f3aedccd25398e80a19324 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/InsertSparseEntity.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/InsertSparseEntity.java
@@ -69,6 +69,7 @@ public class InsertSparseEntity extends BackendTransaction {
               public void cleanUp() {}
             });
     this.entity.setId(e.id);
-    this.entity.setVersion(new Version(e.version));
+    this.entity.setVersion(new Version(e.versionId));
+    this.entity.getVersion().setHead(true);
   }
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveTransactionHistory.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveTransactionHistory.java
deleted file mode 100644
index 632ce20cfe71d0e116bbe3987a60c45b11085eec..0000000000000000000000000000000000000000
--- a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveTransactionHistory.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- * Copyright (C) 2019 IndiScale GmbH
- * Copyright (C) 2019 Timm Fitschen (t.fitschen@indiscale.com)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package org.caosdb.server.database.backend.transaction;
-
-import java.util.ArrayList;
-import org.caosdb.datetime.UTCDateTime;
-import org.caosdb.server.database.BackendTransaction;
-import org.caosdb.server.database.backend.interfaces.RetrieveTransactionHistoryImpl;
-import org.caosdb.server.database.exceptions.TransactionException;
-import org.caosdb.server.database.proto.ProtoTransactionLogMessage;
-import org.caosdb.server.entity.EntityInterface;
-import org.caosdb.server.utils.TransactionLogMessage;
-
-public class RetrieveTransactionHistory extends BackendTransaction {
-
-  private final EntityInterface entity;
-
-  public RetrieveTransactionHistory(final EntityInterface entity) {
-    this.entity = entity;
-  }
-
-  @Override
-  protected void execute() {
-    final RetrieveTransactionHistoryImpl t =
-        getImplementation(RetrieveTransactionHistoryImpl.class);
-    process(t.execute(entity.getId()));
-  }
-
-  private void process(final ArrayList<ProtoTransactionLogMessage> l) throws TransactionException {
-    for (final ProtoTransactionLogMessage t : l) {
-      final UTCDateTime dateTime = UTCDateTime.UTCSeconds(t.seconds, t.nanos);
-
-      this.entity.addTransactionLog(
-          new TransactionLogMessage(t.transaction, this.entity, t.username, dateTime));
-    }
-  }
-}
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveVersionHistory.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveVersionHistory.java
index 6077d6491fda93b96262b48783092578af37f4f8..a5079c4e033882c4b1068615551b5132e4ee0cba 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveVersionHistory.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveVersionHistory.java
@@ -22,58 +22,43 @@
  */
 package org.caosdb.server.database.backend.transaction;
 
-import java.util.Collection;
 import java.util.HashMap;
-import org.caosdb.server.database.CacheableBackendTransaction;
-import org.caosdb.server.database.backend.interfaces.RetrieveVersionHistoryImpl;
+import org.caosdb.datetime.UTCDateTime;
 import org.caosdb.server.database.exceptions.TransactionException;
 import org.caosdb.server.database.proto.VersionHistoryItem;
 import org.caosdb.server.entity.EntityInterface;
+import org.caosdb.server.entity.Version;
 
-public abstract class RetrieveVersionHistory
-    extends CacheableBackendTransaction<Integer, HashMap<String, VersionHistoryItem>> {
-
-  // TODO
-  // private static final ICacheAccess<String, Version> cache =
-  // Cache.getCache("BACKEND_RetrieveVersionHistory");
-  private EntityInterface entity;
-  private HashMap<String, VersionHistoryItem> map;
-
-  public static void removeCached(Integer entityId) {
-    // TODO
-  }
+public class RetrieveVersionHistory extends VersionTransaction {
 
   public RetrieveVersionHistory(EntityInterface e) {
-    super(null); // TODO caching
-    this.entity = e;
-  }
-
-  @Override
-  public HashMap<String, VersionHistoryItem> executeNoCache() throws TransactionException {
-    RetrieveVersionHistoryImpl impl = getImplementation(RetrieveVersionHistoryImpl.class);
-    return impl.execute(getKey());
+    super(e);
   }
 
-  /** After this method call, the version map is available to the object. */
   @Override
   protected void process(HashMap<String, VersionHistoryItem> map) throws TransactionException {
-    this.map = map;
+    super.process(map);
+    if (!map.isEmpty()) getEntity().setVersion(getHistory());
   }
 
   @Override
-  protected Integer getKey() {
-    return entity.getId();
-  }
-
-  public HashMap<String, VersionHistoryItem> getMap() {
-    return this.map;
-  }
-
-  public EntityInterface getEntity() {
-    return this.entity;
+  protected Version getVersion(String id) {
+    Version v = new Version(id);
+    VersionHistoryItem i = getHistoryItems().get(v.getId());
+    if (i != null) {
+      v.setDate(UTCDateTime.UTCSeconds(i.seconds, i.nanos));
+      v.setUsername(i.username);
+      v.setRealm(i.realm);
+    }
+    return v;
   }
 
-  public Collection<VersionHistoryItem> getList() {
-    return this.map.values();
+  private Version getHistory() {
+    Version v = getVersion(getEntity().getVersion().getId());
+    v.setSuccessors(getSuccessors(v.getId(), true));
+    v.setPredecessors(getPredecessors(v.getId(), true));
+    v.setHead(v.getSuccessors().isEmpty());
+    v.setCompleteHistory(true);
+    return v;
   }
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveVersionInfo.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveVersionInfo.java
index 7d9fe5fd7ea42cf3d6aa29a127a7f82f7ec1764c..8d0e54c95d17d0eb83af541b9fee2fb605b17ec9 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveVersionInfo.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveVersionInfo.java
@@ -23,14 +23,12 @@
 package org.caosdb.server.database.backend.transaction;
 
 import java.util.HashMap;
-import java.util.LinkedList;
-import org.caosdb.datetime.UTCDateTime;
 import org.caosdb.server.database.exceptions.TransactionException;
 import org.caosdb.server.database.proto.VersionHistoryItem;
 import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.entity.Version;
 
-public class RetrieveVersionInfo extends RetrieveVersionHistory {
+public class RetrieveVersionInfo extends VersionTransaction {
 
   public RetrieveVersionInfo(EntityInterface e) {
     super(e);
@@ -39,46 +37,19 @@ public class RetrieveVersionInfo extends RetrieveVersionHistory {
   @Override
   protected void process(HashMap<String, VersionHistoryItem> map) throws TransactionException {
     super.process(map); // Make the map available to the object.
-    if (!map.isEmpty()) getVersion();
+    if (!map.isEmpty()) getEntity().setVersion(getVersion());
   }
 
-  public Version getVersion() {
-    Version v = getEntity().getVersion();
-    VersionHistoryItem i = getMap().get(v.getId());
-    if (i != null) v.setDate(UTCDateTime.UTCSeconds(i.seconds, i.nanos));
-
-    v.setPredecessors(getPredecessors(v.getId()));
-    v.setSuccessors(getSuccessors(v.getId()));
-    return v;
-  }
-
-  /** Return a list of direct children. */
-  private LinkedList<Version> getSuccessors(String id) {
-    LinkedList<Version> result = new LinkedList<>();
-
-    outer:
-    for (VersionHistoryItem i : getList()) {
-      if (i.parents != null)
-        for (String p : i.parents) {
-          if (id.equals(p)) {
-            Version successor = new Version(i.id, i.seconds, i.nanos);
-            result.add(successor);
-            continue outer;
-          }
-        }
-    }
-    return result;
+  @Override
+  protected Version getVersion(String id) {
+    return new Version(id);
   }
 
-  /** Return a list of direct parents. */
-  private LinkedList<Version> getPredecessors(String id) {
-    LinkedList<Version> result = new LinkedList<>();
-    if (getMap().containsKey(id) && getMap().get(id).parents != null)
-      for (String p : getMap().get(id).parents) {
-        VersionHistoryItem i = getMap().get(p);
-        Version predecessor = new Version(i.id, i.seconds, i.nanos);
-        result.add(predecessor);
-      }
-    return result;
+  public Version getVersion() {
+    Version v = getVersion(getEntity().getVersion().getId());
+    v.setPredecessors(getPredecessors(v.getId(), false));
+    v.setSuccessors(getSuccessors(v.getId(), false));
+    v.setHead(v.getSuccessors().isEmpty());
+    return v;
   }
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/UpdateEntity.java b/src/main/java/org/caosdb/server/database/backend/transaction/UpdateEntity.java
index 9c20cb1d2d112e8d64ce5be2cfa8a7f8da20fe10..2bf965575884f29640a80ec3d3ab284b7779c8f2 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/UpdateEntity.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/UpdateEntity.java
@@ -54,6 +54,7 @@ public class UpdateEntity extends BackendTransaction {
 
         execute(new InsertEntityProperties(e));
 
+        VersionTransaction.removeCached(e.getId());
         execute(new RetrieveVersionInfo(e));
       }
     }
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/UpdateSparseEntity.java b/src/main/java/org/caosdb/server/database/backend/transaction/UpdateSparseEntity.java
index 2385ec843a3d499d1184b58eca06f8ae05cbe4f8..8548fb0477de9d8b52b18b8132af15295dbc9353 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/UpdateSparseEntity.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/UpdateSparseEntity.java
@@ -52,6 +52,6 @@ public class UpdateSparseEntity extends BackendTransaction {
 
     t.execute(spe);
 
-    this.entity.setVersion(new Version(spe.version));
+    this.entity.setVersion(new Version(spe.versionId));
   }
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/VersionTransaction.java b/src/main/java/org/caosdb/server/database/backend/transaction/VersionTransaction.java
new file mode 100644
index 0000000000000000000000000000000000000000..8a98564050f5fb860402aff0f55f1dd9de417a39
--- /dev/null
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/VersionTransaction.java
@@ -0,0 +1,159 @@
+/*
+ * ** header v3.0
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * ** end header
+ */
+package org.caosdb.server.database.backend.transaction;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
+import org.caosdb.server.caching.Cache;
+import org.caosdb.server.database.CacheableBackendTransaction;
+import org.caosdb.server.database.backend.interfaces.RetrieveVersionHistoryImpl;
+import org.caosdb.server.database.exceptions.TransactionException;
+import org.caosdb.server.database.proto.VersionHistoryItem;
+import org.caosdb.server.entity.EntityInterface;
+import org.caosdb.server.entity.Version;
+
+/**
+ * Abstract base class which retrieves and caches the full, but flag version history. The
+ * implementations then use the flat version history to construct either single version information
+ * items (see {@link RetrieveVersionInfo}) or the complete history as a tree (see {@link
+ * RetrieveVersionHistory})
+ *
+ * @author Timm Fitschen (t.fitschen@indiscale.com)
+ */
+public abstract class VersionTransaction
+    extends CacheableBackendTransaction<Integer, HashMap<String, VersionHistoryItem>> {
+
+  private static final ICacheAccess<Integer, HashMap<String, VersionHistoryItem>> cache =
+      Cache.getCache("BACKEND_RetrieveVersionHistory");
+  private EntityInterface entity;
+
+  /** A map of all history items which belong to this entity. The keys are the version ids. */
+  private HashMap<String, VersionHistoryItem> historyItems;
+
+  /**
+   * Invalidate a cache item. This should be called upon update of entities.
+   *
+   * @param entityId
+   */
+  public static void removeCached(Integer entityId) {
+    cache.remove(entityId);
+  }
+
+  public VersionTransaction(EntityInterface e) {
+    super(cache);
+    this.entity = e;
+  }
+
+  @Override
+  public HashMap<String, VersionHistoryItem> executeNoCache() throws TransactionException {
+    RetrieveVersionHistoryImpl impl = getImplementation(RetrieveVersionHistoryImpl.class);
+    return impl.execute(getKey());
+  }
+
+  /** After this method call, the version map is available to the object. */
+  @Override
+  protected void process(HashMap<String, VersionHistoryItem> historyItems)
+      throws TransactionException {
+    this.historyItems = historyItems;
+  }
+
+  @Override
+  protected Integer getKey() {
+    return entity.getId();
+  }
+
+  public HashMap<String, VersionHistoryItem> getHistoryItems() {
+    return this.historyItems;
+  }
+
+  public EntityInterface getEntity() {
+    return this.entity;
+  }
+
+  /**
+   * Return a list of direct predecessors. The predecessors are constructed by {@link
+   * #getVersion(String)}.
+   *
+   * <p>If transitive is true, this function is called recursively on the predecessors as well,
+   * resulting in a list of trees of predecessors, with the direct predecessors at the root(s).
+   *
+   * @param versionId
+   * @param transitive
+   * @return A list of predecessors.
+   */
+  protected List<Version> getPredecessors(String versionId, boolean transitive) {
+    LinkedList<Version> result = new LinkedList<>();
+    if (getHistoryItems().containsKey(versionId)
+        && getHistoryItems().get(versionId).parents != null)
+      for (String p : getHistoryItems().get(versionId).parents) {
+        Version predecessor = getVersion(p);
+        if (transitive) {
+          predecessor.setPredecessors(getPredecessors(p, transitive));
+        }
+        result.add(predecessor);
+      }
+    return result;
+  }
+
+  /**
+   * To be implemented by the base class. The idea is, that the base class decides which information
+   * is being included into the Version instance.
+   *
+   * @param versionId - the id of the version
+   * @return
+   */
+  protected abstract Version getVersion(String versionId);
+
+  /**
+   * Return a list of direct successors. The successors are constructed by {@link
+   * #getVersion(String)}.
+   *
+   * <p>If transitive is true, this function is called recursively on the successors as well,
+   * resulting in a list of trees of successors, with the direct successors at the root(s).
+   *
+   * @param versionId
+   * @param transitive
+   * @return A list of successors.
+   */
+  protected List<Version> getSuccessors(String versionId, boolean transitive) {
+    LinkedList<Version> result = new LinkedList<>();
+
+    outer:
+    for (VersionHistoryItem i : getHistoryItems().values()) {
+      if (i.parents != null)
+        for (String p : i.parents) {
+          if (versionId.equals(p)) {
+            Version successor = getVersion(i.id);
+            result.add(successor);
+            if (transitive) {
+              successor.setSuccessors(getSuccessors(i.id, transitive));
+            }
+            continue outer;
+          }
+        }
+    }
+    return result;
+  }
+}
diff --git a/src/main/java/org/caosdb/server/database/proto/ProtoTransactionLogMessage.java b/src/main/java/org/caosdb/server/database/proto/ProtoTransactionLogMessage.java
deleted file mode 100644
index dd5cdbebcda7dd2c5c7f9ca93be69972ecef0e2c..0000000000000000000000000000000000000000
--- a/src/main/java/org/caosdb/server/database/proto/ProtoTransactionLogMessage.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * ** header v3.0
- * This file is a part of the CaosDB Project.
- *
- * Copyright (C) 2018 Research Group Biomedical Physics,
- * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
- */
-package org.caosdb.server.database.proto;
-
-import java.io.Serializable;
-
-public class ProtoTransactionLogMessage implements Serializable {
-
-  public ProtoTransactionLogMessage(
-      final String transaction,
-      final String realm,
-      final String username,
-      final long seconds,
-      final int nanos) {
-    this.transaction = transaction;
-    this.realm = realm;
-    this.username = username;
-    this.seconds = seconds;
-    this.nanos = nanos;
-  }
-
-  public ProtoTransactionLogMessage() {}
-
-  private static final long serialVersionUID = -5856887517281480754L;
-  public String transaction = null;
-  public String realm;
-  public String username = null;
-  public Long seconds = null;
-  public Integer nanos = null;
-}
diff --git a/src/main/java/org/caosdb/server/database/proto/SparseEntity.java b/src/main/java/org/caosdb/server/database/proto/SparseEntity.java
index 3d5a29c9c4a69670d49347864b7ed885e758192f..d2292c58825ac6807e24f3ad3d086b29dd8a6e7e 100644
--- a/src/main/java/org/caosdb/server/database/proto/SparseEntity.java
+++ b/src/main/java/org/caosdb/server/database/proto/SparseEntity.java
@@ -38,9 +38,7 @@ public class SparseEntity extends VerySparseEntity {
   public String filePath = null;
   public Long fileSize = null;
   public Long fileChecked = null;
-  public String version = null;
-  public Long versionSeconds = null;
-  public Integer versionNanos = null;
+  public String versionId = null;
 
   @Override
   public String toString() {
@@ -54,9 +52,7 @@ public class SparseEntity extends VerySparseEntity {
         .append(this.fileHash)
         .append(this.filePath)
         .append(this.fileSize)
-        .append(this.version)
-        .append(this.versionSeconds)
-        .append(this.versionNanos)
+        .append(this.versionId)
         .toString();
   }
 }
diff --git a/src/main/java/org/caosdb/server/database/proto/VersionHistoryItem.java b/src/main/java/org/caosdb/server/database/proto/VersionHistoryItem.java
index 26109760ecde53ea20405bd02095cd951818677d..4c0a60ce57cce03cfaa7a2c4d41af7039c463838 100644
--- a/src/main/java/org/caosdb/server/database/proto/VersionHistoryItem.java
+++ b/src/main/java/org/caosdb/server/database/proto/VersionHistoryItem.java
@@ -1,13 +1,27 @@
 package org.caosdb.server.database.proto;
 
 import java.io.Serializable;
-import java.util.LinkedList;
+import java.util.List;
 
+/**
+ * This class is a flat, data-only representation of a single item of version information. This
+ * class is an intermediate representation which abstracts away the data base results and comes in a
+ * form which is easily cacheable.
+ *
+ * @author Timm Fitschen (t.fitschen@indiscale.com)
+ */
 public class VersionHistoryItem implements Serializable {
 
-  private static final long serialVersionUID = 6319362462701459355L;
+  private static final long serialVersionUID = 428030617967255942L;
   public String id = null;
-  public LinkedList<String> parents = null;
+  public List<String> parents = null;
   public Long seconds = null;
   public Integer nanos = null;
+  public String username = null;
+  public String realm = null;
+
+  @Override
+  public String toString() {
+    return id;
+  }
 }
diff --git a/src/main/java/org/caosdb/server/entity/Entity.java b/src/main/java/org/caosdb/server/entity/Entity.java
index 5522b9be33f2225412c0ba473d7c5c0eec2e4507..abbdaf3372a491e6b1b02bda12bbeff25f47de79 100644
--- a/src/main/java/org/caosdb/server/entity/Entity.java
+++ b/src/main/java/org/caosdb/server/entity/Entity.java
@@ -34,7 +34,6 @@ import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.authz.AuthorizationException;
 import org.apache.shiro.authz.Permission;
 import org.apache.shiro.subject.Subject;
-import org.caosdb.datetime.UTCDateTime;
 import org.caosdb.server.CaosDBException;
 import org.caosdb.server.accessControl.Principal;
 import org.caosdb.server.database.proto.SparseEntity;
@@ -1055,11 +1054,9 @@ public class Entity extends AbstractObservable implements EntityInterface {
     setId(spe.id);
     this.setRole(spe.role);
     setEntityACL(spe.acl);
-    UTCDateTime versionDate = null;
-    if (spe.versionSeconds != null) {
-      versionDate = UTCDateTime.UTCSeconds(spe.versionSeconds, spe.versionNanos);
+    if (spe.versionId != null) {
+      this.version = new Version(spe.versionId);
     }
-    this.version = new Version(spe.version, versionDate);
 
     if (!isNameOverride()) {
       setName(spe.name);
diff --git a/src/main/java/org/caosdb/server/entity/Version.java b/src/main/java/org/caosdb/server/entity/Version.java
index 3f9e148583686887fab39038aa427bccf1c69b25..5529c7c5f5e8c83d95ad5afae7c1ae786806df89 100644
--- a/src/main/java/org/caosdb/server/entity/Version.java
+++ b/src/main/java/org/caosdb/server/entity/Version.java
@@ -20,7 +20,7 @@
 
 package org.caosdb.server.entity;
 
-import java.util.LinkedList;
+import java.util.List;
 import org.caosdb.datetime.UTCDateTime;
 
 /**
@@ -31,25 +31,35 @@ import org.caosdb.datetime.UTCDateTime;
 public class Version {
 
   private String id = null;
-  private LinkedList<Version> predecessors = null;
-  private LinkedList<Version> successors = null;
+  private String username = null;
+  private String realm = null;
+  private List<Version> predecessors = null;
+  private List<Version> successors = null;
   private UTCDateTime date = null;
+  private boolean isHead = false;
+  private boolean isCompleteHistory = false;
 
   public Version(String id, long seconds, int nanos) {
-    this(id, UTCDateTime.UTCSeconds(seconds, nanos));
+    this(id, UTCDateTime.UTCSeconds(seconds, nanos), null, null);
   }
 
-  public Version(String id, UTCDateTime date) {
+  public Version(String id, UTCDateTime date, String username, String realm) {
     this.id = id;
     this.date = date;
+    this.username = username;
+    this.realm = realm;
   }
 
   public Version(String id) {
-    this(id, null);
+    this(id, null, null, null);
   }
 
   public Version() {}
 
+  public Version(String id, UTCDateTime timestamp) {
+    this(id, timestamp, null, null);
+  }
+
   public UTCDateTime getDate() {
     return date;
   }
@@ -66,19 +76,55 @@ public class Version {
     this.id = id;
   }
 
-  public LinkedList<Version> getSuccessors() {
+  public List<Version> getSuccessors() {
     return successors;
   }
 
-  public void setSuccessors(LinkedList<Version> successors) {
+  public void setSuccessors(List<Version> successors) {
     this.successors = successors;
   }
 
-  public LinkedList<Version> getPredecessors() {
+  public List<Version> getPredecessors() {
     return predecessors;
   }
 
-  public void setPredecessors(LinkedList<Version> predecessors) {
+  public void setPredecessors(List<Version> predecessors) {
     this.predecessors = predecessors;
   }
+
+  public String getRealm() {
+    return realm;
+  }
+
+  public void setRealm(String realm) {
+    this.realm = realm;
+  }
+
+  public String getUsername() {
+    return username;
+  }
+
+  public void setUsername(String username) {
+    this.username = username;
+  }
+
+  public void setDate(Long timestamp) {
+    this.date = UTCDateTime.SystemMillisToUTCDateTime(timestamp);
+  }
+
+  public boolean isHead() {
+    return isHead;
+  }
+
+  public void setHead(boolean isHead) {
+    this.isHead = isHead;
+  }
+
+  public boolean isCompleteHistory() {
+    return isCompleteHistory;
+  }
+
+  public void setCompleteHistory(boolean isCompleteHistory) {
+    this.isCompleteHistory = isCompleteHistory;
+  }
 }
diff --git a/src/main/java/org/caosdb/server/entity/xml/VersionXMLSerializer.java b/src/main/java/org/caosdb/server/entity/xml/VersionXMLSerializer.java
index 65ec9880dfef43a4e460bba692ece6c9f87faa7e..40d19a5f3a0ef6d9448ed8567840e610be712391 100644
--- a/src/main/java/org/caosdb/server/entity/xml/VersionXMLSerializer.java
+++ b/src/main/java/org/caosdb/server/entity/xml/VersionXMLSerializer.java
@@ -30,28 +30,43 @@ import org.jdom2.Element;
  * @author Timm Fitschen <t.fitschen@indiscale.com>
  */
 class VersionXMLSerializer {
+
   public Element toElement(Version version) {
-    Element result = new Element("Version");
-    result.setAttribute("id", version.getId());
-    if (version.getDate() != null) {
-      result.setAttribute("date", version.getDate().toDateTimeString(TimeZone.getDefault()));
-    }
+    return toElement(version, "Version");
+  }
+
+  private Element toElement(Version version, String tag) {
+    Element element = new Element(tag);
+    setAttributes(version, element);
     if (version.getPredecessors() != null) {
       for (Version p : version.getPredecessors()) {
-        Element predecessor = new Element("Predecessor");
-        predecessor.setAttribute("id", p.getId());
-        predecessor.setAttribute("date", p.getDate().toDateTimeString(TimeZone.getDefault()));
-        result.addContent(predecessor);
+        element.addContent(toElement(p, "Predecessor"));
       }
     }
     if (version.getSuccessors() != null) {
       for (Version s : version.getSuccessors()) {
-        Element successor = new Element("Successor");
-        successor.setAttribute("id", s.getId());
-        successor.setAttribute("date", s.getDate().toDateTimeString(TimeZone.getDefault()));
-        result.addContent(successor);
+        element.addContent(toElement(s, "Successor"));
       }
     }
-    return result;
+    return element;
+  }
+
+  private void setAttributes(Version version, Element element) {
+    element.setAttribute("id", version.getId());
+    if (version.getUsername() != null) {
+      element.setAttribute("username", version.getUsername());
+    }
+    if (version.getRealm() != null) {
+      element.setAttribute("realm", version.getRealm());
+    }
+    if (version.getDate() != null) {
+      element.setAttribute("date", version.getDate().toDateTimeString(TimeZone.getDefault()));
+    }
+    if (version.isHead()) {
+      element.setAttribute("head", "true");
+    }
+    if (version.isCompleteHistory()) {
+      element.setAttribute("completeHistory", "true");
+    }
   }
 }
diff --git a/src/main/java/org/caosdb/server/jobs/core/History.java b/src/main/java/org/caosdb/server/jobs/core/History.java
index a7c58979f49b2a7abdc313924dc8fb6b86bbffd4..5594b5afdb841436dbdd63f96b8e2c44faef51c6 100644
--- a/src/main/java/org/caosdb/server/jobs/core/History.java
+++ b/src/main/java/org/caosdb/server/jobs/core/History.java
@@ -23,7 +23,7 @@
 package org.caosdb.server.jobs.core;
 
 import org.apache.shiro.authz.AuthorizationException;
-import org.caosdb.server.database.backend.transaction.RetrieveTransactionHistory;
+import org.caosdb.server.database.backend.transaction.RetrieveVersionHistory;
 import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.jobs.FlagJob;
 import org.caosdb.server.jobs.JobAnnotation;
@@ -32,6 +32,11 @@ import org.caosdb.server.permissions.EntityPermission;
 import org.caosdb.server.utils.EntityStatus;
 import org.caosdb.server.utils.ServerMessages;
 
+/**
+ * Retrieves the complete version history of each entity and appends it to the entity.
+ *
+ * @author Timm Fitschen (t.fitschen@indiscale.com)
+ */
 @JobAnnotation(time = JobExecutionTime.POST_TRANSACTION, flag = "H")
 public class History extends FlagJob {
 
@@ -42,7 +47,7 @@ public class History extends FlagJob {
         if (entity.getId() != null && entity.getId() > 0) {
           try {
             entity.checkPermission(EntityPermission.RETRIEVE_HISTORY);
-            final RetrieveTransactionHistory t = new RetrieveTransactionHistory(entity);
+            final RetrieveVersionHistory t = new RetrieveVersionHistory(entity);
             execute(t);
           } catch (final AuthorizationException e) {
             entity.setEntityStatus(EntityStatus.UNQUALIFIED);
diff --git a/src/main/java/org/caosdb/server/transaction/Insert.java b/src/main/java/org/caosdb/server/transaction/Insert.java
index f60fc1286ae909eb7fd999aa3af1f8379dd331ff..d37f7ce516f36252466555ea18fe249f2730fea1 100644
--- a/src/main/java/org/caosdb/server/transaction/Insert.java
+++ b/src/main/java/org/caosdb/server/transaction/Insert.java
@@ -27,7 +27,6 @@ import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.transaction.InsertEntity;
 import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.entity.FileProperties;
-import org.caosdb.server.entity.Version;
 import org.caosdb.server.entity.container.InsertContainer;
 import org.caosdb.server.entity.container.TransactionContainer;
 import org.caosdb.server.permissions.EntityACL;
@@ -104,10 +103,6 @@ public class Insert extends WriteTransaction<InsertContainer> {
   public void insert(final TransactionContainer container, final Access access) throws Exception {
     if (container.getStatus().ordinal() >= EntityStatus.QUALIFIED.ordinal()) {
       execute(new InsertEntity(container), access);
-      for (EntityInterface e : container) {
-        // TODO move to InsertEntity transaction
-        e.setVersion(new Version(e.getVersion().getId(), this.getTimestamp()));
-      }
     }
   }