mirror of
https://github.com/usatiuk/backup.git
synced 2025-10-26 09:27:48 +01:00
149 lines
6.4 KiB
C++
149 lines
6.4 KiB
C++
//
|
|
// Created by Stepan Usatiuk on 23.05.2023.
|
|
//
|
|
|
|
#include "CommandDiff.h"
|
|
|
|
#include "BytesFormatter.h"
|
|
#include "ChangeDetectorFactory.h"
|
|
#include "ChunkerFactory.h"
|
|
#include "Diff.h"
|
|
#include "Exception.h"
|
|
#include "Progress.h"
|
|
#include "RunningDiffAverage.h"
|
|
#include "Serialize.h"
|
|
#include "Signals.h"
|
|
#include "ThreadPool.h"
|
|
#include "objects/Archive.h"
|
|
#include "objects/Chunk.h"
|
|
|
|
using namespace CommandsCommon;
|
|
|
|
CommandDiff::CommandDiff() : Command() {}
|
|
|
|
void CommandDiff::run(Context ctx) {
|
|
std::string diffMode = ctx.repo->getConfig().getStr("diff-mode");
|
|
|
|
Object::idType archive1;
|
|
if (!ctx.repo->getConfig().exists("aid")) {
|
|
auto archives = ctx.repo->getObjects(Object::ObjectType::Archive);
|
|
archive1 = std::max_element(archives.begin(), archives.end(), [](const auto &a1, const auto &a2) {
|
|
return a1.second < a2.second;
|
|
})->second;
|
|
} else {
|
|
archive1 = ctx.repo->getConfig().getInt("aid");
|
|
}
|
|
|
|
ThreadPool threadPool([&](const std::string &error) { ctx.logger->write("Error: " + error, 0); },
|
|
ctx.repo->getConfig().exists("threads") ? ctx.repo->getConfig().getInt("threads")
|
|
: std::thread::hardware_concurrency());
|
|
|
|
auto archiveO1 = Serialize::deserialize<Archive>(ctx.repo->getObjectRaw(archive1));
|
|
std::mutex filesLock;
|
|
std::map<std::filesystem::path, File> files;///< Files in the first archive
|
|
for (auto id: archiveO1.files) {
|
|
auto file = Serialize::deserialize<File>(ctx.repo->getObjectRaw(id));
|
|
auto path = std::filesystem::path(file.name);
|
|
if (isSubpath(ctx.repo->getConfig().getStr("prefix"), path)) files.emplace(file.getKey(), std::move(file));
|
|
}
|
|
|
|
/// Container of ChangeDetectors built using the config of the repository
|
|
ChangeDetectorContainer changeDetector = ChangeDetectorFactory::getChangeDetectors(ctx.repo->getConfig());
|
|
|
|
/// Task to to compare the given file with the first archive
|
|
auto processFile = [&, this](ComparableFile p) {
|
|
auto relPath = p.path;
|
|
std::unique_lock lock(filesLock);
|
|
if (files.count(relPath) == 0) {
|
|
ctx.logger->write(relPath + " is new\n", 0);
|
|
lock.unlock();
|
|
} else {
|
|
File repoFile = files.at(relPath);
|
|
lock.unlock();
|
|
if (changeDetector.check({repoFile, ctx.repo}, p)) {
|
|
ctx.logger->write(relPath + " is different " + Diff::diff({repoFile, ctx.repo}, p) + "\n", 1);
|
|
} else {
|
|
if (diffMode == "file") ctx.logger->write(relPath + " are same ", 0);
|
|
}
|
|
}
|
|
|
|
lock.lock();
|
|
files.erase(relPath);
|
|
};
|
|
|
|
std::optional<Archive> archiveO2;
|
|
if (diffMode == "normal") {
|
|
/// If a second archive is given, run the task for each of its files, otherwise use the "from" config option
|
|
if (ctx.repo->getConfig().exists("aid2")) {
|
|
archiveO2.emplace(
|
|
Serialize::deserialize<Archive>(ctx.repo->getObjectRaw(ctx.repo->getConfig().getInt("aid2"))));
|
|
|
|
threadPool.push([&]() {
|
|
for (auto id: archiveO2.value().files) {
|
|
/// Exit when asked to
|
|
if (Signals::shouldQuit) throw Exception("Quitting");
|
|
auto file = Serialize::deserialize<File>(ctx.repo->getObjectRaw(id));
|
|
if (isSubpath(ctx.repo->getConfig().getStr("prefix"), std::filesystem::path(file.name)))
|
|
threadPool.push([&, file]() { processFile(ComparableFile{file, ctx.repo}); });
|
|
if (Signals::shouldQuit) break;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
} else {
|
|
std::filesystem::path from = ctx.repo->getConfig().getStr("from");
|
|
/// Start the diff with the root directory and empty ignore list
|
|
threadPool.push([&, from]() {
|
|
processDirWithIgnore(
|
|
from, {}, [&](std::function<void()> f) { threadPool.push(std::move(f)); },
|
|
[processFile, from, prefix = ctx.repo->getConfig().getStr("prefix")](
|
|
const std::filesystem::directory_entry &dirEntry) {
|
|
if (isSubpath(prefix, dirEntry.path().lexically_relative(from)))
|
|
processFile(ComparableFile{dirEntry, from});
|
|
});
|
|
});
|
|
}
|
|
} else if (diffMode == "file") {
|
|
if (files.count(ctx.repo->getConfig().getStr("prefix")) == 0) {
|
|
ctx.logger->write("Doesn't exist in the first archive", 0);
|
|
return;
|
|
}
|
|
|
|
if (ctx.repo->getConfig().exists("aid2")) {
|
|
archiveO2.emplace(
|
|
Serialize::deserialize<Archive>(ctx.repo->getObjectRaw(ctx.repo->getConfig().getInt("aid2"))));
|
|
std::map<std::filesystem::path, File> files2;///< Files in the first archive
|
|
for (auto id: archiveO2->files) {
|
|
auto file = Serialize::deserialize<File>(ctx.repo->getObjectRaw(id));
|
|
auto path = std::filesystem::path(file.name);
|
|
if (isSubpath(ctx.repo->getConfig().getStr("prefix"), path))
|
|
files2.emplace(file.getKey(), std::move(file));
|
|
}
|
|
|
|
if (files2.count(ctx.repo->getConfig().getStr("prefix")) == 0) {
|
|
ctx.logger->write("Doesn't exist in the second archive", 0);
|
|
return;
|
|
} else {
|
|
processFile(ComparableFile{files2.at(ctx.repo->getConfig().getStr("prefix")), ctx.repo});
|
|
}
|
|
} else {
|
|
std::filesystem::path from = ctx.repo->getConfig().getStr("from");
|
|
if (!std::filesystem::exists(from / ctx.repo->getConfig().getStr("prefix"))) {
|
|
ctx.logger->write("Doesn't exist in the filesystem archive", 0);
|
|
return;
|
|
}
|
|
/// Start the diff with the root directory and empty ignore list
|
|
processFile(ComparableFile{from / ctx.repo->getConfig().getStr("prefix"), from});
|
|
}
|
|
|
|
} else {
|
|
throw Exception("Unknown diff-mode: " + diffMode);
|
|
}
|
|
|
|
/// Wait for diff to end
|
|
std::unique_lock finishedLock(threadPool.finishedLock);
|
|
threadPool.finished.wait(finishedLock, [&threadPool] { return threadPool.empty(); });
|
|
if (diffMode == "normal")
|
|
for (auto const &s: files) { ctx.logger->write(s.first.string() + " is removed\n", 0); }
|
|
}
|