--- Junio C Hamano <[EMAIL PROTECTED]> wrote:
> >>>>> "DB" == Daniel Barkalow <[EMAIL PROTECTED]> writes: > > DB> [perl script] > > >> How does this work, and what do we do about merges? > Checking diffs of all the parents can be computational expensive. I'am developing a different alghoritm for qgit, where I know, before to run annotate all the revs that changed a given file. The revs are saved in the shaHist list according to git-rev-list --merge-order. The algoritm operates form the oldest to the newst calculating annotation for each following rev. This alghoritm takes advantage of the fact that mostly revs belong to 2 categories: 1) revs of the same development-sequence (e.g. connected without merges) 2) empty merges, i.e. merges where the file we are interested in is listed in only one merging branch If a rev in shaHist does not belong to above categories a reach analisys must be performed in order to find the direct ancestor among all the previous revs in the list. So starting from shaHist a ReachList rl is populated by getReachability(): void Annotate::getReachability(ReachList& rl, const QString& file, const QStringList& shaHist) { rl.clear(); QStringList::const_iterator it = shaHist.begin(); Rev* r = revLookup(*it); rl.append(ReachInfo(*it, r->id, INITIAL)); for (it++; it != shaHist.end(); it++) { Rev* r = revLookup(*it); // initial revision case if (r->parents.count() == 0) { rl.append(ReachInfo(*it, r->id, INITIAL)); continue; } // merge case if (r->parents.count() > 1) { // check empty merges diffing against previous in list QString prevSha = rl.last().sha; QString d = getFileDiff(*it, prevSha, file); if (d.isEmpty()) { rl.append(ReachInfo(*it, r->id, EMPTY_MERGE)); rl.last().roots.append(prevSha); } else { // real merge: we need reach test. QStringList roots; roots.clear(); for (uint i = 0; i < r->parents.count(); i++) { // here tree walking is performed QString root = getRoot(r->parents[i], rl); if (!root.isEmpty()) roots.append(root); } rl.append(ReachInfo(*it, r->id, MERGE)); rl.last().roots = roots; } } continue; } // normal case: a revision with a single parent if (r->id == rl.last().id) { // same development sequence QString prevSha = rl.last().sha; rl.append(ReachInfo(*it, r->id, SAME_BR)); // same branch rl.last().roots.append(prevSha); } else { // different development sequence QString root = getRoot(*it, rl); // <-- here tree walking is performed if (!root.isEmpty()) { rl.append(ReachInfo(*it, r->id, DIFF_BR)); rl.last().roots.append(root); } else qDebug("ASSERT getReachability: rev %s not reachable", (*it).latin1()); } } } Then ReachList rl is then used to compute correct annotations: void Annotate::run(const QString& file, const QStringList& shaHist, AnnotateHistory& ah) { QString d, diffTarget; QStringList t; int pos; ReachList rl; ah.clear(); getReachability(rl, file, shaHist); // <-- here ReachList rl is calculated for (uint i = 0; i < rl.count(); i++) { switch(rl[i].type) { // <-- here ReachList rl is used to find annotations case SAME_BR: case DIFF_BR: diffTarget = rl[i].roots.first(); d = getFileDiff(rl[i].sha, diffTarget, file); pos = shaHist.findIndex(diffTarget); ah.append(processDiff(d, ah[pos], getAuthor(rl[i].sha, shaHist))); break; case INITIAL: pos = shaHist.findIndex(rl[i].sha); ah.append(getFirstAnnotation(file, shaHist, pos)); break; case EMPTY_MERGE: diffTarget = rl[i].roots.first(); pos = shaHist.findIndex(diffTarget); t = ah[pos]; // copy annotation from previous ah.append(t); break; case MERGE: // append annotation calculated on first root diffTarget = rl[i].roots.first(); d = getFileDiff(rl[i].sha, diffTarget, file); pos = shaHist.findIndex(diffTarget); ah.append(processDiff(d, ah[pos], "Merge")); // update annotation with all the others roots for (uint j = 1; j < rl[i].roots.count(); j++) { diffTarget = rl[i].roots[j]; d = getFileDiff(rl[i].sha, diffTarget, file); pos = shaHist.findIndex(diffTarget); // create an annotation calculated between root[i] and // the merge. QStringList scndAnn = processDiff(d, ah[pos], getAuthor(diffTarget, shaHist)); // finally we unify the two annotations, so we catch // also the case of a 3-way merge unify(ah.last(), scndAnn); } break; } } } Advantage of this algorithm is that reach analisys, i.e. tree walking, is done, statistically, only for a small subset of revs. Another side benefit is that correct annotation is calculated for each rev and not only for newest one. Of course there are issues: 1) A ordered list of revs (file history) must be known in advance. This is not a problem for qgit because file history is calculated while loading revs. 2) Does not takes in account file renames. Hope this notes can help in ongoing discussion Marco __________________________________________________ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com - To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html