Made GitClient a Qt-object. Impreoved several utility functions handling git commands.
Added slot onIsmasUpdatesAvailable(). Added helpers getLastCommit(), gitBlob(), gitCommitForBlob(), gitIsFileTracked().
This commit is contained in:
		@@ -1,36 +1,97 @@
 | 
			
		||||
#include "git_client.h"
 | 
			
		||||
#include "update.h"
 | 
			
		||||
#include "worker.h"
 | 
			
		||||
 | 
			
		||||
#include <QRegularExpression>
 | 
			
		||||
#include <QDebug>
 | 
			
		||||
#include <QDir>
 | 
			
		||||
 | 
			
		||||
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<Worker *>(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<QString> changes = gitFetch();
 | 
			
		||||
        if (changes) {
 | 
			
		||||
            std::optional<QStringList> changedFileNames = gitDiff(changes.value());
 | 
			
		||||
            if (changedFileNames) {
 | 
			
		||||
                for (int i=0;i<changedFileNames.value().size();++i) {
 | 
			
		||||
                    QString fname = changedFileNames.value().at(i);
 | 
			
		||||
                    QString lastCommit = gitLastCommit(fname);
 | 
			
		||||
                    qDebug() << "CCCC" << changedFileNames.value().at(i) << lastCommit;
 | 
			
		||||
                }
 | 
			
		||||
                if (gitPull()) {
 | 
			
		||||
                    emit m_worker->handleChangedFiles(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<QString> 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<QString> 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<QString> GitClient::gitCloneBranch(QString const &repPath,
 | 
			
		||||
                                                 QString const &branchName) {
 | 
			
		||||
    if (std::optional<QString> 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<QStringList> 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<QString> 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<QStringList> GitClient::gitMerge() {
 | 
			
		||||
@@ -142,3 +255,65 @@ std::optional<QStringList> 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")
 | 
			
		||||
//}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user