gitdiff.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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. qDebug() << "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. return diff;
  50. }
  51. GitDiff* GitDiff::diff(GitCommit* a)
  52. {
  53. GitDiff* diff = nullptr;
  54. git_diff *diffRaw = nullptr;
  55. git_tree *aTree = nullptr;
  56. if(a == nullptr) {
  57. return nullptr;
  58. }
  59. git_commit_tree(&aTree, a->raw());
  60. git_diff_tree_to_workdir(&diffRaw, a->repository()->raw(), aTree, nullptr);
  61. git_tree_free(aTree);
  62. diff = new GitDiff(diffRaw, a->repository());
  63. diff->readBody(diffRaw);
  64. return diff;
  65. }
  66. GitDiff::~GitDiff()
  67. {
  68. reset();
  69. git_diff_free(m_raw);
  70. }
  71. void GitDiff::readBody(git_diff *diff)
  72. {
  73. git_diff_find_options similarityOpts;
  74. git_diff_find_init_options(&similarityOpts, GIT_DIFF_FIND_OPTIONS_VERSION);
  75. git_diff_find_similar(diff, &similarityOpts);
  76. git_diff_print(diff,
  77. GIT_DIFF_FORMAT_PATCH,
  78. [](const git_diff_delta *delta, const git_diff_hunk *hunk,
  79. const git_diff_line *line, void *payload) -> int
  80. {
  81. Q_UNUSED(hunk)
  82. QString prefix("<font color=\"%1\">%2");
  83. QString suffix("</font><br/>");
  84. GitDiff* diff = static_cast<GitDiff*>(payload);
  85. QString newFileName(delta->new_file.path);
  86. QString oldFileName(delta->old_file.path);
  87. QString diffData;
  88. switch(line->origin) {
  89. case GIT_DIFF_LINE_ADDITION:
  90. prefix = prefix.arg("#00ff00");
  91. break;
  92. case GIT_DIFF_LINE_DELETION:
  93. prefix = prefix.arg("#ff0000");
  94. break;
  95. case GIT_DIFF_LINE_HUNK_HDR:
  96. prefix = "<br/><b>";
  97. suffix = "</b><br/>";
  98. break;
  99. default:
  100. prefix = prefix.arg("#000000").arg("&nbsp;");
  101. break;
  102. }
  103. if ( line->origin == GIT_DIFF_LINE_ADDITION ||
  104. line->origin == GIT_DIFF_LINE_DELETION) {
  105. prefix = prefix.arg(line->origin);
  106. }
  107. if(!diff->m_diffList.contains(newFileName)) {
  108. diff->m_diffList.insert(newFileName, QPointer<DiffModel>(new DiffModel(newFileName, diff)));
  109. }
  110. if(line->origin != GIT_DIFF_LINE_FILE_HDR) { //Add line only in case if origin is not file header
  111. diffData = QString::fromUtf8(line->content, line->content_len);
  112. } else if(delta->status == GIT_DELTA_RENAMED){ //else check the similarity
  113. if(delta->similarity == 100) {
  114. diffData = QString(oldFileName + " -> " + newFileName);
  115. }
  116. diff->m_diffList[newFileName]->setSimilarity(delta->similarity);
  117. }
  118. if(!diffData.isEmpty()) {
  119. diff->m_diffList[newFileName]->append(prefix);
  120. diff->m_diffList[newFileName]->append(diffData.toHtmlEscaped().replace(" ", "&nbsp;"));
  121. diff->m_diffList[newFileName]->append(suffix);
  122. }
  123. diff->m_diffList[newFileName]->setType(delta->status);
  124. return 0;
  125. }, this);
  126. }
  127. void GitDiff::reset()
  128. {
  129. foreach (QPointer<DiffModel> model, m_diffList) {
  130. model.clear();
  131. }
  132. m_diffList.clear();
  133. }
  134. QStringList GitDiff::files()
  135. {
  136. return m_diffList.keys();
  137. }
  138. DiffModel* GitDiff::model(const QString& file)
  139. {
  140. DiffModel* model = m_diffList.value(file).data();
  141. return model;
  142. }