1 | #include "PrChangesList.h" |
2 | |
3 | #include <DiffHelper.h> |
4 | #include <GitHistory.h> |
5 | #include <FileDiffView.h> |
6 | #include <PrChangeListItem.h> |
7 | #include <PullRequest.h> |
8 | #include <GitRemote.h> |
9 | #include <GitConfig.h> |
10 | #include <QLogger.h> |
11 | |
12 | #include <QGridLayout> |
13 | #include <QLabel> |
14 | #include <QScrollArea> |
15 | |
16 | using namespace GitServer; |
17 | using namespace QLogger; |
18 | |
19 | PrChangesList::PrChangesList(const QSharedPointer<GitBase> &git, QWidget *parent) |
20 | : QFrame(parent) |
21 | , mGit(git) |
22 | { |
23 | QScopedPointer<GitRemote> gitRemote(new GitRemote(mGit)); |
24 | gitRemote->fetch(); |
25 | } |
26 | |
27 | void PrChangesList::loadData(const GitServer::PullRequest &prInfo) |
28 | { |
29 | GitExecResult ret; |
30 | bool showDiff = true; |
31 | QString head; |
32 | |
33 | if (prInfo.headRepo != prInfo.baseRepo) |
34 | { |
35 | QScopedPointer<GitConfig> git(new GitConfig(mGit)); |
36 | const auto ret = git->getGitValue(QString("remote.%1.url" ).arg(prInfo.headRepo.split("/" ).constFirst())); |
37 | |
38 | if (ret.output.isEmpty()) |
39 | { |
40 | const auto response = QMessageBox::question( |
41 | this, tr("Getting remote branch" ), |
42 | tr("The head branch of the Pull Request is not in the same repository. In order to show " |
43 | "the changes the remote must be added. <b>Do you want to get the remote branch?</b>" )); |
44 | |
45 | if (response == QMessageBox::Yes) |
46 | { |
47 | QScopedPointer<GitRemote> git(new GitRemote(mGit)); |
48 | const auto remoteAdded = git->addRemote(prInfo.headUrl, prInfo.headRepo.split("/" ).constFirst()); |
49 | |
50 | showDiff = remoteAdded.success; |
51 | |
52 | if (!showDiff) |
53 | QLog_Warning("UI" , QString("Problems adding a remote: {%1}" ).arg(remoteAdded.output)); |
54 | } |
55 | else |
56 | showDiff = false; |
57 | |
58 | if (!showDiff) |
59 | return; |
60 | } |
61 | |
62 | head = QString("%1/%2" ).arg(prInfo.headRepo.split("/" ).constFirst(), prInfo.head); |
63 | } |
64 | else |
65 | { |
66 | QScopedPointer<GitConfig> git(new GitConfig(mGit)); |
67 | auto retBase = git->getRemoteForBranch(prInfo.head); |
68 | head = QString("%1/%2" ).arg(retBase.success ? retBase.output : "origin" , prInfo.head); |
69 | } |
70 | |
71 | QScopedPointer<GitConfig> gitConfig(new GitConfig(mGit)); |
72 | auto retBase = gitConfig->getRemoteForBranch(prInfo.head); |
73 | const auto base = QString("%1/%2" ).arg(retBase.success ? retBase.output : "origin" , prInfo.base); |
74 | |
75 | QScopedPointer<GitHistory> git(new GitHistory(mGit)); |
76 | ret = git->getBranchesDiff(base, head); |
77 | |
78 | if (ret.success) |
79 | { |
80 | auto diff = ret.output; |
81 | auto changes = DiffHelper::splitDiff(diff); |
82 | |
83 | if (!changes.isEmpty()) |
84 | { |
85 | delete layout(); |
86 | |
87 | const auto mainLayout = new QVBoxLayout(); |
88 | mainLayout->setContentsMargins(20, 20, 20, 20); |
89 | mainLayout->setSpacing(0); |
90 | |
91 | for (auto &change : changes) |
92 | { |
93 | const auto changeListItem = new PrChangeListItem(change); |
94 | connect(changeListItem, &PrChangeListItem::gotoReview, this, &PrChangesList::gotoReview); |
95 | connect(changeListItem, &PrChangeListItem::addCodeReview, this, &PrChangesList::addCodeReview); |
96 | |
97 | mListItems.append(changeListItem); |
98 | |
99 | mainLayout->addWidget(changeListItem); |
100 | mainLayout->addSpacing(10); |
101 | } |
102 | |
103 | const auto mIssuesFrame = new QFrame(); |
104 | mIssuesFrame->setObjectName("IssuesViewFrame" ); |
105 | mIssuesFrame->setLayout(mainLayout); |
106 | |
107 | const auto mScroll = new QScrollArea(); |
108 | mScroll->setWidgetResizable(true); |
109 | mScroll->setWidget(mIssuesFrame); |
110 | |
111 | const auto aLayout = new QVBoxLayout(this); |
112 | aLayout->setContentsMargins(QMargins()); |
113 | aLayout->setSpacing(0); |
114 | aLayout->addWidget(mScroll); |
115 | } |
116 | } |
117 | } |
118 | |
119 | void PrChangesList::onReviewsReceived(PullRequest pr) |
120 | { |
121 | using Bookmark = QPair<int, int>; |
122 | QMultiMap<QString, Bookmark> bookmarksPerFile; |
123 | |
124 | auto = pr.reviewComment; |
125 | |
126 | for (const auto &review : qAsConst(pr.reviews)) |
127 | { |
128 | QMap<int, QVector<CodeReview>> reviews; |
129 | QVector<int> codeReviewIds; |
130 | |
131 | auto iter = comments.begin(); |
132 | |
133 | while (iter != comments.end()) |
134 | { |
135 | if (iter->reviewId == review.id) |
136 | { |
137 | codeReviewIds.append(iter->id); |
138 | reviews[iter->id].append(*iter); |
139 | comments.erase(iter); |
140 | } |
141 | else if (codeReviewIds.contains(iter->replyToId)) |
142 | { |
143 | reviews[iter->replyToId].append(*iter); |
144 | comments.erase(iter); |
145 | } |
146 | else |
147 | ++iter; |
148 | } |
149 | |
150 | if (!reviews.isEmpty()) |
151 | { |
152 | for (auto &codeReviews : reviews) |
153 | { |
154 | std::sort(codeReviews.begin(), codeReviews.end(), |
155 | [](const CodeReview &r1, const CodeReview &r2) { return r1.creation < r2.creation; }); |
156 | |
157 | const auto first = codeReviews.constFirst(); |
158 | |
159 | if (!first.outdated) |
160 | bookmarksPerFile.insert(first.diff.file, { first.diff.line, first.id }); |
161 | else |
162 | bookmarksPerFile.insert(first.diff.file, { first.diff.originalLine, first.id }); |
163 | } |
164 | } |
165 | } |
166 | |
167 | for (auto iter : qAsConst(mListItems)) |
168 | { |
169 | QMap<int, int> bookmarks; |
170 | const auto values = bookmarksPerFile.values(iter->getFileName()); |
171 | |
172 | for (auto bookmark : qAsConst(values)) |
173 | { |
174 | if (bookmark.first >= iter->getStartingLine() && bookmark.first <= iter->getEndingLine()) |
175 | bookmarks.insert(bookmark.first, bookmark.second); |
176 | } |
177 | |
178 | if (!bookmarks.isEmpty()) |
179 | iter->setBookmarks(bookmarks); |
180 | } |
181 | } |
182 | |
183 | void PrChangesList::addLinks(PullRequest pr, const QMap<int, int> &) |
184 | { |
185 | QMultiMap<QString, QPair<int, int>> bookmarksPerFile; |
186 | |
187 | for (auto reviewId : reviewLinkToComments) |
188 | { |
189 | for (const auto &review : qAsConst(pr.reviewComment)) |
190 | { |
191 | if (review.id == reviewId) |
192 | { |
193 | if (!review.outdated) |
194 | bookmarksPerFile.insert(review.diff.file, { review.diff.line, review.id }); |
195 | else |
196 | bookmarksPerFile.insert(review.diff.file, { review.diff.originalLine, review.id }); |
197 | |
198 | break; |
199 | } |
200 | } |
201 | } |
202 | |
203 | for (const auto &iter : qAsConst(mListItems)) |
204 | { |
205 | QMap<int, int> bookmarks; |
206 | |
207 | const auto values = bookmarksPerFile.values(iter->getFileName()); |
208 | for (const auto &bookmark : values) |
209 | { |
210 | if (bookmark.first >= iter->getStartingLine() && bookmark.first <= iter->getEndingLine()) |
211 | bookmarks.insert(bookmark.first, reviewLinkToComments.key(bookmark.second)); |
212 | } |
213 | |
214 | if (!bookmarks.isEmpty()) |
215 | iter->setBookmarks(bookmarks); |
216 | } |
217 | } |
218 | |