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 }