diff --git a/git/git_client.cpp b/git/git_client.cpp index 17eacd9..d71374d 100644 --- a/git/git_client.cpp +++ b/git/git_client.cpp @@ -1,36 +1,97 @@ #include "git_client.h" #include "update.h" +#include "worker.h" #include #include #include -GitClient::GitClient(QString const &workingDirectory, QString const &branchName) - : m_workingDirectory(workingDirectory) - , m_branchName(branchName) { + +GitClient::GitClient(QString const &repositoryPath, + QString const &customerId, + QString const &workingDirectory, + QString const &branchName, + QObject *parent) + : QObject(parent) + , m_worker(qobject_cast(parent)) + , m_repositoryPath(repositoryPath) + , m_customerId(customerId) + , m_workingDirectory(workingDirectory) + , m_branchName(branchName) + , m_customerRepository(QDir::cleanPath(m_workingDirectory + + QDir::separator() + + m_customerId)) { + if (!m_worker) { + qCritical() << "ERROR CASTING PARENT TO WORKER FAILED"; + } + + connect(this, SIGNAL(ismasUpdatesAvailable()), + this, SLOT(onIsmasUpdatesAvailable()), Qt::QueuedConnection); } -void GitClient::setWorkingDirectory(QString const &workingDirectory) { - m_workingDirectory = workingDirectory; +void GitClient::onIsmasUpdatesAvailable() { + if (QDir(m_customerRepository).exists()) { + qInfo() << "FETCHING OF" << m_repositoryPath + << "INTO" << m_customerRepository; + std::optional changes = gitFetch(); + if (changes) { + std::optional changedFileNames = gitDiff(changes.value()); + if (changedFileNames) { + for (int i=0;ihandleChangedFiles(changedFileNames.value()); + } else { + qCritical() << "PULL FAILED FOR" << m_repositoryPath + << "IN " << m_customerRepository; + emit m_worker->terminateUpdateProcess(); + } + } else { + qCritical() << "NO CHANGES IN" << m_repositoryPath + << "(" << m_customerRepository << ")"; + emit m_worker->finishUpdateProcess(false); + } + } else { + qCritical() << "NO CHANGES IN" << m_repositoryPath + << "(" << m_customerRepository << ")"; + emit m_worker->finishUpdateProcess(false); + } + } else { + if (gitCloneAndCheckoutBranch()) { + qInfo() << "CLONED" << m_repositoryPath + << "AND CHECKED OUT INTO" << m_customerRepository; + if (m_worker) { + qDebug() << "WORKER EXECUTE OPKG COMMANDS"; + QStringList opkgCommands; + // To make sure that opkg upgrade does not break your system + // because of an unstable connection + // Add a line "option cache cachedir" to /etc/opkg/opkg.conf to + // avoid the --cache option on command line. + opkgCommands << "opkg update"; + //opkgCommands << "opkg --cache cachedir --download-only upgrade"; + //opkgCommands << "opkg --cache cachedir upgrade"; + emit m_worker->executeOpkgCommands(opkgCommands); + } + } else { + qCritical() << "ERROR CLONING " << m_repositoryPath + << "AND/OR CHECKING OUT INTO" << m_customerRepository; + emit m_worker->terminateUpdateProcess(); + } + } } -QString GitClient::workingDirectory() const { - return m_workingDirectory; -} - -void GitClient::setBranchName(QString const &branchName) { - m_branchName = branchName; -} - -QString GitClient::branchName() const { - return m_branchName; -} - -std::optional GitClient::gitCloneRepository(QString const &repPath) { +bool GitClient::gitCloneCustomerRepository() { QString gitCommand("git clone "); - gitCommand += repPath; + gitCommand += m_repositoryPath; Command c(gitCommand); - if (c.execute(m_workingDirectory)) { + + qInfo() << "IN CURRENT WD" << m_workingDirectory + << "CLONE" << m_repositoryPath << "..."; + + if (c.execute(m_workingDirectory)) { // execute the command in wd QString result = c.getCommandResult(); if (!result.isEmpty()) { // Cloning into 'customer_281'...\n @@ -38,83 +99,128 @@ std::optional GitClient::gitCloneRepository(QString const &repPath) { QRegularExpressionMatch match = re.match(result); if (match.hasMatch()) { if (re.captureCount() == 3) { // start with full match (0), then the other 3 matches - return match.captured(2); + if (match.captured(2).trimmed() == m_customerId) { + qInfo() << "CLONING" << m_repositoryPath << "OK"; + return true; + } } } + qCritical() << "ERROR CLONE RESULT HAS WRONG FORMAT"; } } - return std::nullopt; + return false; } -bool GitClient::gitCheckout(QString const &branchName) { - QString gitCommand("git checkout "); - gitCommand += branchName; - Command c(gitCommand); - return c.execute(m_workingDirectory); +bool GitClient::copyGitConfigFromMaster() { // only allowed when called in + // master branch + if (QDir(m_customerRepository).exists()) { + QString const cp = QString("cp .gitconfig .git/config"); + Command c("bash"); + if (c.execute(m_customerRepository, QStringList() << "-c" << cp)) { + qInfo() << "cp .gitconfig .git/config OK"; + return true; + } + qCritical() << "ERROR cp .gitconfig .git/config"; + } + return false; } -std::optional GitClient::gitCloneBranch(QString const &repPath, - QString const &branchName) { - if (std::optional rep = gitCloneRepository(repPath)) { - QDir wd(m_workingDirectory); - if (wd.cd(rep.value())) { - m_workingDirectory = wd.absolutePath(); - if (gitCheckout(branchName)) { - return branchName; - } +bool GitClient::gitCheckoutBranch() { + if (QDir(m_customerRepository).exists()) { + QString gitCommand("git checkout "); + gitCommand += m_branchName; + Command c(gitCommand); + return c.execute(m_customerRepository); // execute command in customerRepo + } + qCritical() << "ERROR" << m_customerRepository << "DOES NOT EXIST"; + return false; +} + +bool GitClient::gitCloneAndCheckoutBranch() { + qInfo() << "CLONE" << m_repositoryPath << "AND CHECKOUT" << m_branchName; + if (gitCloneCustomerRepository()) { + if (copyGitConfigFromMaster()) { + return gitCheckoutBranch(); } } - return std::nullopt; + return false; } +/* + Zu beachten: wird eine datei neu hinzugefuegt (git add/commit) dann aber gleich + wieder geloscht, so wird sie im diff nicht angezeigt. + */ std::optional GitClient::gitDiff(QString const &commits) { - // 409f198..6c22726 - QString gitCommand("git diff --compact-summary "); - gitCommand += commits; + if (QDir(m_customerRepository).exists()) { + // 409f198..6c22726 + QString gitCommand("git diff --compact-summary "); + gitCommand += commits; - Command c(gitCommand); - if (c.execute(m_workingDirectory)) { - QString s = c.getCommandResult().trimmed(); - QStringList lines = Update::split(s, '\n'); - QStringList fileNames; - // each line has the format "etc/psa_config/DC2C_print01.json | 1 + - // or the format "etc/psa_config/DC2C_print01.json (new) | 1 + - // the filenames are relativ to the repository - for (int i = 0; i < lines.size(); ++i) { - int newIndex = lines.at(i).indexOf("(new)"); - if (newIndex != -1) { - QString fileName = lines.at(i).mid(0, newIndex).trimmed(); - fileNames << fileName; - } else { - int pipeIndex = lines.at(i).indexOf('|'); - if (pipeIndex != -1) { - QString fileName = lines.at(i).mid(0, pipeIndex).trimmed(); + Command c(gitCommand); + if (c.execute(m_customerRepository)) { // execute command in local customerRepo + QString s = c.getCommandResult().trimmed(); + QStringList lines = Update::split(s, '\n'); + QStringList fileNames; + // each line has the format "etc/psa_config/DC2C_print01.json | 1 + + // or the format "etc/psa_config/DC2C_print01.json (new) | 1 + + // the filenames are relativ to the repository + for (int i = 0; i < lines.size(); ++i) { + // TODO: koennte auch (delete) kommen ? + int newIndex = lines.at(i).indexOf("(new)"); // for new files + // int goneIndex = lines.at(i).indexOf("(gone)"); // for removed files + if (newIndex != -1) { + QString fileName = lines.at(i).mid(0, newIndex).trimmed(); fileNames << fileName; + } else { + int pipeIndex = lines.at(i).indexOf('|'); + if (pipeIndex != -1) { + QString fileName = lines.at(i).mid(0, pipeIndex).trimmed(); + fileNames << fileName; + } } } - } - if (!fileNames.isEmpty()) { - return fileNames; + if (!fileNames.isEmpty()) { + return fileNames; + } } } return std::nullopt; } +/* + Hat sich nichts geaendert, so werden auch keine Commits <>..<> angezeigt + */ std::optional GitClient::gitFetch() { - Command c("git fetch"); - if (c.execute(m_workingDirectory)) { - QString s = c.getCommandResult().trimmed(); - QStringList lines = Update::split(s, '\n'); - if (!lines.empty()) { - // 409f198..6c22726 zg1/zone1 -> origin/zg1/zone1 - static QRegularExpression re("(^\\s*)([0-9A-Fa-f]+..[0-9A-Fa-f]+)(.*$)"); - QRegularExpressionMatch match = re.match(lines.last()); - if (match.hasMatch()) { - if (re.captureCount() == 3) { // start with full match (0), then the other 3 matches - return match.captured(2); + QString const &customerRepository + = QDir::cleanPath(m_workingDirectory + QDir::separator() + m_customerId); + if (QDir(customerRepository).exists()) { + Command c("git fetch"); + if (c.execute(customerRepository)) { + QString const s = c.getCommandResult().trimmed(); + if (!s.isEmpty()) { + QStringList lines = Update::split(s, '\n'); + if (!lines.empty()) { + // 409f198..6c22726 zg1/zone1 -> origin/zg1/zone1 + static QRegularExpression re("(^\\s*)([0-9A-Fa-f]+..[0-9A-Fa-f]+)(.*$)"); + QRegularExpressionMatch match = re.match(lines.last()); + if (match.hasMatch()) { + if (re.captureCount() == 3) { // start with full match (0), then the other 3 matches + return match.captured(2); + } else { + qCritical() << "ERROR WRONG CAPTURE COUNT FOR 'GIT FETCH'" << re.captureCount(); + } + } else { + qCritical() << "ERROR NO MATCH OF COMMITS FOR 'GIT FETCH'"; + } + } else { + qCritical() << "ERROR WRONG FORMAT FOR RESULT FOR 'GIT FETCH'" << s; } + } else { + qCritical() << "ERROR EMPTY RESULT FROM 'GIT FETCH'"; } } + } else { + qCritical() << "ERROR" << customerRepository << "DOES NOT EXIST"; } return std::nullopt; } @@ -129,8 +235,15 @@ bool GitClient::gitFetchAndDiff() { } bool GitClient::gitPull() { - Command c("git pull"); - return c.execute(m_workingDirectory); + if (QDir(m_customerRepository).exists()) { + Command c("git pull"); + if (c.execute(m_customerRepository)) { + qInfo() << "PULLED INTO" << m_customerRepository; + return true; + } + qCritical() << "PULL INTO" << m_customerRepository << "FAILED"; + } + return false; } std::optional GitClient::gitMerge() { @@ -142,3 +255,65 @@ std::optional GitClient::gitMerge() { } return std::nullopt; } + +QString GitClient::gitLastCommit(QString fileName) { + if (QDir(m_customerRepository).exists()) { + QString const filePath + = QDir::cleanPath(m_customerRepository + QDir::separator() + fileName); + QString const gitCommand = QString("git log %1 | head -n 1").arg(fileName); + Command c("bash"); + if (c.execute(m_customerRepository, QStringList() << "-c" << gitCommand)) { + QString const r = c.getCommandResult(); + int const idx = r.indexOf("commit "); + if (idx != -1) { + return r.mid(idx + 8).trimmed(); + } + } + } + return ""; +} + +// get the blob of the file(name) passed as $1 +// note: this can be used for any file in the filesystem +QString GitClient::gitBlob(QString fileName) { + if (QDir(m_customerRepository).exists()) { + QString const filePath + = QDir::cleanPath(m_customerRepository + QDir::separator() + fileName); + QString const gitCommand = QString("git hash-object %1").arg(fileName); + Command c(gitCommand); + if (c.execute(m_customerRepository)) { + return c.getCommandResult(); + } + } + return ""; +} + +QString GitClient::gitCommitForBlob(QString blob) { + if (QDir(m_customerRepository).exists()) { + QString const gitCommand + = QString("git whatchanged --all --find-object=%1 | head -n 1").arg(blob); + Command c(gitCommand); + if (c.execute(m_customerRepository)) { + return c.getCommandResult(); + } + } + return ""; +} + +bool GitClient::gitIsFileTracked(QString fName) { + if (QDir(m_customerRepository).exists()) { + QString const gitCommand + = QString("git ls-files --error-unmatch %1").arg(fName); + Command c(gitCommand); + return c.execute(m_customerRepository); + } + return false; +} + + +//get_commit_for_blob () { +// # search for the blob in all commits for the file(name) $1 +// echo $(git log --all --pretty=format:%H -- $2 | +// xargs -I{} bash -c "git ls-tree {} -- $2 | +// grep -q $1 && echo -n {} && head -n 1") +//}