#include "githandler.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include GitHandler::GitHandler() : QObject() ,m_repositories(new RepositoryModel(this)) ,m_commits(nullptr) ,m_graph(nullptr) ,m_activeRepo(nullptr) ,m_activeDiff(nullptr) ,m_branchList(new BranchListModel(this)) ,m_tagList(new TagListModel(this)) ,m_activeRepoWatcher(new QFileSystemWatcher(this)) ,m_console(new GitConsole(this)) ,m_homePath(QUrl::fromLocalFile(QDir::homePath())) { git_libgit2_init(); connect(&m_diffTask, &QFutureWatcher::finished, this, &GitHandler::onDiffReady); connect(&m_graphTask, &QFutureWatcher::finished, this, &GitHandler::onGraphReady); connect(&m_graphTask, &QFutureWatcher::started, this, &GitHandler::isBusyChanged); connect(&m_graphTask, &QFutureWatcher::finished, this, &GitHandler::isBusyChanged); connect(&m_graphTask, &QFutureWatcher::canceled, this, &GitHandler::isBusyChanged); connect(&m_graphTask, &QFutureWatcher::paused, this, &GitHandler::isBusyChanged); connect(&m_graphTask, &QFutureWatcher::resumed, this, &GitHandler::isBusyChanged); loadCachedRepos(); } GitHandler::~GitHandler() { git_libgit2_shutdown(); } void GitHandler::open(const QUrl &url, bool activate) { if(url.isLocalFile()) { open(url.toLocalFile(), activate); } } void GitHandler::open(const QString &path, bool activate) { qDebug() << "path" << path; git_buf root = {0, 0, 0}; if(git_repository_discover(&root, path.toUtf8().data(), 0, NULL) != 0) { qDebug() << lastError(); return; } QString rootStr = QString::fromUtf8(root.ptr, root.size); GitRepository* repo = new GitRepository(rootStr); if (!m_repositories->findByProperty("path", QVariant::fromValue(repo->path())).isNull()) { qDebug() << "Repository is already in list"; delete repo; return; } Settings::instance()->add(repo); m_repositories->addRepository(repo); if (activate) { setActiveRepo(repo); } } void GitHandler::activateRepository(int i) { GitRepository* repo = m_repositories->at(i); setActiveRepo(repo); } void GitHandler::setActiveRepo(GitRepository* repo) { if (repo == nullptr || !repo->isValid()) { qDebug() << lastError(); return; } if (m_activeRepo) { disconnect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, m_activeRepo, &GitRepository::readBranches); disconnect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, m_activeRepo, &GitRepository::readRemotes); disconnect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, m_activeRepo, &GitRepository::readTags); disconnect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, this, &GitHandler::updateModels); m_activeRepoWatcher->removePath(m_activeRepo->root()); disconnect(m_activeRepo, &GitRepository::branchesChanged, this, &GitHandler::updateModels); } m_activeRepo = repo; connect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, m_activeRepo, &GitRepository::readBranches); connect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, m_activeRepo, &GitRepository::readRemotes); connect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, m_activeRepo, &GitRepository::readTags); connect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, this, &GitHandler::updateModels); connect(m_activeRepo, &GitRepository::branchesChanged, this, &GitHandler::updateModels); ColorHandler::instance().updateColors(m_activeRepo); // m_constantHead = m_activeRepo->head(); // TODO m_console->setRepository(m_activeRepo); updateModels(); m_activeRepoWatcher->addPath(m_activeRepo->root()); m_repositories->setActiveRepositoryIndex(m_repositories->indexOf(m_activeRepo)); activeRepoChanged(m_activeRepo); Settings::instance()->saveLastRepo(m_activeRepo); } QString GitHandler::lastError() const { const git_error *e = giterr_last(); if(e) { return QString("(%1): %2").arg(e->klass).arg(e->message); giterr_clear(); } giterr_clear(); return QString(); } void GitHandler::diff(GitCommit* a, GitCommit* b) { if(!m_activeDiff.isNull()) { m_activeDiff->deleteLater(); } Q_ASSERT_X(a->repository() == b->repository(), "GitHandler", "Cross repository diff requested"); if(m_diffTask.isRunning()) { m_diffTask.cancel(); } qDebug() << "Diff start thread: " << QThread::currentThreadId(); QFuture future = QtConcurrent::run(&GitDiff::diff, a, b); m_diffTask.setFuture(future); } void GitHandler::diff() { if(!m_activeDiff.isNull()) { m_activeDiff->deleteLater(); } GitCommit* commit = GitCommit::fromOid(activeRepo()->head()); QFuture future = QtConcurrent::run(&GitDiff::diff, commit); m_diffTask.setFuture(future); } void GitHandler::diffReset() { delete m_activeDiff; } void GitHandler::onDiffReady() { setActiveDiff(m_diffTask.result()); } void GitHandler::setActiveDiff(GitDiff* activeDiff) { if (m_activeDiff == activeDiff) return; m_activeDiff = activeDiff; emit activeDiffChanged(activeDiff); } GitDiff* GitHandler::activeDiff() const { return m_activeDiff.data(); } void GitHandler::pull(GitBranch::PullStrategy strategy) const { if(m_activeRepo->remotes().count() <= 0) { qWarning() << "No remotes available for repository. Please add manually in console"; //TODO: duplicate with warning popup return; } GitRemote *remote = m_activeRepo->remotes().first().data(); if(remote == nullptr) { qCritical() << "Null remote in remotes detected. It's critical"; Q_ASSERT(remote == nullptr); return; } remote->fetch(); GitOid oid = m_activeRepo->head(); auto branches = m_activeRepo->branches(); auto result = std::find_if(branches.begin(), branches.end(), [&](QPointer& branch) { return branch->oid() == oid; }); if(result == branches.end() || (*result).isNull()) { qDebug() << "HEAD is not on branch"; return; } GitBranch *headBranch = (*result); headBranch->pull(strategy); } void GitHandler::updateModels() { if(!m_activeRepo) { return; } BranchContainer &branches = m_activeRepo->branches(); m_activeRepo->updateHead(); m_graphTask.cancel(); m_graphTask.setFuture(QtConcurrent::run(&GitHandler::updateGraph, m_activeRepo->head(), branches)); m_branchList->reset(branches.values()); m_tagList->reset(m_activeRepo->tags().values()); } void GitHandler::copy(const QString& sha1) { QGuiApplication::clipboard()->setText(sha1); } CommitGraph* GitHandler::updateGraph(const GitOid &head, const BranchContainer &branches) { CommitGraph* graph = new CommitGraph(); bool headIsBranch = false; //TODO: Need to think about constant head more deeply // foreach(GitBranch* branch, branches) { // if(branch->oid() == m_constantHead) { // graph->addHead(branch); // headIsBranch = true; // break; // } // } if(!headIsBranch) { graph->addHead(head); } foreach(GitBranch* branch, branches) { qDebug() << "Next head " << branch->fullName(); graph->addHead(branch); } graph->addWorkdir(); graph->moveToThread(head.repository()->thread()); return graph; } void GitHandler::onGraphReady() { m_commits->deleteLater(); m_commits = nullptr; emit commitsChanged(m_commits); m_graph->deleteLater(); m_graph = nullptr; emit graphChanged(m_graph); m_graph = m_graphTask.result(); m_commits = CommitModel::fromGraph(m_graph); emit graphChanged(m_graph); emit commitsChanged(m_commits); } void GitHandler::loadCachedRepos() { QString activeRepo = Settings::instance()->loadLastRepo(); QStringList cachedRepos; Settings::instance()->load(cachedRepos); foreach (QString repoPath, cachedRepos) { open(repoPath, false); } QPointer repo = m_repositories->findByProperty("id", QVariant::fromValue(activeRepo)); setActiveRepo(repo); }