Vadim Markovtsev, source{d}.
Vadim Markovtsev
source{d}
git blame foo.go
func foo() {println("bar")}
func foo() {println("bar")}func qux() {println("baz")}
func foo() {println("waldo")}const X = 10func spam() {println("baz")}
<root> D < E < F < G < H < I HEAD
<root> D > E > F > G > H > I HEAD
Where are my branches?
A---B---C/ \D---E---F---G---H---I HEAD----------> timeway <----------
Git branches are pointers, and the history is lost. Merges are ordered.
Which way to go?
A---B---C topic/ \D---E---F---G---H---I master----------> timeway <----------
Blaming using both branches is hard and sometimes even impossible.
There can be "octopus" merges.
A---B---C topic1/ \D---E---F---G---H---I master\ /J---K topic2
There can be multiple roots.
A---B---C subproject1\D---E---F---G---H---I master/J---K subproject2
git checkout <commit> && git blame <file>
takes O(n) steps where n is the number of commits before
<commit>.
Thus naïve burndown takes O(n2).
We can reduce it to O(n) using the incremental algorithm!
Incremental blame is not possible using libgit2, jgit and cgit API.
We need to compare file trees.
git log -M=50%
For each inserted file:
Myers algorithm is enough for our analytics.
func foo() {println("waldo")}const X = 10func spam() {println("baz")}
func foo() {println("waldo")}const X = 10func spam() {println("baz")}
git logFileDiff 0.503959
TreeDiff 0.457430
Burndown 0.033033
RenameAnalysis 0.003440
BlobCache 0.002025
DaysSinceStart 0.000064
IdentityDetector 0.000051
flat% sum% cum%
8.82% 8.82% 17.13% runtime.scanobject
6.05% 14.86% 26.95% runtime.mallocgc
5.79% 20.65% 5.79% runtime.heapBitsSetType
5.54% 26.20% 5.54% runtime.heapBitsForObject
4.53% 30.73% 4.53% runtime.memclrNoHeapPointers
2.77% 33.50% 2.77% runtime.memmove
2.27% 35.77% 2.27% runtime.greyobject
2.27% 38.04% 2.27% runtime.nextFreeFast
2.02% 40.05% 2.02% runtime.indexbytebody
2.02% 42.07% 3.78% runtime.mapaccess2_faststr
More common commits ⇒ closer in 3D
Works with files, developers, classes, functions, ...
internal packageinterfacefunc getPtr(ip *interface{}) unsafe.Pointer {return unsafe.Pointer(intptr(unsafe.Pointer(ip)) +unsafe.Sizeof(ip))}iface := interface{}(111);ptr := (**int)(getPtr(&iface));i := 7;*ptr = &i;println(iface.(int)) // 7i = 8;println(iface.(int)) // 8
var err errorif true {ret, err := call() // error}if err != nil {// nope}