1#include "RevisionFiles.h"
2
3RevisionFiles::RevisionFiles(const QString &diff, bool cached)
4{
5 auto parNum = 1;
6
7#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
8 const auto lines = diff.split("\n", Qt::SkipEmptyParts);
9#else
10 const auto lines = diff.split("\n", QString::SkipEmptyParts);
11#endif
12
13 for (auto line : lines)
14 {
15 if (line[0] == ':') // avoid sha's in merges output
16 {
17 if (line[1] == ':')
18 {
19 mFiles.append(line.section('\t', -1));
20 setStatus("M");
21 mergeParent.append(parNum);
22 }
23 else
24 {
25 if (line.at(98) == '\t') // Faster parsing in normal case
26 {
27 auto fields = line.split(" ");
28 const auto dstSha = fields.at(3);
29 auto fileIsCached = !dstSha.startsWith(QStringLiteral("000000"));
30 const auto flag = fields.at(4).at(0);
31
32 mFiles.append(line.mid(99));
33 setStatus(flag, cached ? cached : fileIsCached);
34 mergeParent.append(parNum);
35 }
36 else // It's a rename or a copy, we are not in fast path now!
37 setExtStatus(line.mid(97), parNum);
38 }
39 }
40 else
41 ++parNum;
42 }
43}
44
45RevisionFiles::~RevisionFiles()
46{
47 mFileStatus.clear();
48 mFileStatus.squeeze();
49 mRenamedFiles.clear();
50 mRenamedFiles.squeeze();
51 mergeParent.clear();
52 mergeParent.squeeze();
53 mFiles.clear();
54 mFiles.squeeze();
55}
56
57bool RevisionFiles::isValid() const
58{
59 return !(mFiles.empty() && mFileStatus.empty() && mRenamedFiles.empty());
60}
61
62bool RevisionFiles::operator==(const RevisionFiles &revFiles) const
63{
64 return mFiles == revFiles.mFiles && mOnlyModified == revFiles.mOnlyModified && mergeParent == revFiles.mergeParent
65 && mFileStatus == revFiles.mFileStatus && mRenamedFiles == revFiles.mRenamedFiles;
66}
67
68bool RevisionFiles::operator!=(const RevisionFiles &revFiles) const
69{
70 return !(*this == revFiles);
71}
72
73bool RevisionFiles::statusCmp(int idx, RevisionFiles::StatusFlag sf) const
74{
75 if (idx >= mFileStatus.count())
76 return false;
77
78 return (mOnlyModified ? MODIFIED : mFileStatus.at(static_cast<int>(idx))) & sf;
79}
80
81const QString RevisionFiles::extendedStatus(int idx) const
82{
83 /*
84 rf.extStatus has size equal to position of latest copied/renamed file,
85 that could be lower then count(), so we have to explicitly check for
86 an out of bound condition.
87 */
88 return !mRenamedFiles.isEmpty() && idx < mRenamedFiles.count() ? mRenamedFiles.at(idx) : "";
89}
90
91void RevisionFiles::setStatus(const QString &rowSt, bool isStaged)
92{
93 switch (rowSt.at(0).toLatin1())
94 {
95 case 'M':
96 case 'T':
97 mFileStatus.append(RevisionFiles::MODIFIED);
98 if (isStaged)
99 mFileStatus[mFileStatus.count() - 1] |= RevisionFiles::IN_INDEX;
100 break;
101 case 'U':
102 mFileStatus.append(RevisionFiles::MODIFIED);
103 mFileStatus[mFileStatus.count() - 1] |= RevisionFiles::CONFLICT;
104 if (isStaged)
105 mFileStatus[mFileStatus.count() - 1] |= RevisionFiles::IN_INDEX;
106 mOnlyModified = false;
107 break;
108 case 'D':
109 mFileStatus.append(RevisionFiles::DELETED);
110 mOnlyModified = false;
111 if (isStaged)
112 mFileStatus[mFileStatus.count() - 1] |= RevisionFiles::IN_INDEX;
113 break;
114 case 'A':
115 mFileStatus.append(RevisionFiles::NEW);
116 mOnlyModified = false;
117 if (isStaged)
118 mFileStatus[mFileStatus.count() - 1] |= RevisionFiles::IN_INDEX;
119 break;
120 case '?':
121 mFileStatus.append(RevisionFiles::UNKNOWN);
122 mOnlyModified = false;
123 break;
124 default:
125 mFileStatus.append(RevisionFiles::MODIFIED);
126 break;
127 }
128}
129
130void RevisionFiles::setStatus(RevisionFiles::StatusFlag flag)
131{
132 mFileStatus.append(flag);
133
134 if (flag == RevisionFiles::DELETED || flag == RevisionFiles::NEW || flag == RevisionFiles::UNKNOWN)
135 mOnlyModified = false;
136}
137
138void RevisionFiles::setStatus(int pos, RevisionFiles::StatusFlag flag)
139{
140 mFileStatus[pos] = flag;
141}
142
143void RevisionFiles::appendStatus(int pos, RevisionFiles::StatusFlag flag)
144{
145 mFileStatus[pos] |= flag;
146}
147
148void RevisionFiles::setExtStatus(const QString &rowSt, int parNum)
149{
150#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
151 const QStringList sl(rowSt.split('\t', Qt::SkipEmptyParts));
152#else
153 const QStringList sl(rowSt.split('\t', QString::SkipEmptyParts));
154#endif
155 if (sl.count() != 3)
156 return;
157
158 // we want store extra info with format "orig --> dest (Rxx%)"
159 // but git give us something like "Rxx\t<orig>\t<dest>"
160 QString type = sl[0];
161 type.remove(0, 1);
162 const QString &orig = sl[1];
163 const QString &dest = sl[2];
164 const QString extStatusInfo(orig + " --> " + dest + " (" + QString::number(type.toInt()) + "%)");
165
166 mFiles.append(dest);
167 mergeParent.append(parNum);
168 setStatus(RevisionFiles::NEW);
169 appendExtStatus(extStatusInfo);
170
171 // simulate deleted orig file only in case of rename
172 if (type.at(0) == 'R')
173 {
174 mFiles.append(orig);
175 mergeParent.append(parNum);
176 setStatus(RevisionFiles::DELETED);
177 appendExtStatus(extStatusInfo);
178 }
179
180 setOnlyModified(false);
181}
182