00001
00002
00003
00004
00005
00006
00007
00008
00009 #include <qapplication.h>
00010 #include "git.h"
00011 #include "annotate.h"
00012
00013 #define MAX_AUTHOR_LEN 16
00014
00015 Annotate::Annotate(Git* parent, QObject* guiObj) : QObject(parent) {
00016
00017 EM_INIT(exAnnCanceled, "Canceling annotation");
00018
00019 git = parent;
00020 gui = guiObj;
00021 cancelingAnnotate = annotateRunning = annotateActivity = false;
00022 valid = canceled = false;
00023
00024 patchProcBuf.reserve(1000000);
00025
00026 processingTime.start();
00027
00028 connect(&patchProc, SIGNAL(readyReadStdout()), this, SLOT(on_patchProc_readFromStdout()));
00029 connect(&patchProc, SIGNAL(processExited()), this, SLOT(on_patchProc_processExited()));
00030 }
00031
00032 const FileAnnotation* Annotate::lookupAnnotation(SCRef sha, SCRef fn) {
00033
00034 if (!valid || fileName != fn)
00035 return NULL;
00036
00037 AnnotateHistory::const_iterator it = ah.find(sha);
00038 if (it != ah.constEnd())
00039 return &(it.data());
00040
00041
00042 int shaIdx;
00043 const QString ancestorSha = getAncestor(sha, fileName, &shaIdx);
00044 if (!ancestorSha.isEmpty()) {
00045 it = ah.find(ancestorSha);
00046 if (it != ah.constEnd())
00047 return &(it.data());
00048 }
00049 return NULL;
00050 }
00051
00052 bool Annotate::startPatchProc(SCRef buf, SCRef fileName) {
00053
00054 QString cmd("git diff-tree --no-color -r -m --patch-with-raw --no-commit-id --stdin --");
00055 QStringList args(QStringList::split(' ', cmd));
00056 args.append(fileName);
00057 patchProc.setArguments(args);
00058 patchProc.setWorkingDirectory(git->workDir);
00059 patchProcBuf = "";
00060 return patchProc.launch(buf);
00061 }
00062
00063 void Annotate::on_patchProc_readFromStdout() {
00064
00065 const QString tmp(patchProc.readStdout());
00066 annFilesNum += tmp.contains("diff --git ");
00067 if (annFilesNum > (int)histRevOrder.count())
00068 annFilesNum = histRevOrder.count();
00069 patchProcBuf.append(tmp);
00070 }
00071
00072 void Annotate::deleteWhenDone() {
00073
00074 if (!EM_IS_PENDING(exAnnCanceled))
00075 EM_RAISE(exAnnCanceled);
00076
00077 if (annotateRunning)
00078 cancelingAnnotate = true;
00079
00080 if (patchProc.isRunning())
00081 patchProc.tryTerminate();
00082
00083 on_deleteWhenDone();
00084 }
00085
00086 void Annotate::on_deleteWhenDone() {
00087
00088 if (!(annotateRunning || EM_IS_PENDING(exAnnCanceled)))
00089 deleteLater();
00090 else
00091 QTimer::singleShot(20, this, SLOT(on_deleteWhenDone()));
00092 }
00093
00094 void Annotate::on_progressTimer_timeout() {
00095
00096 if (!cancelingAnnotate && !isError) {
00097 const QString n(QString::number(annFilesNum));
00098 QApplication::postEvent(gui, new AnnotateProgressEvent(n));
00099 }
00100 }
00101
00102 bool Annotate::start(const FileHistory* _fh) {
00103
00104
00105 fh = _fh;
00106 fileName = fh->fileName;
00107 histRevOrder = fh->revOrder;
00108
00109 if (histRevOrder.isEmpty()) {
00110 valid = false;
00111 return false;
00112 }
00113 annotateRunning = true;
00114
00115
00116 annFilesNum = 0;
00117 annId = histRevOrder.count();
00118 annNumLen = QString::number(histRevOrder.count()).length();
00119 StrVect::const_iterator it(histRevOrder.constBegin());
00120 do
00121 ah.insert(*it, FileAnnotation(annId--));
00122 while (++it != histRevOrder.constEnd());
00123
00124
00125
00126
00127 isError = false;
00128 annotateFileHistory(fileName, true);
00129
00130 if (isError || cancelingAnnotate) {
00131 slotComputeDiffs();
00132 return false;
00133 }
00134 connect(&progressTimer, SIGNAL(timeout()), this, SLOT(on_progressTimer_timeout()));
00135 progressTimer.start(500);
00136
00137
00138 return startPatchProc(patchScript, fileName);
00139 }
00140
00141 void Annotate::on_patchProc_processExited() {
00142
00143
00144 QTimer::singleShot(1, this, SLOT(slotComputeDiffs()));
00145 progressTimer.stop();
00146 }
00147
00148 void Annotate::slotComputeDiffs() {
00149
00150
00151
00152 if (!cancelingAnnotate) {
00153
00154 diffMap.clear();
00155 AnnotateHistory::iterator it(ah.begin());
00156 do
00157 (*it).isValid = false;
00158 while (++it != ah.end());
00159
00160
00161 int first = patchProcBuf.find(':');
00162 if (first != -1) {
00163 nextFileSha = patchProcBuf.mid(first + 56, 40);
00164 patchProcBuf.remove(0, first + 100);
00165 }
00166 annotateFileHistory(fileName, false);
00167 }
00168 valid = !(isError || cancelingAnnotate);
00169 canceled = cancelingAnnotate;
00170 cancelingAnnotate = annotateRunning = false;
00171 if (canceled)
00172 deleteWhenDone();
00173 else
00174 git->annotateExited(this);
00175
00176
00177
00178
00179
00180 }
00181
00182 void Annotate::annotateFileHistory(SCRef fileName, bool buildPatchScript) {
00183
00184
00185
00186 StrVect::const_iterator it(histRevOrder.constEnd());
00187 do {
00188 --it;
00189 doAnnotate(fileName, *it, buildPatchScript);
00190 } while (it != histRevOrder.constBegin() && !isError && !cancelingAnnotate);
00191 }
00192
00193 void Annotate::doAnnotate(SCRef fileName, SCRef sha, bool buildPatchScript) {
00194
00195
00196 FileAnnotation* fa = getFileAnnotation(sha);
00197 if (fa == NULL || fa->isValid || isError || cancelingAnnotate)
00198 return;
00199
00200 const Rev* r = git->revLookup(sha, fh);
00201 if (r == NULL) {
00202 dbp("ASSERT doAnnotate: no revision %1", sha);
00203 isError = true;
00204 return;
00205 }
00206 if (r->parentsCount() == 0) {
00207 if (!buildPatchScript)
00208 setInitialAnnotation(fileName, sha, fa);
00209 fa->isValid = true;
00210 return;
00211 }
00212
00213 const QStringList parents(r->parents());
00214 const QString& parSha = parents.first();
00215 FileAnnotation* pa = getFileAnnotation(parSha);
00216
00217 if (!(pa && pa->isValid)) {
00218 dbp("ASSERT in doAnnotate: annotation for %1 not valid", parSha);
00219 isError = true;
00220 return;
00221 }
00222 if (buildPatchScript)
00223 updatePatchScript(sha, parSha);
00224 else {
00225 const QString diff(getNextPatch(patchProcBuf, fileName, sha));
00226 const QString author(setupAuthor(r->author(), fa->annId));
00227 setAnnotation(diff, author, pa->lines, fa->lines);
00228 }
00229
00230 QStringList::const_iterator it(parents.constBegin());
00231 ++it;
00232 while (it != parents.constEnd()) {
00233
00234 FileAnnotation* pa = getFileAnnotation(*it);
00235
00236 if (buildPatchScript) {
00237 updatePatchScript(sha, *it);
00238 ++it;
00239 continue;
00240 }
00241 const QString diff(getNextPatch(patchProcBuf, fileName, sha));
00242 QStringList tmpAnn;
00243 setAnnotation(diff, "Merge", pa->lines, tmpAnn);
00244
00245
00246 if (fa->lines.count() != tmpAnn.count()) {
00247 qDebug("ASSERT: merging annotations of different length\n"
00248 " merging %s in %s", (*it).latin1(), sha.latin1());
00249 isError = true;
00250 return;
00251 }
00252
00253 unify(tmpAnn, fa->lines);
00254 fa->lines = tmpAnn;
00255 ++it;
00256 }
00257 fa->isValid = true;
00258 }
00259
00260 FileAnnotation* Annotate::getFileAnnotation(SCRef sha) {
00261
00262 AnnotateHistory::iterator it(ah.find(sha));
00263 if (it == ah.end()) {
00264 dbp("ASSERT getFileAnnotation: no revision %1", sha);
00265 isError = true;
00266 return NULL;
00267 }
00268 return &(*it);
00269 }
00270
00271 void Annotate::setInitialAnnotation(SCRef fileName, SCRef sha, FileAnnotation* fa) {
00272
00273 QString fileSha;
00274 QByteArray fileData;
00275 git->getFile(fileName, sha, NULL, &fileData, &fileSha);
00276 if (cancelingAnnotate)
00277 return;
00278
00279 if (fileSha.isEmpty()) {
00280 dbp("ASSERT in setInitialAnnotation: empty file of initial rev %1", sha);
00281 isError = true;
00282 return;
00283 }
00284 ah[sha].fileSha = fileSha;
00285 QString fileTxt(fileData);
00286 int lineNum = fileTxt.contains('\n');
00287 if (!fileTxt.endsWith("\n"))
00288 lineNum++;
00289
00290 fa->lines.insert(fa->lines.end(), lineNum, "");
00291 }
00292
00293 const QString Annotate::setupAuthor(SCRef origAuthor, int annId) {
00294
00295 QString author(QString("%1.").arg(annId, annNumLen));
00296 QString tmp(origAuthor.section('<', 0, 0).stripWhiteSpace());
00297 if (tmp.isEmpty()) {
00298 tmp = origAuthor;
00299 tmp.remove('<').remove('>');
00300 tmp = tmp.stripWhiteSpace();
00301 tmp.truncate(MAX_AUTHOR_LEN);
00302 }
00303
00304 if (tmp.length() > MAX_AUTHOR_LEN) {
00305 SCRef firstName(tmp.section(' ', 0, 0));
00306 SCRef surname(tmp.section(' ', 1));
00307 if (!firstName.isEmpty() && !surname.isEmpty())
00308 tmp = firstName.left(1) + ". " + surname;
00309 tmp.truncate(MAX_AUTHOR_LEN);
00310 }
00311 author.append(tmp);
00312 return author;
00313 }
00314
00315 void Annotate::unify(SList dst, SCList src) {
00316
00317 QStringList::Iterator itd(dst.begin());
00318 QStringList::const_iterator its(src.constBegin());
00319 for ( ; itd != dst.end(); ++itd, ++its)
00320 if (*itd == "Merge")
00321 *itd = *its;
00322 }
00323
00324 void Annotate::setAnnotation(SCRef diff, SCRef author, SCList prevAnn,
00325 QStringList& newAnn, int ofs) {
00326 newAnn = prevAnn;
00327 QStringList::iterator cur(newAnn.begin());
00328 QString line;
00329 int idx = 0, num, lineNumStart, lineNumEnd;
00330 while (getNextSection(diff, idx, line, "\n")) {
00331 char firstChar = line[0].latin1();
00332 switch (firstChar) {
00333 case '@':
00334
00335
00336
00337
00338
00339 lineNumStart = line.find('+') + 1;
00340 lineNumEnd = line.find(',', lineNumStart);
00341 if (lineNumEnd == -1)
00342 lineNumEnd = line.find(' ', lineNumStart);
00343
00344 num = line.mid(lineNumStart, lineNumEnd - lineNumStart).toInt();
00345 num -= ofs;
00346
00347
00348
00349 if (num <= 0) {
00350 if (num < 0)
00351 dbp("ASSERT processDiff: start line number is %1", num);
00352 newAnn.clear();
00353 return;
00354 }
00355 cur = newAnn.at(num - 1);
00356 break;
00357 case '+':
00358 if (cur != newAnn.end()) {
00359 cur = newAnn.insert(cur, author);
00360 ++cur;
00361 } else {
00362 newAnn.append(author);
00363 cur = newAnn.end();
00364 }
00365 break;
00366 case '-':
00367 if (!newAnn.isEmpty()) {
00368 if (cur != newAnn.end())
00369 cur = newAnn.remove(cur);
00370 else {
00371 dbp("ASSERT processDiff: remove end of "
00372 "file, diff is %1", diff);
00373 isError = true;
00374 return;
00375 }
00376 } else {
00377 dbp("ASSERT processDiff: remove line from "
00378 "empty annotation, diff is %1", diff);
00379 isError = true;
00380 return;
00381 }
00382 break;
00383 case '\\':
00384
00385
00386 if (line[1] == ' ')
00387 break;
00388
00389
00390 default:
00391 ++cur;
00392 break;
00393 }
00394 }
00395 }
00396
00397 void Annotate::updatePatchScript(SCRef sha, SCRef par) {
00398
00399 if (sha == QGit::ZERO_SHA)
00400 return;
00401
00402 const QString runCmd(sha + " " + par);
00403 patchScript.append(runCmd).append('\n');
00404 }
00405
00406 const QString Annotate::getNextPatch(QString& patchFile, SCRef fileName, SCRef sha) {
00407
00408 if (sha == QGit::ZERO_SHA) {
00409
00410
00411 QString runOutput;
00412 QString runCmd("git diff-index --no-color -r -m -p HEAD -- " + QGit::QUOTE_CHAR +
00413 fileName + QGit::QUOTE_CHAR);
00414
00415 git->run(runCmd, &runOutput);
00416 if (cancelingAnnotate)
00417 return "";
00418
00419
00420 const QString nextHeader("\n:100644 100644 " + QGit::ZERO_SHA + ' '
00421 + nextFileSha + " M\t" + fileName + '\n');
00422 runOutput.append(nextHeader);
00423 patchFile.prepend(runOutput);
00424 nextFileSha = QGit::ZERO_SHA;
00425 }
00426
00427
00428 ah[sha].fileSha = nextFileSha;
00429
00430 bool noNewLine = (patchFile[0] == ':');
00431 if (noNewLine)
00432 dbp("WARNING: No newline at the end of %1 patch", sha);
00433
00434 int end = (noNewLine) ? 0 : patchFile.find("\n:");
00435 QString diff;
00436 if (end != -1) {
00437 diff = patchFile.left(end + 1);
00438 nextFileSha = patchFile.mid(end + 57 - (int)noNewLine, 40);
00439 patchFile.remove(0, end + 100);
00440 } else
00441 diff = patchFile;
00442
00443 int start = diff.find('@');
00444
00445 diff = (start != -1) ? diff.mid(start) : "";
00446
00447 int i = 0;
00448 while (diffMap.contains(Key(sha, i)))
00449 i++;
00450 diffMap.insert(Key(sha, i), diff);
00451 return diff;
00452 }
00453
00454 bool Annotate::getNextSection(SCRef d, int& idx, QString& sec, SCRef target) {
00455
00456 if (idx >= (int)d.length())
00457 return false;
00458
00459 int newIdx = d.find(target, idx);
00460 if (newIdx == -1)
00461 newIdx = d.length() - 1;
00462
00463 sec = d.mid(idx, newIdx - idx + 1);
00464 idx = newIdx + 1;
00465 return true;
00466 }
00467
00468
00469
00470
00471
00472
00473 bool Annotate::getRange(SCRef sha, RangeInfo* r) {
00474
00475 if (!rangeMap.contains(sha) || !valid || canceled) {
00476 r->clear();
00477 return false;
00478 }
00479 *r = rangeMap[sha];
00480 return true;
00481 }
00482
00483 void Annotate::updateCrossRanges(SCRef chunk, bool rev, int fileLen, int ofs, RangeInfo* r) {
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506 QStringList beforeAnn, afterAnn;
00507 for (int lineNum = ofs + 1; lineNum <= ofs + fileLen; lineNum++)
00508 beforeAnn.append(QString::number(lineNum));
00509
00510 const QString fakedAuthor("*");
00511 setAnnotation(chunk, fakedAuthor, beforeAnn, afterAnn, ofs);
00512 int newStart = ofs + 1;
00513 int newEnd = ofs + fileLen;
00514
00515 if (rev) {
00516
00517
00518 QStringList::const_iterator itStart(afterAnn.at(r->start - ofs - 1));
00519 QStringList::const_iterator itEnd(afterAnn.at(r->end - ofs - 1));
00520
00521 bool leftExtended = (*itStart == fakedAuthor);
00522 bool rightExtended = (*itEnd == fakedAuthor);
00523
00524
00525
00526 ++itStart;
00527 do {
00528 --itStart;
00529 if (*itStart != fakedAuthor) {
00530 newStart = (*itStart).toInt();
00531 break;
00532 }
00533 } while (itStart != afterAnn.constBegin());
00534
00535 while (itEnd != afterAnn.constEnd()) {
00536
00537 if (*itEnd != fakedAuthor) {
00538 newEnd = (*itEnd).toInt();
00539 break;
00540 }
00541 ++itEnd;
00542 }
00543 if (leftExtended && *itStart != fakedAuthor)
00544 newStart++;
00545
00546 if (rightExtended && itEnd != afterAnn.constEnd())
00547 newEnd--;
00548
00549 r->modified = (leftExtended || rightExtended);
00550
00551 if (!r->modified) {
00552 for (int i = r->start; i <= r->end; ++i, ++itStart)
00553 if (i - r->start != (*itStart).toInt() - newStart) {
00554 r->modified = true;
00555 break;
00556 }
00557 }
00558 if (newStart > newEnd)
00559 newStart = newEnd = 0;
00560
00561 } else {
00562
00563
00564 QStringList::const_iterator itStart(afterAnn.constEnd());
00565 QStringList::const_iterator itEnd(afterAnn.constEnd());
00566
00567 QStringList::const_iterator it(afterAnn.constBegin());
00568 for (int lineNum = ofs + 1; it != afterAnn.constEnd(); ++lineNum, ++it) {
00569
00570 if (*it != fakedAuthor) {
00571
00572 if ((*it).toInt() <= r->start) {
00573 newStart = lineNum;
00574 itStart = it;
00575 }
00576 if ( (*it).toInt() >= r->end
00577 && itEnd == afterAnn.constEnd()) {
00578 newEnd = lineNum;
00579 itEnd = it;
00580 }
00581 }
00582 }
00583 if (itStart != afterAnn.constEnd() && (*itStart).toInt() < r->start)
00584 newStart++;
00585
00586 if (itEnd != afterAnn.constEnd() && (*itEnd).toInt() > r->end)
00587 newEnd--;
00588
00589 r->modified = (itStart == afterAnn.constEnd() || itEnd == afterAnn.constEnd());
00590
00591 if (!r->modified) {
00592 for (int i = r->start; i <= r->end; ++itStart, i++)
00593 if ((*itStart).toInt() != i) {
00594 r->modified = true;
00595 break;
00596 }
00597 }
00598 if (newStart > newEnd)
00599 newStart = newEnd = 0;
00600 }
00601 r->start = newStart;
00602 r->end = newEnd;
00603 }
00604
00605 void Annotate::updateRange(RangeInfo* r, SCRef diff, bool reverse) {
00606
00607 r->modified = false;
00608 if (r->start == 0)
00609 return;
00610
00611
00612
00613
00614
00615
00616
00617 int idx = 0;
00618 QString chunk;
00619 QStringList chunkList;
00620 while (getNextSection(diff, idx, chunk, "\n@"))
00621 if (reverse)
00622 chunkList.prepend(chunk);
00623 else
00624 chunkList.append(chunk);
00625
00626 QStringList::const_iterator chunkIt(chunkList.constBegin());
00627 while (chunkIt != chunkList.constEnd()) {
00628
00629
00630
00631
00632
00633
00634 chunk = *chunkIt++;
00635 int m = chunk.find('-');
00636 int c1 = chunk.find(',', m);
00637 int p = chunk.find('+', c1);
00638 int c2 = chunk.find(',', p);
00639 int e = chunk.find(' ', c2);
00640
00641 int oldLineCnt = chunk.mid(c1 + 1, p - c1 - 2).toInt();
00642 int newLineId = chunk.mid(p + 1, c2 - p - 1).toInt();
00643 int newLineCnt = chunk.mid(c2 + 1, e - c2 - 1).toInt();
00644 int lineNumDiff = newLineCnt - oldLineCnt;
00645
00646
00647
00648 int patchStart = newLineId;
00649
00650
00651 int patchEnd = patchStart + ((reverse) ? newLineCnt : oldLineCnt);
00652 patchEnd--;
00653
00654
00655 if (patchStart > r->end)
00656 continue;
00657
00658
00659 if (patchEnd < r->start) {
00660 r->start += ((reverse) ? -lineNumDiff : lineNumDiff);
00661 r->end += ((reverse) ? -lineNumDiff : lineNumDiff);
00662 continue;
00663 }
00664
00665 if (patchStart >= r->start && patchEnd <= r->end) {
00666 r->end += ((reverse) ? -lineNumDiff : lineNumDiff);
00667 r->modified = true;
00668 continue;
00669 }
00670
00671
00672
00673
00674 int beforePadding = (r->start > patchStart) ? 0 : patchStart - r->start;
00675
00676
00677 int afterPadding = (patchEnd > r->end) ? 0 : r->end - patchEnd;
00678
00679
00680
00681 int fileLenght = beforePadding + oldLineCnt + afterPadding;
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691 int fileOffset = newLineId - beforePadding - 1;
00692
00693
00694
00695 updateCrossRanges(chunk, reverse, fileLenght, fileOffset, r);
00696 }
00697 }
00698
00699 const QString Annotate::getAncestor(SCRef sha, SCRef fileName, int* shaIdx) {
00700
00701 QString fileSha;
00702
00703 try {
00704 annotateActivity = true;
00705 EM_REGISTER(exAnnCanceled);
00706
00707 fileSha = git->getFileSha(fileName, sha);
00708 if (fileSha.isEmpty()) {
00709 dbp("ASSERT in getAncestor: empty file from %1", sha);
00710 return "";
00711 }
00712 EM_REMOVE(exAnnCanceled);
00713 annotateActivity = false;
00714
00715 } catch(int i) {
00716
00717 EM_REMOVE(exAnnCanceled);
00718 annotateActivity = false;
00719
00720 if (EM_MATCH(i, exAnnCanceled, "getting ancestor")) {
00721 EM_THROW_PENDING;
00722 return "";
00723 }
00724 const QString info("Exception \'" + EM_DESC(i) + "\' "
00725 "not handled in Annotation lookup...re-throw");
00726 dbs(info);
00727 throw;
00728 }
00729
00730
00731
00732
00733
00734 for (*shaIdx = 0; *shaIdx < (int)histRevOrder.count(); (*shaIdx)++) {
00735
00736 const FileAnnotation& fa(ah[histRevOrder[*shaIdx]]);
00737 if (fa.fileSha == fileSha)
00738 return histRevOrder[*shaIdx];
00739 }
00740
00741
00742
00743 if (git->getAllRefSha(Git::UN_APPLIED).contains(sha))
00744 return histRevOrder.first();
00745
00746 dbp("ASSERT in getAncestor: ancestor of %1 not found", sha);
00747 return "";
00748 }
00749
00750 bool Annotate::isDescendant(SCRef sha, SCRef target) {
00751
00752
00753
00754
00755
00756 const Rev* r = git->revLookup(sha, fh);
00757 if (!r)
00758 return false;
00759
00760 int shaIdx = r->orderIdx;
00761 r = git->revLookup(target, fh);
00762
00763 while (r && r->orderIdx < shaIdx && r->parentsCount() == 1)
00764 r = git->revLookup(r->parent(0), fh);
00765
00766 return (r && r->orderIdx == shaIdx);
00767 }
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779 const QString Annotate::computeRanges(SCRef sha, int rangeStart, int rangeEnd, SCRef target) {
00780
00781 rangeMap.clear();
00782
00783 if (!valid || canceled) {
00784 dbp("ASSERT in computeRanges: annotation from %1 not valid", sha);
00785 return "";
00786 }
00787 QString ancestor(sha);
00788 int shaIdx;
00789 for (shaIdx = 0; shaIdx < (int)histRevOrder.count(); shaIdx++)
00790 if (histRevOrder[shaIdx] == sha)
00791 break;
00792
00793 if (shaIdx == (int)histRevOrder.count()) {
00794 ancestor = getAncestor(sha, fileName, &shaIdx);
00795 if (ancestor.isEmpty())
00796 return "";
00797 }
00798
00799 rangeMap.insert(ancestor, RangeInfo(rangeStart, rangeEnd, true));
00800
00801
00802 bool isDirectDescendant = isDescendant(ancestor, target);
00803
00804
00805 const QString oldest(histRevOrder.last());
00806 const Rev* curRev = git->revLookup(ancestor, fh);
00807 QString curRevSha(curRev->sha());
00808 while (curRevSha != oldest && !isDirectDescendant) {
00809
00810 if (!diffMap.contains(Key(curRevSha, 0))) {
00811 if (curRev->parentsCount() == 0)
00812 break;
00813
00814 dbp("ASSERT in rangeFilter 1: diff for %1 not found", curRevSha);
00815 return "";
00816 }
00817 RangeInfo r(rangeMap[curRevSha]);
00818 updateRange(&r, diffMap[Key(curRevSha, 0)], true);
00819
00820
00821
00822
00823 rangeMap[curRevSha].modified = r.modified;
00824
00825
00826
00827
00828
00829
00830
00831
00832 r.modified = (r.start != 0);
00833
00834 if (curRev->parentsCount() == 0)
00835 break;
00836
00837 curRev = git->revLookup(curRev->parent(0), fh);
00838 curRevSha = curRev->sha();
00839 rangeMap.insert(curRevSha, r);
00840
00841 if (curRevSha == target)
00842 return ancestor;
00843 }
00844
00845
00846
00847 if (!isDirectDescendant)
00848 shaIdx = histRevOrder.count() - 1;
00849
00850 for ( ; shaIdx >= 0; shaIdx--) {
00851
00852 SCRef sha(histRevOrder[shaIdx]);
00853
00854 if (!rangeMap.contains(sha)) {
00855
00856 curRev = git->revLookup(sha, fh);
00857
00858 if (curRev->parentsCount() == 0) {
00859
00860
00861
00862
00863 rangeMap.insert(sha, RangeInfo());
00864 continue;
00865 }
00866 if (!diffMap.contains(Key(sha, 0))) {
00867 dbp("ASSERT in rangeFilter 2: diff for %1 not found", sha);
00868 return "";
00869 }
00870 SCRef parSha(curRev->parent(0));
00871
00872 if (!rangeMap.contains(parSha)) {
00873
00874 if (isDirectDescendant)
00875 continue;
00876
00877 dbp("ASSERT in rangeFilter: range info for %1 not found", parSha);
00878 return "";
00879 }
00880 RangeInfo r(rangeMap[parSha]);
00881 updateRange(&r, diffMap[Key(sha, 0)], false);
00882 rangeMap.insert(sha, r);
00883
00884 if (sha == target)
00885 return ancestor;
00886 }
00887 }
00888 return ancestor;
00889 }
00890
00891 bool Annotate::seekPosition(int* paraFrom, int* paraTo, SCRef fromSha, SCRef toSha) {
00892
00893 if ((*paraFrom == 0 && *paraTo == 0) || fromSha == toSha)
00894 return true;
00895
00896 QMap<QString, RangeInfo> backup;
00897 backup = rangeMap;
00898
00899
00900 if (computeRanges(fromSha, *paraFrom + 1, *paraTo + 1, toSha).isEmpty())
00901 goto fail;
00902
00903 if (!rangeMap.contains(toSha))
00904 goto fail;
00905
00906 *paraFrom = rangeMap[toSha].start - 1;
00907 *paraTo = rangeMap[toSha].end - 1;
00908 rangeMap = backup;
00909 return true;
00910
00911 fail:
00912 rangeMap = backup;
00913 return false;
00914 }