gitdiff.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. #include "gitdiff.h"
  2. #include <gitrepository.h>
  3. #include <diffmodel.h>
  4. #include <gitcommit.h>
  5. #include <QDebug>
  6. #include <git2/types.h>
  7. #include <git2/diff.h>
  8. #include <git2/tree.h>
  9. #include <git2/commit.h>
  10. GitDiff::GitDiff(git_diff *raw, GitRepository* repository) : GitBase(raw, repository)
  11. {
  12. Q_ASSERT_X(m_repository, "GitDiff", "Repository of NULL");
  13. connect(m_repository, &GitRepository::destroyed, this, &GitDiff::deleteLater);
  14. }
  15. GitDiff* GitDiff::diff(GitCommit* a, GitCommit* b)
  16. {
  17. GitDiff* diff = nullptr;
  18. git_diff *diffRaw = nullptr;
  19. git_tree *aTree = nullptr;
  20. git_tree *bTree = nullptr;
  21. GitRepository* repo = nullptr;
  22. bool isValid = false;
  23. if(a == nullptr && b == nullptr) {
  24. return diff;
  25. }
  26. if(a != nullptr) {
  27. git_commit_tree(&aTree, a->raw());
  28. repo = a->repository();
  29. isValid |= a->isValid();
  30. }
  31. if(b != nullptr) {
  32. git_commit_tree(&bTree, b->raw());
  33. if(repo != nullptr && repo != b->repository()) {
  34. qDebug() << "Requested diff from different repositories";
  35. return diff;
  36. }
  37. repo = b->repository();
  38. isValid |= b->isValid();
  39. }
  40. if(!isValid) {
  41. qCritical() << "Both compared commits are invalid";
  42. return diff;
  43. }
  44. git_diff_tree_to_tree(&diffRaw, repo->raw(), aTree, bTree, nullptr);
  45. git_tree_free(aTree);
  46. git_tree_free(bTree);
  47. diff = new GitDiff(diffRaw,repo);
  48. diff->readBody(diffRaw);
  49. diff->moveToThread(a->repository()->thread());
  50. diff->setParent(a->repository());
  51. return diff;
  52. }
  53. GitDiff* GitDiff::diff(GitCommit* a)
  54. {
  55. GitDiff* diff = nullptr;
  56. git_diff *diffRaw = nullptr;
  57. git_tree *aTree = nullptr;
  58. if(a == nullptr) {
  59. qCritical() << "Requested diff for zero-pointer commit";
  60. return nullptr;
  61. }
  62. git_commit_tree(&aTree, a->raw());
  63. git_diff_tree_to_workdir(&diffRaw, a->repository()->raw(), aTree, nullptr);
  64. git_tree_free(aTree);
  65. diff = new GitDiff(diffRaw, a->repository());
  66. diff->readBody(diffRaw);
  67. diff->moveToThread(a->repository()->thread());
  68. diff->setParent(a->repository());
  69. delete a;//TODO need indication if "a" could be deleted
  70. return diff;
  71. }
  72. GitDiff::~GitDiff()
  73. {
  74. reset();
  75. git_diff_free(m_raw);
  76. }
  77. void GitDiff::readBody(git_diff *diff)
  78. {
  79. git_diff_find_options similarityOpts;
  80. git_diff_find_init_options(&similarityOpts, GIT_DIFF_FIND_OPTIONS_VERSION);
  81. git_diff_find_similar(diff, &similarityOpts);
  82. git_diff_print(diff,
  83. GIT_DIFF_FORMAT_PATCH,
  84. [](const git_diff_delta *delta, const git_diff_hunk *hunk,
  85. const git_diff_line *line, void *payload) -> int
  86. {
  87. Q_UNUSED(hunk)
  88. QString prefix("<font color=\"%1\">%2");
  89. QString suffix("</font><br/>");
  90. GitDiff* diff = static_cast<GitDiff*>(payload);
  91. QString newFileName(delta->new_file.path);
  92. QString oldFileName(delta->old_file.path);
  93. QString diffData;
  94. switch(line->origin) {
  95. case GIT_DIFF_LINE_ADDITION:
  96. prefix = prefix.arg("#00ff00");
  97. break;
  98. case GIT_DIFF_LINE_DELETION:
  99. prefix = prefix.arg("#ff0000");
  100. break;
  101. case GIT_DIFF_LINE_HUNK_HDR:
  102. prefix = "<br/><b>";
  103. suffix = "</b><br/>";
  104. break;
  105. default:
  106. prefix = prefix.arg("#000000").arg("&nbsp;");
  107. break;
  108. }
  109. if ( line->origin == GIT_DIFF_LINE_ADDITION ||
  110. line->origin == GIT_DIFF_LINE_DELETION) {
  111. prefix = prefix.arg(line->origin);
  112. }
  113. if(!diff->m_diffList.contains(newFileName)) {
  114. diff->m_diffList.insert(newFileName, QPointer<DiffModel>(new DiffModel(newFileName, diff)));
  115. }
  116. if(line->origin != GIT_DIFF_LINE_FILE_HDR) { //Add line only in case if origin is not file header
  117. diffData = QString::fromUtf8(line->content, line->content_len);
  118. } else if(delta->status == GIT_DELTA_RENAMED){ //else check the similarity
  119. if(delta->similarity == 100) {
  120. diffData = QString(oldFileName + " -> " + newFileName);
  121. }
  122. diff->m_diffList[newFileName]->setSimilarity(delta->similarity);
  123. }
  124. if(!diffData.isEmpty()) {
  125. diff->m_diffList[newFileName]->append(prefix);
  126. diff->m_diffList[newFileName]->append(diffData.toHtmlEscaped().replace(" ", "&nbsp;"));
  127. diff->m_diffList[newFileName]->append(suffix);
  128. }
  129. diff->m_diffList[newFileName]->setType(delta->status);
  130. return 0;
  131. }, this);
  132. }
  133. void GitDiff::reset()
  134. {
  135. foreach (QPointer<DiffModel> model, m_diffList) {
  136. model.clear();
  137. }
  138. m_diffList.clear();
  139. }
  140. QStringList GitDiff::files()
  141. {
  142. return m_diffList.keys();
  143. }
  144. DiffModel* GitDiff::model(const QString& file)
  145. {
  146. DiffModel* model = m_diffList.value(file).data();
  147. return model;
  148. }