--- 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