gitrepository.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #include "gitrepository.h"
  2. #include <QDebug>
  3. #include <QFileInfo>
  4. #include <QDir>
  5. #include <QCryptographicHash>
  6. #include <gitbranch.h>
  7. #include <gitcommit.h>
  8. #include <gittag.h>
  9. #include <gitremote.h>
  10. #include <gitdiff.h>
  11. #include <git2.h>
  12. GitRepository::GitRepository(const QString& root) : QObject(nullptr)
  13. {
  14. if(git_repository_open(&m_raw, root.toUtf8().data()) != 0) {
  15. qDebug() << "Cannot open repository";
  16. close();
  17. return;
  18. }
  19. m_root = root;
  20. m_path = git_repository_workdir(m_raw);
  21. m_name = m_path.split("/", QString::SkipEmptyParts).last();
  22. qDebug() << "New repo:" << m_name << m_root << m_path;
  23. readBranches();
  24. readTags();
  25. readRemotes();
  26. updateHead();
  27. }
  28. GitRepository::~GitRepository()
  29. {
  30. qDebug() << "GitRepository::~GitRepository";
  31. close();
  32. }
  33. void GitRepository::close()
  34. {
  35. if(m_raw) {
  36. git_repository_free(m_raw);
  37. }
  38. m_raw = nullptr;
  39. }
  40. void GitRepository::readBranches()
  41. {
  42. git_reference *branchRef;
  43. git_branch_t branchType;
  44. git_branch_iterator* iter;
  45. git_branch_iterator_new(&iter, m_raw, GIT_BRANCH_ALL);
  46. while(git_branch_next(&branchRef, &branchType, iter) == 0) {
  47. GitBranch* branch = new GitBranch(branchRef, branchType, this);
  48. m_branches.insert(branch->fullName(), QPointer<GitBranch>(branch));
  49. qDebug() << branch->fullName();
  50. qDebug() << branch->type();
  51. }
  52. emit branchesChanged();
  53. }
  54. void GitRepository::readTags()
  55. {
  56. git_tag_foreach(raw(),
  57. [](const char *name, git_oid *oid, void *payload) -> int {
  58. Q_UNUSED(payload)
  59. Q_UNUSED(name)
  60. GitRepository* repo = static_cast<GitRepository*>(payload);
  61. git_tag* tagraw = 0;
  62. if(git_tag_lookup(&tagraw, repo->raw(), oid) != 0) {
  63. qCritical() << "Invalid tag found. Broken repository";
  64. return 1;
  65. }
  66. GitTag* tag = new GitTag(tagraw, repo);
  67. if(tag->isValid()) {
  68. repo->m_tags.insert(tag->targetId(), tag);
  69. }
  70. qDebug() << "Tag found: " << tag->name() << tag->sha1();
  71. return 0;
  72. },
  73. this);
  74. }
  75. void GitRepository::readRemotes()
  76. {
  77. git_strarray str_array;
  78. git_remote_list(&str_array, raw());
  79. for(int i = 0; i < str_array.count; i++) {
  80. GitRemote* remote = GitRemote::fromName(QString::fromLatin1(str_array.strings[i]), this);
  81. if(remote) {
  82. m_remotes.insert(remote->name(), QPointer<GitRemote>(remote));
  83. }
  84. }
  85. //TODO: Why this code was left?
  86. // git_reference_foreach_glob(raw(),"refs/remotes/*", [](const char *name, void *payload) -> int
  87. // {
  88. // qDebug() << "Remotes: " << name;
  89. // return 0;
  90. //// if(git_reference_is_remote(reference)) {
  91. //// }
  92. // }, this);
  93. // git_remote* remoteRaw;
  94. // git_reference_is_remote()
  95. }
  96. void GitRepository::checkout(QObject* object)
  97. {
  98. git_checkout_options opts;
  99. git_checkout_init_options(&opts, GIT_CHECKOUT_OPTIONS_VERSION);
  100. opts.checkout_strategy = GIT_CHECKOUT_FORCE;//TODO: modify to merge policy
  101. opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
  102. opts.notify_cb = [](
  103. git_checkout_notify_t why,
  104. const char *path,
  105. const git_diff_file *baseline,
  106. const git_diff_file *target,
  107. const git_diff_file *workdir,
  108. void *payload) -> int {
  109. //TODO: make popup with progressbar
  110. // qDebug() << "path:" << path;
  111. // switch (why) {
  112. // case GIT_CHECKOUT_NOTIFY_CONFLICT:
  113. // qDebug() << "conflict";
  114. // break;
  115. // case GIT_CHECKOUT_NOTIFY_DIRTY:
  116. // qDebug() << "dirty";
  117. // break;
  118. // case GIT_CHECKOUT_NOTIFY_UPDATED:
  119. // qDebug() << "updated";
  120. // break;
  121. // case GIT_CHECKOUT_NOTIFY_UNTRACKED:
  122. // qDebug() << "untracked";
  123. // break;
  124. // case GIT_CHECKOUT_NOTIFY_IGNORED:
  125. // qDebug() << "ignored";
  126. // break;
  127. // default:
  128. // break;
  129. // }
  130. return 0;
  131. };
  132. GitOid oid;
  133. GitBranch* branch = dynamic_cast<GitBranch*>(object);
  134. GitCommit* commit = dynamic_cast<GitCommit*>(object);
  135. if(branch != nullptr) {
  136. commit = GitCommit::fromOid(branch->oid());
  137. qDebug() << "Checkout branch: " << git_checkout_tree(raw(), (git_object*)(commit->raw()), &opts);
  138. switch(branch->type()) {
  139. case GitBranch::Local:
  140. break;
  141. case GitBranch::Remote:
  142. git_reference* ref;
  143. //TODO: better to create API for local branch creation
  144. if(0 == git_branch_create_from_annotated(&ref, raw(), branch->name().toUtf8().data(), branch->annotatedCommit(), 0)) {
  145. branch = new GitBranch(ref, GIT_BRANCH_LOCAL, this);
  146. branch->setUpstream(*branch);
  147. m_branches.insert(branch->fullName(), QPointer<GitBranch>(branch));
  148. emit branchesChanged();
  149. } else if(m_branches.contains(branch->name())) { /*
  150. TODO: need to think how valid is this.
  151. In case if local branch already exists
  152. it will be checked out.
  153. But from commit tree perspective it doesn't
  154. look as valid behavior.
  155. */
  156. branch = m_branches.value(branch->name());
  157. }
  158. break;
  159. }
  160. git_repository_set_head(raw(), branch->refName().toLatin1().data());
  161. oid = branch->oid();
  162. delete commit;
  163. } else if(commit != nullptr) {
  164. qDebug() << "Checkout commit: " << git_checkout_tree(raw(), (git_object*)(commit->raw()), &opts);
  165. git_repository_set_head_detached(raw(), commit->oid().raw());
  166. oid = commit->oid();
  167. } else {
  168. qDebug() << "Invalid object for checkout";
  169. return;
  170. }
  171. setHead(oid);
  172. }
  173. void GitRepository::updateHead()
  174. {
  175. //Read head
  176. git_reference* ref = nullptr;
  177. git_repository_head(&ref, raw());
  178. git_annotated_commit* commit = nullptr;
  179. git_annotated_commit_from_ref(&commit, raw(), ref);
  180. setHead(GitOid(git_annotated_commit_id(commit), this));
  181. qDebug() << "Repo head" << m_head.toString();
  182. }
  183. QString GitRepository::id() const
  184. {
  185. return QCryptographicHash::hash(name().toUtf8() + path().toUtf8(), QCryptographicHash::Sha1).toHex();
  186. }
  187. #if 0 //Test functionality
  188. void GitRepository::testReadBranches()
  189. {
  190. QList<GitBranch*> m_branches;
  191. git_reference *branchRef;
  192. git_branch_t branchType;
  193. git_branch_iterator* iter;
  194. git_branch_iterator_new(&iter, m_raw, GIT_BRANCH_ALL);
  195. while(git_branch_next(&branchRef, &branchType, iter) == 0) {
  196. GitBranch* branch = new GitBranch(branchRef, branchType, this);
  197. qDebug() << branch->fullName();
  198. qDebug() << branch->type();
  199. GitBranch upstreamBranch = std::move(branch->upstream());
  200. m_branches.append(branch);
  201. // qDebug() << upstreamBranch.fullName();
  202. }
  203. for(auto branch : m_branches)
  204. {
  205. GitBranch branchMoved(std::move(*branch));
  206. delete branch;
  207. GitBranch upstream = branchMoved.upstream();
  208. qDebug() << "valid upstream:" << upstream.isValid();
  209. }
  210. }
  211. #endif