2 Description: interface to git programs
4 Author: Marco Costalba (C) 2005-2007
6 Copyright: See COPYING file that comes with this distribution
10 #include <QDataStream>
11 #include <QTextDocument>
14 const QString
Rev::mid(int start
, int len
) const {
16 // warning no sanity check is done on arguments
17 const char* data
= ba
.constData();
18 return QString::fromLocal8Bit(data
+ start
, len
);
21 const QString
Rev::midSha(int start
, int len
) const {
23 // warning no sanity check is done on arguments
24 const char* data
= ba
.constData();
25 return QString::fromLatin1(data
+ start
, len
); // faster then formAscii
28 const ShaString
Rev::parent(int idx
) const {
30 return ShaString(ba
.constData() + shaStart
+ 41 + 41 * idx
);
33 const QStringList
Rev::parents() const {
36 int idx
= shaStart
+ 41;
38 for (int i
= 0; i
< parentsCnt
; i
++) {
39 p
.append(midSha(idx
, 40));
45 int Rev::indexData(bool quick
, bool withDiff
) const {
47 This is what 'git log' produces:
49 - a possible one line with "Final output:\n" in case of --early-output option
50 - one line with "log size" + len of this record
51 - one line with boundary info + sha + an arbitrary amount of parent's sha
52 - one line with committer name + e-mail
53 - one line with author name + e-mail
54 - one line with author date as unix timestamp
55 - zero or more non blank lines with other info, as the encoding FIXME
57 - zero or one line with log title
58 - zero or more lines with log message
59 - zero or more lines with diff content (only for file history)
62 static int error
= -1;
63 static int shaLength
= 40; // from git ref. spec.
64 static int shaEndlLength
= shaLength
+ 1; // an sha key + \n
65 static int shaXEndlLength
= shaLength
+ 2; // an sha key + X marker + \n
66 static char finalOutputMarker
= 'F'; // marks the beginning of "Final output" string
67 static char logSizeMarker
= 'l'; // marks the beginning of "log size" string
68 static int logSizeStrLength
= 9; // "log size"
69 static int asciiPosOfZeroChar
= 48; // char "0" has value 48 in ascii table
71 const int last
= ba
.size() - 1;
72 int logSize
= 0, idx
= start
;
75 // direct access is faster then QByteArray.at()
76 const char* data
= ba
.constData();
77 char* fixup
= const_cast<char*>(data
); // to build '\0' terminating strings
79 if (start
+ shaXEndlLength
> last
) // at least sha header must be present
82 if (data
[start
] == finalOutputMarker
) // "Final output", let caller handle this
83 return (ba
.indexOf('\n', start
) != -1 ? -2 : -1);
85 // parse 'log size xxx\n' if present -- from git ref. spec.
86 if (data
[idx
] == logSizeMarker
) {
87 idx
+= logSizeStrLength
; // move idx to beginning of log size value
89 // parse log size value
91 while ((digit
= data
[idx
++]) != '\n')
92 logSize
= logSize
* 10 + digit
- asciiPosOfZeroChar
;
94 // idx points to the boundary information, which has the same length as an sha header.
95 if (++idx
+ shaXEndlLength
> last
)
100 // ok, now shaStart is valid but msgSize could be still 0 if not available
101 logEnd
= shaStart
- 1 + logSize
;
105 idx
+= shaLength
; // now points to 'X' place holder
107 fixup
[idx
] = '\0'; // we want sha to be a '\0' terminated ascii string
111 if (data
[idx
+ 2] == '\n') // initial revision
115 idx
+= shaEndlLength
;
120 fixup
[idx
] = '\0'; // we want parents '\0' terminated
122 } while (data
[idx
+ 1] != '\n');
124 ++idx
; // now points to the trailing '\n' of sha line
126 // check for !msgSize
127 if (withDiff
|| !logSize
) {
129 revEnd
= (logEnd
> idx
) ? logEnd
- 1: idx
;
130 revEnd
= ba
.indexOf('\0', revEnd
+ 1);
137 if (revEnd
> last
) // after this point we know to have the whole record
140 // ok, now revEnd is valid but logEnd could be not if !logSize
141 // in case of diff we are sure content will be consumed so
143 if (quick
&& !withDiff
)
148 idx
= ba
.indexOf('\n', idx
); // committer line end
150 dbs("ASSERT in indexData: unexpected end of data");
156 idx
= ba
.indexOf('\n', idx
); // author line end
158 dbs("ASSERT in indexData: unexpected end of data");
162 // author date in Unix format (seconds since epoch)
163 autDateStart
= ++idx
;
164 idx
= ba
.indexOf('\n', idx
); // author date end without '\n'
166 dbs("ASSERT in indexData: unexpected end of data");
169 // if no error, point to trailing \n
172 diffStart
= diffLen
= 0;
174 diffStart
= logSize
? logEnd
: ba
.indexOf("\ndiff ", idx
);
176 if (diffStart
!= -1 && diffStart
< revEnd
)
177 diffLen
= revEnd
- ++diffStart
;
182 logEnd
= diffStart
? diffStart
: revEnd
;
184 // ok, now logEnd is valid and we can handle the log
187 if (logEnd
< sLogStart
) { // no shortlog no longLog
189 sLogStart
= sLogLen
= 0;
190 lLogStart
= lLogLen
= 0;
192 lLogStart
= ba
.indexOf('\n', sLogStart
);
193 if (lLogStart
!= -1 && lLogStart
< logEnd
- 1) {
195 sLogLen
= lLogStart
- sLogStart
; // skip sLog trailing '\n'
196 lLogLen
= logEnd
- lLogStart
; // include heading '\n' in long log
198 } else { // no longLog
199 sLogLen
= logEnd
- sLogStart
;
200 if (data
[sLogStart
+ sLogLen
- 1] == '\n')
201 sLogLen
--; // skip trailing '\n' if any
203 lLogStart
= lLogLen
= 0;
212 * RevFile streaming out
214 const RevFile
& RevFile::operator>>(QDataStream
& stream
) const {
218 // skip common case of only modified files
219 bool isEmpty
= onlyModified
;
220 stream
<< (quint32
)isEmpty
;
224 // skip common case of just one parent
225 isEmpty
= (mergeParent
.isEmpty() || mergeParent
.last() == 1);
226 stream
<< (quint32
)isEmpty
;
228 stream
<< mergeParent
;
230 // skip common case of no rename/copies
231 isEmpty
= extStatus
.isEmpty();
232 stream
<< (quint32
)isEmpty
;
240 * RevFile streaming in
242 RevFile
& RevFile::operator<<(QDataStream
& stream
) {
250 onlyModified
= (bool)tmp
;
257 stream
>> mergeParent
;
267 QString
qt4and5escaping(QString toescape
) {
268 #if QT_VERSION >= 0x050000
269 return toescape
.toHtmlEscaped();
271 return Qt::escape(toescape
);