diff --git a/internal/pb/pb.pb.go b/internal/pb/pb.pb.go index b412caab..9eb2f0c5 100644 --- a/internal/pb/pb.pb.go +++ b/internal/pb/pb.pb.go @@ -529,7 +529,8 @@ func (m *ShotnessAnalysisResults) GetRecords() []*ShotnessRecord { } type FileHistory struct { - Commits []string `protobuf:"bytes,1,rep,name=commits" json:"commits,omitempty"` + Commits []string `protobuf:"bytes,1,rep,name=commits" json:"commits,omitempty"` + ChangesByDeveloper map[int32]*LineStats `protobuf:"bytes,2,rep,name=changes_by_developer,json=changesByDeveloper" json:"changes_by_developer,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` } func (m *FileHistory) Reset() { *m = FileHistory{} } @@ -544,6 +545,13 @@ func (m *FileHistory) GetCommits() []string { return nil } +func (m *FileHistory) GetChangesByDeveloper() map[int32]*LineStats { + if m != nil { + return m.ChangesByDeveloper + } + return nil +} + type FileHistoryResultMessage struct { Files map[string]*FileHistory `protobuf:"bytes,1,rep,name=files" json:"files,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"` } @@ -765,90 +773,92 @@ func init() { func init() { proto.RegisterFile("pb.proto", fileDescriptorPb) } var fileDescriptorPb = []byte{ - // 1350 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x57, 0xcd, 0x6e, 0xdb, 0xc6, - 0x16, 0x06, 0xf5, 0xaf, 0x23, 0x5b, 0x4e, 0x26, 0xb9, 0x31, 0xa3, 0x0b, 0xe7, 0xea, 0x12, 0xb9, - 0xb9, 0x6e, 0x93, 0x32, 0x81, 0xd2, 0x45, 0x9a, 0x6e, 0xe2, 0xd8, 0x0d, 0x62, 0x20, 0x6e, 0x8a, - 0x51, 0x92, 0x2e, 0x89, 0xb1, 0x38, 0xb6, 0xd8, 0x8a, 0x43, 0x62, 0x86, 0x94, 0x2d, 0xa0, 0xcf, - 0xd2, 0x5d, 0x17, 0x2d, 0xd0, 0x55, 0x5f, 0xa0, 0x8b, 0x6e, 0xfa, 0x22, 0x05, 0xfa, 0x16, 0xc5, - 0xfc, 0x51, 0xa4, 0x4a, 0xa7, 0xed, 0x4e, 0xe7, 0x9c, 0xef, 0xcc, 0x7c, 0xf3, 0x9d, 0x33, 0x67, - 0x28, 0xe8, 0xa5, 0xa7, 0x7e, 0xca, 0x93, 0x2c, 0xf1, 0x7e, 0x6b, 0x40, 0xef, 0x84, 0x66, 0x24, - 0x24, 0x19, 0x41, 0x2e, 0x74, 0x97, 0x94, 0x8b, 0x28, 0x61, 0xae, 0x33, 0x76, 0xf6, 0xdb, 0xd8, - 0x9a, 0x08, 0x41, 0x6b, 0x4e, 0xc4, 0xdc, 0x6d, 0x8c, 0x9d, 0xfd, 0x3e, 0x56, 0xbf, 0xd1, 0x1d, - 0x00, 0x4e, 0xd3, 0x44, 0x44, 0x59, 0xc2, 0x57, 0x6e, 0x53, 0x45, 0x4a, 0x1e, 0x74, 0x0f, 0x76, - 0x4e, 0xe9, 0x79, 0xc4, 0x82, 0x9c, 0x45, 0x97, 0x41, 0x16, 0xc5, 0xd4, 0x6d, 0x8d, 0x9d, 0xfd, - 0x26, 0xde, 0x56, 0xee, 0xb7, 0x2c, 0xba, 0x7c, 0x13, 0xc5, 0x14, 0x79, 0xb0, 0x4d, 0x59, 0x58, - 0x42, 0xb5, 0x15, 0x6a, 0x40, 0x59, 0x58, 0x60, 0x5c, 0xe8, 0xce, 0x92, 0x38, 0x8e, 0x32, 0xe1, - 0x76, 0x34, 0x33, 0x63, 0xa2, 0xdb, 0xd0, 0xe3, 0x39, 0xd3, 0x89, 0x5d, 0x95, 0xd8, 0xe5, 0x39, - 0x53, 0x49, 0x2f, 0xe1, 0xba, 0x0d, 0x05, 0x29, 0xe5, 0x41, 0x94, 0xd1, 0xd8, 0xed, 0x8d, 0x9b, - 0xfb, 0x83, 0xc9, 0x9e, 0x6f, 0x0f, 0xed, 0x63, 0x8d, 0xfe, 0x82, 0xf2, 0xe3, 0x8c, 0xc6, 0x9f, - 0xb1, 0x8c, 0xaf, 0xf0, 0x90, 0x57, 0x9c, 0xa3, 0x03, 0xb8, 0x51, 0x03, 0x43, 0xd7, 0xa0, 0xf9, - 0x35, 0x5d, 0x29, 0xad, 0xfa, 0x58, 0xfe, 0x44, 0x37, 0xa1, 0xbd, 0x24, 0x8b, 0x9c, 0x2a, 0xa1, - 0x1c, 0xac, 0x8d, 0xa7, 0x8d, 0x27, 0x8e, 0xf7, 0x18, 0x76, 0x9f, 0xe7, 0x9c, 0x85, 0xc9, 0x05, - 0x9b, 0xa6, 0x84, 0x0b, 0x7a, 0x42, 0x32, 0x1e, 0x5d, 0xe2, 0xe4, 0x42, 0x1f, 0x6e, 0x91, 0xc7, - 0x4c, 0xb8, 0xce, 0xb8, 0xb9, 0xbf, 0x8d, 0xad, 0xe9, 0xfd, 0xe0, 0xc0, 0xcd, 0xba, 0x2c, 0x59, - 0x0f, 0x46, 0x62, 0x6a, 0xb6, 0x56, 0xbf, 0xd1, 0x5d, 0x18, 0xb2, 0x3c, 0x3e, 0xa5, 0x3c, 0x48, - 0xce, 0x02, 0x9e, 0x5c, 0x08, 0x45, 0xa2, 0x8d, 0xb7, 0xb4, 0xf7, 0xf5, 0x19, 0x4e, 0x2e, 0x04, - 0xfa, 0x10, 0xae, 0xaf, 0x51, 0x76, 0xdb, 0xa6, 0x02, 0xee, 0x58, 0xe0, 0xa1, 0x76, 0xa3, 0x07, - 0xd0, 0x52, 0xeb, 0xb4, 0x94, 0x66, 0xae, 0x7f, 0xc5, 0x01, 0xb0, 0x42, 0x79, 0xdf, 0xc0, 0xf0, - 0x45, 0xb4, 0xa0, 0xe2, 0xf5, 0x05, 0xa3, 0x5c, 0xcc, 0xa3, 0x14, 0x3d, 0xb2, 0x6a, 0x38, 0x6a, - 0x81, 0x91, 0x5f, 0x8d, 0xfb, 0xef, 0x64, 0x50, 0x2b, 0xae, 0x81, 0xa3, 0x27, 0x00, 0x6b, 0x67, - 0x59, 0xdf, 0x76, 0x8d, 0xbe, 0xed, 0xb2, 0xbe, 0xbf, 0x37, 0xd6, 0x02, 0x1f, 0x30, 0xb2, 0x58, - 0x89, 0x48, 0x60, 0x2a, 0xf2, 0x45, 0x26, 0xd0, 0x18, 0x06, 0xe7, 0x9c, 0xb0, 0x7c, 0x41, 0x78, - 0x94, 0xd9, 0xf5, 0xca, 0x2e, 0x34, 0x82, 0x9e, 0x20, 0x71, 0xba, 0x88, 0xd8, 0xb9, 0x59, 0xba, - 0xb0, 0xd1, 0x43, 0xe8, 0xa6, 0x3c, 0xf9, 0x8a, 0xce, 0x32, 0xa5, 0xd3, 0x60, 0xf2, 0xaf, 0x7a, - 0x21, 0x2c, 0x0a, 0xdd, 0x87, 0xf6, 0x99, 0x3c, 0xa8, 0xd1, 0xed, 0x0a, 0xb8, 0xc6, 0xa0, 0x8f, - 0xa0, 0x93, 0xd2, 0x24, 0x5d, 0xc8, 0xb6, 0x7f, 0x0f, 0xda, 0x80, 0xd0, 0x31, 0x20, 0xfd, 0x2b, - 0x88, 0x58, 0x46, 0x39, 0x99, 0x65, 0xf2, 0xb6, 0x76, 0x14, 0xaf, 0x91, 0x7f, 0x98, 0xc4, 0x29, - 0xa7, 0x42, 0xd0, 0x50, 0x27, 0xe3, 0xe4, 0xc2, 0xe4, 0x5f, 0xd7, 0x59, 0xc7, 0xeb, 0x24, 0xf4, - 0x04, 0x76, 0x14, 0x85, 0x20, 0xb1, 0x05, 0x71, 0xbb, 0x8a, 0xc2, 0xce, 0x46, 0x9d, 0xf0, 0xf0, - 0xac, 0x62, 0x7b, 0x3f, 0x39, 0x70, 0xfb, 0xca, 0xad, 0x6a, 0xfa, 0xd0, 0xf9, 0xbb, 0x7d, 0xd8, - 0xa8, 0xef, 0x43, 0x04, 0x2d, 0x79, 0x55, 0xdd, 0xe6, 0xb8, 0xb9, 0xdf, 0xc4, 0x2d, 0x3b, 0xab, - 0x22, 0x16, 0x46, 0x33, 0x23, 0x73, 0x1b, 0x5b, 0x13, 0xdd, 0x82, 0x4e, 0xc4, 0xc2, 0x34, 0xe3, - 0x4a, 0xd1, 0x26, 0x36, 0x96, 0x37, 0x85, 0xee, 0x61, 0x92, 0xa7, 0x52, 0xf4, 0x9b, 0xd0, 0x8e, - 0x58, 0x48, 0x2f, 0x55, 0x63, 0xf6, 0xb1, 0x36, 0xd0, 0x04, 0x3a, 0xb1, 0x3a, 0x82, 0xe2, 0xf1, - 0x7e, 0x3d, 0x0d, 0xd2, 0xbb, 0x0b, 0x5b, 0x6f, 0x92, 0x7c, 0x36, 0xa7, 0xa1, 0xd2, 0x4c, 0xae, - 0xac, 0x6b, 0xef, 0x28, 0x52, 0xda, 0xf0, 0x7e, 0x75, 0xe0, 0x96, 0xd9, 0x7b, 0xb3, 0x37, 0xef, - 0xc3, 0x96, 0xc4, 0x04, 0x33, 0x1d, 0x36, 0xa5, 0xec, 0xf9, 0x06, 0x8e, 0x07, 0x32, 0x6a, 0x79, - 0x3f, 0x84, 0xa1, 0xa9, 0xbe, 0x85, 0x77, 0x37, 0xe0, 0xdb, 0x3a, 0x6e, 0x13, 0x1e, 0xc1, 0x96, - 0x49, 0xd0, 0xac, 0xf4, 0xf4, 0xdb, 0xf6, 0xcb, 0x9c, 0xf1, 0x40, 0x43, 0xf4, 0x01, 0xfe, 0x03, - 0x03, 0xdd, 0x15, 0x8b, 0x88, 0x51, 0xe1, 0xf6, 0xd5, 0x31, 0x40, 0xb9, 0x5e, 0x49, 0x8f, 0xf7, - 0x9d, 0x03, 0xf0, 0xf6, 0x60, 0xfa, 0xe6, 0x70, 0x4e, 0xd8, 0x39, 0x45, 0xff, 0x86, 0xbe, 0xe2, - 0x5f, 0x1a, 0x47, 0x3d, 0xe9, 0xf8, 0x5c, 0x8e, 0xa4, 0x3d, 0x00, 0xc1, 0x67, 0xc1, 0x29, 0x3d, - 0x4b, 0x38, 0x35, 0x8f, 0x47, 0x5f, 0xf0, 0xd9, 0x73, 0xe5, 0x90, 0xb9, 0x32, 0x4c, 0xce, 0x32, - 0xca, 0xcd, 0x03, 0xd2, 0x13, 0x7c, 0x76, 0x20, 0x6d, 0x49, 0x24, 0x27, 0x22, 0xb3, 0xc9, 0x2d, - 0xfd, 0xbe, 0x48, 0x97, 0xc9, 0xde, 0x03, 0x65, 0x99, 0xf4, 0xb6, 0x5e, 0x5c, 0x7a, 0x54, 0xbe, - 0xf7, 0x0c, 0x76, 0xd7, 0x34, 0xc5, 0x94, 0x2c, 0x29, 0xb7, 0x9a, 0xff, 0x0f, 0xba, 0x33, 0xed, - 0x36, 0x93, 0x69, 0xe0, 0xaf, 0xa1, 0xd8, 0xc6, 0xbc, 0x5f, 0x1c, 0x18, 0x4e, 0xe7, 0x49, 0xc6, - 0xa8, 0x10, 0x98, 0xce, 0x12, 0x1e, 0xca, 0x4e, 0xcc, 0x56, 0x69, 0x31, 0x77, 0xe5, 0xef, 0x62, - 0x16, 0x37, 0x4a, 0xb3, 0x18, 0x41, 0x4b, 0x8a, 0x60, 0x0e, 0xa5, 0x7e, 0xa3, 0x4f, 0xa0, 0x37, - 0x4b, 0x72, 0x79, 0x01, 0xed, 0x64, 0xd8, 0xf3, 0xab, 0xcb, 0xcb, 0x2a, 0xaa, 0xb8, 0x9e, 0x89, - 0x05, 0x7c, 0xf4, 0x29, 0x6c, 0x57, 0x42, 0xff, 0x68, 0x32, 0x1e, 0xc1, 0xae, 0xdd, 0x66, 0xb3, - 0xf9, 0x3e, 0x80, 0x2e, 0x57, 0x3b, 0x5b, 0x21, 0x76, 0x36, 0x18, 0x61, 0x1b, 0xf7, 0xfe, 0x0f, - 0x03, 0xd9, 0x20, 0x2f, 0x23, 0xa1, 0x1e, 0xf7, 0xd2, 0x83, 0xac, 0xef, 0x90, 0x35, 0xbd, 0x6f, - 0x1d, 0x70, 0x4b, 0x48, 0xbd, 0xd5, 0x09, 0x15, 0x82, 0x9c, 0x53, 0xf4, 0xb4, 0x7c, 0x3d, 0x06, - 0x93, 0xbb, 0xfe, 0x55, 0x48, 0x3d, 0x82, 0xcc, 0xdb, 0xa0, 0x52, 0x46, 0x2f, 0x00, 0xd6, 0xce, - 0x9a, 0xb7, 0xd7, 0x2b, 0x2b, 0x30, 0x98, 0x6c, 0x55, 0xd6, 0x2e, 0xe9, 0xf1, 0x16, 0xfa, 0xb2, - 0x93, 0xa7, 0x19, 0xc9, 0xd4, 0x7d, 0x25, 0x61, 0x48, 0x43, 0x23, 0xa5, 0x36, 0xe4, 0xe9, 0x38, - 0x8d, 0x93, 0x25, 0x0d, 0x8d, 0x9c, 0xd6, 0x54, 0xe7, 0x56, 0xed, 0x11, 0x9a, 0x47, 0xd3, 0x9a, - 0xb2, 0x5b, 0x3a, 0x47, 0x74, 0x79, 0x44, 0x36, 0xc4, 0xa9, 0x7c, 0xad, 0x8c, 0xa1, 0x2d, 0xe4, - 0xbe, 0x86, 0x23, 0xf8, 0x05, 0x13, 0xac, 0x03, 0xe8, 0x63, 0xe8, 0x2f, 0x08, 0x3b, 0xcf, 0x89, - 0xec, 0xce, 0xa6, 0x52, 0xe9, 0x96, 0xaf, 0xd7, 0xf5, 0x5f, 0xd9, 0x80, 0xd6, 0x65, 0x0d, 0x1c, - 0xbd, 0x84, 0x61, 0x35, 0x58, 0xa3, 0xcf, 0xb8, 0xaa, 0x4f, 0x65, 0xef, 0xb5, 0x3a, 0x02, 0xba, - 0x47, 0x64, 0x75, 0x44, 0x97, 0x02, 0xdd, 0x83, 0x56, 0x48, 0x97, 0xb6, 0x56, 0xc8, 0x37, 0x7e, - 0xc9, 0xc6, 0x30, 0x50, 0xf1, 0xd1, 0x33, 0xe8, 0x17, 0xae, 0x9a, 0xce, 0xdc, 0xab, 0xee, 0xdb, - 0x35, 0xa7, 0x29, 0x6f, 0xfa, 0xbd, 0x03, 0x37, 0xe4, 0x12, 0x9b, 0xfd, 0x39, 0x91, 0x83, 0x7f, - 0x65, 0x19, 0xdc, 0xf1, 0x6b, 0x30, 0x92, 0x55, 0xc1, 0x86, 0xac, 0x84, 0x1c, 0x2a, 0x21, 0x5d, - 0x06, 0x7a, 0xbe, 0x37, 0x54, 0x6f, 0xf6, 0x42, 0xba, 0x3c, 0x96, 0xf6, 0xe8, 0x00, 0xfa, 0x05, - 0xbe, 0x86, 0xea, 0x9d, 0x2a, 0xd5, 0x9e, 0x3d, 0x72, 0x99, 0xeb, 0x97, 0xd0, 0x9f, 0x52, 0x26, - 0x3f, 0x2a, 0x59, 0xb6, 0xbe, 0x75, 0x72, 0x91, 0x86, 0x81, 0xc9, 0xaf, 0x09, 0x59, 0x70, 0xca, - 0x54, 0xa1, 0x15, 0x03, 0x6b, 0x97, 0x7b, 0xa3, 0x59, 0xbd, 0x38, 0x3f, 0x3b, 0xb0, 0x7b, 0xa8, - 0x61, 0xc5, 0x06, 0x56, 0x88, 0x77, 0x70, 0x4d, 0x58, 0x5f, 0x70, 0xba, 0x0a, 0x42, 0xb2, 0x32, - 0xa2, 0x3c, 0xf0, 0xaf, 0xc8, 0xf1, 0x0b, 0xc7, 0xf3, 0xd5, 0x11, 0x59, 0x99, 0x0f, 0x5b, 0x51, - 0x71, 0x8e, 0x4e, 0xe0, 0x46, 0x0d, 0xac, 0x46, 0x99, 0x3f, 0x35, 0xcf, 0x7a, 0xbb, 0x92, 0x36, - 0x3f, 0x3a, 0xb0, 0xb3, 0x59, 0xc3, 0xff, 0x42, 0x67, 0x4e, 0x49, 0x48, 0xb9, 0x5a, 0x6e, 0x30, - 0xe9, 0x17, 0x9f, 0xde, 0xd8, 0x04, 0xd0, 0x53, 0xa9, 0x17, 0xcb, 0x0a, 0xbd, 0x64, 0xa9, 0x37, - 0xcb, 0x7c, 0x68, 0x00, 0xc5, 0x68, 0xd4, 0xa6, 0x1e, 0x8d, 0xa5, 0xd0, 0x5f, 0x7d, 0x94, 0x6f, - 0x95, 0xf8, 0x9e, 0x76, 0xd4, 0x9f, 0xa0, 0xc7, 0x7f, 0x04, 0x00, 0x00, 0xff, 0xff, 0x0b, 0x16, - 0xa3, 0xa0, 0x10, 0x0d, 0x00, 0x00, + // 1390 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x57, 0x4d, 0x8f, 0xdc, 0x44, + 0x13, 0x96, 0xe7, 0x7b, 0x6a, 0x76, 0x67, 0x93, 0xce, 0xbe, 0x59, 0x67, 0x5e, 0x6d, 0x18, 0xac, + 0x10, 0x2d, 0x24, 0x38, 0xd1, 0x86, 0x43, 0x08, 0x97, 0xec, 0x07, 0x51, 0x56, 0xca, 0x12, 0xf0, + 0x24, 0xe1, 0x68, 0xf5, 0xda, 0xbd, 0x3b, 0x86, 0x71, 0xdb, 0xea, 0xb6, 0x67, 0x76, 0x24, 0x7e, + 0x0b, 0x37, 0x0e, 0x20, 0x71, 0xe2, 0x0f, 0x70, 0xe0, 0xc2, 0x95, 0x1f, 0x81, 0xc4, 0xbf, 0x40, + 0xfd, 0xe5, 0xb1, 0x07, 0x6f, 0x08, 0x37, 0x57, 0xd5, 0x53, 0xdd, 0x4f, 0x3f, 0x55, 0x5d, 0x3d, + 0x03, 0xbd, 0xf4, 0xcc, 0x4d, 0x59, 0x92, 0x25, 0xce, 0x9f, 0x0d, 0xe8, 0x9d, 0x92, 0x0c, 0x87, + 0x38, 0xc3, 0xc8, 0x86, 0xee, 0x9c, 0x30, 0x1e, 0x25, 0xd4, 0xb6, 0xc6, 0xd6, 0x5e, 0xdb, 0x33, + 0x26, 0x42, 0xd0, 0x9a, 0x62, 0x3e, 0xb5, 0x1b, 0x63, 0x6b, 0xaf, 0xef, 0xc9, 0x6f, 0x74, 0x1b, + 0x80, 0x91, 0x34, 0xe1, 0x51, 0x96, 0xb0, 0xa5, 0xdd, 0x94, 0x91, 0x92, 0x07, 0xdd, 0x85, 0xad, + 0x33, 0x72, 0x11, 0x51, 0x3f, 0xa7, 0xd1, 0xa5, 0x9f, 0x45, 0x31, 0xb1, 0x5b, 0x63, 0x6b, 0xaf, + 0xe9, 0x6d, 0x4a, 0xf7, 0x6b, 0x1a, 0x5d, 0xbe, 0x8a, 0x62, 0x82, 0x1c, 0xd8, 0x24, 0x34, 0x2c, + 0xa1, 0xda, 0x12, 0x35, 0x20, 0x34, 0x2c, 0x30, 0x36, 0x74, 0x83, 0x24, 0x8e, 0xa3, 0x8c, 0xdb, + 0x1d, 0xc5, 0x4c, 0x9b, 0xe8, 0x16, 0xf4, 0x58, 0x4e, 0x55, 0x62, 0x57, 0x26, 0x76, 0x59, 0x4e, + 0x65, 0xd2, 0x73, 0xb8, 0x6e, 0x42, 0x7e, 0x4a, 0x98, 0x1f, 0x65, 0x24, 0xb6, 0x7b, 0xe3, 0xe6, + 0xde, 0x60, 0x7f, 0xd7, 0x35, 0x87, 0x76, 0x3d, 0x85, 0xfe, 0x92, 0xb0, 0x93, 0x8c, 0xc4, 0x9f, + 0xd3, 0x8c, 0x2d, 0xbd, 0x21, 0xab, 0x38, 0x47, 0x07, 0x70, 0xa3, 0x06, 0x86, 0xae, 0x41, 0xf3, + 0x5b, 0xb2, 0x94, 0x5a, 0xf5, 0x3d, 0xf1, 0x89, 0xb6, 0xa1, 0x3d, 0xc7, 0xb3, 0x9c, 0x48, 0xa1, + 0x2c, 0x4f, 0x19, 0x4f, 0x1a, 0x8f, 0x2d, 0xe7, 0x11, 0xec, 0x1c, 0xe6, 0x8c, 0x86, 0xc9, 0x82, + 0x4e, 0x52, 0xcc, 0x38, 0x39, 0xc5, 0x19, 0x8b, 0x2e, 0xbd, 0x64, 0xa1, 0x0e, 0x37, 0xcb, 0x63, + 0xca, 0x6d, 0x6b, 0xdc, 0xdc, 0xdb, 0xf4, 0x8c, 0xe9, 0xfc, 0x64, 0xc1, 0x76, 0x5d, 0x96, 0xa8, + 0x07, 0xc5, 0x31, 0xd1, 0x5b, 0xcb, 0x6f, 0x74, 0x07, 0x86, 0x34, 0x8f, 0xcf, 0x08, 0xf3, 0x93, + 0x73, 0x9f, 0x25, 0x0b, 0x2e, 0x49, 0xb4, 0xbd, 0x0d, 0xe5, 0x7d, 0x79, 0xee, 0x25, 0x0b, 0x8e, + 0x3e, 0x82, 0xeb, 0x2b, 0x94, 0xd9, 0xb6, 0x29, 0x81, 0x5b, 0x06, 0x78, 0xa4, 0xdc, 0xe8, 0x3e, + 0xb4, 0xe4, 0x3a, 0x2d, 0xa9, 0x99, 0xed, 0x5e, 0x71, 0x00, 0x4f, 0xa2, 0x9c, 0xef, 0x60, 0xf8, + 0x2c, 0x9a, 0x11, 0xfe, 0x72, 0x41, 0x09, 0xe3, 0xd3, 0x28, 0x45, 0x0f, 0x8d, 0x1a, 0x96, 0x5c, + 0x60, 0xe4, 0x56, 0xe3, 0xee, 0x1b, 0x11, 0x54, 0x8a, 0x2b, 0xe0, 0xe8, 0x31, 0xc0, 0xca, 0x59, + 0xd6, 0xb7, 0x5d, 0xa3, 0x6f, 0xbb, 0xac, 0xef, 0x5f, 0x8d, 0x95, 0xc0, 0x07, 0x14, 0xcf, 0x96, + 0x3c, 0xe2, 0x1e, 0xe1, 0xf9, 0x2c, 0xe3, 0x68, 0x0c, 0x83, 0x0b, 0x86, 0x69, 0x3e, 0xc3, 0x2c, + 0xca, 0xcc, 0x7a, 0x65, 0x17, 0x1a, 0x41, 0x8f, 0xe3, 0x38, 0x9d, 0x45, 0xf4, 0x42, 0x2f, 0x5d, + 0xd8, 0xe8, 0x01, 0x74, 0x53, 0x96, 0x7c, 0x43, 0x82, 0x4c, 0xea, 0x34, 0xd8, 0xff, 0x5f, 0xbd, + 0x10, 0x06, 0x85, 0xee, 0x41, 0xfb, 0x5c, 0x1c, 0x54, 0xeb, 0x76, 0x05, 0x5c, 0x61, 0xd0, 0xc7, + 0xd0, 0x49, 0x49, 0x92, 0xce, 0x44, 0xdb, 0xbf, 0x05, 0xad, 0x41, 0xe8, 0x04, 0x90, 0xfa, 0xf2, + 0x23, 0x9a, 0x11, 0x86, 0x83, 0x4c, 0xdc, 0xd6, 0x8e, 0xe4, 0x35, 0x72, 0x8f, 0x92, 0x38, 0x65, + 0x84, 0x73, 0x12, 0xaa, 0x64, 0x2f, 0x59, 0xe8, 0xfc, 0xeb, 0x2a, 0xeb, 0x64, 0x95, 0x84, 0x1e, + 0xc3, 0x96, 0xa4, 0xe0, 0x27, 0xa6, 0x20, 0x76, 0x57, 0x52, 0xd8, 0x5a, 0xab, 0x93, 0x37, 0x3c, + 0xaf, 0xd8, 0xce, 0x2f, 0x16, 0xdc, 0xba, 0x72, 0xab, 0x9a, 0x3e, 0xb4, 0xde, 0xb5, 0x0f, 0x1b, + 0xf5, 0x7d, 0x88, 0xa0, 0x25, 0xae, 0xaa, 0xdd, 0x1c, 0x37, 0xf7, 0x9a, 0x5e, 0xcb, 0xcc, 0xaa, + 0x88, 0x86, 0x51, 0xa0, 0x65, 0x6e, 0x7b, 0xc6, 0x44, 0x37, 0xa1, 0x13, 0xd1, 0x30, 0xcd, 0x98, + 0x54, 0xb4, 0xe9, 0x69, 0xcb, 0x99, 0x40, 0xf7, 0x28, 0xc9, 0x53, 0x21, 0xfa, 0x36, 0xb4, 0x23, + 0x1a, 0x92, 0x4b, 0xd9, 0x98, 0x7d, 0x4f, 0x19, 0x68, 0x1f, 0x3a, 0xb1, 0x3c, 0x82, 0xe4, 0xf1, + 0x76, 0x3d, 0x35, 0xd2, 0xb9, 0x03, 0x1b, 0xaf, 0x92, 0x3c, 0x98, 0x92, 0x50, 0x6a, 0x26, 0x56, + 0x56, 0xb5, 0xb7, 0x24, 0x29, 0x65, 0x38, 0xbf, 0x5b, 0x70, 0x53, 0xef, 0xbd, 0xde, 0x9b, 0xf7, + 0x60, 0x43, 0x60, 0xfc, 0x40, 0x85, 0x75, 0x29, 0x7b, 0xae, 0x86, 0x7b, 0x03, 0x11, 0x35, 0xbc, + 0x1f, 0xc0, 0x50, 0x57, 0xdf, 0xc0, 0xbb, 0x6b, 0xf0, 0x4d, 0x15, 0x37, 0x09, 0x0f, 0x61, 0x43, + 0x27, 0x28, 0x56, 0x6a, 0xfa, 0x6d, 0xba, 0x65, 0xce, 0xde, 0x40, 0x41, 0xd4, 0x01, 0xde, 0x83, + 0x81, 0xea, 0x8a, 0x59, 0x44, 0x09, 0xb7, 0xfb, 0xf2, 0x18, 0x20, 0x5d, 0x2f, 0x84, 0xc7, 0xf9, + 0xc1, 0x02, 0x78, 0x7d, 0x30, 0x79, 0x75, 0x34, 0xc5, 0xf4, 0x82, 0xa0, 0xff, 0x43, 0x5f, 0xf2, + 0x2f, 0x8d, 0xa3, 0x9e, 0x70, 0x7c, 0x21, 0x46, 0xd2, 0x2e, 0x00, 0x67, 0x81, 0x7f, 0x46, 0xce, + 0x13, 0x46, 0xf4, 0xe3, 0xd1, 0xe7, 0x2c, 0x38, 0x94, 0x0e, 0x91, 0x2b, 0xc2, 0xf8, 0x3c, 0x23, + 0x4c, 0x3f, 0x20, 0x3d, 0xce, 0x82, 0x03, 0x61, 0x0b, 0x22, 0x39, 0xe6, 0x99, 0x49, 0x6e, 0xa9, + 0xf7, 0x45, 0xb8, 0x74, 0xf6, 0x2e, 0x48, 0x4b, 0xa7, 0xb7, 0xd5, 0xe2, 0xc2, 0x23, 0xf3, 0x9d, + 0xa7, 0xb0, 0xb3, 0xa2, 0xc9, 0x27, 0x78, 0x4e, 0x98, 0xd1, 0xfc, 0x03, 0xe8, 0x06, 0xca, 0xad, + 0x27, 0xd3, 0xc0, 0x5d, 0x41, 0x3d, 0x13, 0x73, 0x7e, 0xb3, 0x60, 0x38, 0x99, 0x26, 0x19, 0x25, + 0x9c, 0x7b, 0x24, 0x48, 0x58, 0x28, 0x3a, 0x31, 0x5b, 0xa6, 0xc5, 0xdc, 0x15, 0xdf, 0xc5, 0x2c, + 0x6e, 0x94, 0x66, 0x31, 0x82, 0x96, 0x10, 0x41, 0x1f, 0x4a, 0x7e, 0xa3, 0x4f, 0xa1, 0x17, 0x24, + 0xb9, 0xb8, 0x80, 0x66, 0x32, 0xec, 0xba, 0xd5, 0xe5, 0x45, 0x15, 0x65, 0x5c, 0xcd, 0xc4, 0x02, + 0x3e, 0xfa, 0x0c, 0x36, 0x2b, 0xa1, 0xff, 0x34, 0x19, 0x8f, 0x61, 0xc7, 0x6c, 0xb3, 0xde, 0x7c, + 0x1f, 0x42, 0x97, 0xc9, 0x9d, 0x8d, 0x10, 0x5b, 0x6b, 0x8c, 0x3c, 0x13, 0x77, 0xfe, 0xb0, 0x60, + 0x20, 0x3a, 0xe4, 0x79, 0xc4, 0xe5, 0xeb, 0x5e, 0x7a, 0x91, 0xd5, 0x25, 0x2a, 0x5e, 0xe4, 0x37, + 0xb0, 0xad, 0x15, 0xf4, 0xcf, 0x96, 0x7e, 0x48, 0xe6, 0x64, 0x96, 0xa4, 0x84, 0xd9, 0x0d, 0xb9, + 0xc3, 0x1d, 0xb7, 0xb4, 0x8a, 0xab, 0xab, 0x73, 0xb8, 0x3c, 0x36, 0x30, 0x75, 0x74, 0x14, 0xfc, + 0x23, 0x30, 0xfa, 0x0a, 0x76, 0xae, 0x80, 0xd7, 0xc8, 0x31, 0x2e, 0xcb, 0x31, 0xd8, 0x07, 0x57, + 0x34, 0xef, 0x24, 0xc3, 0x19, 0x2f, 0x4b, 0xf3, 0xbd, 0x05, 0x76, 0x89, 0x8e, 0x92, 0xe5, 0x94, + 0x70, 0x8e, 0x2f, 0x08, 0x7a, 0x52, 0xbe, 0xca, 0x6b, 0xc4, 0x2b, 0x48, 0x35, 0x2e, 0xf5, 0x3b, + 0x26, 0x53, 0x46, 0xcf, 0x00, 0x56, 0xce, 0x9a, 0xdf, 0x09, 0x4e, 0x95, 0xde, 0x46, 0x65, 0xed, + 0x12, 0xc1, 0xd7, 0xd0, 0x2f, 0x88, 0x8b, 0x12, 0xe3, 0x30, 0x24, 0xa1, 0x3e, 0xa7, 0x32, 0x44, + 0x21, 0x18, 0x89, 0x93, 0x39, 0x09, 0x75, 0xe9, 0x8d, 0x29, 0x4b, 0x24, 0x05, 0x0b, 0xf5, 0x03, + 0x6f, 0x4c, 0xd1, 0xd9, 0x9d, 0x63, 0x32, 0x3f, 0xc6, 0x6b, 0x75, 0xac, 0xfc, 0xb2, 0x1a, 0x43, + 0x9b, 0x8b, 0x7d, 0xeb, 0x24, 0x94, 0x01, 0xf4, 0x09, 0xf4, 0x67, 0x98, 0x5e, 0xe4, 0x58, 0xdc, + 0xa4, 0xa6, 0x54, 0xe9, 0xa6, 0xab, 0xd6, 0x75, 0x5f, 0x98, 0x80, 0xd2, 0x65, 0x05, 0x1c, 0x3d, + 0x87, 0x61, 0x35, 0x58, 0xa3, 0xcf, 0xbb, 0x95, 0x8f, 0x43, 0xf7, 0x18, 0x8b, 0x5e, 0xe0, 0xe8, + 0x2e, 0xb4, 0x42, 0x32, 0x37, 0xb5, 0x42, 0xae, 0xf6, 0x0b, 0x36, 0x9a, 0x81, 0x8c, 0x8f, 0x9e, + 0x42, 0xbf, 0x70, 0xd5, 0xb4, 0xcd, 0x6e, 0x75, 0xdf, 0xae, 0x3e, 0x4d, 0x79, 0xd3, 0x1f, 0x2d, + 0xb8, 0x21, 0x96, 0x58, 0xbf, 0x4b, 0xfb, 0xe2, 0x91, 0x5a, 0x1a, 0x06, 0xb7, 0xdd, 0x1a, 0x8c, + 0x60, 0x55, 0xb0, 0xc1, 0x4b, 0x2e, 0x06, 0x60, 0x48, 0xe6, 0xbe, 0x7a, 0x8b, 0x1a, 0xf2, 0x1a, + 0xf5, 0x42, 0x32, 0x3f, 0x11, 0xf6, 0xe8, 0x00, 0xfa, 0x05, 0xbe, 0x86, 0xea, 0xed, 0x2a, 0xd5, + 0x9e, 0x39, 0x72, 0x99, 0xeb, 0xd7, 0xd0, 0x9f, 0x10, 0x2a, 0x7e, 0x00, 0xd3, 0x6c, 0x35, 0x21, + 0xc4, 0x22, 0x0d, 0x0d, 0x13, 0xbf, 0x7c, 0x44, 0xc1, 0x09, 0x95, 0x85, 0x96, 0x0c, 0x8c, 0x5d, + 0xee, 0x8d, 0x66, 0xe5, 0x8e, 0x3b, 0xbf, 0x5a, 0xb0, 0x73, 0xa4, 0x60, 0xc5, 0x06, 0x46, 0x88, + 0x37, 0x70, 0x8d, 0x1b, 0x9f, 0x9c, 0x00, 0x78, 0xa9, 0x45, 0xb9, 0xef, 0x5e, 0x91, 0xe3, 0x16, + 0x8e, 0xc3, 0xe5, 0x31, 0x5e, 0xea, 0x1f, 0xe1, 0xbc, 0xe2, 0x1c, 0x9d, 0xc2, 0x8d, 0x1a, 0xd8, + 0xbb, 0xdc, 0xfd, 0xd5, 0x76, 0x25, 0x6d, 0x7e, 0xb6, 0x60, 0x6b, 0xbd, 0x86, 0xef, 0x43, 0x67, + 0x4a, 0x70, 0x48, 0x98, 0x5c, 0x6e, 0xb0, 0xdf, 0x2f, 0xfe, 0x26, 0x78, 0x3a, 0x80, 0x9e, 0x08, + 0xbd, 0x68, 0x56, 0xe8, 0x25, 0x4a, 0xbd, 0x5e, 0xe6, 0x23, 0x0d, 0x28, 0xc6, 0xb8, 0x32, 0xd5, + 0x18, 0x2f, 0x85, 0xfe, 0xed, 0x0f, 0xc4, 0x46, 0x89, 0xef, 0x59, 0x47, 0xfe, 0x61, 0x7b, 0xf4, + 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x48, 0x21, 0x38, 0x53, 0xbc, 0x0d, 0x00, 0x00, } diff --git a/internal/pb/pb.proto b/internal/pb/pb.proto index 4e5227d2..d6439434 100644 --- a/internal/pb/pb.proto +++ b/internal/pb/pb.proto @@ -109,7 +109,7 @@ message ShotnessAnalysisResults { message FileHistory { repeated string commits = 1; - //map changes_by_developer = 2; + map changes_by_developer = 2; } message FileHistoryResultMessage { diff --git a/internal/pb/pb_pb2.py b/internal/pb/pb_pb2.py index 9f99b66c..5fea2bf4 100644 --- a/internal/pb/pb_pb2.py +++ b/internal/pb/pb_pb2.py @@ -19,7 +19,7 @@ package='', syntax='proto3', serialized_options=None, - serialized_pb=_b('\n\x08pb.proto\"\x81\x02\n\x08Metadata\x12\x0f\n\x07version\x18\x01 \x01(\x05\x12\x0c\n\x04hash\x18\x02 \x01(\t\x12\x12\n\nrepository\x18\x03 \x01(\t\x12\x17\n\x0f\x62\x65gin_unix_time\x18\x04 \x01(\x03\x12\x15\n\rend_unix_time\x18\x05 \x01(\x03\x12\x0f\n\x07\x63ommits\x18\x06 \x01(\x05\x12\x10\n\x08run_time\x18\x07 \x01(\x03\x12\x38\n\x11run_time_per_item\x18\x08 \x03(\x0b\x32\x1d.Metadata.RunTimePerItemEntry\x1a\x35\n\x13RunTimePerItemEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x01:\x02\x38\x01\"*\n\x17\x42urndownSparseMatrixRow\x12\x0f\n\x07\x63olumns\x18\x01 \x03(\r\"\x7f\n\x14\x42urndownSparseMatrix\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x16\n\x0enumber_of_rows\x18\x02 \x01(\x05\x12\x19\n\x11number_of_columns\x18\x03 \x01(\x05\x12&\n\x04rows\x18\x04 \x03(\x0b\x32\x18.BurndownSparseMatrixRow\"i\n\x0e\x46ilesOwnership\x12)\n\x05value\x18\x01 \x03(\x0b\x32\x1a.FilesOwnership.ValueEntry\x1a,\n\nValueEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\"\x97\x02\n\x17\x42urndownAnalysisResults\x12\x13\n\x0bgranularity\x18\x01 \x01(\x05\x12\x10\n\x08sampling\x18\x02 \x01(\x05\x12&\n\x07project\x18\x03 \x01(\x0b\x32\x15.BurndownSparseMatrix\x12$\n\x05\x66iles\x18\x04 \x03(\x0b\x32\x15.BurndownSparseMatrix\x12%\n\x06people\x18\x05 \x03(\x0b\x32\x15.BurndownSparseMatrix\x12\x36\n\x12people_interaction\x18\x06 \x01(\x0b\x32\x1a.CompressedSparseRowMatrix\x12(\n\x0f\x66iles_ownership\x18\x07 \x03(\x0b\x32\x0f.FilesOwnership\"}\n\x19\x43ompressedSparseRowMatrix\x12\x16\n\x0enumber_of_rows\x18\x01 \x01(\x05\x12\x19\n\x11number_of_columns\x18\x02 \x01(\x05\x12\x0c\n\x04\x64\x61ta\x18\x03 \x03(\x03\x12\x0f\n\x07indices\x18\x04 \x03(\x05\x12\x0e\n\x06indptr\x18\x05 \x03(\x03\"D\n\x07\x43ouples\x12\r\n\x05index\x18\x01 \x03(\t\x12*\n\x06matrix\x18\x02 \x01(\x0b\x32\x1a.CompressedSparseRowMatrix\"\x1d\n\x0cTouchedFiles\x12\r\n\x05\x66iles\x18\x01 \x03(\x05\"\x94\x01\n\x16\x43ouplesAnalysisResults\x12\x1e\n\x0c\x66ile_couples\x18\x06 \x01(\x0b\x32\x08.Couples\x12 \n\x0epeople_couples\x18\x07 \x01(\x0b\x32\x08.Couples\x12#\n\x0cpeople_files\x18\x08 \x03(\x0b\x32\r.TouchedFiles\x12\x13\n\x0b\x66iles_lines\x18\t \x03(\x05\"o\n\nUASTChange\x12\x11\n\tfile_name\x18\x01 \x01(\t\x12\x12\n\nsrc_before\x18\x02 \x01(\t\x12\x11\n\tsrc_after\x18\x03 \x01(\t\x12\x13\n\x0buast_before\x18\x04 \x01(\t\x12\x12\n\nuast_after\x18\x05 \x01(\t\"7\n\x17UASTChangesSaverResults\x12\x1c\n\x07\x63hanges\x18\x01 \x03(\x0b\x32\x0b.UASTChange\"\x9c\x01\n\x0eShotnessRecord\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0c\n\x04\x66ile\x18\x03 \x01(\t\x12/\n\x08\x63ounters\x18\x04 \x03(\x0b\x32\x1d.ShotnessRecord.CountersEntry\x1a/\n\rCountersEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\";\n\x17ShotnessAnalysisResults\x12 \n\x07records\x18\x01 \x03(\x0b\x32\x0f.ShotnessRecord\"\x1e\n\x0b\x46ileHistory\x12\x0f\n\x07\x63ommits\x18\x01 \x03(\t\"\x8b\x01\n\x18\x46ileHistoryResultMessage\x12\x33\n\x05\x66iles\x18\x01 \x03(\x0b\x32$.FileHistoryResultMessage.FilesEntry\x1a:\n\nFilesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.FileHistory:\x02\x38\x01\"<\n\tLineStats\x12\r\n\x05\x61\x64\x64\x65\x64\x18\x01 \x01(\x05\x12\x0f\n\x07removed\x18\x02 \x01(\x05\x12\x0f\n\x07\x63hanged\x18\x03 \x01(\x05\"\x9d\x01\n\x06\x44\x65vDay\x12\x0f\n\x07\x63ommits\x18\x01 \x01(\x05\x12\x19\n\x05stats\x18\x02 \x01(\x0b\x32\n.LineStats\x12)\n\tlanguages\x18\x03 \x03(\x0b\x32\x16.DevDay.LanguagesEntry\x1a<\n\x0eLanguagesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x19\n\x05value\x18\x02 \x01(\x0b\x32\n.LineStats:\x02\x38\x01\"a\n\x07\x44\x61yDevs\x12 \n\x04\x64\x65vs\x18\x01 \x03(\x0b\x32\x12.DayDevs.DevsEntry\x1a\x34\n\tDevsEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\x16\n\x05value\x18\x02 \x01(\x0b\x32\x07.DevDay:\x02\x38\x01\"\x8d\x01\n\x13\x44\x65vsAnalysisResults\x12,\n\x04\x64\x61ys\x18\x01 \x03(\x0b\x32\x1e.DevsAnalysisResults.DaysEntry\x12\x11\n\tdev_index\x18\x02 \x03(\t\x1a\x35\n\tDaysEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\x17\n\x05value\x18\x02 \x01(\x0b\x32\x08.DayDevs:\x02\x38\x01\"=\n\tSentiment\x12\r\n\x05value\x18\x01 \x01(\x02\x12\x10\n\x08\x63omments\x18\x02 \x03(\t\x12\x0f\n\x07\x63ommits\x18\x03 \x03(\t\"\xa4\x01\n\x17\x43ommentSentimentResults\x12\x46\n\x10sentiment_by_day\x18\x01 \x03(\x0b\x32,.CommentSentimentResults.SentimentByDayEntry\x1a\x41\n\x13SentimentByDayEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\x19\n\x05value\x18\x02 \x01(\x0b\x32\n.Sentiment:\x02\x38\x01\"\x8f\x01\n\x0f\x41nalysisResults\x12\x19\n\x06header\x18\x01 \x01(\x0b\x32\t.Metadata\x12\x30\n\x08\x63ontents\x18\x02 \x03(\x0b\x32\x1e.AnalysisResults.ContentsEntry\x1a/\n\rContentsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\x62\x06proto3') + serialized_pb=_b('\n\x08pb.proto\"\x81\x02\n\x08Metadata\x12\x0f\n\x07version\x18\x01 \x01(\x05\x12\x0c\n\x04hash\x18\x02 \x01(\t\x12\x12\n\nrepository\x18\x03 \x01(\t\x12\x17\n\x0f\x62\x65gin_unix_time\x18\x04 \x01(\x03\x12\x15\n\rend_unix_time\x18\x05 \x01(\x03\x12\x0f\n\x07\x63ommits\x18\x06 \x01(\x05\x12\x10\n\x08run_time\x18\x07 \x01(\x03\x12\x38\n\x11run_time_per_item\x18\x08 \x03(\x0b\x32\x1d.Metadata.RunTimePerItemEntry\x1a\x35\n\x13RunTimePerItemEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x01:\x02\x38\x01\"*\n\x17\x42urndownSparseMatrixRow\x12\x0f\n\x07\x63olumns\x18\x01 \x03(\r\"\x7f\n\x14\x42urndownSparseMatrix\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x16\n\x0enumber_of_rows\x18\x02 \x01(\x05\x12\x19\n\x11number_of_columns\x18\x03 \x01(\x05\x12&\n\x04rows\x18\x04 \x03(\x0b\x32\x18.BurndownSparseMatrixRow\"i\n\x0e\x46ilesOwnership\x12)\n\x05value\x18\x01 \x03(\x0b\x32\x1a.FilesOwnership.ValueEntry\x1a,\n\nValueEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\"\x97\x02\n\x17\x42urndownAnalysisResults\x12\x13\n\x0bgranularity\x18\x01 \x01(\x05\x12\x10\n\x08sampling\x18\x02 \x01(\x05\x12&\n\x07project\x18\x03 \x01(\x0b\x32\x15.BurndownSparseMatrix\x12$\n\x05\x66iles\x18\x04 \x03(\x0b\x32\x15.BurndownSparseMatrix\x12%\n\x06people\x18\x05 \x03(\x0b\x32\x15.BurndownSparseMatrix\x12\x36\n\x12people_interaction\x18\x06 \x01(\x0b\x32\x1a.CompressedSparseRowMatrix\x12(\n\x0f\x66iles_ownership\x18\x07 \x03(\x0b\x32\x0f.FilesOwnership\"}\n\x19\x43ompressedSparseRowMatrix\x12\x16\n\x0enumber_of_rows\x18\x01 \x01(\x05\x12\x19\n\x11number_of_columns\x18\x02 \x01(\x05\x12\x0c\n\x04\x64\x61ta\x18\x03 \x03(\x03\x12\x0f\n\x07indices\x18\x04 \x03(\x05\x12\x0e\n\x06indptr\x18\x05 \x03(\x03\"D\n\x07\x43ouples\x12\r\n\x05index\x18\x01 \x03(\t\x12*\n\x06matrix\x18\x02 \x01(\x0b\x32\x1a.CompressedSparseRowMatrix\"\x1d\n\x0cTouchedFiles\x12\r\n\x05\x66iles\x18\x01 \x03(\x05\"\x94\x01\n\x16\x43ouplesAnalysisResults\x12\x1e\n\x0c\x66ile_couples\x18\x06 \x01(\x0b\x32\x08.Couples\x12 \n\x0epeople_couples\x18\x07 \x01(\x0b\x32\x08.Couples\x12#\n\x0cpeople_files\x18\x08 \x03(\x0b\x32\r.TouchedFiles\x12\x13\n\x0b\x66iles_lines\x18\t \x03(\x05\"o\n\nUASTChange\x12\x11\n\tfile_name\x18\x01 \x01(\t\x12\x12\n\nsrc_before\x18\x02 \x01(\t\x12\x11\n\tsrc_after\x18\x03 \x01(\t\x12\x13\n\x0buast_before\x18\x04 \x01(\t\x12\x12\n\nuast_after\x18\x05 \x01(\t\"7\n\x17UASTChangesSaverResults\x12\x1c\n\x07\x63hanges\x18\x01 \x03(\x0b\x32\x0b.UASTChange\"\x9c\x01\n\x0eShotnessRecord\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0c\n\x04\x66ile\x18\x03 \x01(\t\x12/\n\x08\x63ounters\x18\x04 \x03(\x0b\x32\x1d.ShotnessRecord.CountersEntry\x1a/\n\rCountersEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\";\n\x17ShotnessAnalysisResults\x12 \n\x07records\x18\x01 \x03(\x0b\x32\x0f.ShotnessRecord\"\xa9\x01\n\x0b\x46ileHistory\x12\x0f\n\x07\x63ommits\x18\x01 \x03(\t\x12\x42\n\x14\x63hanges_by_developer\x18\x02 \x03(\x0b\x32$.FileHistory.ChangesByDeveloperEntry\x1a\x45\n\x17\x43hangesByDeveloperEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\x19\n\x05value\x18\x02 \x01(\x0b\x32\n.LineStats:\x02\x38\x01\"\x8b\x01\n\x18\x46ileHistoryResultMessage\x12\x33\n\x05\x66iles\x18\x01 \x03(\x0b\x32$.FileHistoryResultMessage.FilesEntry\x1a:\n\nFilesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1b\n\x05value\x18\x02 \x01(\x0b\x32\x0c.FileHistory:\x02\x38\x01\"<\n\tLineStats\x12\r\n\x05\x61\x64\x64\x65\x64\x18\x01 \x01(\x05\x12\x0f\n\x07removed\x18\x02 \x01(\x05\x12\x0f\n\x07\x63hanged\x18\x03 \x01(\x05\"\x9d\x01\n\x06\x44\x65vDay\x12\x0f\n\x07\x63ommits\x18\x01 \x01(\x05\x12\x19\n\x05stats\x18\x02 \x01(\x0b\x32\n.LineStats\x12)\n\tlanguages\x18\x03 \x03(\x0b\x32\x16.DevDay.LanguagesEntry\x1a<\n\x0eLanguagesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x19\n\x05value\x18\x02 \x01(\x0b\x32\n.LineStats:\x02\x38\x01\"a\n\x07\x44\x61yDevs\x12 \n\x04\x64\x65vs\x18\x01 \x03(\x0b\x32\x12.DayDevs.DevsEntry\x1a\x34\n\tDevsEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\x16\n\x05value\x18\x02 \x01(\x0b\x32\x07.DevDay:\x02\x38\x01\"\x8d\x01\n\x13\x44\x65vsAnalysisResults\x12,\n\x04\x64\x61ys\x18\x01 \x03(\x0b\x32\x1e.DevsAnalysisResults.DaysEntry\x12\x11\n\tdev_index\x18\x02 \x03(\t\x1a\x35\n\tDaysEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\x17\n\x05value\x18\x02 \x01(\x0b\x32\x08.DayDevs:\x02\x38\x01\"=\n\tSentiment\x12\r\n\x05value\x18\x01 \x01(\x02\x12\x10\n\x08\x63omments\x18\x02 \x03(\t\x12\x0f\n\x07\x63ommits\x18\x03 \x03(\t\"\xa4\x01\n\x17\x43ommentSentimentResults\x12\x46\n\x10sentiment_by_day\x18\x01 \x03(\x0b\x32,.CommentSentimentResults.SentimentByDayEntry\x1a\x41\n\x13SentimentByDayEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\x19\n\x05value\x18\x02 \x01(\x0b\x32\n.Sentiment:\x02\x38\x01\"\x8f\x01\n\x0f\x41nalysisResults\x12\x19\n\x06header\x18\x01 \x01(\x0b\x32\t.Metadata\x12\x30\n\x08\x63ontents\x18\x02 \x03(\x0b\x32\x1e.AnalysisResults.ContentsEntry\x1a/\n\rContentsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\x62\x06proto3') ) @@ -756,6 +756,43 @@ ) +_FILEHISTORY_CHANGESBYDEVELOPERENTRY = _descriptor.Descriptor( + name='ChangesByDeveloperEntry', + full_name='FileHistory.ChangesByDeveloperEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='FileHistory.ChangesByDeveloperEntry.key', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='value', full_name='FileHistory.ChangesByDeveloperEntry.value', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=_b('8\001'), + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1704, + serialized_end=1773, +) + _FILEHISTORY = _descriptor.Descriptor( name='FileHistory', full_name='FileHistory', @@ -770,10 +807,17 @@ message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='changes_by_developer', full_name='FileHistory.changes_by_developer', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], - nested_types=[], + nested_types=[_FILEHISTORY_CHANGESBYDEVELOPERENTRY, ], enum_types=[ ], serialized_options=None, @@ -782,8 +826,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1603, - serialized_end=1633, + serialized_start=1604, + serialized_end=1773, ) @@ -820,8 +864,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1717, - serialized_end=1775, + serialized_start=1857, + serialized_end=1915, ) _FILEHISTORYRESULTMESSAGE = _descriptor.Descriptor( @@ -850,8 +894,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1636, - serialized_end=1775, + serialized_start=1776, + serialized_end=1915, ) @@ -895,8 +939,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1777, - serialized_end=1837, + serialized_start=1917, + serialized_end=1977, ) @@ -933,8 +977,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1937, - serialized_end=1997, + serialized_start=2077, + serialized_end=2137, ) _DEVDAY = _descriptor.Descriptor( @@ -977,8 +1021,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1840, - serialized_end=1997, + serialized_start=1980, + serialized_end=2137, ) @@ -1015,8 +1059,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2044, - serialized_end=2096, + serialized_start=2184, + serialized_end=2236, ) _DAYDEVS = _descriptor.Descriptor( @@ -1045,8 +1089,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=1999, - serialized_end=2096, + serialized_start=2139, + serialized_end=2236, ) @@ -1083,8 +1127,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2187, - serialized_end=2240, + serialized_start=2327, + serialized_end=2380, ) _DEVSANALYSISRESULTS = _descriptor.Descriptor( @@ -1120,8 +1164,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2099, - serialized_end=2240, + serialized_start=2239, + serialized_end=2380, ) @@ -1165,8 +1209,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2242, - serialized_end=2303, + serialized_start=2382, + serialized_end=2443, ) @@ -1203,8 +1247,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2405, - serialized_end=2470, + serialized_start=2545, + serialized_end=2610, ) _COMMENTSENTIMENTRESULTS = _descriptor.Descriptor( @@ -1233,8 +1277,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2306, - serialized_end=2470, + serialized_start=2446, + serialized_end=2610, ) @@ -1271,8 +1315,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2569, - serialized_end=2616, + serialized_start=2709, + serialized_end=2756, ) _ANALYSISRESULTS = _descriptor.Descriptor( @@ -1308,8 +1352,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=2473, - serialized_end=2616, + serialized_start=2613, + serialized_end=2756, ) _METADATA_RUNTIMEPERITEMENTRY.containing_type = _METADATA @@ -1330,6 +1374,9 @@ _SHOTNESSRECORD_COUNTERSENTRY.containing_type = _SHOTNESSRECORD _SHOTNESSRECORD.fields_by_name['counters'].message_type = _SHOTNESSRECORD_COUNTERSENTRY _SHOTNESSANALYSISRESULTS.fields_by_name['records'].message_type = _SHOTNESSRECORD +_FILEHISTORY_CHANGESBYDEVELOPERENTRY.fields_by_name['value'].message_type = _LINESTATS +_FILEHISTORY_CHANGESBYDEVELOPERENTRY.containing_type = _FILEHISTORY +_FILEHISTORY.fields_by_name['changes_by_developer'].message_type = _FILEHISTORY_CHANGESBYDEVELOPERENTRY _FILEHISTORYRESULTMESSAGE_FILESENTRY.fields_by_name['value'].message_type = _FILEHISTORY _FILEHISTORYRESULTMESSAGE_FILESENTRY.containing_type = _FILEHISTORYRESULTMESSAGE _FILEHISTORYRESULTMESSAGE.fields_by_name['files'].message_type = _FILEHISTORYRESULTMESSAGE_FILESENTRY @@ -1489,11 +1536,19 @@ _sym_db.RegisterMessage(ShotnessAnalysisResults) FileHistory = _reflection.GeneratedProtocolMessageType('FileHistory', (_message.Message,), dict( + + ChangesByDeveloperEntry = _reflection.GeneratedProtocolMessageType('ChangesByDeveloperEntry', (_message.Message,), dict( + DESCRIPTOR = _FILEHISTORY_CHANGESBYDEVELOPERENTRY, + __module__ = 'pb_pb2' + # @@protoc_insertion_point(class_scope:FileHistory.ChangesByDeveloperEntry) + )) + , DESCRIPTOR = _FILEHISTORY, __module__ = 'pb_pb2' # @@protoc_insertion_point(class_scope:FileHistory) )) _sym_db.RegisterMessage(FileHistory) +_sym_db.RegisterMessage(FileHistory.ChangesByDeveloperEntry) FileHistoryResultMessage = _reflection.GeneratedProtocolMessageType('FileHistoryResultMessage', (_message.Message,), dict( @@ -1603,6 +1658,7 @@ _METADATA_RUNTIMEPERITEMENTRY._options = None _FILESOWNERSHIP_VALUEENTRY._options = None _SHOTNESSRECORD_COUNTERSENTRY._options = None +_FILEHISTORY_CHANGESBYDEVELOPERENTRY._options = None _FILEHISTORYRESULTMESSAGE_FILESENTRY._options = None _DEVDAY_LANGUAGESENTRY._options = None _DAYDEVS_DEVSENTRY._options = None diff --git a/internal/plumbing/line_stats.go b/internal/plumbing/line_stats.go new file mode 100644 index 00000000..3f3e8cce --- /dev/null +++ b/internal/plumbing/line_stats.go @@ -0,0 +1,161 @@ +package plumbing + +import ( + "unicode/utf8" + + "github.com/sergi/go-diff/diffmatchpatch" + "gopkg.in/src-d/go-git.v4" + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/object" + "gopkg.in/src-d/go-git.v4/utils/merkletrie" + "gopkg.in/src-d/hercules.v8/internal/core" +) + +// LinesStatsCalculator measures line statistics for each text file in the commit. +type LinesStatsCalculator struct { + core.NoopMerger +} + +// LineStats holds the numbers of inserted, deleted and changed lines. +type LineStats struct { + // Added is the number of added lines by a particular developer in a particular day. + Added int + // Removed is the number of removed lines by a particular developer in a particular day. + Removed int + // Changed is the number of changed lines by a particular developer in a particular day. + Changed int +} + +const ( + // DependencyLineStats is the identifier of the data provided by LinesStatsCalculator - line + // statistics for each file in the commit. + DependencyLineStats = "line_stats" +) + +// Name of this PipelineItem. Uniquely identifies the type, used for mapping keys, etc. +func (lsc *LinesStatsCalculator) Name() string { + return "LinesStats" +} + +// Provides returns the list of names of entities which are produced by this PipelineItem. +// Each produced entity will be inserted into `deps` of dependent Consume()-s according +// to this list. Also used by core.Registry to build the global map of providers. +func (lsc *LinesStatsCalculator) Provides() []string { + arr := [...]string{DependencyLineStats} + return arr[:] +} + +// Requires returns the list of names of entities which are needed by this PipelineItem. +// Each requested entity will be inserted into `deps` of Consume(). In turn, those +// entities are Provides() upstream. +func (lsc *LinesStatsCalculator) Requires() []string { + arr := [...]string{DependencyTreeChanges, DependencyBlobCache, DependencyFileDiff} + return arr[:] +} + +// ListConfigurationOptions returns the list of changeable public properties of this PipelineItem. +func (lsc *LinesStatsCalculator) ListConfigurationOptions() []core.ConfigurationOption { + return nil +} + +// Configure sets the properties previously published by ListConfigurationOptions(). +func (lsc *LinesStatsCalculator) Configure(facts map[string]interface{}) error { + return nil +} + +// Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume() +// calls. The repository which is going to be analysed is supplied as an argument. +func (lsc *LinesStatsCalculator) Initialize(repository *git.Repository) error { + return nil +} + +// Consume runs this PipelineItem on the next commit data. +// `deps` contain all the results from upstream PipelineItem-s as requested by Requires(). +// Additionally, DependencyCommit is always present there and represents the analysed *object.Commit. +// This function returns the mapping with analysis results. The keys must be the same as +// in Provides(). If there was an error, nil is returned. +func (lsc *LinesStatsCalculator) Consume(deps map[string]interface{}) (map[string]interface{}, error) { + result := map[object.ChangeEntry]LineStats{} + if deps[core.DependencyIsMerge].(bool) { + // we ignore merge commit diffs + // TODO(vmarkovtsev): handle them better + return map[string]interface{}{DependencyLineStats: result}, nil + } + treeDiff := deps[DependencyTreeChanges].(object.Changes) + cache := deps[DependencyBlobCache].(map[plumbing.Hash]*CachedBlob) + fileDiffs := deps[DependencyFileDiff].(map[string]FileDiffData) + for _, change := range treeDiff { + action, err := change.Action() + if err != nil { + return nil, err + } + switch action { + case merkletrie.Insert: + blob := cache[change.To.TreeEntry.Hash] + lines, err := blob.CountLines() + if err != nil { + // binary + continue + } + result[change.To] = LineStats{ + Added: lines, + Removed: 0, + Changed: 0, + } + case merkletrie.Delete: + blob := cache[change.From.TreeEntry.Hash] + lines, err := blob.CountLines() + if err != nil { + // binary + continue + } + result[change.From] = LineStats{ + Added: 0, + Removed: lines, + Changed: 0, + } + case merkletrie.Modify: + thisDiffs := fileDiffs[change.To.Name] + var added, removed, changed, removedPending int + for _, edit := range thisDiffs.Diffs { + switch edit.Type { + case diffmatchpatch.DiffEqual: + if removedPending > 0 { + removed += removedPending + } + removedPending = 0 + case diffmatchpatch.DiffInsert: + delta := utf8.RuneCountInString(edit.Text) + if removedPending > delta { + changed += delta + removed += removedPending - delta + } else { + changed += removedPending + added += delta - removedPending + } + removedPending = 0 + case diffmatchpatch.DiffDelete: + removedPending = utf8.RuneCountInString(edit.Text) + } + } + if removedPending > 0 { + removed += removedPending + } + result[change.To] = LineStats{ + Added: added, + Removed: removed, + Changed: changed, + } + } + } + return map[string]interface{}{DependencyLineStats: result}, nil +} + +// Fork clones this PipelineItem. +func (lsc *LinesStatsCalculator) Fork(n int) []core.PipelineItem { + return core.ForkSamePipelineItem(lsc, n) +} + +func init() { + core.Registry.Register(&LinesStatsCalculator{}) +} diff --git a/internal/plumbing/line_stats_test.go b/internal/plumbing/line_stats_test.go new file mode 100644 index 00000000..5828bcec --- /dev/null +++ b/internal/plumbing/line_stats_test.go @@ -0,0 +1,130 @@ +package plumbing_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing/object" + "gopkg.in/src-d/hercules.v8/internal/core" + items "gopkg.in/src-d/hercules.v8/internal/plumbing" + "gopkg.in/src-d/hercules.v8/internal/plumbing/identity" + "gopkg.in/src-d/hercules.v8/internal/test" + "gopkg.in/src-d/hercules.v8/internal/test/fixtures" +) + +func TestLinesStatsMeta(t *testing.T) { + ra := &items.LinesStatsCalculator{} + assert.Equal(t, ra.Name(), "LinesStats") + assert.Equal(t, len(ra.Provides()), 1) + assert.Equal(t, ra.Provides()[0], items.DependencyLineStats) + assert.Equal(t, len(ra.Requires()), 3) + assert.Equal(t, ra.Requires()[0], items.DependencyTreeChanges) + assert.Equal(t, ra.Requires()[1], items.DependencyBlobCache) + assert.Equal(t, ra.Requires()[2], items.DependencyFileDiff) + assert.Nil(t, ra.ListConfigurationOptions()) + assert.Nil(t, ra.Configure(nil)) + for _, f := range ra.Fork(10) { + assert.Equal(t, f, ra) + } +} + +func TestLinesStatsRegistration(t *testing.T) { + summoned := core.Registry.Summon((&items.LinesStatsCalculator{}).Name()) + assert.Len(t, summoned, 1) + assert.Equal(t, summoned[0].Name(), "LinesStats") + summoned = core.Registry.Summon((&items.LinesStatsCalculator{}).Provides()[0]) + assert.True(t, len(summoned) >= 1) + matched := false + for _, tp := range summoned { + matched = matched || tp.Name() == "LinesStats" + } + assert.True(t, matched) +} + +func TestLinesStatsConsume(t *testing.T) { + deps := map[string]interface{}{} + + // stage 1 + deps[identity.DependencyAuthor] = 0 + cache := map[plumbing.Hash]*items.CachedBlob{} + items.AddHash(t, cache, "291286b4ac41952cbd1389fda66420ec03c1a9fe") + items.AddHash(t, cache, "c29112dbd697ad9b401333b80c18a63951bc18d9") + items.AddHash(t, cache, "baa64828831d174f40140e4b3cfa77d1e917a2c1") + items.AddHash(t, cache, "dc248ba2b22048cc730c571a748e8ffcf7085ab9") + deps[items.DependencyBlobCache] = cache + changes := make(object.Changes, 3) + treeFrom, _ := test.Repository.TreeObject(plumbing.NewHash( + "a1eb2ea76eb7f9bfbde9b243861474421000eb96")) + treeTo, _ := test.Repository.TreeObject(plumbing.NewHash( + "994eac1cd07235bb9815e547a75c84265dea00f5")) + changes[0] = &object.Change{From: object.ChangeEntry{ + Name: "analyser.go", + Tree: treeFrom, + TreeEntry: object.TreeEntry{ + Name: "analyser.go", + Mode: 0100644, + Hash: plumbing.NewHash("dc248ba2b22048cc730c571a748e8ffcf7085ab9"), + }, + }, To: object.ChangeEntry{ + Name: "analyser2.go", + Tree: treeTo, + TreeEntry: object.TreeEntry{ + Name: "analyser2.go", + Mode: 0100644, + Hash: plumbing.NewHash("baa64828831d174f40140e4b3cfa77d1e917a2c1"), + }, + }} + changes[1] = &object.Change{From: object.ChangeEntry{}, To: object.ChangeEntry{ + Name: "cmd/hercules/main.go", + Tree: treeTo, + TreeEntry: object.TreeEntry{ + Name: "cmd/hercules/main.go", + Mode: 0100644, + Hash: plumbing.NewHash("c29112dbd697ad9b401333b80c18a63951bc18d9"), + }, + }, + } + changes[2] = &object.Change{From: object.ChangeEntry{ + Name: ".travis.yml", + Tree: treeTo, + TreeEntry: object.TreeEntry{ + Name: ".travis.yml", + Mode: 0100644, + Hash: plumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe"), + }, + }, To: object.ChangeEntry{}, + } + deps[items.DependencyTreeChanges] = changes + fd := fixtures.FileDiff() + result, err := fd.Consume(deps) + assert.Nil(t, err) + deps[items.DependencyFileDiff] = result[items.DependencyFileDiff] + deps[core.DependencyCommit], _ = test.Repository.CommitObject(plumbing.NewHash( + "cce947b98a050c6d356bc6ba95030254914027b1")) + deps[core.DependencyIsMerge] = false + lsc := &items.LinesStatsCalculator{} + result, err = lsc.Consume(deps) + assert.Nil(t, err) + stats := result[items.DependencyLineStats].(map[object.ChangeEntry]items.LineStats) + assert.Len(t, stats, 3) + nameMap := map[string]items.LineStats{} + for ch, val := range stats { + nameMap[ch.Name] = val + } + assert.Equal(t, nameMap["analyser2.go"], items.LineStats{ + Added: 628, + Removed: 9, + Changed: 67, + }) + assert.Equal(t, nameMap[".travis.yml"], items.LineStats{ + Added: 0, + Removed: 12, + Changed: 0, + }) + assert.Equal(t, nameMap["cmd/hercules/main.go"], items.LineStats{ + Added: 207, + Removed: 0, + Changed: 0, + }) +} diff --git a/leaves/devs.go b/leaves/devs.go index d85221d7..72ef29a7 100644 --- a/leaves/devs.go +++ b/leaves/devs.go @@ -5,14 +5,11 @@ import ( "io" "sort" "strings" - "unicode/utf8" "github.com/gogo/protobuf/proto" - "github.com/sergi/go-diff/diffmatchpatch" "gopkg.in/src-d/go-git.v4" "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/object" - "gopkg.in/src-d/go-git.v4/utils/merkletrie" "gopkg.in/src-d/hercules.v8/internal/core" "gopkg.in/src-d/hercules.v8/internal/pb" items "gopkg.in/src-d/hercules.v8/internal/plumbing" @@ -46,23 +43,13 @@ type DevsResult struct { reversedPeopleDict []string } -// LineStats holds the numbers of inserted, deleted and changed lines. -type LineStats struct { - // Added is the number of added lines by a particular developer in a particular day. - Added int - // Removed is the number of removed lines by a particular developer in a particular day. - Removed int - // Changed is the number of changed lines by a particular developer in a particular day. - Changed int -} - // DevDay is the statistics for a development day and a particular developer. type DevDay struct { // Commits is the number of commits made by a particular developer in a particular day. Commits int - LineStats + items.LineStats // LanguagesDetection carries fine-grained line stats per programming language. - Languages map[string]LineStats + Languages map[string]items.LineStats } const ( @@ -87,8 +74,8 @@ func (devs *DevsAnalysis) Provides() []string { // entities are Provides() upstream. func (devs *DevsAnalysis) Requires() []string { arr := [...]string{ - identity.DependencyAuthor, items.DependencyTreeChanges, items.DependencyFileDiff, - items.DependencyBlobCache, items.DependencyDay, items.DependencyLanguages} + identity.DependencyAuthor, items.DependencyTreeChanges, items.DependencyDay, + items.DependencyLanguages, items.DependencyLineStats} return arr[:] } @@ -154,7 +141,7 @@ func (devs *DevsAnalysis) Consume(deps map[string]interface{}) (map[string]inter } dd, exists := devsDay[author] if !exists { - dd = &DevDay{Languages: map[string]LineStats{}} + dd = &DevDay{Languages: map[string]items.LineStats{}} devsDay[author] = dd } dd.Commits++ @@ -163,99 +150,18 @@ func (devs *DevsAnalysis) Consume(deps map[string]interface{}) (map[string]inter // TODO(vmarkovtsev): handle them return nil, nil } - cache := deps[items.DependencyBlobCache].(map[plumbing.Hash]*items.CachedBlob) - fileDiffs := deps[items.DependencyFileDiff].(map[string]items.FileDiffData) langs := deps[items.DependencyLanguages].(map[plumbing.Hash]string) - for _, change := range treeDiff { - action, err := change.Action() - if err != nil { - return nil, err - } - switch action { - case merkletrie.Insert: - blob := cache[change.To.TreeEntry.Hash] - lines, err := blob.CountLines() - if err != nil { - // binary - continue - } - dd.Added += lines - lang := langs[change.To.TreeEntry.Hash] - langStats := dd.Languages[lang] - dd.Languages[lang] = LineStats{ - Added: langStats.Added + lines, - Removed: langStats.Removed, - Changed: langStats.Changed, - } - case merkletrie.Delete: - blob := cache[change.From.TreeEntry.Hash] - lines, err := blob.CountLines() - if err != nil { - // binary - continue - } - dd.Removed += lines - lang := langs[change.From.TreeEntry.Hash] - langStats := dd.Languages[lang] - dd.Languages[lang] = LineStats{ - Added: langStats.Added, - Removed: langStats.Removed + lines, - Changed: langStats.Changed, - } - case merkletrie.Modify: - lang := langs[change.To.TreeEntry.Hash] - thisDiffs := fileDiffs[change.To.Name] - var removedPending int - for _, edit := range thisDiffs.Diffs { - switch edit.Type { - case diffmatchpatch.DiffEqual: - if removedPending > 0 { - dd.Removed += removedPending - langStats := dd.Languages[lang] - dd.Languages[lang] = LineStats{ - Added: langStats.Added, - Removed: langStats.Removed + removedPending, - Changed: langStats.Changed, - } - } - removedPending = 0 - case diffmatchpatch.DiffInsert: - added := utf8.RuneCountInString(edit.Text) - if removedPending > added { - removed := removedPending - added - dd.Changed += added - dd.Removed += removed - langStats := dd.Languages[lang] - dd.Languages[lang] = LineStats{ - Added: langStats.Added, - Removed: langStats.Removed + removed, - Changed: langStats.Changed + added, - } - } else { - added := added - removedPending - dd.Changed += removedPending - dd.Added += added - langStats := dd.Languages[lang] - dd.Languages[lang] = LineStats{ - Added: langStats.Added + added, - Removed: langStats.Removed, - Changed: langStats.Changed + removedPending, - } - } - removedPending = 0 - case diffmatchpatch.DiffDelete: - removedPending = utf8.RuneCountInString(edit.Text) - } - } - if removedPending > 0 { - dd.Removed += removedPending - langStats := dd.Languages[lang] - dd.Languages[lang] = LineStats{ - Added: langStats.Added, - Removed: langStats.Removed + removedPending, - Changed: langStats.Changed, - } - } + lineStats := deps[items.DependencyLineStats].(map[object.ChangeEntry]items.LineStats) + for changeEntry, stats := range lineStats { + dd.Added += stats.Added + dd.Removed += stats.Removed + dd.Changed += stats.Changed + lang := langs[changeEntry.TreeEntry.Hash] + langStats := dd.Languages[lang] + dd.Languages[lang] = items.LineStats{ + Added: langStats.Added + stats.Added, + Removed: langStats.Removed + stats.Removed, + Changed: langStats.Changed + stats.Changed, } } return nil, nil @@ -300,10 +206,10 @@ func (devs *DevsAnalysis) Deserialize(pbmessage []byte) (interface{}, error) { if dev == -1 { dev = identity.AuthorMissing } - languages := map[string]LineStats{} + languages := map[string]items.LineStats{} rdd[int(dev)] = &DevDay{ Commits: int(stats.Commits), - LineStats: LineStats{ + LineStats: items.LineStats{ Added: int(stats.Stats.Added), Removed: int(stats.Stats.Removed), Changed: int(stats.Stats.Changed), @@ -311,7 +217,7 @@ func (devs *DevsAnalysis) Deserialize(pbmessage []byte) (interface{}, error) { Languages: languages, } for lang, ls := range stats.Languages { - languages[lang] = LineStats{ + languages[lang] = items.LineStats{ Added: int(ls.Added), Removed: int(ls.Removed), Changed: int(ls.Changed), @@ -378,7 +284,7 @@ func (devs *DevsAnalysis) MergeResults(r1, r2 interface{}, c1, c2 *core.CommonAn } newstats, exists := newdd[newdev] if !exists { - newstats = &DevDay{Languages: map[string]LineStats{}} + newstats = &DevDay{Languages: map[string]items.LineStats{}} newdd[newdev] = newstats } newstats.Commits += stats.Commits @@ -387,7 +293,7 @@ func (devs *DevsAnalysis) MergeResults(r1, r2 interface{}, c1, c2 *core.CommonAn newstats.Changed += stats.Changed for lang, ls := range stats.Languages { prev := newstats.Languages[lang] - newstats.Languages[lang] = LineStats{ + newstats.Languages[lang] = items.LineStats{ Added: prev.Added + ls.Added, Removed: prev.Removed + ls.Removed, Changed: prev.Changed + ls.Changed, @@ -408,7 +314,7 @@ func (devs *DevsAnalysis) MergeResults(r1, r2 interface{}, c1, c2 *core.CommonAn } newstats, exists := newdd[newdev] if !exists { - newstats = &DevDay{Languages: map[string]LineStats{}} + newstats = &DevDay{Languages: map[string]items.LineStats{}} newdd[newdev] = newstats } newstats.Commits += stats.Commits @@ -417,7 +323,7 @@ func (devs *DevsAnalysis) MergeResults(r1, r2 interface{}, c1, c2 *core.CommonAn newstats.Changed += stats.Changed for lang, ls := range stats.Languages { prev := newstats.Languages[lang] - newstats.Languages[lang] = LineStats{ + newstats.Languages[lang] = items.LineStats{ Added: prev.Added + ls.Added, Removed: prev.Removed + ls.Removed, Changed: prev.Changed + ls.Changed, diff --git a/leaves/devs_test.go b/leaves/devs_test.go index 6be01c59..7f10607f 100644 --- a/leaves/devs_test.go +++ b/leaves/devs_test.go @@ -6,11 +6,11 @@ import ( "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/assert" - gitplumbing "gopkg.in/src-d/go-git.v4/plumbing" + "gopkg.in/src-d/go-git.v4/plumbing" "gopkg.in/src-d/go-git.v4/plumbing/object" "gopkg.in/src-d/hercules.v8/internal/core" "gopkg.in/src-d/hercules.v8/internal/pb" - "gopkg.in/src-d/hercules.v8/internal/plumbing" + items "gopkg.in/src-d/hercules.v8/internal/plumbing" "gopkg.in/src-d/hercules.v8/internal/plumbing/identity" "gopkg.in/src-d/hercules.v8/internal/test" "gopkg.in/src-d/hercules.v8/internal/test/fixtures" @@ -28,13 +28,12 @@ func TestDevsMeta(t *testing.T) { d := fixtureDevs() assert.Equal(t, d.Name(), "Devs") assert.Equal(t, len(d.Provides()), 0) - assert.Equal(t, len(d.Requires()), 6) + assert.Equal(t, len(d.Requires()), 5) assert.Equal(t, d.Requires()[0], identity.DependencyAuthor) - assert.Equal(t, d.Requires()[1], plumbing.DependencyTreeChanges) - assert.Equal(t, d.Requires()[2], plumbing.DependencyFileDiff) - assert.Equal(t, d.Requires()[3], plumbing.DependencyBlobCache) - assert.Equal(t, d.Requires()[4], plumbing.DependencyDay) - assert.Equal(t, d.Requires()[5], plumbing.DependencyLanguages) + assert.Equal(t, d.Requires()[1], items.DependencyTreeChanges) + assert.Equal(t, d.Requires()[2], items.DependencyDay) + assert.Equal(t, d.Requires()[3], items.DependencyLanguages) + assert.Equal(t, d.Requires()[4], items.DependencyLineStats) assert.Equal(t, d.Flag(), "devs") assert.Len(t, d.ListConfigurationOptions(), 1) assert.Equal(t, d.ListConfigurationOptions()[0].Name, ConfigDevsConsiderEmptyCommits) @@ -78,23 +77,23 @@ func TestDevsConsumeFinalize(t *testing.T) { // stage 1 deps[identity.DependencyAuthor] = 0 - deps[plumbing.DependencyDay] = 0 - cache := map[gitplumbing.Hash]*plumbing.CachedBlob{} + deps[items.DependencyDay] = 0 + cache := map[plumbing.Hash]*items.CachedBlob{} AddHash(t, cache, "291286b4ac41952cbd1389fda66420ec03c1a9fe") AddHash(t, cache, "c29112dbd697ad9b401333b80c18a63951bc18d9") AddHash(t, cache, "baa64828831d174f40140e4b3cfa77d1e917a2c1") AddHash(t, cache, "dc248ba2b22048cc730c571a748e8ffcf7085ab9") - deps[plumbing.DependencyBlobCache] = cache - deps[plumbing.DependencyLanguages] = map[gitplumbing.Hash]string{ - gitplumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe"): "Go", - gitplumbing.NewHash("c29112dbd697ad9b401333b80c18a63951bc18d9"): "Go", - gitplumbing.NewHash("baa64828831d174f40140e4b3cfa77d1e917a2c1"): "Go", - gitplumbing.NewHash("dc248ba2b22048cc730c571a748e8ffcf7085ab9"): "Go", + deps[items.DependencyBlobCache] = cache + deps[items.DependencyLanguages] = map[plumbing.Hash]string{ + plumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe"): "Go", + plumbing.NewHash("c29112dbd697ad9b401333b80c18a63951bc18d9"): "Go", + plumbing.NewHash("baa64828831d174f40140e4b3cfa77d1e917a2c1"): "Go", + plumbing.NewHash("dc248ba2b22048cc730c571a748e8ffcf7085ab9"): "Go", } changes := make(object.Changes, 3) - treeFrom, _ := test.Repository.TreeObject(gitplumbing.NewHash( + treeFrom, _ := test.Repository.TreeObject(plumbing.NewHash( "a1eb2ea76eb7f9bfbde9b243861474421000eb96")) - treeTo, _ := test.Repository.TreeObject(gitplumbing.NewHash( + treeTo, _ := test.Repository.TreeObject(plumbing.NewHash( "994eac1cd07235bb9815e547a75c84265dea00f5")) changes[0] = &object.Change{From: object.ChangeEntry{ Name: "analyser.go", @@ -102,7 +101,7 @@ func TestDevsConsumeFinalize(t *testing.T) { TreeEntry: object.TreeEntry{ Name: "analyser.go", Mode: 0100644, - Hash: gitplumbing.NewHash("dc248ba2b22048cc730c571a748e8ffcf7085ab9"), + Hash: plumbing.NewHash("dc248ba2b22048cc730c571a748e8ffcf7085ab9"), }, }, To: object.ChangeEntry{ Name: "analyser.go", @@ -110,7 +109,7 @@ func TestDevsConsumeFinalize(t *testing.T) { TreeEntry: object.TreeEntry{ Name: "analyser.go", Mode: 0100644, - Hash: gitplumbing.NewHash("baa64828831d174f40140e4b3cfa77d1e917a2c1"), + Hash: plumbing.NewHash("baa64828831d174f40140e4b3cfa77d1e917a2c1"), }, }} changes[1] = &object.Change{From: object.ChangeEntry{}, To: object.ChangeEntry{ @@ -119,7 +118,7 @@ func TestDevsConsumeFinalize(t *testing.T) { TreeEntry: object.TreeEntry{ Name: "cmd/hercules/main.go", Mode: 0100644, - Hash: gitplumbing.NewHash("c29112dbd697ad9b401333b80c18a63951bc18d9"), + Hash: plumbing.NewHash("c29112dbd697ad9b401333b80c18a63951bc18d9"), }, }, } @@ -129,18 +128,23 @@ func TestDevsConsumeFinalize(t *testing.T) { TreeEntry: object.TreeEntry{ Name: ".travis.yml", Mode: 0100644, - Hash: gitplumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe"), + Hash: plumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe"), }, }, } - deps[plumbing.DependencyTreeChanges] = changes + deps[items.DependencyTreeChanges] = changes fd := fixtures.FileDiff() result, err := fd.Consume(deps) assert.Nil(t, err) - deps[plumbing.DependencyFileDiff] = result[plumbing.DependencyFileDiff] - deps[core.DependencyCommit], _ = test.Repository.CommitObject(gitplumbing.NewHash( + deps[items.DependencyFileDiff] = result[items.DependencyFileDiff] + deps[core.DependencyCommit], _ = test.Repository.CommitObject(plumbing.NewHash( "cce947b98a050c6d356bc6ba95030254914027b1")) deps[core.DependencyIsMerge] = false + lsc := &items.LinesStatsCalculator{} + lscres, err := lsc.Consume(deps) + assert.Nil(t, err) + deps[items.DependencyLineStats] = lscres[items.DependencyLineStats] + result, err = devs.Consume(deps) assert.Nil(t, result) assert.Nil(t, err) @@ -157,6 +161,9 @@ func TestDevsConsumeFinalize(t *testing.T) { assert.Equal(t, dev.Languages["Go"].Changed, 67) deps[core.DependencyIsMerge] = true + lscres, err = lsc.Consume(deps) + assert.Nil(t, err) + deps[items.DependencyLineStats] = lscres[items.DependencyLineStats] result, err = devs.Consume(deps) assert.Nil(t, result) assert.Nil(t, err) @@ -174,6 +181,9 @@ func TestDevsConsumeFinalize(t *testing.T) { deps[core.DependencyIsMerge] = false deps[identity.DependencyAuthor] = 1 + lscres, err = lsc.Consume(deps) + assert.Nil(t, err) + deps[items.DependencyLineStats] = lscres[items.DependencyLineStats] result, err = devs.Consume(deps) assert.Nil(t, result) assert.Nil(t, err) @@ -218,7 +228,7 @@ func TestDevsConsumeFinalize(t *testing.T) { assert.Equal(t, dev.Languages["Go"].Removed, 9*2) assert.Equal(t, dev.Languages["Go"].Changed, 67*2) - deps[plumbing.DependencyDay] = 1 + deps[items.DependencyDay] = 1 result, err = devs.Consume(deps) assert.Nil(t, result) assert.Nil(t, err) @@ -253,10 +263,14 @@ func TestDevsConsumeFinalize(t *testing.T) { assert.Equal(t, dev.Languages["Go"].Changed, 67) } +func ls(added, removed, changed int) items.LineStats { + return items.LineStats{Added: added, Removed: removed, Changed: changed} +} + func TestDevsFinalize(t *testing.T) { devs := fixtureDevs() devs.days[1] = map[int]*DevDay{} - devs.days[1][1] = &DevDay{10, LineStats{20, 30, 40}, nil} + devs.days[1][1] = &DevDay{10, ls(20, 30, 40), nil} x := devs.Finalize().(DevsResult) assert.Equal(t, x.Days, devs.days) assert.Equal(t, x.reversedPeopleDict, devs.reversedPeopleDict) @@ -271,12 +285,12 @@ func TestDevsFork(t *testing.T) { func TestDevsSerialize(t *testing.T) { devs := fixtureDevs() devs.days[1] = map[int]*DevDay{} - devs.days[1][0] = &DevDay{10, LineStats{20, 30, 40}, map[string]LineStats{"Go": {2, 3, 4}}} - devs.days[1][1] = &DevDay{1, LineStats{2, 3, 4}, map[string]LineStats{"Go": {25, 35, 45}}} + devs.days[1][0] = &DevDay{10, ls(20, 30, 40), map[string]items.LineStats{"Go": ls(2, 3, 4)}} + devs.days[1][1] = &DevDay{1, ls(2, 3, 4), map[string]items.LineStats{"Go": ls(25, 35, 45)}} devs.days[10] = map[int]*DevDay{} - devs.days[10][0] = &DevDay{11, LineStats{21, 31, 41}, map[string]LineStats{"": {12, 13, 14}}} + devs.days[10][0] = &DevDay{11, ls(21, 31, 41), map[string]items.LineStats{"": ls(12, 13, 14)}} devs.days[10][identity.AuthorMissing] = &DevDay{ - 100, LineStats{200, 300, 400}, map[string]LineStats{"Go": {32, 33, 34}}} + 100, ls(200, 300, 400), map[string]items.LineStats{"Go": ls(32, 33, 34)}} res := devs.Finalize().(DevsResult) buffer := &bytes.Buffer{} err := devs.Serialize(res, false, buffer) @@ -297,7 +311,7 @@ func TestDevsSerialize(t *testing.T) { err = devs.Serialize(res, true, buffer) assert.Nil(t, err) msg := pb.DevsAnalysisResults{} - proto.Unmarshal(buffer.Bytes(), &msg) + assert.Nil(t, proto.Unmarshal(buffer.Bytes(), &msg)) assert.Equal(t, msg.DevIndex, devs.reversedPeopleDict) assert.Len(t, msg.Days, 2) assert.Len(t, msg.Days[1].Devs, 2) @@ -319,12 +333,12 @@ func TestDevsSerialize(t *testing.T) { func TestDevsDeserialize(t *testing.T) { devs := fixtureDevs() devs.days[1] = map[int]*DevDay{} - devs.days[1][0] = &DevDay{10, LineStats{20, 30, 40}, map[string]LineStats{"Go": {12, 13, 14}}} - devs.days[1][1] = &DevDay{1, LineStats{2, 3, 4}, map[string]LineStats{"Go": {22, 23, 24}}} + devs.days[1][0] = &DevDay{10, ls(20, 30, 40), map[string]items.LineStats{"Go": ls(12, 13, 14)}} + devs.days[1][1] = &DevDay{1, ls(2, 3, 4), map[string]items.LineStats{"Go": ls(22, 23, 24)}} devs.days[10] = map[int]*DevDay{} - devs.days[10][0] = &DevDay{11, LineStats{21, 31, 41}, map[string]LineStats{"Go": {32, 33, 34}}} + devs.days[10][0] = &DevDay{11, ls(21, 31, 41), map[string]items.LineStats{"Go": ls(32, 33, 34)}} devs.days[10][identity.AuthorMissing] = &DevDay{ - 100, LineStats{200, 300, 400}, map[string]LineStats{"Go": {42, 43, 44}}} + 100, ls(200, 300, 400), map[string]items.LineStats{"Go": ls(42, 43, 44)}} res := devs.Finalize().(DevsResult) buffer := &bytes.Buffer{} err := devs.Serialize(res, true, buffer) @@ -343,29 +357,29 @@ func TestDevsMergeResults(t *testing.T) { reversedPeopleDict: people1[:], } r1.Days[1] = map[int]*DevDay{} - r1.Days[1][0] = &DevDay{10, LineStats{20, 30, 40}, map[string]LineStats{"Go": {12, 13, 14}}} - r1.Days[1][1] = &DevDay{1, LineStats{2, 3, 4}, map[string]LineStats{"Go": {22, 23, 24}}} + r1.Days[1][0] = &DevDay{10, ls(20, 30, 40), map[string]items.LineStats{"Go": ls(12, 13, 14)}} + r1.Days[1][1] = &DevDay{1, ls(2, 3, 4), map[string]items.LineStats{"Go": ls(22, 23, 24)}} r1.Days[10] = map[int]*DevDay{} - r1.Days[10][0] = &DevDay{11, LineStats{21, 31, 41}, nil} + r1.Days[10][0] = &DevDay{11, ls(21, 31, 41), nil} r1.Days[10][identity.AuthorMissing] = &DevDay{ - 100, LineStats{200, 300, 400}, map[string]LineStats{"Go": {32, 33, 34}}} + 100, ls(200, 300, 400), map[string]items.LineStats{"Go": ls(32, 33, 34)}} r1.Days[11] = map[int]*DevDay{} - r1.Days[11][1] = &DevDay{10, LineStats{20, 30, 40}, map[string]LineStats{"Go": {42, 43, 44}}} + r1.Days[11][1] = &DevDay{10, ls(20, 30, 40), map[string]items.LineStats{"Go": ls(42, 43, 44)}} r2 := DevsResult{ Days: map[int]map[int]*DevDay{}, reversedPeopleDict: people2[:], } r2.Days[1] = map[int]*DevDay{} - r2.Days[1][0] = &DevDay{10, LineStats{20, 30, 40}, map[string]LineStats{"Go": {12, 13, 14}}} - r2.Days[1][1] = &DevDay{1, LineStats{2, 3, 4}, map[string]LineStats{"Go": {22, 23, 24}}} + r2.Days[1][0] = &DevDay{10, ls(20, 30, 40), map[string]items.LineStats{"Go": ls(12, 13, 14)}} + r2.Days[1][1] = &DevDay{1, ls(2, 3, 4), map[string]items.LineStats{"Go": ls(22, 23, 24)}} r2.Days[2] = map[int]*DevDay{} - r2.Days[2][0] = &DevDay{11, LineStats{21, 31, 41}, map[string]LineStats{"Go": {32, 33, 34}}} + r2.Days[2][0] = &DevDay{11, ls(21, 31, 41), map[string]items.LineStats{"Go": ls(32, 33, 34)}} r2.Days[2][identity.AuthorMissing] = &DevDay{ - 100, LineStats{200, 300, 400}, map[string]LineStats{"Go": {42, 43, 44}}} + 100, ls(200, 300, 400), map[string]items.LineStats{"Go": ls(42, 43, 44)}} r2.Days[10] = map[int]*DevDay{} - r2.Days[10][0] = &DevDay{11, LineStats{21, 31, 41}, map[string]LineStats{"Go": {52, 53, 54}}} + r2.Days[10][0] = &DevDay{11, ls(21, 31, 41), map[string]items.LineStats{"Go": ls(52, 53, 54)}} r2.Days[10][identity.AuthorMissing] = &DevDay{ - 100, LineStats{200, 300, 400}, map[string]LineStats{"Go": {62, 63, 64}}} + 100, ls(200, 300, 400), map[string]items.LineStats{"Go": ls(62, 63, 64)}} devs := fixtureDevs() rm := devs.MergeResults(r1, r2, nil, nil).(DevsResult) @@ -373,20 +387,20 @@ func TestDevsMergeResults(t *testing.T) { assert.Equal(t, rm.reversedPeopleDict, peoplerm[:]) assert.Len(t, rm.Days, 4) assert.Equal(t, rm.Days[11], map[int]*DevDay{ - 1: {10, LineStats{20, 30, 40}, map[string]LineStats{"Go": {42, 43, 44}}}}) + 1: {10, ls(20, 30, 40), map[string]items.LineStats{"Go": ls(42, 43, 44)}}}) assert.Equal(t, rm.Days[2], map[int]*DevDay{ - identity.AuthorMissing: {100, LineStats{200, 300, 400}, map[string]LineStats{"Go": {42, 43, 44}}}, - 2: {11, LineStats{21, 31, 41}, map[string]LineStats{"Go": {32, 33, 34}}}, + identity.AuthorMissing: {100, ls(200, 300, 400), map[string]items.LineStats{"Go": ls(42, 43, 44)}}, + 2: {11, ls(21, 31, 41), map[string]items.LineStats{"Go": ls(32, 33, 34)}}, }) assert.Equal(t, rm.Days[1], map[int]*DevDay{ - 0: {11, LineStats{22, 33, 44}, map[string]LineStats{"Go": {34, 36, 38}}}, - 1: {1, LineStats{2, 3, 4}, map[string]LineStats{"Go": {22, 23, 24}}}, - 2: {10, LineStats{20, 30, 40}, map[string]LineStats{"Go": {12, 13, 14}}}, + 0: {11, ls(22, 33, 44), map[string]items.LineStats{"Go": ls(34, 36, 38)}}, + 1: {1, ls(2, 3, 4), map[string]items.LineStats{"Go": ls(22, 23, 24)}}, + 2: {10, ls(20, 30, 40), map[string]items.LineStats{"Go": ls(12, 13, 14)}}, }) assert.Equal(t, rm.Days[10], map[int]*DevDay{ - 0: {11, LineStats{21, 31, 41}, map[string]LineStats{}}, - 2: {11, LineStats{21, 31, 41}, map[string]LineStats{"Go": {52, 53, 54}}}, + 0: {11, ls(21, 31, 41), map[string]items.LineStats{}}, + 2: {11, ls(21, 31, 41), map[string]items.LineStats{"Go": ls(52, 53, 54)}}, identity.AuthorMissing: { - 100 * 2, LineStats{200 * 2, 300 * 2, 400 * 2}, map[string]LineStats{"Go": {94, 96, 98}}}, + 100 * 2, ls(200*2, 300*2, 400*2), map[string]items.LineStats{"Go": ls(94, 96, 98)}}, }) } diff --git a/leaves/file_history.go b/leaves/file_history.go index b11311e3..03e671ab 100644 --- a/leaves/file_history.go +++ b/leaves/file_history.go @@ -3,6 +3,7 @@ package leaves import ( "fmt" "io" + "log" "sort" "strings" @@ -14,65 +15,77 @@ import ( "gopkg.in/src-d/hercules.v8/internal/core" "gopkg.in/src-d/hercules.v8/internal/pb" items "gopkg.in/src-d/hercules.v8/internal/plumbing" + "gopkg.in/src-d/hercules.v8/internal/plumbing/identity" ) -// FileHistory contains the intermediate state which is mutated by Consume(). It should implement +// FileHistoryAnalysis contains the intermediate state which is mutated by Consume(). It should implement // LeafPipelineItem. -type FileHistory struct { +type FileHistoryAnalysis struct { core.NoopMerger core.OneShotMergeProcessor - files map[string][]plumbing.Hash + files map[string]*FileHistory + lastCommit *object.Commit } // FileHistoryResult is returned by Finalize() and represents the analysis result. type FileHistoryResult struct { - Files map[string][]plumbing.Hash + Files map[string]FileHistory +} + +// FileHistory is the gathered stats about a particular file. +type FileHistory struct { + // Hashes is the list of commit hashes which changed this file. + Hashes []plumbing.Hash + // People is the mapping from developers to the number of lines they altered. + People map[int]items.LineStats } // Name of this PipelineItem. Uniquely identifies the type, used for mapping keys, etc. -func (history *FileHistory) Name() string { - return "FileHistory" +func (history *FileHistoryAnalysis) Name() string { + return "FileHistoryAnalysis" } // Provides returns the list of names of entities which are produced by this PipelineItem. // Each produced entity will be inserted into `deps` of dependent Consume()-s according // to this list. Also used by core.Registry to build the global map of providers. -func (history *FileHistory) Provides() []string { +func (history *FileHistoryAnalysis) Provides() []string { return []string{} } // Requires returns the list of names of entities which are needed by this PipelineItem. // Each requested entity will be inserted into `deps` of Consume(). In turn, those // entities are Provides() upstream. -func (history *FileHistory) Requires() []string { - arr := [...]string{items.DependencyTreeChanges} +func (history *FileHistoryAnalysis) Requires() []string { + arr := [...]string{items.DependencyTreeChanges, items.DependencyLineStats, identity.DependencyAuthor} return arr[:] } // ListConfigurationOptions returns the list of changeable public properties of this PipelineItem. -func (history *FileHistory) ListConfigurationOptions() []core.ConfigurationOption { +func (history *FileHistoryAnalysis) ListConfigurationOptions() []core.ConfigurationOption { return []core.ConfigurationOption{} } // Flag for the command line switch which enables this analysis. -func (history *FileHistory) Flag() string { +func (history *FileHistoryAnalysis) Flag() string { return "file-history" } // Description returns the text which explains what the analysis is doing. -func (history *FileHistory) Description() string { - return "Each file path is mapped to the list of commits which involve that file." +func (history *FileHistoryAnalysis) Description() string { + return "Each file path is mapped to the list of commits which touch that file and the mapping " + + "from involved developers to the corresponding line statistics: how many lines were added, " + + "removed and changed throughout the whole history." } // Configure sets the properties previously published by ListConfigurationOptions(). -func (history *FileHistory) Configure(facts map[string]interface{}) error { +func (history *FileHistoryAnalysis) Configure(facts map[string]interface{}) error { return nil } // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume() // calls. The repository which is going to be analysed is supplied as an argument. -func (history *FileHistory) Initialize(repository *git.Repository) error { - history.files = map[string][]plumbing.Hash{} +func (history *FileHistoryAnalysis) Initialize(repository *git.Repository) error { + history.files = map[string]*FileHistory{} history.OneShotMergeProcessor.Initialize() return nil } @@ -82,46 +95,91 @@ func (history *FileHistory) Initialize(repository *git.Repository) error { // Additionally, DependencyCommit is always present there and represents the analysed *object.Commit. // This function returns the mapping with analysis results. The keys must be the same as // in Provides(). If there was an error, nil is returned. -func (history *FileHistory) Consume(deps map[string]interface{}) (map[string]interface{}, error) { - if !history.ShouldConsumeCommit(deps) { +func (history *FileHistoryAnalysis) Consume(deps map[string]interface{}) (map[string]interface{}, error) { + if deps[core.DependencyIsMerge].(bool) { + // we ignore merge commits + // TODO(vmarkovtsev): handle them better return nil, nil } - commit := deps[core.DependencyCommit].(*object.Commit).Hash + history.lastCommit = deps[core.DependencyCommit].(*object.Commit) + commit := history.lastCommit.Hash changes := deps[items.DependencyTreeChanges].(object.Changes) for _, change := range changes { action, _ := change.Action() + var fh *FileHistory + if action != merkletrie.Delete { + fh = history.files[change.To.Name] + } else { + fh = history.files[change.From.Name] + } + if fh == nil { + fh = &FileHistory{} + history.files[change.To.Name] = fh + } switch action { case merkletrie.Insert: - hashes := make([]plumbing.Hash, 1) - hashes[0] = commit - history.files[change.To.Name] = hashes + fh.Hashes = []plumbing.Hash{commit} case merkletrie.Delete: - delete(history.files, change.From.Name) + fh.Hashes = append(fh.Hashes, commit) case merkletrie.Modify: - hashes := history.files[change.From.Name] + hashes := history.files[change.From.Name].Hashes if change.From.Name != change.To.Name { delete(history.files, change.From.Name) } hashes = append(hashes, commit) - history.files[change.To.Name] = hashes + fh.Hashes = hashes + } + } + lineStats := deps[items.DependencyLineStats].(map[object.ChangeEntry]items.LineStats) + author := deps[identity.DependencyAuthor].(int) + for changeEntry, stats := range lineStats { + file := history.files[changeEntry.Name] + if file == nil { + file = &FileHistory{} + history.files[changeEntry.Name] = file + } + people := file.People + if people == nil { + people = map[int]items.LineStats{} + file.People = people + } + oldStats := people[author] + people[author] = items.LineStats{ + Added: oldStats.Added + stats.Added, + Removed: oldStats.Removed + stats.Removed, + Changed: oldStats.Changed + stats.Changed, } } return nil, nil } // Finalize returns the result of the analysis. Further Consume() calls are not expected. -func (history *FileHistory) Finalize() interface{} { - return FileHistoryResult{Files: history.files} +func (history *FileHistoryAnalysis) Finalize() interface{} { + files := map[string]FileHistory{} + fileIter, err := history.lastCommit.Files() + if err != nil { + log.Panicf("Failed to iterate files of %s", history.lastCommit.Hash.String()) + } + err = fileIter.ForEach(func(file *object.File) error { + if fh := history.files[file.Name]; fh != nil { + files[file.Name] = *fh + } + return nil + }) + if err != nil { + log.Panicf("Failed to iterate files of %s", history.lastCommit.Hash.String()) + } + return FileHistoryResult{Files: files} } // Fork clones this PipelineItem. -func (history *FileHistory) Fork(n int) []core.PipelineItem { +func (history *FileHistoryAnalysis) Fork(n int) []core.PipelineItem { return core.ForkSamePipelineItem(history, n) } // Serialize converts the analysis result as returned by Finalize() to text or bytes. // The text format is YAML and the bytes format is Protocol Buffers. -func (history *FileHistory) Serialize(result interface{}, binary bool, writer io.Writer) error { +func (history *FileHistoryAnalysis) Serialize(result interface{}, binary bool, writer io.Writer) error { historyResult := result.(FileHistoryResult) if binary { return history.serializeBinary(&historyResult, writer) @@ -130,7 +188,7 @@ func (history *FileHistory) Serialize(result interface{}, binary bool, writer io return nil } -func (history *FileHistory) serializeText(result *FileHistoryResult, writer io.Writer) { +func (history *FileHistoryAnalysis) serializeText(result *FileHistoryResult, writer io.Writer) { keys := make([]string, len(result.Files)) i := 0 for key := range result.Files { @@ -139,27 +197,44 @@ func (history *FileHistory) serializeText(result *FileHistoryResult, writer io.W } sort.Strings(keys) for _, key := range keys { - hashes := result.Files[key] + fmt.Fprintf(writer, " - %s:\n", key) + file := result.Files[key] + hashes := file.Hashes strhashes := make([]string, len(hashes)) for i, hash := range hashes { strhashes[i] = "\"" + hash.String() + "\"" } - fmt.Fprintf(writer, " - %s: [%s]\n", key, strings.Join(strhashes, ",")) + sort.Strings(strhashes) + fmt.Fprintf(writer, " commits: [%s]\n", strings.Join(strhashes, ",")) + strpeople := make([]string, 0, len(file.People)) + for key, val := range file.People { + strpeople = append(strpeople, fmt.Sprintf("%d:[%d,%d,%d]", key, val.Added, val.Removed, val.Changed)) + } + sort.Strings(strpeople) + fmt.Fprintf(writer, " people: {%s}\n", strings.Join(strpeople, ",")) } } -func (history *FileHistory) serializeBinary(result *FileHistoryResult, writer io.Writer) error { +func (history *FileHistoryAnalysis) serializeBinary(result *FileHistoryResult, writer io.Writer) error { message := pb.FileHistoryResultMessage{ Files: map[string]*pb.FileHistory{}, } for key, vals := range result.Files { - hashes := &pb.FileHistory{ - Commits: make([]string, len(vals)), + fh := &pb.FileHistory{ + Commits: make([]string, len(vals.Hashes)), + ChangesByDeveloper: map[int32]*pb.LineStats{}, } - for i, hash := range vals { - hashes.Commits[i] = hash.String() + for i, hash := range vals.Hashes { + fh.Commits[i] = hash.String() + } + for key, val := range vals.People { + fh.ChangesByDeveloper[int32(key)] = &pb.LineStats{ + Added: int32(val.Added), + Removed: int32(val.Removed), + Changed: int32(val.Changed), + } } - message.Files[key] = hashes + message.Files[key] = fh } serialized, err := proto.Marshal(&message) if err != nil { @@ -170,5 +245,5 @@ func (history *FileHistory) serializeBinary(result *FileHistoryResult, writer io } func init() { - core.Registry.Register(&FileHistory{}) + core.Registry.Register(&FileHistoryAnalysis{}) } diff --git a/leaves/file_history_test.go b/leaves/file_history_test.go index 7cea6f44..3e998962 100644 --- a/leaves/file_history_test.go +++ b/leaves/file_history_test.go @@ -11,33 +11,37 @@ import ( "gopkg.in/src-d/hercules.v8/internal/core" "gopkg.in/src-d/hercules.v8/internal/pb" items "gopkg.in/src-d/hercules.v8/internal/plumbing" + "gopkg.in/src-d/hercules.v8/internal/plumbing/identity" "gopkg.in/src-d/hercules.v8/internal/test" + "gopkg.in/src-d/hercules.v8/internal/test/fixtures" ) -func fixtureFileHistory() *FileHistory { - fh := FileHistory{} +func fixtureFileHistory() *FileHistoryAnalysis { + fh := FileHistoryAnalysis{} fh.Initialize(test.Repository) return &fh } func TestFileHistoryMeta(t *testing.T) { fh := fixtureFileHistory() - assert.Equal(t, fh.Name(), "FileHistory") + assert.Equal(t, fh.Name(), "FileHistoryAnalysis") assert.Equal(t, len(fh.Provides()), 0) - assert.Equal(t, len(fh.Requires()), 1) + assert.Equal(t, len(fh.Requires()), 3) assert.Equal(t, fh.Requires()[0], items.DependencyTreeChanges) + assert.Equal(t, fh.Requires()[1], items.DependencyLineStats) + assert.Equal(t, fh.Requires()[2], identity.DependencyAuthor) assert.Len(t, fh.ListConfigurationOptions(), 0) - fh.Configure(nil) + assert.Nil(t, fh.Configure(nil)) } func TestFileHistoryRegistration(t *testing.T) { - summoned := core.Registry.Summon((&FileHistory{}).Name()) + summoned := core.Registry.Summon((&FileHistoryAnalysis{}).Name()) assert.Len(t, summoned, 1) - assert.Equal(t, summoned[0].Name(), "FileHistory") + assert.Equal(t, summoned[0].Name(), "FileHistoryAnalysis") leaves := core.Registry.GetLeaves() matched := false for _, tp := range leaves { - if tp.Flag() == (&FileHistory{}).Flag() { + if tp.Flag() == (&FileHistoryAnalysis{}).Flag() { matched = true break } @@ -46,8 +50,95 @@ func TestFileHistoryRegistration(t *testing.T) { } func TestFileHistoryConsume(t *testing.T) { + fh, deps := bakeFileHistoryForSerialization(t) + validate := func() { + assert.Len(t, fh.files, 3) + assert.Equal(t, fh.files["cmd/hercules/main.go"].People, + map[int]items.LineStats{1: ls(0, 207, 0)}) + assert.Equal(t, fh.files[".travis.yml"].People, map[int]items.LineStats{1: ls(12, 0, 0)}) + assert.Equal(t, fh.files["analyser.go"].People, map[int]items.LineStats{1: ls(628, 9, 67)}) + assert.Len(t, fh.files["analyser.go"].Hashes, 2) + assert.Equal(t, fh.files["analyser.go"].Hashes[0], plumbing.NewHash( + "ffffffffffffffffffffffffffffffffffffffff")) + assert.Equal(t, fh.files["analyser.go"].Hashes[1], plumbing.NewHash( + "2b1ed978194a94edeabbca6de7ff3b5771d4d665")) + assert.Len(t, fh.files[".travis.yml"].Hashes, 1) + assert.Equal(t, fh.files[".travis.yml"].Hashes[0], plumbing.NewHash( + "2b1ed978194a94edeabbca6de7ff3b5771d4d665")) + assert.Len(t, fh.files["cmd/hercules/main.go"].Hashes, 2) + assert.Equal(t, fh.files["cmd/hercules/main.go"].Hashes[0], plumbing.NewHash( + "0000000000000000000000000000000000000000")) + assert.Equal(t, fh.files["cmd/hercules/main.go"].Hashes[1], plumbing.NewHash( + "2b1ed978194a94edeabbca6de7ff3b5771d4d665")) + } + validate() + res := fh.Finalize().(FileHistoryResult) + assert.Equal(t, 2, len(res.Files)) + for key, val := range res.Files { + assert.Equal(t, val, *fh.files[key]) + } + deps[core.DependencyIsMerge] = true + cres, err := fh.Consume(deps) + assert.Nil(t, cres) + assert.Nil(t, err) + validate() + fh.lastCommit = &object.Commit{} + assert.Panics(t, func() { fh.Finalize() }) +} + +func TestFileHistoryFork(t *testing.T) { + fh1 := fixtureFileHistory() + clones := fh1.Fork(1) + assert.Len(t, clones, 1) + fh2 := clones[0].(*FileHistoryAnalysis) + assert.True(t, fh1 == fh2) + fh1.Merge([]core.PipelineItem{fh2}) +} + +func TestFileHistorySerializeText(t *testing.T) { + fh, _ := bakeFileHistoryForSerialization(t) + res := fh.Finalize().(FileHistoryResult) + buffer := &bytes.Buffer{} + assert.Nil(t, fh.Serialize(res, false, buffer)) + assert.Equal(t, buffer.String(), ` - .travis.yml: + commits: ["2b1ed978194a94edeabbca6de7ff3b5771d4d665"] + people: {1:[12,0,0]} + - cmd/hercules/main.go: + commits: ["0000000000000000000000000000000000000000","2b1ed978194a94edeabbca6de7ff3b5771d4d665"] + people: {1:[0,207,0]} +`) +} + +func TestFileHistorySerializeBinary(t *testing.T) { + fh, _ := bakeFileHistoryForSerialization(t) + res := fh.Finalize().(FileHistoryResult) + buffer := &bytes.Buffer{} + assert.Nil(t, fh.Serialize(res, true, buffer)) + msg := pb.FileHistoryResultMessage{} + assert.Nil(t, proto.Unmarshal(buffer.Bytes(), &msg)) + assert.Len(t, msg.Files, 2) + assert.Len(t, msg.Files[".travis.yml"].Commits, 1) + assert.Equal(t, msg.Files[".travis.yml"].Commits[0], "2b1ed978194a94edeabbca6de7ff3b5771d4d665") + assert.Len(t, msg.Files["cmd/hercules/main.go"].Commits, 2) + assert.Equal(t, msg.Files["cmd/hercules/main.go"].Commits[0], + "0000000000000000000000000000000000000000") + assert.Equal(t, msg.Files["cmd/hercules/main.go"].Commits[1], + "2b1ed978194a94edeabbca6de7ff3b5771d4d665") + assert.Equal(t, msg.Files[".travis.yml"].ChangesByDeveloper, + map[int32]*pb.LineStats{1: {Added: 12, Removed: 0, Changed: 0}}) + assert.Equal(t, msg.Files["cmd/hercules/main.go"].ChangesByDeveloper, + map[int32]*pb.LineStats{1: {Added: 0, Removed: 207, Changed: 0}}) +} + +func bakeFileHistoryForSerialization(t *testing.T) (*FileHistoryAnalysis, map[string]interface{}) { fh := fixtureFileHistory() deps := map[string]interface{}{} + cache := map[plumbing.Hash]*items.CachedBlob{} + AddHash(t, cache, "291286b4ac41952cbd1389fda66420ec03c1a9fe") + AddHash(t, cache, "c29112dbd697ad9b401333b80c18a63951bc18d9") + AddHash(t, cache, "baa64828831d174f40140e4b3cfa77d1e917a2c1") + AddHash(t, cache, "dc248ba2b22048cc730c571a748e8ffcf7085ab9") + deps[items.DependencyBlobCache] = cache changes := make(object.Changes, 3) treeFrom, _ := test.Repository.TreeObject(plumbing.NewHash( "a1eb2ea76eb7f9bfbde9b243861474421000eb96")) @@ -94,88 +185,23 @@ func TestFileHistoryConsume(t *testing.T) { commit, _ := test.Repository.CommitObject(plumbing.NewHash( "2b1ed978194a94edeabbca6de7ff3b5771d4d665")) deps[core.DependencyCommit] = commit - fh.files["cmd/hercules/main.go"] = []plumbing.Hash{plumbing.NewHash( - "0000000000000000000000000000000000000000")} - fh.files["analyser.go"] = []plumbing.Hash{plumbing.NewHash( - "ffffffffffffffffffffffffffffffffffffffff")} - fh.Consume(deps) - assert.Len(t, fh.files, 2) - assert.Nil(t, fh.files["cmd/hercules/main.go"]) - assert.Len(t, fh.files["analyser.go"], 2) - assert.Equal(t, fh.files["analyser.go"][0], plumbing.NewHash( - "ffffffffffffffffffffffffffffffffffffffff")) - assert.Equal(t, fh.files["analyser.go"][1], plumbing.NewHash( - "2b1ed978194a94edeabbca6de7ff3b5771d4d665")) - assert.Len(t, fh.files[".travis.yml"], 1) - assert.Equal(t, fh.files[".travis.yml"][0], plumbing.NewHash( - "2b1ed978194a94edeabbca6de7ff3b5771d4d665")) - res := fh.Finalize().(FileHistoryResult) - assert.Equal(t, fh.files, res.Files) -} - -func TestFileHistoryFork(t *testing.T) { - fh1 := fixtureFileHistory() - clones := fh1.Fork(1) - assert.Len(t, clones, 1) - fh2 := clones[0].(*FileHistory) - assert.True(t, fh1 == fh2) - fh1.Merge([]core.PipelineItem{fh2}) -} + deps[core.DependencyIsMerge] = false + deps[identity.DependencyAuthor] = 1 + fd := fixtures.FileDiff() + result, err := fd.Consume(deps) + assert.Nil(t, err) + deps[items.DependencyFileDiff] = result[items.DependencyFileDiff] -func TestFileHistorySerializeText(t *testing.T) { - fh := fixtureFileHistory() - deps := map[string]interface{}{} - changes := make(object.Changes, 1) - treeTo, _ := test.Repository.TreeObject(plumbing.NewHash( - "994eac1cd07235bb9815e547a75c84265dea00f5")) - changes[0] = &object.Change{From: object.ChangeEntry{}, To: object.ChangeEntry{ - Name: ".travis.yml", - Tree: treeTo, - TreeEntry: object.TreeEntry{ - Name: ".travis.yml", - Mode: 0100644, - Hash: plumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe"), - }, - }, - } - deps[items.DependencyTreeChanges] = changes - commit, _ := test.Repository.CommitObject(plumbing.NewHash( - "2b1ed978194a94edeabbca6de7ff3b5771d4d665")) - deps[core.DependencyCommit] = commit - fh.Consume(deps) - res := fh.Finalize().(FileHistoryResult) - buffer := &bytes.Buffer{} - fh.Serialize(res, false, buffer) - assert.Equal(t, buffer.String(), " - .travis.yml: [\"2b1ed978194a94edeabbca6de7ff3b5771d4d665\"]\n") -} + lineStats, err := (&items.LinesStatsCalculator{}).Consume(deps) + assert.Nil(t, err) + deps[items.DependencyLineStats] = lineStats[items.DependencyLineStats] -func TestFileHistorySerializeBinary(t *testing.T) { - fh := fixtureFileHistory() - deps := map[string]interface{}{} - changes := make(object.Changes, 1) - treeTo, _ := test.Repository.TreeObject(plumbing.NewHash( - "994eac1cd07235bb9815e547a75c84265dea00f5")) - changes[0] = &object.Change{From: object.ChangeEntry{}, To: object.ChangeEntry{ - Name: ".travis.yml", - Tree: treeTo, - TreeEntry: object.TreeEntry{ - Name: ".travis.yml", - Mode: 0100644, - Hash: plumbing.NewHash("291286b4ac41952cbd1389fda66420ec03c1a9fe"), - }, - }, - } - deps[items.DependencyTreeChanges] = changes - commit, _ := test.Repository.CommitObject(plumbing.NewHash( - "2b1ed978194a94edeabbca6de7ff3b5771d4d665")) - deps[core.DependencyCommit] = commit - fh.Consume(deps) - res := fh.Finalize().(FileHistoryResult) - buffer := &bytes.Buffer{} - fh.Serialize(res, true, buffer) - msg := pb.FileHistoryResultMessage{} - proto.Unmarshal(buffer.Bytes(), &msg) - assert.Len(t, msg.Files, 1) - assert.Len(t, msg.Files[".travis.yml"].Commits, 1) - assert.Equal(t, msg.Files[".travis.yml"].Commits[0], "2b1ed978194a94edeabbca6de7ff3b5771d4d665") + fh.files["cmd/hercules/main.go"] = &FileHistory{Hashes: []plumbing.Hash{plumbing.NewHash( + "0000000000000000000000000000000000000000")}} + fh.files["analyser.go"] = &FileHistory{Hashes: []plumbing.Hash{plumbing.NewHash( + "ffffffffffffffffffffffffffffffffffffffff")}} + cres, err := fh.Consume(deps) + assert.Nil(t, cres) + assert.Nil(t, err) + return fh, deps }