#include "git_client.h" #include "update.h" #include "worker.h" #include "utils.h" #include <QRegularExpression> #include <QDebug> #include <QDir> GitClient::GitClient(QString const &customerNrStr, QString const &customerRepository, QString const &workingDirectory, QString const &branchName, QObject *parent) : QObject(parent) , m_worker(qobject_cast<Worker *>(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"; } } 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 const 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. CLONE_RESULT=" << result; } 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; } QStringList GitClient::gitBranchNames() { // git config --global pager.branch false QStringList bNames; if (QDir(m_customerRepository).exists()) { QString gitCommand("git branch -a"); Command c(gitCommand); if (c.execute(m_customerRepository)) { QString const result = c.getCommandResult(); return result.split('\n'); } } return bNames; } bool GitClient::gitCheckoutBranch() { // TODO: nachsehen, ob der Branch ueberhaupt existiert if (QDir(m_customerRepository).exists()) { int zoneNr = Utils::read1stLineOfFile("/mnt/system_data/zone_nr"); m_branchName = (zoneNr != 0) ? QString("zg1/zone%1").arg(zoneNr) : "master"; 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()) { if (gitCheckoutBranch()) { return true; } else { // TODO // m_worker->terminateUpdateProcess(); } //} } else { // TODO //m_worker->terminateUpdateProcess(); } 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<QStringList> 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<QString> GitClient::gitFetch() { if (QDir(m_customerRepository).exists()) { qCritical() << "BRANCH NAME" << m_branchName; 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()) { int zoneNr = Utils::read1stLineOfFile("/mnt/system_data/zone_nr"); m_branchName = (zoneNr != 0) ? QString("zg1/zone%1").arg(zoneNr) : "master"; // lines can look like this: // From https://git.mimbach49.de/GerhardHoffmann/customer_281 // 41ec581..5d25ac3 master -> origin/master // ff10f57..43530a1 zg1/zone1 -> origin/zg1/zone1 // 6ed893f..5d9882c zg1/zone2 -> origin/zg1/zone2 // 4384d17..77045d8 zg1/zone3 -> origin/zg1/zone3 // 89d2812..36a0d74 zg1/zone5 -> origin/zg1/zone5 bool found = false; for (int i=0; i < lines.size(); ++i) { if (lines.at(i).contains(m_branchName)) { found = true; // 409f198..6c22726 zg1/zone1 -> origin/zg1/zone1 static QRegularExpression re("(^\\s*)([0-9A-Fa-f]+..[0-9A-Fa-f]+)(.*$)"); QRegularExpressionMatch match = re.match(lines.at(i)); if (match.hasMatch()) { if (re.captureCount() == 3) { // start with full match (0), then the other 3 matches return match.captured(2); } else { emit m_worker->showErrorMessage("git fetch", QString("(wrong cap-count (%1)").arg(re.captureCount())); } } else { emit m_worker->showErrorMessage("git fetch", "regex-match for commits"); } } } if (!found) { emit m_worker->showErrorMessage("git fetch", QString("unkown branch name ") + m_branchName); } } else { emit m_worker->showErrorMessage("git fetch", QString("wrong format for result of 'git fetch' ") + s); } } else { emit m_worker->showErrorMessage("git fetch", "empty result for 'git fetch'"); } } } else { emit m_worker->showErrorMessage("git fetch", QString("repository ") + 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<QStringList> 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 ""; } // fileName has to an absolute path QString GitClient::gitBlob(QString fileName) { QFileInfo fi(fileName); if (fi.exists()) { QString const gitCommand = QString("git hash-object %1").arg(fileName); Command c(gitCommand); if (c.execute(m_workingDirectory)) { return c.getCommandResult().trimmed(); } } return "N/A"; } 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") //}