#include "git_client.h" #include "update.h" #include "worker.h" #include #include #include GitClient::GitClient(QString const &customerNrStr, QString const &customerRepository, QString const &workingDirectory, QString const &branchName, QObject *parent) : QObject(parent) , m_worker(qobject_cast(parent)) , m_repositoryPath(QString("https://git.mimbach49.de/GerhardHoffmann/%1.git").arg(customerNrStr)) , m_customerNr(customerNrStr) , m_workingDirectory(workingDirectory) , m_branchName(branchName) , m_customerRepository(customerRepository) { if (!m_worker) { qCritical() << "ERROR CASTING PARENT TO WORKER FAILED"; } connect(this, SIGNAL(ismasUpdatesAvailable()), this, SLOT(onIsmasUpdatesAvailable()), Qt::QueuedConnection); } 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; } else { qCritical() << "ERROR CLONING " << m_repositoryPath << "AND/OR CHECKING OUT INTO" << m_customerRepository; emit m_worker->terminateUpdateProcess(); } } } bool GitClient::gitCloneCustomerRepository() { QString gitCommand("git clone "); gitCommand += m_repositoryPath; Command c(gitCommand); 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 static QRegularExpression re("(^\\s*Cloning\\s+into\\s+[']\\s*)(.*)(\\s*['].*$)"); QRegularExpressionMatch match = re.match(result); if (match.hasMatch()) { if (re.captureCount() == 3) { // start with full match (0), then the other 3 matches if (match.captured(2).trimmed() == m_customerNr) { qInfo() << "CLONING" << m_repositoryPath << "OK"; return true; } } } qCritical() << "ERROR CLONE RESULT HAS WRONG FORMAT"; } } return false; } 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; } 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 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) { if (QDir(m_customerRepository).exists()) { // 409f198..6c22726 QString gitCommand("git diff --compact-summary "); gitCommand += commits; 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; } } } return std::nullopt; } /* Hat sich nichts geaendert, so werden auch keine Commits <>..<> angezeigt */ std::optional GitClient::gitFetch() { if (QDir(m_customerRepository).exists()) { Command c("git fetch"); if (c.execute(m_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" << m_customerRepository << "DOES NOT EXIST"; } return std::nullopt; } bool GitClient::gitFetchAndDiff() { if (gitFetch()) { QString gitCommand("git diff --compact-summary HEAD..FETCH_HEAD"); Command c(gitCommand); return c.execute(m_workingDirectory); } return false; } bool GitClient::gitPull() { 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() { Command c("git merge"); if (c.execute(m_workingDirectory)) { QString s = c.getCommandResult(); QStringList lst = Update::split(s, '\n'); return lst; } 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") //}