00001
00002
00003
00004
00005
00006
00007
00008
00009 #include <unistd.h>
00010 #include <qapplication.h>
00011 #include <qsettings.h>
00012 #include <qeventloop.h>
00013 #include <qregexp.h>
00014 #include <qtextcodec.h>
00015 #include "exceptionmanager.h"
00016 #include "rangeselectimpl.h"
00017 #include "lanes.h"
00018 #include "myprocess.h"
00019 #include "cache.h"
00020 #include "annotate.h"
00021 #include "mainimpl.h"
00022 #include "dataloader.h"
00023 #include "git.h"
00024
00025 #define POST_MSG(x) QApplication::postEvent(parent(), new MessageEvent(x))
00026
00027 using namespace QGit;
00028
00029 const QStringList Git::getArgs(bool askForRange, bool* quit) {
00030
00031 static bool startup = true;
00032 if (startup) {
00033 curRange = "";
00034 for (int i = 1; i < qApp->argc(); i++) {
00035
00036
00037 QString arg(qApp->argv()[i]);
00038 if (arg.contains(' '))
00039 arg.prepend('\"').append('\"');
00040
00041 curRange.append(arg + ' ');
00042 }
00043 }
00044 if ( askForRange
00045 && testFlag(RANGE_SELECT_F)
00046 && (!startup || curRange.isEmpty())) {
00047
00048 RangeSelectImpl rs((QWidget*)parent(), &curRange,
00049 getAllRefNames(TAG, !optOnlyLoaded), quit, this);
00050 rs.exec();
00051 if (*quit)
00052 return QStringList();
00053 }
00054 startup = false;
00055 return MyProcess::splitArgList(curRange);
00056 }
00057
00058 const QString Git::getBaseDir(bool* changed, SCRef wd, bool* ok, QString* gd) {
00059
00060
00061 QString runOutput, tmp(workDir);
00062 workDir = wd;
00063 errorReportingEnabled = false;
00064 bool ret = run("git rev-parse --git-dir", &runOutput);
00065 errorReportingEnabled = true;
00066 workDir = tmp;
00067 runOutput = runOutput.stripWhiteSpace();
00068 if (!ret || runOutput.isEmpty()) {
00069 *changed = true;
00070 if (ok)
00071 *ok = false;
00072 return wd;
00073 }
00074
00075
00076 QDir d(runOutput.startsWith("/") ? runOutput : wd + "/" + runOutput);
00077 *changed = (d.absPath() != gitDir);
00078 if (gd)
00079 *gd = d.absPath();
00080 if (ok)
00081 *ok = true;
00082 d.cdUp();
00083 return d.absPath();
00084 }
00085
00086 Git::Reference* Git::lookupReference(SCRef sha, bool create) {
00087
00088 RefMap::iterator it(refsShaMap.find(sha));
00089 if (it == refsShaMap.end() && create)
00090 it = refsShaMap.insert(sha, Reference());
00091
00092 return (it != refsShaMap.end() ? &(*it) : NULL);
00093 }
00094
00095 bool Git::getRefs() {
00096
00097
00098 QDir d(gitDir);
00099 QString stgCurBranch;
00100 if (d.exists("patches")) {
00101 errorReportingEnabled = false;
00102 isStGIT = run("stg branch", &stgCurBranch);
00103 errorReportingEnabled = true;
00104 stgCurBranch = stgCurBranch.stripWhiteSpace();
00105 } else
00106 isStGIT = false;
00107
00108
00109 isMergeHead = d.exists("MERGE_HEAD");
00110 QString curBranchSHA, curBranchName;
00111 if (!run("git rev-parse HEAD", &curBranchSHA))
00112 return false;
00113
00114 if (!run("git branch", &curBranchName))
00115 return false;
00116
00117 curBranchSHA = curBranchSHA.stripWhiteSpace();
00118 curBranchName = curBranchName.prepend('\n').section("\n*", 1);
00119 curBranchName = curBranchName.section('\n', 0, 0).stripWhiteSpace();
00120
00121
00122 QString runOutput;
00123 if (!run("git show-ref -d", &runOutput))
00124 return false;
00125
00126 refsShaMap.clear();
00127 QString prevRefSha;
00128 QStringList patchNames, patchShas;
00129 const QStringList rLst(QStringList::split('\n', runOutput));
00130 FOREACH_SL (it, rLst) {
00131
00132 SCRef revSha = (*it).left(40);
00133 SCRef refName = (*it).mid(41);
00134
00135 if (refName.startsWith("refs/patches/")) {
00136
00137
00138 SCRef patchesDir("refs/patches/" + stgCurBranch + "/");
00139 if (refName.startsWith(patchesDir)) {
00140 patchNames.append(refName.mid(patchesDir.length()));
00141 patchShas.append(revSha);
00142 }
00143
00144
00145
00146 continue;
00147 }
00148
00149 Reference* cur = lookupReference(revSha, optCreate);
00150
00151 if (refName.startsWith("refs/tags/")) {
00152
00153 if (refName.endsWith("^{}")) {
00154
00155
00156
00157
00158 cur->tags.append(refName.mid(10, refName.length() - 13));
00159
00160
00161
00162 cur->tagObj = prevRefSha;
00163
00164
00165 refsShaMap.remove(prevRefSha);
00166
00167 } else
00168 cur->tags.append(refName.mid(10));
00169
00170 cur->type |= TAG;
00171
00172 } else if (refName.startsWith("refs/heads/")) {
00173
00174 cur->branches.append(refName.mid(11));
00175 cur->type |= BRANCH;
00176 if (curBranchSHA == revSha) {
00177 cur->type |= CUR_BRANCH;
00178 cur->currentBranch = curBranchName;
00179 }
00180 } else if (refName.startsWith("refs/remotes/") && !refName.endsWith("HEAD")) {
00181
00182 cur->remoteBranches.append(refName.mid(13));
00183 cur->type |= RMT_BRANCH;
00184
00185 } else if (!refName.startsWith("refs/bases/") && !refName.endsWith("HEAD")) {
00186
00187 cur->refs.append(refName);
00188 cur->type |= REF;
00189 }
00190 prevRefSha = revSha;
00191 }
00192 if (isStGIT && !patchNames.isEmpty())
00193 parseStGitPatches(patchNames, patchShas);
00194
00195 return !refsShaMap.empty();
00196 }
00197
00198 void Git::parseStGitPatches(SCList patchNames, SCList patchShas) {
00199
00200 patchesStillToFind = 0;
00201
00202
00203 QString runOutput;
00204 if (!run("stg series", &runOutput))
00205 return;
00206
00207 const QStringList pl(QStringList::split('\n', runOutput));
00208 FOREACH_SL (it, pl) {
00209
00210 SCRef status = (*it).left(1);
00211 SCRef patchName = (*it).mid(2);
00212
00213 bool applied = (status == "+" || status == ">");
00214 int pos = patchNames.findIndex(patchName);
00215 if (pos == -1) {
00216 dbp("ASSERT in Git::parseStGitPatches(), patch %1 "
00217 "not found in references list.", patchName);
00218 continue;
00219 }
00220 Reference* cur = lookupReference(patchShas[pos], optCreate);
00221 cur->stgitPatch = patchName;
00222 cur->type |= (applied ? APPLIED : UN_APPLIED);
00223
00224 if (applied)
00225 patchesStillToFind++;
00226 }
00227 }
00228
00229 const QStringList Git::getOthersFiles() {
00230
00231
00232 QString runCmd("git ls-files --others");
00233 QSettings settings;
00234 QString exFile(settings.readEntry(APP_KEY + EX_KEY, EX_DEF));
00235 if (!exFile.isEmpty()) {
00236 QString path = (exFile.startsWith("/")) ? exFile : workDir + "/" + exFile;
00237 if (QFile::exists(path))
00238 runCmd.append(" --exclude-from=" + quote(exFile));
00239 }
00240 QString exPerDir(settings.readEntry(APP_KEY + EX_PER_DIR_KEY, EX_PER_DIR_DEF));
00241 if (!exPerDir.isEmpty())
00242 runCmd.append(" --exclude-per-directory=" + quote(exPerDir));
00243
00244 QString runOutput;
00245 run(runCmd, &runOutput);
00246 return QStringList::split('\n', runOutput);
00247 }
00248
00249 const Rev* Git::fakeWorkDirRev(SCRef parent, SCRef log, SCRef longLog, int idx, FileHistory* fh) {
00250
00251 QString date(QString::number(QDateTime::currentDateTime().toTime_t()) + " +0200");
00252 QString data(ZERO_SHA + ' ' + parent + "\ntree ");
00253 data.append(ZERO_SHA);
00254 data.append("\nparent " + parent);
00255 data.append("\nauthor Working Dir " + date);
00256 data.append("\ncommitter Working Dir " + date);
00257 data.append("\n\n " + log + '\n');
00258 data.append(longLog);
00259
00260 QByteArray* ba = new QByteArray(data.length() + 1);
00261 memcpy(ba->data(), data.ascii(), data.length());
00262 ba->at(data.length()) = '\0';
00263 fh->rowData.append(ba);
00264 int dummy;
00265 Rev* c = new Rev(*ba, 0, idx, &dummy);
00266 c->isDiffCache = true;
00267 c->lanes.append(EMPTY);
00268 return c;
00269 }
00270
00271 const RevFile* Git::fakeWorkDirRevFile(const WorkingDirInfo& wd) {
00272
00273 RevFile* rf = new RevFile();
00274 parseDiffFormat(*rf, wd.diffIndex);
00275 rf->onlyModified = false;
00276
00277 FOREACH_SL (it, wd.otherFiles) {
00278
00279 appendFileName(*rf, *it);
00280 rf->status.append(RevFile::UNKNOWN);
00281 rf->mergeParent.append(1);
00282 }
00283 RevFile cachedFiles;
00284 parseDiffFormat(cachedFiles, wd.diffIndexCached);
00285 for (uint i = 0; i < rf->status.count(); i++)
00286 if (findFileIndex(cachedFiles, filePath(*rf, i)) != -1)
00287 rf->status[i] |= RevFile::IN_INDEX;
00288 return rf;
00289 }
00290
00291 void Git::getDiffIndex() {
00292
00293 QString status;
00294 if (!run("git status", &status))
00295 return;
00296
00297 if (!run("git diff-index --no-color HEAD", &_wd.diffIndex))
00298 return;
00299
00300
00301
00302 if (!run("git diff-index --no-color --cached HEAD", &_wd.diffIndexCached))
00303 return;
00304
00305
00306 _wd.otherFiles = getOthersFiles();
00307
00308
00309 revsFiles.insert(ZERO_SHA, fakeWorkDirRevFile(_wd));
00310
00311
00312 QString parent;
00313 if (!run("git rev-parse HEAD", &parent))
00314 return;
00315
00316 parent = parent.section('\n', 0, 0);
00317 SCRef log = (isNothingToCommit() ? "Nothing to commit" : "Working dir changes");
00318 const Rev* r = fakeWorkDirRev(parent, log, status, revData.revOrder.count(), &revData);
00319 revData.revs.insert(ZERO_SHA, r);
00320 revData.revOrder.append(ZERO_SHA);
00321
00322
00323 emit newRevsAdded(&revData, revData.revOrder);
00324 }
00325
00326 void Git::parseDiffFormatLine(RevFile& rf, SCRef line, int parNum) {
00327
00328 if (line[1] == ':') {
00329
00330
00331
00332
00333
00334
00335
00336
00337 appendFileName(rf, line.section('\t', -1));
00338 setStatus(rf, "M");
00339 rf.mergeParent.append(parNum);
00340 } else {
00341
00342 if (line[98] == '\t') {
00343 appendFileName(rf, line.mid(99));
00344 setStatus(rf, line.at(97));
00345 rf.mergeParent.append(parNum);
00346 } else
00347
00348 setExtStatus(rf, line.mid(97), parNum);
00349 }
00350 }
00351
00352 void Git::setStatus(RevFile& rf, SCRef rowSt) {
00353
00354 char status = rowSt.at(0).latin1();
00355 switch (status) {
00356 case 'M':
00357 case 'T':
00358 rf.status.append(RevFile::MODIFIED);
00359 break;
00360 case 'D':
00361 rf.status.append(RevFile::DELETED);
00362 rf.onlyModified = false;
00363 break;
00364 case 'A':
00365 rf.status.append(RevFile::NEW);
00366 rf.onlyModified = false;
00367 break;
00368 case '?':
00369 rf.status.append(RevFile::UNKNOWN);
00370 rf.onlyModified = false;
00371 break;
00372 default:
00373 dbp("ASSERT in Git::setStatus, unknown status <%1>. "
00374 "'MODIFIED' will be used instead.", rowSt);
00375 rf.status.append(RevFile::MODIFIED);
00376 break;
00377 }
00378 }
00379
00380 void Git::setExtStatus(RevFile& rf, SCRef rowSt, int parNum) {
00381
00382 const QStringList sl(QStringList::split('\t', rowSt));
00383 if (sl.count() != 3) {
00384 dbp("ASSERT in setExtStatus, unexpected status string %1", rowSt);
00385 return;
00386 }
00387
00388
00389 SCRef type = sl[0];
00390 SCRef orig = sl[1];
00391 SCRef dest = sl[2];
00392 const QString extStatusInfo(orig + " --> " + dest + " (" + type + "%)");
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403 appendFileName(rf, dest);
00404 rf.mergeParent.append(parNum);
00405 rf.status.append(RevFile::NEW);
00406 rf.extStatus.resize(rf.status.size());
00407 rf.extStatus[rf.status.size() - 1] = extStatusInfo;
00408
00409
00410 if (type.at(0) == 'R') {
00411 appendFileName(rf, orig);
00412 rf.mergeParent.append(parNum);
00413 rf.status.append(RevFile::DELETED);
00414 rf.extStatus.resize(rf.status.size());
00415 rf.extStatus[rf.status.size() - 1] = extStatusInfo;
00416 }
00417 rf.onlyModified = false;
00418 }
00419
00420 void Git::parseDiffFormat(RevFile& rf, SCRef buf) {
00421
00422 int parNum = 1, startPos = 0, endPos = buf.find('\n');
00423 while (endPos != -1) {
00424
00425 SCRef line = buf.mid(startPos, endPos - startPos);
00426 if (line[0] == ':')
00427 parseDiffFormatLine(rf, line, parNum);
00428 else
00429 parNum++;
00430
00431 startPos = endPos + 1;
00432 endPos = buf.find('\n', endPos + 99);
00433 }
00434 }
00435
00436 bool Git::startParseProc(SCList initCmd, FileHistory* fh) {
00437
00438 DataLoader* dl = new DataLoader(this, fh);
00439
00440 connect(this, SIGNAL(cancelLoading(const FileHistory*)),
00441 dl, SLOT(on_cancel(const FileHistory*)));
00442
00443 connect(dl, SIGNAL(newDataReady(const FileHistory*)),
00444 this, SLOT(on_newDataReady(const FileHistory*)));
00445
00446 connect(dl, SIGNAL(loaded(const FileHistory*, ulong, int,
00447 bool, const QString&, const QString&)), this,
00448 SLOT(on_loaded(const FileHistory*, ulong, int,
00449 bool, const QString&, const QString&)));
00450
00451 return dl->start(initCmd, workDir);
00452 }
00453
00454 bool Git::startRevList(SCList args, FileHistory* fh) {
00455
00456 const QString baseCmd("git rev-list --header --parents --boundary --default HEAD");
00457 QStringList initCmd(QStringList::split(' ', baseCmd));
00458 if (!isMainHistory(fh)) {
00459
00460
00461
00462
00463
00464
00465
00466 initCmd += getAllRefSha(BRANCH | RMT_BRANCH);
00467 initCmd += "--";
00468 } else
00469 initCmd += "--topo-order";
00470
00471 return startParseProc(initCmd + args, fh);
00472 }
00473
00474 bool Git::startUnappliedList() {
00475
00476
00477
00478 const QStringList unAppliedSha(getAllRefSha(UN_APPLIED));
00479 if (unAppliedSha.isEmpty())
00480 return false;
00481
00482
00483
00484 QStringList initCmd(QStringList::split(' ', "git rev-list --header --parents"));
00485 initCmd += unAppliedSha;
00486 initCmd += QString::fromLatin1("^HEAD");
00487 return startParseProc(initCmd, &revData);
00488 }
00489
00490 bool Git::stop(bool saveCache) {
00491
00492
00493 EM_RAISE(exGitStopped);
00494
00495
00496
00497
00498 emit cancelAllProcesses();
00499
00500 if (cacheNeedsUpdate && saveCache) {
00501
00502 cacheNeedsUpdate = false;
00503 if (!filesLoadingCurSha.isEmpty())
00504 revsFiles.remove(filesLoadingCurSha);
00505
00506 if (!revsFiles.isEmpty()) {
00507 POST_MSG("Saving cache. Please wait...");
00508 EM_PROCESS_EVENTS_NO_INPUT;
00509 if (!Cache::save(gitDir, revsFiles, dirNamesVec, fileNamesVec))
00510 dbs("ERROR unable to save file names cache");
00511 }
00512 }
00513 return allProcessDeleted();
00514 }
00515
00516 void Git::clearRevs() {
00517
00518 revData.clear("");
00519 patchesStillToFind = 0;
00520 firstNonStGitPatch = "";
00521 _wd.clear();
00522 revsFiles.remove(ZERO_SHA);
00523 }
00524
00525 void Git::clearFileNames() {
00526
00527 revsFiles.clear();
00528 fileNamesMap.clear();
00529 dirNamesMap.clear();
00530 dirNamesVec.clear();
00531 fileNamesVec.clear();
00532 cacheNeedsUpdate = false;
00533 }
00534
00535 bool Git::init(SCRef wd, bool askForRange, QStringList* filterList, bool* quit) {
00536
00537
00538 *quit = false;
00539 bool filteredLoading = (filterList != NULL);
00540 clearRevs();
00541 try {
00542 setThrowOnStop(true);
00543
00544
00545 bool repoChanged;
00546 workDir = getBaseDir(&repoChanged, wd, &isGIT, &gitDir);
00547
00548 if (repoChanged) {
00549 clearFileNames();
00550 fileCacheAccessed = false;
00551 }
00552 if (!isGIT) {
00553 setThrowOnStop(false);
00554 return false;
00555 }
00556 const QString msg1("Path is '" + workDir + "' Loading ");
00557 QStringList args;
00558 if (!filteredLoading) {
00559
00560
00561 bool dummy;
00562 QTextCodec::setCodecForCStrings(getTextCodec(&dummy));
00563
00564
00565 POST_MSG(msg1 + "refs...");
00566 if (!getRefs())
00567 dbs("WARNING: no tags or heads found");
00568
00569
00570 POST_MSG("");
00571 args = getArgs(askForRange, quit);
00572 if (*quit) {
00573 setThrowOnStop(false);
00574 return false;
00575 }
00576
00577 if (isStGIT) {
00578 loadingUnAppliedPatches = startUnappliedList();
00579 if (loadingUnAppliedPatches) {
00580
00581 POST_MSG(msg1 + "StGIT unapplied patches...");
00582 while (loadingUnAppliedPatches) {
00583
00584 usleep(20000);
00585 EM_PROCESS_EVENTS;
00586 }
00587 revData.lns->clear();
00588 }
00589 }
00590
00591 if (testFlag(DIFF_INDEX_F)) {
00592 POST_MSG(msg1 + "working directory changed files...");
00593 getDiffIndex();
00594 }
00595
00596 } else {
00597 args += getAllRefSha(BRANCH | RMT_BRANCH);
00598 args += "--";
00599 args += *filterList;
00600 }
00601 POST_MSG(msg1 + "revisions...");
00602 if (!startRevList(args, &revData))
00603 dbs("ERROR: unable to start git-rev-list loading");
00604
00605 setThrowOnStop(false);
00606 return true;
00607
00608 } catch (int i) {
00609
00610 setThrowOnStop(false);
00611
00612 if (isThrowOnStopRaised(i, "initializing")) {
00613 EM_THROW_PENDING;
00614 return false;
00615 }
00616 const QString info("Exception \'" + EM_DESC(i) + "\' "
00617 "not handled in init...re-throw");
00618 dbs(info);
00619 throw;
00620 }
00621 }
00622
00623 void Git::on_newDataReady(const FileHistory* fh) {
00624
00625 emit newRevsAdded(fh , fh->revOrder);
00626 }
00627
00628 void Git::on_loaded(const FileHistory* fh, ulong byteSize, int loadTime,
00629 bool normalExit, SCRef cmd, SCRef errorDesc) {
00630
00631 if (!errorDesc.isEmpty()) {
00632 MainExecErrorEvent* e = new MainExecErrorEvent(cmd, errorDesc);
00633 QApplication::postEvent(parent(), e);
00634 }
00635 if (normalExit) {
00636
00637 on_newDataReady(fh);
00638
00639 if (!loadingUnAppliedPatches) {
00640
00641 uint kb = byteSize / 1024;
00642 float mbs = (float)byteSize / loadTime / 1000;
00643 QString tmp;
00644 tmp.sprintf("Loaded %i revisions (%i KB), "
00645 "time elapsed: %i ms (%.2f MB/s)",
00646 fh->revs.count(), kb, loadTime, mbs);
00647
00648 emit loadCompleted(fh, tmp);
00649
00650 if (isMainHistory(fh))
00651
00652
00653
00654 QTimer::singleShot(500, this, SLOT(loadFileNames()));
00655 }
00656 }
00657 if (loadingUnAppliedPatches)
00658 loadingUnAppliedPatches = false;
00659 }
00660
00661 void Git::populateFileNamesMap() {
00662
00663 for (uint i = 0; i < dirNamesVec.count(); ++i)
00664 dirNamesMap.insert(dirNamesVec[i], i);
00665
00666 for (uint i = 0; i < fileNamesVec.count(); ++i)
00667 fileNamesMap.insert(fileNamesVec[i], i);
00668 }
00669
00670 void Git::loadFileNames() {
00671
00672
00673
00674 if (!fileCacheAccessed) {
00675
00676 fileCacheAccessed = true;
00677 bool isWorkingDirRevFile = (getFiles(ZERO_SHA) != NULL);
00678 clearFileNames();
00679
00680 if (Cache::load(gitDir, revsFiles, dirNamesVec, fileNamesVec))
00681 populateFileNamesMap();
00682 else
00683 dbs("ERROR: unable to load file names cache");
00684
00685 if (isWorkingDirRevFile)
00686 revsFiles.insert(ZERO_SHA, fakeWorkDirRevFile(_wd));
00687 }
00688
00689 QString diffTreeBuf;
00690 FOREACH (StrVect, it, revData.revOrder) {
00691 if (!revsFiles.find(*it)) {
00692 const Rev* c = revLookup(*it);
00693 if (c->parentsCount() == 1)
00694 diffTreeBuf.append(*it).append('\n');
00695 }
00696 }
00697 if (!diffTreeBuf.isEmpty()) {
00698 filesLoadingPending = filesLoadingCurSha = "";
00699 const QString runCmd("git diff-tree --no-color -r -C --stdin");
00700 runAsync(runCmd, this, diffTreeBuf);
00701 }
00702 indexTree();
00703 }
00704
00705 int Git::addChunk(FileHistory* fh, const QByteArray& ba, int start) {
00706
00707 RevMap& r = fh->revs;
00708 int nextStart;
00709 Rev* rev = new Rev(ba, start, r.count(), &nextStart);
00710
00711 if (nextStart == -1) {
00712 delete rev;
00713 return -1;
00714 }
00715
00716 SCRef sha = rev->sha();
00717
00718 if (isStGIT) {
00719 if (loadingUnAppliedPatches) {
00720 Reference* rf = lookupReference(sha);
00721 if (!(rf && (rf->type & UN_APPLIED))) {
00722 delete rev;
00723 return nextStart;
00724 }
00725 }
00726
00727 if (!firstNonStGitPatch.isEmpty() && firstNonStGitPatch == sha)
00728 firstNonStGitPatch = "";
00729
00730
00731
00732
00733 if (!(firstNonStGitPatch.isEmpty() && patchesStillToFind == 0) &&
00734 !loadingUnAppliedPatches && isMainHistory(fh)) {
00735
00736 Reference* rf = lookupReference(sha);
00737 if (!(rf && (rf->type & APPLIED))) {
00738 delete rev;
00739 return nextStart;
00740 }
00741 }
00742 if (r.find(sha)) {
00743
00744
00745 if (r[sha]->isUnApplied) {
00746 delete rev;
00747 return nextStart;
00748 }
00749 dbp("ASSERT: addChunk sha <%1> already received", sha);
00750 }
00751 }
00752 if (r.isEmpty() && !isMainHistory(fh)) {
00753 bool added = copyDiffIndex(fh, sha);
00754 rev->orderIdx = added ? 1 : 0;
00755 }
00756 r.insert(sha, rev);
00757 fh->revOrder.append(sha);
00758
00759 if (isStGIT) {
00760
00761
00762 if (loadingUnAppliedPatches) {
00763
00764 Rev* c = const_cast<Rev*>(revLookup(sha, fh));
00765 c->isUnApplied = true;
00766 c->lanes.append(UNAPPLIED);
00767
00768 } else if (patchesStillToFind > 0 || !isMainHistory(fh)) {
00769
00770 Reference* rf = lookupReference(sha);
00771 if (rf && (rf->type & APPLIED)) {
00772
00773 Rev* c = const_cast<Rev*>(revLookup(sha, fh));
00774 c->isApplied = true;
00775 if (isMainHistory(fh)) {
00776 patchesStillToFind--;
00777 if (patchesStillToFind == 0)
00778
00779
00780 firstNonStGitPatch = c->parent(0);
00781 }
00782 }
00783 }
00784 }
00785 return nextStart;
00786 }
00787
00788 bool Git::copyDiffIndex(FileHistory* fh, SCRef parent) {
00789
00790
00791 if (!fh->revOrder.isEmpty() || !fh->revs.isEmpty()) {
00792 dbs("ASSERT in copyDiffIndex: called with wrong context");
00793 return false;
00794 }
00795 const Rev* r = revLookup(ZERO_SHA);
00796 if (!r)
00797 return false;
00798
00799 const RevFile* files = getFiles(ZERO_SHA);
00800 if (!files || findFileIndex(*files, fh->fileName) == -1)
00801 return false;
00802
00803
00804 const Rev* rf = fakeWorkDirRev(parent, "Working dir changes", "dummy", 0, fh);
00805 fh->revs.insert(ZERO_SHA, rf);
00806 fh->revOrder.append(ZERO_SHA);
00807 return true;
00808 }
00809
00810 void Git::setLane(SCRef sha, FileHistory* fh) {
00811
00812 Lanes* l = fh->lns;
00813 uint i = fh->firstFreeLane;
00814 const QValueVector<QString>& shaVec(fh->revOrder);
00815 for (uint cnt = shaVec.count(); i < cnt; ++i) {
00816 SCRef curSha = shaVec[i];
00817 Rev* r = const_cast<Rev*>(revLookup(curSha, fh));
00818 if (r->lanes.count() == 0)
00819 updateLanes(*r, *l, curSha);
00820
00821 if (curSha == sha)
00822 break;
00823 }
00824 fh->firstFreeLane = ++i;
00825 }
00826
00827 void Git::updateLanes(Rev& c, Lanes& lns, SCRef sha) {
00828
00829
00830
00831 if (lns.isEmpty())
00832 lns.init(sha);
00833
00834 bool isDiscontinuity;
00835 bool isFork = lns.isFork(sha, isDiscontinuity);
00836 bool isMerge = (c.parentsCount() > 1);
00837 bool isInitial = (c.parentsCount() == 0);
00838
00839 if (isDiscontinuity)
00840 lns.changeActiveLane(sha);
00841
00842 lns.setBoundary(c.isBoundary());
00843
00844 if (isFork)
00845 lns.setFork(sha);
00846 if (isMerge)
00847 lns.setMerge(c.parents());
00848 if (c.isApplied)
00849 lns.setApplied();
00850 if (isInitial)
00851 lns.setInitial();
00852
00853 lns.getLanes(c.lanes);
00854
00855 SCRef nextSha = (isInitial) ? "" : c.parent(0);
00856 lns.nextParent(nextSha);
00857
00858 if (c.isApplied)
00859 lns.afterApplied();
00860 if (isMerge)
00861 lns.afterMerge();
00862 if (isFork)
00863 lns.afterFork();
00864 if (lns.isBranch())
00865 lns.afterBranch();
00866
00867
00868
00869
00870
00871
00872
00873 }
00874
00875 void Git::on_procDataReady(const QByteArray& fileChunk) {
00876
00877 if (filesLoadingPending.isEmpty())
00878 filesLoadingPending = QString(fileChunk);
00879 else
00880 filesLoadingPending.append(fileChunk);
00881
00882 RevFile* rf = revsFiles[filesLoadingCurSha];
00883 int nextEOL = filesLoadingPending.find('\n');
00884 int lastEOL = -1;
00885 while (nextEOL != -1) {
00886
00887 SCRef line(filesLoadingPending.mid(lastEOL + 1, nextEOL - lastEOL - 1));
00888 if (line.constref(0) != ':') {
00889 SCRef sha = line.left(40);
00890 if (!rf || sha != filesLoadingCurSha) {
00891 rf = new RevFile();
00892 revsFiles.insert(sha, rf);
00893 filesLoadingCurSha = sha;
00894 cacheNeedsUpdate = true;
00895 } else
00896 dbp("ASSERT: repeated sha %1 in file names loading", sha);
00897 } else
00898 parseDiffFormatLine(*rf, line, 1);
00899
00900 lastEOL = nextEOL;
00901 nextEOL = filesLoadingPending.find('\n', lastEOL + 1);
00902 }
00903 if (lastEOL != -1)
00904 filesLoadingPending.remove(0, lastEOL + 1);
00905 }
00906
00907 void Git::appendFileName(RevFile& rf, SCRef name) {
00908
00909 int idx = name.findRev('/') + 1;
00910 SCRef dr = name.left(idx);
00911 SCRef nm = name.mid(idx);
00912
00913 QMap<QString, int>::const_iterator it(dirNamesMap.find(dr));
00914 if (it == dirNamesMap.constEnd()) {
00915 int idx = dirNamesVec.count();
00916 dirNamesMap.insert(dr, idx);
00917 dirNamesVec.append(dr);
00918 rf.dirs.append(idx);
00919 } else
00920 rf.dirs.append(*it);
00921
00922 it = fileNamesMap.find(nm);
00923 if (it == fileNamesMap.constEnd()) {
00924 int idx = fileNamesVec.count();
00925 fileNamesMap.insert(nm, idx);
00926 fileNamesVec.append(nm);
00927 rf.names.append(idx);
00928 } else
00929 rf.names.append(*it);
00930 }
00931
00932 static bool vecContains(const QValueVector<int>& vect, int value) {
00933
00934 for (uint i = 0, end = vect.count(); i < end; i++)
00935 if (vect[i] == value)
00936 return true;
00937 return false;
00938 }
00939
00940 void Git::updateDescMap(const Rev* r,uint idx, QMap<QPair<uint, uint>, bool>& dm,
00941 QMap<uint, QValueVector<int> >& dv) {
00942
00943 QValueVector<int> descVec;
00944 if (r->descRefsMaster != -1) {
00945
00946 const Rev* tmp = revLookup(revData.revOrder[r->descRefsMaster]);
00947 const QValueVector<int>& nr = tmp->descRefs;
00948
00949 for (uint i = 0; i < nr.count(); i++) {
00950
00951 if (!dv.contains(nr[i])) {
00952 dbp("ASSERT descendant for %1 not found", r->sha());
00953 return;
00954 }
00955 const QValueVector<int>& dvv = dv[nr[i]];
00956
00957
00958
00959 descVec = dvv;
00960
00961 for (uint y = 0; y < dvv.count(); y++) {
00962
00963 uint v = (uint)dvv[y];
00964 QPair<uint, uint> key = qMakePair(idx, v);
00965 QPair<uint, uint> keyN = qMakePair(v, idx);
00966 dm.insert(key, true);
00967 dm.insert(keyN, false);
00968
00969
00970
00971 if (i > 0 && !vecContains(descVec, v))
00972 descVec.append(v);
00973 }
00974 }
00975 }
00976 descVec.append(idx);
00977 dv.insert(idx, descVec);
00978 }
00979
00980 void Git::mergeBranches(Rev* p, const Rev* r) {
00981
00982 int r_descBrnMaster = (checkRef(r->sha(), BRANCH | RMT_BRANCH) ? r->orderIdx : r->descBrnMaster);
00983
00984 if (p->descBrnMaster == r_descBrnMaster || r_descBrnMaster == -1)
00985 return;
00986
00987
00988 const QValueVector<int>& src1 = revLookup(revData.revOrder[p->descBrnMaster])->descBranches;
00989 const QValueVector<int>& src2 = revLookup(revData.revOrder[r_descBrnMaster])->descBranches;
00990 QValueVector<int> dst(src1);
00991 for (uint i = 0; i < src2.count(); i++)
00992 if (qFind(src1.constBegin(), src1.constEnd(), src2[i]) == src1.constEnd())
00993 dst.append(src2[i]);
00994
00995 p->descBranches = dst;
00996 p->descBrnMaster = p->orderIdx;
00997 }
00998
00999 void Git::mergeNearTags(bool down, Rev* p, const Rev* r, const QMap<QPair<uint, uint>, bool>& dm) {
01000
01001 bool isTag = checkRef(r->sha(), TAG);
01002 int r_descRefsMaster = isTag ? r->orderIdx : r->descRefsMaster;
01003 int r_ancRefsMaster = isTag ? r->orderIdx : r->ancRefsMaster;
01004
01005 if (down && (p->descRefsMaster == r_descRefsMaster || r_descRefsMaster == -1))
01006 return;
01007
01008 if (!down && (p->ancRefsMaster == r_ancRefsMaster || r_ancRefsMaster == -1))
01009 return;
01010
01011
01012
01013 const StrVect& ro = revData.revOrder;
01014 SCRef sha1 = down ? ro[p->descRefsMaster] : ro[p->ancRefsMaster];
01015 SCRef sha2 = down ? ro[r_descRefsMaster] : ro[r_ancRefsMaster];
01016 const QValueVector<int>& src1 = down ? revLookup(sha1)->descRefs : revLookup(sha1)->ancRefs;
01017 const QValueVector<int>& src2 = down ? revLookup(sha2)->descRefs : revLookup(sha2)->ancRefs;
01018 QValueVector<int> dst(src1);
01019
01020 for (uint s2 = 0; s2 < src2.count(); s2++) {
01021
01022 bool add = false;
01023 for (uint s1 = 0; s1 < src1.count(); s1++) {
01024
01025 if (src2[s2] == src1[s1]) {
01026 add = false;
01027 break;
01028 }
01029 QPair<uint, uint> key = qMakePair((uint)src2[s2], (uint)src1[s1]);
01030
01031 if (!dm.contains(key)) {
01032 add = true;
01033 continue;
01034 }
01035 add = (down && dm[key]) || (!down && !dm[key]);
01036 if (add)
01037 dst[s1] = -1;
01038 else
01039 break;
01040 }
01041 if (add)
01042 dst.append(src2[s2]);
01043 }
01044 QValueVector<int>& nearRefs = (down) ? p->descRefs : p->ancRefs;
01045 int& nearRefsMaster = (down) ? p->descRefsMaster : p->ancRefsMaster;
01046
01047 nearRefs.clear();
01048 for (uint s2 = 0; s2 < dst.count(); s2++)
01049 if (dst[s2] != -1)
01050 nearRefs.append(dst[s2]);
01051
01052 nearRefsMaster = p->orderIdx;
01053 }
01054
01055 void Git::indexTree() {
01056
01057 const StrVect& ro = revData.revOrder;
01058 if (ro.count() == 0)
01059 return;
01060
01061
01062
01063 QMap<QPair<uint, uint>, bool> descMap;
01064 QMap<uint, QValueVector<int> > descVect;
01065
01066
01067
01068 for (uint i = 0, cnt = ro.count(); i < cnt; i++) {
01069
01070 uint type = checkRef(ro[i]);
01071 bool isB = (type & (BRANCH | RMT_BRANCH));
01072 bool isT = (type & TAG);
01073
01074 const Rev* r = revLookup(ro[i]);
01075
01076 if (isB) {
01077 Rev* rr = const_cast<Rev*>(r);
01078 if (r->descBrnMaster != -1) {
01079 SCRef sha = ro[r->descBrnMaster];
01080 rr->descBranches = revLookup(sha)->descBranches;
01081 }
01082 rr->descBranches.append(i);
01083 }
01084 if (isT) {
01085 updateDescMap(r, i, descMap, descVect);
01086 Rev* rr = const_cast<Rev*>(r);
01087 rr->descRefs.clear();
01088 rr->descRefs.append(i);
01089 }
01090 for (uint y = 0; y < r->parentsCount(); y++) {
01091
01092 Rev* p = const_cast<Rev*>(revLookup(r->parent(y)));
01093 if (p) {
01094 p->childs.append(i);
01095
01096 if (p->descBrnMaster == -1)
01097 p->descBrnMaster = isB ? r->orderIdx : r->descBrnMaster;
01098 else
01099 mergeBranches(p, r);
01100
01101 if (p->descRefsMaster == -1)
01102 p->descRefsMaster = isT ? r->orderIdx : r->descRefsMaster;
01103 else
01104 mergeNearTags(optGoDown, p, r, descMap);
01105 }
01106 }
01107 }
01108
01109 for (int i = ro.count() - 1; i >= 0; i--) {
01110
01111 const Rev* r = revLookup(ro[i]);
01112 bool isTag = checkRef(ro[i], TAG);
01113
01114 if (isTag) {
01115 Rev* rr = const_cast<Rev*>(r);
01116 rr->ancRefs.clear();
01117 rr->ancRefs.append(i);
01118 }
01119 for (uint y = 0; y < r->childs.count(); y++) {
01120
01121 Rev* c = const_cast<Rev*>(revLookup(ro[r->childs[y]]));
01122 if (c) {
01123 if (c->ancRefsMaster == -1)
01124 c->ancRefsMaster = isTag ? r->orderIdx:r->ancRefsMaster;
01125 else
01126 mergeNearTags(!optGoDown, c, r, descMap);
01127 }
01128 }
01129 }
01130 }
01131
01132
01133
01134 const QString Rev::mid(int start, int len) const {
01135
01136
01137 return QString::fromAscii(&(ba[start]), len);
01138 }
01139
01140 const QString Rev::parent(int idx) const {
01141
01142 return mid(start + startOfs + 41 + 41 * idx, 40);
01143 }
01144
01145 const QStringList Rev::parents() const {
01146
01147 if (parentsCnt == 0)
01148 return QStringList();
01149
01150 int ofs = start + startOfs + 41;
01151 return QStringList::split(' ', mid(ofs, 41 * parentsCnt - 1));
01152 }
01153
01154 int Rev::indexData() {
01155
01156
01157
01158
01159
01160
01161
01162
01163
01164
01165
01166
01167
01168
01169 int last = ba.size() - 1;
01170
01171
01172 startOfs = uint(ba.at(start) == '-' || ba.at(start) == '<' || ba.at(start) == '>');
01173 boundary = startOfs && ba.at(start) == '-';
01174
01175 parentsCnt = 0;
01176 int idx = start + startOfs + 40;
01177 while (idx < last && ba[idx] == ' ') {
01178 idx += 41;
01179 parentsCnt++;
01180 }
01181 idx += 47;
01182 while (idx < last && ba[idx] == 'p')
01183 idx += 48;
01184
01185 idx += 23;
01186 if (idx > last)
01187 return -1;
01188
01189 int lineEnd = ba.find('\n', idx);
01190 if (lineEnd == -1)
01191 return -1;
01192
01193 lineEnd += 23;
01194 if (lineEnd > last)
01195 return -1;
01196
01197 autStart = idx - 16;
01198 autLen = lineEnd - idx - 24;
01199 autDateStart = lineEnd - 39;
01200 autDateLen = 10;
01201
01202 idx = ba.find('\n', lineEnd);
01203 if (idx == -1)
01204 return -1;
01205
01206
01207
01208 int end = ba.find('\0', idx);
01209 if (end == -1)
01210 return -1;
01211
01212
01213 while (++idx < end && ba[idx] != '\n')
01214 idx = ba.find('\n', idx);
01215
01216 sLogStart = idx + 5;
01217 if (end < sLogStart) {
01218
01219 sLogStart = sLogLen = 0;
01220 lLogStart = lLogLen = 0;
01221 return ++end;
01222 }
01223 lLogStart = ba.find('\n', sLogStart);
01224 if (lLogStart != -1 && lLogStart < end) {
01225
01226 sLogLen = lLogStart++ - sLogStart;
01227 lLogLen = end - lLogStart;
01228
01229 } else {
01230 sLogLen = end - sLogStart;
01231 lLogStart = lLogLen = 0;
01232 }
01233 return ++end;
01234 }