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 = 10
func 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
----------> time
way <----------
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
----------> time
way <----------
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 = 10
func spam() {
println("baz")
}
func foo() {
println("waldo")
}
const X = 10
func spam() {
println("baz")
}
git log
FileDiff 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
packageinterface
func 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)) // 7
i = 8;
println(iface.(int)) // 8
var err error
if true {
ret, err := call() // error
}
if err != nil {
// nope
}