githandler.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. #include "githandler.h"
  2. #include <QUrl>
  3. #include <QFileSystemWatcher>
  4. #include <QGuiApplication>
  5. #include <QClipboard>
  6. #include <QtConcurrentRun>
  7. #include <QDebug>
  8. #include <QDir>
  9. #include <qqml.h>
  10. #include <gitrepository.h>
  11. #include <gitbranch.h>
  12. #include <gitdiff.h>
  13. #include <commitmodel.h>
  14. #include <tagmodel.h>
  15. #include <git2.h>
  16. #include <colorhandler.h>
  17. #include <commitgraph.h>
  18. #include <graphpoint.h>
  19. #include <branchlistmodel.h>
  20. #include <taglistmodel.h>
  21. #include <gitconsole.h>
  22. #include <graphpoint.h>
  23. #include <settings.h>
  24. #include <gitremote.h>
  25. GitHandler::GitHandler() : QObject()
  26. ,m_repositories(new RepositoryModel(this))
  27. ,m_commits(nullptr)
  28. ,m_graph(nullptr)
  29. ,m_activeRepo(nullptr)
  30. ,m_activeDiff(nullptr)
  31. ,m_branchList(new BranchListModel(this))
  32. ,m_tagList(new TagListModel(this))
  33. ,m_activeRepoWatcher(new QFileSystemWatcher(this))
  34. ,m_console(new GitConsole(this))
  35. ,m_homePath(QUrl::fromLocalFile(QDir::homePath()))
  36. {
  37. git_libgit2_init();
  38. connect(&m_diffTask, &QFutureWatcher<GitDiff*>::finished, this, &GitHandler::onDiffReady);
  39. connect(&m_graphTask, &QFutureWatcher<CommitGraph*>::finished, this, &GitHandler::onGraphReady);
  40. connect(&m_graphTask, &QFutureWatcher<CommitGraph*>::started, this, &GitHandler::isBusyChanged);
  41. connect(&m_graphTask, &QFutureWatcher<CommitGraph*>::finished, this, &GitHandler::isBusyChanged);
  42. connect(&m_graphTask, &QFutureWatcher<CommitGraph*>::canceled, this, &GitHandler::isBusyChanged);
  43. connect(&m_graphTask, &QFutureWatcher<CommitGraph*>::paused, this, &GitHandler::isBusyChanged);
  44. connect(&m_graphTask, &QFutureWatcher<CommitGraph*>::resumed, this, &GitHandler::isBusyChanged);
  45. loadCachedRepos();
  46. }
  47. GitHandler::~GitHandler()
  48. {
  49. git_libgit2_shutdown();
  50. }
  51. void GitHandler::open(const QUrl &url, bool activate)
  52. {
  53. if(url.isLocalFile()) {
  54. open(url.toLocalFile(), activate);
  55. }
  56. }
  57. void GitHandler::open(const QString &path, bool activate)
  58. {
  59. qDebug() << "path" << path;
  60. git_buf root = {0, 0, 0};
  61. if(git_repository_discover(&root, path.toUtf8().data(), 0, NULL) != 0) {
  62. qDebug() << lastError();
  63. return;
  64. }
  65. QString rootStr = QString::fromUtf8(root.ptr, root.size);
  66. GitRepository* repo = new GitRepository(rootStr);
  67. if (!m_repositories->findByProperty("path", QVariant::fromValue(repo->path())).isNull()) {
  68. qDebug() << "Repository is already in list";
  69. delete repo;
  70. return;
  71. }
  72. Settings::instance()->add(repo);
  73. m_repositories->addRepository(repo);
  74. if (activate) {
  75. setActiveRepo(repo);
  76. }
  77. }
  78. void GitHandler::activateRepository(int i)
  79. {
  80. GitRepository* repo = m_repositories->at(i);
  81. setActiveRepo(repo);
  82. }
  83. void GitHandler::setActiveRepo(GitRepository* repo)
  84. {
  85. if (repo == nullptr || !repo->isValid()) {
  86. qDebug() << lastError();
  87. return;
  88. }
  89. if (m_activeRepo) {
  90. disconnect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, m_activeRepo, &GitRepository::readBranches);
  91. disconnect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, m_activeRepo, &GitRepository::readRemotes);
  92. disconnect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, m_activeRepo, &GitRepository::readTags);
  93. disconnect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, this, &GitHandler::updateModels);
  94. m_activeRepoWatcher->removePath(m_activeRepo->root());
  95. disconnect(m_activeRepo, &GitRepository::branchesChanged, this, &GitHandler::updateModels);
  96. }
  97. m_activeRepo = repo;
  98. connect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, m_activeRepo, &GitRepository::readBranches);
  99. connect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, m_activeRepo, &GitRepository::readRemotes);
  100. connect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, m_activeRepo, &GitRepository::readTags);
  101. connect(m_activeRepoWatcher, &QFileSystemWatcher::directoryChanged, this, &GitHandler::updateModels);
  102. connect(m_activeRepo, &GitRepository::branchesChanged, this, &GitHandler::updateModels);
  103. ColorHandler::instance().updateColors(m_activeRepo);
  104. // m_constantHead = m_activeRepo->head(); // TODO
  105. m_console->setRepository(m_activeRepo);
  106. updateModels();
  107. m_activeRepoWatcher->addPath(m_activeRepo->root());
  108. m_repositories->setActiveRepositoryIndex(m_repositories->indexOf(m_activeRepo));
  109. activeRepoChanged(m_activeRepo);
  110. Settings::instance()->saveLastRepo(m_activeRepo);
  111. }
  112. QString GitHandler::lastError() const
  113. {
  114. const git_error *e = giterr_last();
  115. if(e) {
  116. return QString("(%1): %2").arg(e->klass).arg(e->message);
  117. giterr_clear();
  118. }
  119. giterr_clear();
  120. return QString();
  121. }
  122. void GitHandler::diff(GitCommit* a, GitCommit* b)
  123. {
  124. if(!m_activeDiff.isNull()) {
  125. m_activeDiff->deleteLater();
  126. }
  127. Q_ASSERT_X(a->repository() == b->repository(), "GitHandler", "Cross repository diff requested");
  128. if(m_diffTask.isRunning()) {
  129. m_diffTask.cancel();
  130. }
  131. qDebug() << "Diff start thread: " << QThread::currentThreadId();
  132. QFuture<GitDiff*> future = QtConcurrent::run(&GitDiff::diff, a, b);
  133. m_diffTask.setFuture(future);
  134. }
  135. void GitHandler::diff()
  136. {
  137. if(!m_activeDiff.isNull()) {
  138. m_activeDiff->deleteLater();
  139. }
  140. GitCommit* commit = GitCommit::fromOid(activeRepo()->head());
  141. QFuture<GitDiff*> future = QtConcurrent::run(&GitDiff::diff, commit);
  142. m_diffTask.setFuture(future);
  143. }
  144. void GitHandler::diffReset()
  145. {
  146. delete m_activeDiff;
  147. }
  148. void GitHandler::onDiffReady()
  149. {
  150. setActiveDiff(m_diffTask.result());
  151. }
  152. void GitHandler::setActiveDiff(GitDiff* activeDiff)
  153. {
  154. if (m_activeDiff == activeDiff)
  155. return;
  156. m_activeDiff = activeDiff;
  157. emit activeDiffChanged(activeDiff);
  158. }
  159. GitDiff* GitHandler::activeDiff() const
  160. {
  161. return m_activeDiff.data();
  162. }
  163. void GitHandler::pull(GitBranch::PullStrategy strategy) const
  164. {
  165. if(m_activeRepo->remotes().count() <= 0) {
  166. qWarning() << "No remotes available for repository. Please add manually in console";
  167. //TODO: duplicate with warning popup
  168. return;
  169. }
  170. GitRemote *remote = m_activeRepo->remotes().first().data();
  171. if(remote == nullptr) {
  172. qCritical() << "Null remote in remotes detected. It's critical";
  173. Q_ASSERT(remote == nullptr);
  174. return;
  175. }
  176. remote->fetch();
  177. GitOid oid = m_activeRepo->head();
  178. auto branches = m_activeRepo->branches();
  179. auto result = std::find_if(branches.begin(), branches.end(), [&](QPointer<GitBranch>& branch) {
  180. return branch->oid() == oid;
  181. });
  182. if(result == branches.end() || (*result).isNull()) {
  183. qDebug() << "HEAD is not on branch";
  184. return;
  185. }
  186. GitBranch *headBranch = (*result);
  187. headBranch->pull(strategy);
  188. }
  189. void GitHandler::updateModels()
  190. {
  191. if(!m_activeRepo) {
  192. return;
  193. }
  194. BranchContainer &branches = m_activeRepo->branches();
  195. m_activeRepo->updateHead();
  196. m_graphTask.cancel();
  197. m_graphTask.setFuture(QtConcurrent::run(&GitHandler::updateGraph, m_activeRepo->head(), branches));
  198. m_branchList->reset(branches.values());
  199. m_tagList->reset(m_activeRepo->tags().values());
  200. }
  201. void GitHandler::copy(const QString& sha1)
  202. {
  203. QGuiApplication::clipboard()->setText(sha1);
  204. }
  205. CommitGraph* GitHandler::updateGraph(const GitOid &head, const BranchContainer &branches)
  206. {
  207. CommitGraph* graph = new CommitGraph();
  208. bool headIsBranch = false;
  209. //TODO: Need to think about constant head more deeply
  210. // foreach(GitBranch* branch, branches) {
  211. // if(branch->oid() == m_constantHead) {
  212. // graph->addHead(branch);
  213. // headIsBranch = true;
  214. // break;
  215. // }
  216. // }
  217. if(!headIsBranch) {
  218. graph->addHead(head);
  219. }
  220. foreach(GitBranch* branch, branches) {
  221. qDebug() << "Next head " << branch->fullName();
  222. graph->addHead(branch);
  223. }
  224. graph->addWorkdir();
  225. graph->moveToThread(head.repository()->thread());
  226. return graph;
  227. }
  228. void GitHandler::onGraphReady()
  229. {
  230. m_commits->deleteLater();
  231. m_commits = nullptr;
  232. emit commitsChanged(m_commits);
  233. m_graph->deleteLater();
  234. m_graph = nullptr;
  235. emit graphChanged(m_graph);
  236. m_graph = m_graphTask.result();
  237. m_commits = CommitModel::fromGraph(m_graph);
  238. emit graphChanged(m_graph);
  239. emit commitsChanged(m_commits);
  240. }
  241. void GitHandler::loadCachedRepos()
  242. {
  243. QString activeRepo = Settings::instance()->loadLastRepo();
  244. QStringList cachedRepos;
  245. Settings::instance()->load(cachedRepos);
  246. foreach (QString repoPath, cachedRepos) {
  247. open(repoPath, false);
  248. }
  249. QPointer<GitRepository> repo = m_repositories->findByProperty("id", QVariant::fromValue<QString>(activeRepo));
  250. setActiveRepo(repo);
  251. }