Version 2.6
[qgit4/redivivus.git] / src / common.cpp
blob32a821ba24d0995d5dd7600b552ae3b7fa83f492
1 /*
2 Description: interface to git programs
4 Author: Marco Costalba (C) 2005-2007
6 Copyright: See COPYING file that comes with this distribution
8 */
10 #include <QDataStream>
11 #include "common.h"
13 const QString Rev::mid(int start, int len) const {
15 // warning no sanity check is done on arguments
16 const char* data = ba.constData();
17 return QString::fromLocal8Bit(data + start, len);
20 const QString Rev::midSha(int start, int len) const {
22 // warning no sanity check is done on arguments
23 const char* data = ba.constData();
24 return QString::fromLatin1(data + start, len); // faster then formAscii
27 const ShaString Rev::parent(int idx) const {
29 return ShaString(ba.constData() + shaStart + 41 + 41 * idx);
32 const QStringList Rev::parents() const {
34 QStringList p;
35 int idx = shaStart + 41;
37 for (int i = 0; i < parentsCnt; i++) {
38 p.append(midSha(idx, 40));
39 idx += 41;
41 return p;
44 int Rev::indexData(bool quick, bool withDiff) const {
46 This is what 'git log' produces:
48 - a possible one line with "Final output:\n" in case of --early-output option
49 - one line with "log size" + len of this record
50 - one line with boundary info + sha + an arbitrary amount of parent's sha
51 - one line with committer name + e-mail
52 - one line with author name + e-mail
53 - one line with author date as unix timestamp
54 - zero or more non blank lines with other info, as the encoding FIXME
55 - one blank line
56 - zero or one line with log title
57 - zero or more lines with log message
58 - zero or more lines with diff content (only for file history)
59 - a terminating '\0'
61 static int error = -1;
62 static int shaLength = 40; // from git ref. spec.
63 static int shaEndlLength = shaLength + 1; // an sha key + \n
64 static int shaXEndlLength = shaLength + 2; // an sha key + X marker + \n
65 static char finalOutputMarker = 'F'; // marks the beginning of "Final output" string
66 static char logSizeMarker = 'l'; // marks the beginning of "log size" string
67 static int logSizeStrLength = 9; // "log size"
68 static int asciiPosOfZeroChar = 48; // char "0" has value 48 in ascii table
70 const int last = ba.size() - 1;
71 int logSize = 0, idx = start;
72 int logEnd, revEnd;
74 // direct access is faster then QByteArray.at()
75 const char* data = ba.constData();
76 char* fixup = const_cast<char*>(data); // to build '\0' terminating strings
78 if (start + shaXEndlLength > last) // at least sha header must be present
79 return -1;
81 if (data[start] == finalOutputMarker) // "Final output", let caller handle this
82 return (ba.indexOf('\n', start) != -1 ? -2 : -1);
84 // parse 'log size xxx\n' if present -- from git ref. spec.
85 if (data[idx] == logSizeMarker) {
86 idx += logSizeStrLength; // move idx to beginning of log size value
88 // parse log size value
89 int digit;
90 while ((digit = data[idx++]) != '\n')
91 logSize = logSize * 10 + digit - asciiPosOfZeroChar;
93 // idx points to the boundary information, which has the same length as an sha header.
94 if (++idx + shaXEndlLength > last)
95 return error;
97 shaStart = idx;
99 // ok, now shaStart is valid but msgSize could be still 0 if not available
100 logEnd = shaStart - 1 + logSize;
101 if (logEnd > last)
102 return error;
104 idx += shaLength; // now points to 'X' place holder
106 fixup[idx] = '\0'; // we want sha to be a '\0' terminated ascii string
108 parentsCnt = 0;
110 if (data[idx + 2] == '\n') // initial revision
111 ++idx;
112 else do {
113 parentsCnt++;
114 idx += shaEndlLength;
116 if (idx + 1 >= last)
117 break;
119 fixup[idx] = '\0'; // we want parents '\0' terminated
121 } while (data[idx + 1] != '\n');
123 ++idx; // now points to the trailing '\n' of sha line
125 // check for !msgSize
126 if (withDiff || !logSize) {
128 revEnd = (logEnd > idx) ? logEnd - 1: idx;
129 revEnd = ba.indexOf('\0', revEnd + 1);
130 if (revEnd == -1)
131 return -1;
133 } else
134 revEnd = logEnd;
136 if (revEnd > last) // after this point we know to have the whole record
137 return error;
139 // ok, now revEnd is valid but logEnd could be not if !logSize
140 // in case of diff we are sure content will be consumed so
141 // we go all the way
142 if (quick && !withDiff)
143 return ++revEnd;
145 // commiter
146 comStart = ++idx;
147 idx = ba.indexOf('\n', idx); // committer line end
148 if (idx == -1) {
149 dbs("ASSERT in indexData: unexpected end of data");
150 return -1;
153 // author
154 autStart = ++idx;
155 idx = ba.indexOf('\n', idx); // author line end
156 if (idx == -1) {
157 dbs("ASSERT in indexData: unexpected end of data");
158 return -1;
161 // author date in Unix format (seconds since epoch)
162 autDateStart = ++idx;
163 idx = ba.indexOf('\n', idx); // author date end without '\n'
164 if (idx == -1) {
165 dbs("ASSERT in indexData: unexpected end of data");
166 return -1;
168 // if no error, point to trailing \n
169 ++idx;
171 diffStart = diffLen = 0;
172 if (withDiff) {
173 diffStart = logSize ? logEnd : ba.indexOf("\ndiff ", idx);
175 if (diffStart != -1 && diffStart < revEnd)
176 diffLen = revEnd - ++diffStart;
177 else
178 diffStart = 0;
180 if (!logSize)
181 logEnd = diffStart ? diffStart : revEnd;
183 // ok, now logEnd is valid and we can handle the log
184 sLogStart = idx;
186 if (logEnd < sLogStart) { // no shortlog no longLog
188 sLogStart = sLogLen = 0;
189 lLogStart = lLogLen = 0;
190 } else {
191 lLogStart = ba.indexOf('\n', sLogStart);
192 if (lLogStart != -1 && lLogStart < logEnd - 1) {
194 sLogLen = lLogStart - sLogStart; // skip sLog trailing '\n'
195 lLogLen = logEnd - lLogStart; // include heading '\n' in long log
197 } else { // no longLog
198 sLogLen = logEnd - sLogStart;
199 if (data[sLogStart + sLogLen - 1] == '\n')
200 sLogLen--; // skip trailing '\n' if any
202 lLogStart = lLogLen = 0;
205 indexed = true;
206 return ++revEnd;
211 * RevFile streaming out
213 const RevFile& RevFile::operator>>(QDataStream& stream) const {
215 stream << pathsIdx;
217 // skip common case of only modified files
218 bool isEmpty = onlyModified;
219 stream << (quint32)isEmpty;
220 if (!isEmpty)
221 stream << status;
223 // skip common case of just one parent
224 isEmpty = (mergeParent.isEmpty() || mergeParent.last() == 1);
225 stream << (quint32)isEmpty;
226 if (!isEmpty)
227 stream << mergeParent;
229 // skip common case of no rename/copies
230 isEmpty = extStatus.isEmpty();
231 stream << (quint32)isEmpty;
232 if (!isEmpty)
233 stream << extStatus;
235 return *this;
239 * RevFile streaming in
241 RevFile& RevFile::operator<<(QDataStream& stream) {
243 stream >> pathsIdx;
245 bool isEmpty;
246 quint32 tmp;
248 stream >> tmp;
249 onlyModified = (bool)tmp;
250 if (!onlyModified)
251 stream >> status;
253 stream >> tmp;
254 isEmpty = (bool)tmp;
255 if (!isEmpty)
256 stream >> mergeParent;
258 stream >> tmp;
259 isEmpty = (bool)tmp;
260 if (!isEmpty)
261 stream >> extStatus;
263 return *this;
266 QString qt4and5escaping(QString toescape) {
267 #if QT_VERSION >= 0x050000
268 return toescape.toHtmlEscaped();
269 #else
270 return Qt::escape(toescape);
271 #endif