diff --git a/test/src/module/command/backupTest.c b/test/src/module/command/backupTest.c
index be0cd2618..0a7dfbebb 100644
--- a/test/src/module/command/backupTest.c
+++ b/test/src/module/command/backupTest.c
@@ -68,15 +68,206 @@ testBlockDelta(const BlockMap *const blockMap, const size_t blockSize, const siz
 Get a list of all files in the backup and a redacted version of the manifest that can be tested against a static string
 ***********************************************************************************************************************************/
 static String *
-testBackupValidateList(
+testBackupValidateFile(
     const Storage *const storage, const String *const path, Manifest *const manifest, const ManifestData *const manifestData,
-    const CipherType cipherType, const String *const cipherPass, String *const result)
+    const String *const fileName, uint64_t fileSize, ManifestFilePack **const filePack, const CipherType cipherType,
+    const String *const cipherPass)
 {
     FUNCTION_HARNESS_BEGIN();
         FUNCTION_HARNESS_PARAM(STORAGE, storage);
         FUNCTION_HARNESS_PARAM(STRING, path);
         FUNCTION_HARNESS_PARAM(MANIFEST, manifest);
         FUNCTION_HARNESS_PARAM_P(VOID, manifestData);
+        FUNCTION_HARNESS_PARAM(STRING, fileName);
+        FUNCTION_HARNESS_PARAM(UINT64, fileSize);
+        FUNCTION_HARNESS_PARAM_P(VOID, filePack);
+        FUNCTION_HARNESS_PARAM(STRING_ID, cipherType);
+        FUNCTION_HARNESS_PARAM(STRING, cipherPass);
+    FUNCTION_HARNESS_END();
+
+    String *const result = strNew();
+    ManifestFile file = manifestFileUnpack(manifest, *filePack);
+
+    if (file.bundleId != 0)
+        strCatFmt(result, "%s/%s {file", strZ(fileName), strZ(file.name));
+    else
+        strCatFmt(result, "%s {file", strZ(fileName));
+
+    // Validate repo checksum
+    // -------------------------------------------------------------------------------------------------------------
+    if (file.checksumRepoSha1 != NULL)
+    {
+        StorageRead *read = storageNewReadP(
+            storage, strNewFmt("%s/%s", strZ(path), strZ(fileName)), .offset = file.bundleOffset,
+            .limit = VARUINT64(file.sizeRepo));
+        const Buffer *const checksum = cryptoHashOne(hashTypeSha1, storageGetP(read));
+
+        if (!bufEq(checksum, BUF(file.checksumRepoSha1, HASH_TYPE_SHA1_SIZE)))
+            THROW_FMT(AssertError, "'%s' repo checksum does match manifest", strZ(file.name));
+    }
+
+    // Calculate checksum/size and decompress if needed
+    // -------------------------------------------------------------------------------------------------------------
+    uint64_t size = 0;
+    const Buffer *checksum = NULL;
+
+    // If block incremental
+    if (file.blockIncrMapSize != 0)
+    {
+        // Read block map
+        StorageRead *read = storageNewReadP(
+            storage, strNewFmt("%s/%s", strZ(path), strZ(fileName)),
+            .offset = file.bundleOffset + file.sizeRepo - file.blockIncrMapSize,
+            .limit = VARUINT64(file.blockIncrMapSize));
+
+        if (cipherType != cipherTypeNone)
+        {
+            ioFilterGroupAdd(
+                ioReadFilterGroup(storageReadIo(read)),
+                cipherBlockNewP(cipherModeDecrypt, cipherType, BUFSTR(cipherPass), .raw = true));
+        }
+
+        ioReadOpen(storageReadIo(read));
+
+        const BlockMap *const blockMap = blockMapNewRead(
+            storageReadIo(read), file.blockIncrSize, file.blockIncrChecksumSize);
+
+        // Build map log
+        String *const mapLog = strNew();
+        const BlockMapItem *blockMapItemLast = NULL;
+
+        for (unsigned int blockMapIdx = 0; blockMapIdx < blockMapSize(blockMap); blockMapIdx++)
+        {
+            const BlockMapItem *const blockMapItem = blockMapGet(blockMap, blockMapIdx);
+            const bool superBlockChange =
+                blockMapItemLast == NULL || blockMapItemLast->reference != blockMapItem->reference ||
+                blockMapItemLast->offset != blockMapItem->offset;
+
+            if (superBlockChange && blockMapIdx != 0)
+                strCatChr(mapLog, '}');
+
+            if (!strEmpty(mapLog))
+                strCatChr(mapLog, ',');
+
+            if (superBlockChange)
+                strCatFmt(mapLog, "%u:{", blockMapItem->reference);
+
+            strCatFmt(mapLog, "%" PRIu64, blockMapItem->block);
+
+            blockMapItemLast = blockMapItem;
+        }
+
+        // Check blocks
+        Buffer *fileBuffer = bufNew((size_t)file.size);
+        bufUsedSet(fileBuffer, bufSize(fileBuffer));
+
+        BlockDelta *const blockDelta = blockDeltaNew(
+            blockMap, file.blockIncrSize, file.blockIncrChecksumSize, NULL, cipherType, cipherPass,
+            manifestData->backupOptionCompressType);
+
+        for (unsigned int readIdx = 0; readIdx < blockDeltaReadSize(blockDelta); readIdx++)
+        {
+            const BlockDeltaRead *const read = blockDeltaReadGet(blockDelta, readIdx);
+            const String *const blockName = backupFileRepoPathP(
+                strLstGet(manifestReferenceList(manifest), read->reference), .manifestName = file.name,
+                .bundleId = read->bundleId, .blockIncr = true);
+
+            IoRead *blockRead = storageReadIo(
+                storageNewReadP(
+                    storage, blockName, .offset = read->offset, .limit = VARUINT64(read->size)));
+            ioReadOpen(blockRead);
+
+            const BlockDeltaWrite *deltaWrite = blockDeltaNext(blockDelta, read, blockRead);
+
+            while (deltaWrite != NULL)
+            {
+                // Update size and file
+                size += bufUsed(deltaWrite->block);
+                memcpy(
+                    bufPtr(fileBuffer) + deltaWrite->offset, bufPtr(deltaWrite->block),
+                    bufUsed(deltaWrite->block));
+
+                deltaWrite = blockDeltaNext(blockDelta, read, blockRead);
+            }
+        }
+
+        strCatFmt(result, ", m=%s}", strZ(mapLog));
+
+        checksum = cryptoHashOne(hashTypeSha1, fileBuffer);
+    }
+    // Else normal file
+    else
+    {
+        StorageRead *read = storageNewReadP(
+            storage, strNewFmt("%s/%s", strZ(path), strZ(fileName)), .offset = file.bundleOffset,
+            .limit = VARUINT64(file.sizeRepo));
+        const bool raw = file.bundleId != 0 && manifest->pub.data.bundleRaw;
+
+        if (cipherType != cipherTypeNone)
+        {
+            ioFilterGroupAdd(
+                ioReadFilterGroup(storageReadIo(read)),
+                cipherBlockNewP(cipherModeDecrypt, cipherType, BUFSTR(cipherPass), .raw = raw));
+        }
+
+        if (manifestData->backupOptionCompressType != compressTypeNone)
+        {
+            ioFilterGroupAdd(
+                ioReadFilterGroup(storageReadIo(read)),
+                decompressFilterP(manifestData->backupOptionCompressType, .raw = raw));
+        }
+
+        ioFilterGroupAdd(ioReadFilterGroup(storageReadIo(read)), cryptoHashNew(hashTypeSha1));
+
+        size = bufUsed(storageGetP(read));
+        checksum = pckReadBinP(
+            ioFilterGroupResultP(ioReadFilterGroup(storageReadIo(read)), CRYPTO_HASH_FILTER_TYPE));
+    }
+
+    // Validate checksum
+    if (!bufEq(checksum, BUF(file.checksumSha1, HASH_TYPE_SHA1_SIZE)))
+        THROW_FMT(AssertError, "'%s' checksum does match manifest", strZ(file.name));
+
+    strCatFmt(result, ", s=%" PRIu64, size);
+
+    // Test size and repo-size
+    // -------------------------------------------------------------------------------------------------------------
+    if (size != file.size)
+        THROW_FMT(AssertError, "'%s' size does match manifest", strZ(file.name));
+
+    // Repo size can only be compared to file size when not bundled
+    if (file.bundleId == 0 && fileSize != file.sizeRepo)
+        THROW_FMT(AssertError, "'%s' repo size does match manifest", strZ(file.name));
+
+    // pg_control and WAL headers have different checksums depending on cpu architecture so remove the checksum from
+    // the test output.
+    // -------------------------------------------------------------------------------------------------------------
+    if (strEqZ(file.name, MANIFEST_TARGET_PGDATA "/" PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL) ||
+        strBeginsWith(
+            file.name, strNewFmt(MANIFEST_TARGET_PGDATA "/%s/", strZ(pgWalPath(manifestData->pgVersion)))))
+    {
+        file.checksumSha1 = NULL;
+    }
+
+    strCatZ(result, "}\n");
+
+    // Update changes to manifest file
+    manifestFilePackUpdate(manifest, filePack, &file);
+
+    FUNCTION_HARNESS_RETURN(STRING, result);
+}
+
+static String *
+testBackupValidateList(
+    const Storage *const storage, const String *const path, Manifest *const manifest, const ManifestData *const manifestData,
+    StringList *const manifestFileList, const CipherType cipherType, const String *const cipherPass, String *const result)
+{
+    FUNCTION_HARNESS_BEGIN();
+        FUNCTION_HARNESS_PARAM(STORAGE, storage);
+        FUNCTION_HARNESS_PARAM(STRING, path);
+        FUNCTION_HARNESS_PARAM(MANIFEST, manifest);
+        FUNCTION_HARNESS_PARAM_P(VOID, manifestData);
+        FUNCTION_HARNESS_PARAM(STRING_LIST, manifestFileList);
         FUNCTION_HARNESS_PARAM(STRING_ID, cipherType);
         FUNCTION_HARNESS_PARAM(STRING, cipherPass);
         FUNCTION_HARNESS_PARAM(STRING, result);
@@ -161,177 +352,16 @@ testBackupValidateList(
                 // -----------------------------------------------------------------------------------------------------------------
                 for (unsigned int fileIdx = 0; fileIdx < lstSize(fileList); fileIdx++)
                 {
+                    // Remove this file from manifest file list to track what has been updated
                     ManifestFilePack **const filePack = *(ManifestFilePack ***)lstGet(fileList, fileIdx);
-                    ManifestFile file = manifestFileUnpack(manifest, *filePack);
+                    const unsigned int manifestFileIdx = strLstFindIdxP(
+                        manifestFileList, (String *const)*filePack, .required = true);
+                    strLstRemoveIdx(manifestFileList, manifestFileIdx);
 
-                    if (bundle)
-                        strCatFmt(result, "%s/%s {file", strZ(info.name), strZ(file.name));
-                    else
-                        strCatFmt(result, "%s {file", strZ(info.name));
-
-                    // Validate repo checksum
-                    // -------------------------------------------------------------------------------------------------------------
-                    if (file.checksumRepoSha1 != NULL)
-                    {
-                        StorageRead *read = storageNewReadP(
-                            storage, strNewFmt("%s/%s", strZ(path), strZ(info.name)), .offset = file.bundleOffset,
-                            .limit = VARUINT64(file.sizeRepo));
-                        const Buffer *const checksum = cryptoHashOne(hashTypeSha1, storageGetP(read));
-
-                        if (!bufEq(checksum, BUF(file.checksumRepoSha1, HASH_TYPE_SHA1_SIZE)))
-                            THROW_FMT(AssertError, "'%s' repo checksum does match manifest", strZ(file.name));
-                    }
-
-                    // Calculate checksum/size and decompress if needed
-                    // -------------------------------------------------------------------------------------------------------------
-                    uint64_t size = 0;
-                    const Buffer *checksum = NULL;
-
-                    // If block incremental
-                    if (file.blockIncrMapSize != 0)
-                    {
-                        // Read block map
-                        StorageRead *read = storageNewReadP(
-                            storage, strNewFmt("%s/%s", strZ(path), strZ(info.name)),
-                            .offset = file.bundleOffset + file.sizeRepo - file.blockIncrMapSize,
-                            .limit = VARUINT64(file.blockIncrMapSize));
-
-                        if (cipherType != cipherTypeNone)
-                        {
-                            ioFilterGroupAdd(
-                                ioReadFilterGroup(storageReadIo(read)),
-                                cipherBlockNewP(cipherModeDecrypt, cipherType, BUFSTR(cipherPass), .raw = true));
-                        }
-
-                        ioReadOpen(storageReadIo(read));
-
-                        const BlockMap *const blockMap = blockMapNewRead(
-                            storageReadIo(read), file.blockIncrSize, file.blockIncrChecksumSize);
-
-                        // Build map log
-                        String *const mapLog = strNew();
-                        const BlockMapItem *blockMapItemLast = NULL;
-
-                        for (unsigned int blockMapIdx = 0; blockMapIdx < blockMapSize(blockMap); blockMapIdx++)
-                        {
-                            const BlockMapItem *const blockMapItem = blockMapGet(blockMap, blockMapIdx);
-                            const bool superBlockChange =
-                                blockMapItemLast == NULL || blockMapItemLast->reference != blockMapItem->reference ||
-                                blockMapItemLast->offset != blockMapItem->offset;
-
-                            if (superBlockChange && blockMapIdx != 0)
-                                strCatChr(mapLog, '}');
-
-                            if (!strEmpty(mapLog))
-                                strCatChr(mapLog, ',');
-
-                            if (superBlockChange)
-                                strCatFmt(mapLog, "%u:{", blockMapItem->reference);
-
-                            strCatFmt(mapLog, "%" PRIu64, blockMapItem->block);
-
-                            blockMapItemLast = blockMapItem;
-                        }
-
-                        // Check blocks
-                        Buffer *fileBuffer = bufNew((size_t)file.size);
-                        bufUsedSet(fileBuffer, bufSize(fileBuffer));
-
-                        BlockDelta *const blockDelta = blockDeltaNew(
-                            blockMap, file.blockIncrSize, file.blockIncrChecksumSize, NULL, cipherType, cipherPass,
-                            manifestData->backupOptionCompressType);
-
-                        for (unsigned int readIdx = 0; readIdx < blockDeltaReadSize(blockDelta); readIdx++)
-                        {
-                            const BlockDeltaRead *const read = blockDeltaReadGet(blockDelta, readIdx);
-                            const String *const blockName = backupFileRepoPathP(
-                                strLstGet(manifestReferenceList(manifest), read->reference), .manifestName = file.name,
-                                .bundleId = read->bundleId, .blockIncr = true);
-
-                            IoRead *blockRead = storageReadIo(
-                                storageNewReadP(
-                                    storage, blockName, .offset = read->offset, .limit = VARUINT64(read->size)));
-                            ioReadOpen(blockRead);
-
-                            const BlockDeltaWrite *deltaWrite = blockDeltaNext(blockDelta, read, blockRead);
-
-                            while (deltaWrite != NULL)
-                            {
-                                // Update size and file
-                                size += bufUsed(deltaWrite->block);
-                                memcpy(
-                                    bufPtr(fileBuffer) + deltaWrite->offset, bufPtr(deltaWrite->block),
-                                    bufUsed(deltaWrite->block));
-
-                                deltaWrite = blockDeltaNext(blockDelta, read, blockRead);
-                            }
-                        }
-
-                        strCatFmt(result, ", m=%s}", strZ(mapLog));
-
-                        checksum = cryptoHashOne(hashTypeSha1, fileBuffer);
-                    }
-                    // Else normal file
-                    else
-                    {
-                        StorageRead *read = storageNewReadP(
-                            storage, strNewFmt("%s/%s", strZ(path), strZ(info.name)), .offset = file.bundleOffset,
-                            .limit = VARUINT64(file.sizeRepo));
-                        const bool raw = bundle && manifest->pub.data.bundleRaw;
-
-                        if (cipherType != cipherTypeNone)
-                        {
-                            ioFilterGroupAdd(
-                                ioReadFilterGroup(storageReadIo(read)),
-                                cipherBlockNewP(cipherModeDecrypt, cipherType, BUFSTR(cipherPass), .raw = raw));
-                        }
-
-                        if (manifestData->backupOptionCompressType != compressTypeNone)
-                        {
-                            ioFilterGroupAdd(
-                                ioReadFilterGroup(storageReadIo(read)),
-                                decompressFilterP(manifestData->backupOptionCompressType, .raw = raw));
-                        }
-
-                        ioFilterGroupAdd(ioReadFilterGroup(storageReadIo(read)), cryptoHashNew(hashTypeSha1));
-
-                        size = bufUsed(storageGetP(read));
-                        checksum = pckReadBinP(
-                            ioFilterGroupResultP(ioReadFilterGroup(storageReadIo(read)), CRYPTO_HASH_FILTER_TYPE));
-                    }
-
-                    // Validate checksum
-                    if (!bufEq(checksum, BUF(file.checksumSha1, HASH_TYPE_SHA1_SIZE)))
-                        THROW_FMT(AssertError, "'%s' checksum does match manifest", strZ(file.name));
-
-                    strCatFmt(result, ", s=%" PRIu64, size);
-
-                    // Test size and repo-size
-                    // -------------------------------------------------------------------------------------------------------------
-                    if (size != file.size)
-                        THROW_FMT(AssertError, "'%s' size does match manifest", strZ(file.name));
-
-                    // Repo size can only be compared to file size when not bundled
-                    if (!bundle)
-                    {
-                        if (info.size != file.sizeRepo)
-                            THROW_FMT(AssertError, "'%s' repo size does match manifest", strZ(file.name));
-                    }
-
-                    // pg_control and WAL headers have different checksums depending on cpu architecture so remove the checksum from
-                    // the test output.
-                    // -------------------------------------------------------------------------------------------------------------
-                    if (strEqZ(file.name, MANIFEST_TARGET_PGDATA "/" PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL) ||
-                        strBeginsWith(
-                            file.name, strNewFmt(MANIFEST_TARGET_PGDATA "/%s/", strZ(pgWalPath(manifestData->pgVersion)))))
-                    {
-                        file.checksumSha1 = NULL;
-                    }
-
-                    strCatZ(result, "}\n");
-
-                    // Update changes to manifest file
-                    manifestFilePackUpdate(manifest, filePack, &file);
+                    strCat(
+                        result,
+                        testBackupValidateFile(
+                            storage, path, manifest, manifestData, info.name, info.size, filePack, cipherType, cipherPass));
                 }
 
                 break;
@@ -370,30 +400,6 @@ testBackupValidateList(
         }
     }
 
-    // Check all files in manifest. Since the scan above maps from files to the manifest, any referenced files will not be checked.
-    // -----------------------------------------------------------------------------------------------------------------------------
-    for (unsigned int fileIdx = 0; fileIdx < manifestFileTotal(manifest); fileIdx++)
-    {
-        ManifestFilePack **const filePack = lstGet(manifest->pub.fileList, fileIdx);
-        ManifestFile file = manifestFileUnpack(manifest, *filePack);
-
-        // If compressed or block incremental then set the repo-size to size so it will not be in test output. Even the same
-        // compression algorithm can give slightly different results based on the version so repo-size is not deterministic for
-        // compression. Block incremental maps increase repo-size in a non-obvious way.
-        if (manifestData->backupOptionCompressType != compressTypeNone || file.blockIncrMapSize != 0)
-            file.sizeRepo = file.size;
-
-        // Bundle id/offset are too noisy so remove them. They are verified against size/checksum and listed with the files.
-        file.bundleId = 0;
-        file.bundleOffset = 0;
-
-        // Remove repo checksum since it has been validated
-        file.checksumRepoSha1 = NULL;
-
-        // Update changes to manifest file
-        manifestFilePackUpdate(manifest, filePack, &file);
-    }
-
     FUNCTION_HARNESS_RETURN(STRING, result);
 }
 
@@ -429,13 +435,50 @@ testBackupValidate(const Storage *const storage, const String *const path, TestB
         const InfoBackup *const infoBackup = infoBackupLoadFile(
             storageRepo(), INFO_BACKUP_PATH_FILE_STR, param.cipherType == 0 ? cipherTypeNone : param.cipherType,
             param.cipherPass == NULL ? NULL : STR(param.cipherPass));
-
         Manifest *manifest = manifestLoadFile(
             storage, strNewFmt("%s/" BACKUP_MANIFEST_FILE, strZ(path)), param.cipherType == 0 ? cipherTypeNone : param.cipherType,
             param.cipherPass == NULL ? NULL : infoBackupCipherPass(infoBackup));
-        testBackupValidateList(
-            storage, path, manifest, manifestData(manifest), param.cipherType == 0 ? cipherTypeNone : param.cipherType,
-            param.cipherPass == NULL ? NULL : manifestCipherSubPass(manifest), result);
+
+        // Build list of files in the manifest
+        StringList *const manifestFileList = strLstNew();
+
+        for (unsigned int fileIdx = 0; fileIdx < manifestFileTotal(manifest); fileIdx++)
+            strLstAdd(manifestFileList, manifestFileUnpack(manifest, manifestFilePackGet(manifest, fileIdx)).name);
+
+        // Validate files on disk against the manifest
+        const CipherType cipherType = param.cipherType == 0 ? cipherTypeNone : param.cipherType;
+        const String *const cipherPass = param.cipherPass == NULL ? NULL : manifestCipherSubPass(manifest);
+
+        testBackupValidateList(storage, path, manifest, manifestData(manifest), manifestFileList, cipherType, cipherPass, result);
+
+        // Check remaining files in the manifest -- these should all be references
+        for (unsigned int manifestFileIdx = 0; manifestFileIdx < strLstSize(manifestFileList); manifestFileIdx++)
+        {
+            ManifestFilePack **const filePack = manifestFilePackFindInternal(
+                manifest, strLstGet(manifestFileList, manifestFileIdx));
+            const ManifestFile file = manifestFileUnpack(manifest, *filePack);
+
+            // No need to check zero-length files in bundled backups
+            if (manifestData(manifest)->bundle && file.size == 0)
+                continue;
+
+            // Error if reference is NULL
+            if (file.reference == NULL)
+                THROW_FMT(AssertError, "manifest file '%s' not in backup but does not have a reference", strZ(file.name));
+
+            strCat(
+                result,
+                testBackupValidateFile(
+                    storage, strPath(path), manifest, manifestData(manifest),
+                    strSub(
+                        backupFileRepoPathP(
+                            file.reference,
+                            .manifestName = file.name, .bundleId = file.bundleId,
+                            .compressType = manifestData(manifest)->backupOptionCompressType,
+                            .blockIncr = file.blockIncrMapSize != 0),
+                        sizeof(STORAGE_REPO_BACKUP)),
+                    file.sizeRepo, filePack, cipherType, cipherPass));
+        }
 
         // Make sure both backup.manifest files exist since we skipped them in the callback above
         if (!storageExistsP(storage, strNewFmt("%s/" BACKUP_MANIFEST_FILE, strZ(path))))
@@ -444,6 +487,30 @@ testBackupValidate(const Storage *const storage, const String *const path, TestB
         if (!storageExistsP(storage, strNewFmt("%s/" BACKUP_MANIFEST_FILE INFO_COPY_EXT, strZ(path))))
             THROW(AssertError, BACKUP_MANIFEST_FILE INFO_COPY_EXT " is missing");
 
+        // Update manifest to make the output a bit simpler
+        // -------------------------------------------------------------------------------------------------------------------------
+        for (unsigned int fileIdx = 0; fileIdx < manifestFileTotal(manifest); fileIdx++)
+        {
+            ManifestFilePack **const filePack = lstGet(manifest->pub.fileList, fileIdx);
+            ManifestFile file = manifestFileUnpack(manifest, *filePack);
+
+            // If compressed or block incremental then set the repo-size to size so it will not be in test output. Even the same
+            // compression algorithm can give slightly different results based on the version so repo-size is not deterministic for
+            // compression. Block incremental maps increase repo-size in a non-obvious way.
+            if (manifestData(manifest)->backupOptionCompressType != compressTypeNone || file.blockIncrMapSize != 0)
+                file.sizeRepo = file.size;
+
+            // Bundle id/offset are too noisy so remove them. They are verified against size/checksum and listed with the files.
+            file.bundleId = 0;
+            file.bundleOffset = 0;
+
+            // Remove repo checksum since it has been validated
+            file.checksumRepoSha1 = NULL;
+
+            // Update changes to manifest file
+            manifestFilePackUpdate(manifest, filePack, &file);
+        }
+
         // Output the manifest to a string and exclude sections that don't need validation. Note that each of these sections should
         // be considered from automatic validation but adding them to the output will make the tests too noisy. One good technique
         // would be to remove it from the output only after validation so new values will cause changes in the output.
@@ -3706,6 +3773,7 @@ testRun(void)
                 "pg_data {path}\n"
                 "pg_data/backup_label.gz {file, s=17}\n"
                 "pg_data/tablespace_map.gz {file, s=19}\n"
+                "20191030-014640F/bundle/1/pg_data/PG_VERSION {file, s=2}\n"
                 "--------\n"
                 "[backup:target]\n"
                 "pg_data={\"path\":\"" TEST_PATH "/pg1\",\"type\":\"path\"}\n"
@@ -3932,6 +4000,7 @@ testRun(void)
                 "pg_data/block-incr-grow.pgbi {file, m=0:{0},1:{0},0:{2},1:{1,2,3,4,5,6,7,8,9,10,11,12,13}, s=131072}\n"
                 "pg_data/block-incr-larger.pgbi {file, m=1:{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},1:{0,1,2,3,4,5,6}, s=1507328}\n"
                 "pg_data/tablespace_map {file, s=19}\n"
+                "20191103-165320F/bundle/1/pg_data/PG_VERSION {file, s=2}\n"
                 "--------\n"
                 "[backup:target]\n"
                 "pg_data={\"path\":\"" TEST_PATH "/pg1\",\"type\":\"path\"}\n"
@@ -4066,7 +4135,7 @@ testRun(void)
         }
 
         // -------------------------------------------------------------------------------------------------------------------------
-        TEST_TITLE("online 11 diff backup with comp/enc");
+        TEST_TITLE("online 11 diff backup with comp/enc and delta");
 
         backupTimeStart = BACKUP_EPOCH + 3400000;
 
@@ -4081,8 +4150,10 @@ testRun(void)
             hrnCfgArgRawBool(argList, cfgOptRepoBundle, true);
             hrnCfgArgRawZ(argList, cfgOptRepoBundleLimit, "4MiB");
             hrnCfgArgRawBool(argList, cfgOptRepoBlock, true);
+            hrnCfgArgRawBool(argList, cfgOptDelta, true);
             hrnCfgArgRawZ(argList, cfgOptRepoCipherType, "aes-256-cbc");
             hrnCfgArgRawZ(argList, cfgOptRepoBlockAgeMap, "1=2");
+            hrnCfgArgRawZ(argList, cfgOptRepoBlockAgeMap, "2=0");
             hrnCfgArgRawZ(argList, cfgOptRepoBlockChecksumSizeMap, "16KiB=16");
             hrnCfgArgRawZ(argList, cfgOptRepoBlockChecksumSizeMap, "8KiB=12");
             hrnCfgEnvRawZ(cfgOptRepoCipherPass, TEST_CIPHER_PASS);
@@ -4103,6 +4174,13 @@ testRun(void)
 
             HRN_STORAGE_PUT(storagePgWrite(), "block-age-multiplier", file, .timeModified = backupTimeStart - SEC_PER_DAY);
 
+            // File with age old enough to not have block incr
+            file = bufNew(BLOCK_MIN_FILE_SIZE);
+            memset(bufPtr(file), 0, bufSize(file));
+            bufUsedSet(file, bufSize(file));
+
+            HRN_STORAGE_PUT(storagePgWrite(), "block-age-to-zero", file, .timeModified = backupTimeStart - 2 * SEC_PER_DAY);
+
             // Run backup
             hrnBackupPqScriptP(
                 PG_VERSION_11, backupTimeStart, .walCompressType = compressTypeNone, .cipherType = cipherTypeAes256Cbc,
@@ -4114,9 +4192,11 @@ testRun(void)
                 "P00   INFO: execute non-exclusive backup start: backup begins after the next regular checkpoint completes\n"
                 "P00   INFO: backup start archive = 0000000105DC82D000000000, lsn = 5dc82d0/0\n"
                 "P00   INFO: check archive for segment 0000000105DC82D000000000\n"
-                "P01 DETAIL: backup file " TEST_PATH "/pg1/block-age-multiplier (bundle 1/0, 32KB, [PCT]) checksum [SHA1]\n"
-                "P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (bundle 1/128, 8KB, [PCT]) checksum [SHA1]\n"
-                "P01 DETAIL: backup file " TEST_PATH "/pg1/block-incr-grow (bundle 1/216, 48KB, [PCT]) checksum [SHA1]\n"
+                "P01 DETAIL: match file from prior backup " TEST_PATH "/pg1/PG_VERSION (2B, [PCT]) checksum [SHA1]\n"
+                "P01 DETAIL: backup file " TEST_PATH "/pg1/block-age-to-zero (bundle 1/0, 16KB, [PCT]) checksum [SHA1]\n"
+                "P01 DETAIL: backup file " TEST_PATH "/pg1/block-age-multiplier (bundle 1/56, 32KB, [PCT]) checksum [SHA1]\n"
+                "P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (bundle 1/184, 8KB, [PCT]) checksum [SHA1]\n"
+                "P01 DETAIL: backup file " TEST_PATH "/pg1/block-incr-grow (bundle 1/272, 48KB, [PCT]) checksum [SHA1]\n"
                 "P00 DETAIL: reference pg_data/PG_VERSION to 20191108-080000F\n"
                 "P00   INFO: execute non-exclusive backup stop and wait for all WAL segments to archive\n"
                 "P00   INFO: backup stop archive = 0000000105DC82D000000001, lsn = 5dc82d0/300000\n"
@@ -4124,7 +4204,7 @@ testRun(void)
                 "P00 DETAIL: wrote 'tablespace_map' file returned from backup stop function\n"
                 "P00   INFO: check archive for segment(s) 0000000105DC82D000000000:0000000105DC82D000000001\n"
                 "P00   INFO: new backup label = 20191108-080000F_20191110-153320D\n"
-                "P00   INFO: diff backup size = [SIZE], file total = 6");
+                "P00   INFO: diff backup size = [SIZE], file total = 7");
 
             TEST_RESULT_STR_Z(
                 testBackupValidateP(
@@ -4133,11 +4213,13 @@ testRun(void)
                 ". {link, d=20191108-080000F_20191110-153320D}\n"
                 "bundle {path}\n"
                 "bundle/1/pg_data/block-age-multiplier {file, m=1:{0,1}, s=32768}\n"
+                "bundle/1/pg_data/block-age-to-zero {file, s=16384}\n"
                 "bundle/1/pg_data/block-incr-grow {file, m=0:{0,1},1:{0,1,2,3}, s=49152}\n"
                 "bundle/1/pg_data/global/pg_control {file, s=8192}\n"
                 "pg_data {path}\n"
                 "pg_data/backup_label.gz {file, s=17}\n"
                 "pg_data/tablespace_map.gz {file, s=19}\n"
+                "20191108-080000F/bundle/1/pg_data/PG_VERSION {file, s=2}\n"
                 "--------\n"
                 "[backup:target]\n"
                 "pg_data={\"path\":\"" TEST_PATH "/pg1\",\"type\":\"path\"}\n"
@@ -4149,6 +4231,8 @@ testRun(void)
                 ",\"timestamp\":1573400002}\n"
                 "pg_data/block-age-multiplier={\"bi\":2,\"bic\":16,\"bim\":56"
                 ",\"checksum\":\"5188431849b4613152fd7bdba6a3ff0a4fd6424b\",\"size\":32768,\"timestamp\":1573313600}\n"
+                "pg_data/block-age-to-zero={\"checksum\":\"897256b6709e1a4da9daba92b6bde39ccfccd8c1\",\"size\":16384,"
+                "\"timestamp\":1573227200}\n"
                 "pg_data/block-incr-grow={\"bi\":1,\"bim\":72,\"checksum\":\"bd4716c88f38d2540e3024b54308b0b95f34a0cc\""
                 ",\"size\":49152,\"timestamp\":1573400000}\n"
                 "pg_data/global/pg_control={\"size\":8192,\"timestamp\":1573400000}\n"