git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: Last mile for 1.0 again

From
MCMarco Costalba <mcostalba@yahoo.it>
Date
Jul 5, 2005, 13:34 UTC
Message-ID
<20050705133448.21778.qmail@web26309.mail.ukl.yahoo.com>
In-Reply-To
<7v3bqthiso.fsf@assigned-by-dhcp.cox.net>
--- Junio C Hamano <junkio@cox.net> wrote:
Show 6 quoted lines
> >>>>> "DB" == Daniel Barkalow <barkalow@iabervon.org> 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

Previous: Junio C HamanoNext: Junio C Hamano
Message 33 of 38 in “kernel.org and GIT tree rebuilding”
  1. David S. MillerJun 25, 2005
  2. Jeff GarzikJun 25, 2005
  3. Linus TorvaldsJun 25, 2005
  4. Jeff GarzikJun 25, 2005
  5. Linus TorvaldsJun 25, 2005
  6. Linus TorvaldsJun 26, 2005
  7. Junio C HamanoJun 26, 2005
  8. Linus TorvaldsJun 26, 2005
  9. Junio C HamanoJun 26, 2005
  10. Chris MasonJun 26, 2005
  11. Chris MasonJun 26, 2005
  12. Linus TorvaldsJun 26, 2005
  13. Linus TorvaldsJun 26, 2005
  14. Nicolas PitreJun 28, 2005
  15. Linus TorvaldsJun 28, 2005
  16. Nicolas PitreJun 28, 2005
  17. Linus TorvaldsJun 28, 2005
  18. Bugfix: initialize pack_base to NULL.Junio C Hamano, Jun 28, 2005
  19. Nicolas PitreJun 29, 2005
  20. Nicolas PitreJun 29, 2005
  21. Linus TorvaldsJun 29, 2005
  22. Linus TorvaldsJun 29, 2005
  23. Last mile for 1.0 againJunio C Hamano, Jun 29, 2005
  24. Add git-verify-pack command.Junio C Hamano, Jun 29, 2005
  25. Linus TorvaldsJun 29, 2005
  26. Daniel BarkalowJul 4, 2005
  27. Junio C HamanoJul 4, 2005
  28. Linus TorvaldsJul 4, 2005
  29. Daniel BarkalowJul 4, 2005
  30. Junio C HamanoJul 4, 2005
  31. Daniel BarkalowJul 5, 2005
  32. Junio C HamanoJul 5, 2005
  33. Marco CostalbaJul 5, 2005
  34. Junio C HamanoJun 25, 2005
  35. Obtain sha1_file_info() for deltified pack entry properly.Junio C Hamano, Jun 28, 2005
  36. Junio C HamanoJun 28, 2005
  37. 2/3 git-cat-file: use sha1_object_info() on '-t'.Junio C Hamano, Jun 28, 2005
  38. 3/3 git-cat-file: '-s' to find out object size.Junio C Hamano, Jun 28, 2005

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.