Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Git stats libgit2 #63

Merged
merged 37 commits into from
Nov 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2ce824a
git stats table
Vialeon Oct 14, 2020
312eb55
libgit2 stats testing
Vialeon Oct 14, 2020
1e75dcb
libgit2 table added to gitqlite Vtable build
Vialeon Oct 14, 2020
dbaf4fa
lint
Vialeon Oct 14, 2020
480d6ce
fix name of stats table
Vialeon Oct 14, 2020
8426493
testing fix
Vialeon Oct 15, 2020
b861b91
more Testing fixes
Vialeon Oct 15, 2020
743c5d2
git stats table
Vialeon Oct 14, 2020
67b291d
libgit2 stats testing
Vialeon Oct 14, 2020
9fcce66
libgit2 table added to gitqlite Vtable build
Vialeon Oct 14, 2020
8fade5f
lint
Vialeon Oct 14, 2020
06dedfb
fix name of stats table
Vialeon Oct 14, 2020
37f5011
testing fix
Vialeon Oct 15, 2020
3744d9c
more Testing fixes
Vialeon Oct 15, 2020
4f42cf4
off by one error w/ commit ID
Vialeon Oct 19, 2020
250d262
Merge branch
Vialeon Oct 19, 2020
0a2b5eb
fix diffTreeToTree
Vialeon Oct 19, 2020
8bb6037
rm repeat code(can't in parse.go b/c of import cycle)
Vialeon Oct 19, 2020
97d306e
git stats table w/o string parsing. Using callback forEachHunk(Foreac…
Vialeon Oct 31, 2020
e97c514
lint
Vialeon Oct 31, 2020
92b0f78
rm commented code and edit error stdouts
Vialeon Nov 1, 2020
6cb5075
Merge pull request #68 from augmentable-dev/stats_calc_w_callbacks
Vialeon Nov 1, 2020
8295c7a
Merge branch 'master' into git-stats
patrickdevivo Nov 2, 2020
a6c4f42
Merge pull request #69 from augmentable-dev/git-stats
patrickdevivo Nov 5, 2020
8c58213
use parent commit instead of previous commit
patrickdevivo Nov 7, 2020
fd2292c
fix lint issue
patrickdevivo Nov 7, 2020
4be5cbb
add intentionally failing test
patrickdevivo Nov 8, 2020
dfbe79f
add -v flag to vscode go test defaults
patrickdevivo Nov 15, 2020
f308ac6
make some tests work
patrickdevivo Nov 15, 2020
3b3092f
lint fix
patrickdevivo Nov 15, 2020
ec063dd
add some test cases, use an index
patrickdevivo Nov 15, 2020
305ee28
rename column
patrickdevivo Nov 15, 2020
51eaee2
Merge pull request #70 from augmentable-dev/git_stats_libgit2-updates
patrickdevivo Nov 15, 2020
a7e840f
add stats table info
patrickdevivo Nov 15, 2020
d1d233f
remove extra columns
patrickdevivo Nov 15, 2020
a16e65c
Merge pull request #71 from augmentable-dev/update-readme
patrickdevivo Nov 15, 2020
fb9e68c
upgrade some deps
patrickdevivo Nov 15, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"go.buildFlags": [
"-tags='sqlite_vtable'"
],
"go.testFlags": [
"-v"
],
"go.vetFlags": [
"-tags='sqlite_vtable'"
],
Expand Down
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,6 @@ Similar to `git log`, the `commits` table includes all commits in the history of
| parent_id | TEXT |
| parent_count | INT |
| tree_id | TEXT |
| additions | INT |
| deletions | INT |

#### `files`

Expand Down Expand Up @@ -160,6 +158,15 @@ Use the `commit_id` column to filter for files that belong to the work tree of a
| message | TEXT |
| target_type | TEXT |

#### `stats`

| Column | Type |
|-----------|------|
| commit_id | TEXT |
| file | TEXT |
| additions | INT |
| deletions | INT |

### Example Queries

This will return all commits in the history of the currently checked out branch/commit of the repo.
Expand Down
27 changes: 1 addition & 26 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"fmt"
"io/ioutil"
"os"
"os/user"
"path"
"path/filepath"

"github.com/augmentable-dev/askgit/pkg/gitqlite"
Expand Down Expand Up @@ -86,7 +84,7 @@ var rootCmd = &cobra.Command{
if remote, err := vcsurl.Parse(repo); err == nil { // if it can be parsed
dir, err = ioutil.TempDir("", "repo")
handleError(err)
cloneOptions := CreateAuthenticationCallback(remote)
cloneOptions := gitqlite.CreateAuthenticationCallback(remote)
_, err = git.Clone(repo, dir, cloneOptions)
handleError(err)

Expand Down Expand Up @@ -129,29 +127,6 @@ func Execute() {
}

}
func CreateAuthenticationCallback(remote *vcsurl.VCS) *git.CloneOptions {
cloneOptions := &git.CloneOptions{}

if _, err := remote.Remote(vcsurl.SSH); err == nil { // if SSH, use "default" credentials
// use FetchOptions instead of directly RemoteCallbacks
// https://github.com/libgit2/git2go/commit/36e0a256fe79f87447bb730fda53e5cbc90eb47c
cloneOptions.FetchOptions = &git.FetchOptions{
RemoteCallbacks: git.RemoteCallbacks{
CredentialsCallback: func(url string, username string, allowedTypes git.CredType) (*git.Cred, error) {
usr, _ := user.Current()
publicSSH := path.Join(usr.HomeDir, ".ssh/id_rsa.pub")
privateSSH := path.Join(usr.HomeDir, ".ssh/id_rsa")

cred, ret := git.NewCredSshKey("git", publicSSH, privateSSH, "")
return cred, ret
},
CertificateCheckCallback: func(cert *git.Certificate, valid bool, hostname string) git.ErrorCode {
return git.ErrOk
},
}}
}
return cloneOptions
}

func readStdin() (string, error) {
reader := bufio.NewReader(os.Stdin)
Expand Down
9 changes: 4 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ require (
github.com/gitsight/go-vcsurl v1.0.0
github.com/jroimartin/gocui v0.4.0
github.com/kr/text v0.2.0 // indirect
github.com/libgit2/git2go/v30 v30.0.9
github.com/libgit2/git2go/v30 v30.2.2
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mattn/go-sqlite3 v1.14.2
github.com/mattn/go-sqlite3 v1.14.4
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1 // indirect
github.com/nsf/termbox-go v0.0.0-20201107200903-9b52a5faed9e // indirect
github.com/olekukonko/tablewriter v0.0.4
github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/cobra v1.1.1
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
Expand Down
197 changes: 173 additions & 24 deletions go.sum

Large diffs are not rendered by default.

37 changes: 35 additions & 2 deletions pkg/gitlog/parse_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package gitlog

import (
"fmt"
"io"
"io/ioutil"
"os"
"os/user"
"path"
"testing"

"github.com/gitsight/go-vcsurl"
git "github.com/libgit2/git2go/v30"
)

Expand All @@ -31,11 +35,16 @@ func initFixtureRepo() (func() error, error) {
return nil, err
}

fixtureRepo, err = git.Clone(fixtureRepoCloneURL, dir, &git.CloneOptions{})
remote, err := vcsurl.Parse(fixtureRepoCloneURL)
if err != nil {
return nil, err
}

cloneOptions := CreateAuthenticationCallback(remote)
fixtureRepo, err = git.Clone(fixtureRepoCloneURL, dir, cloneOptions)
if err != nil {
fmt.Println(err)
return nil, err
}
fixtureRepoDir = dir

return func() error {
Expand Down Expand Up @@ -96,3 +105,27 @@ func TestParse(t *testing.T) {
t.Fatalf("incorrect number of commits, expected: %d got: %d", shouldBeCount, count)
}
}

func CreateAuthenticationCallback(remote *vcsurl.VCS) *git.CloneOptions {
cloneOptions := &git.CloneOptions{}

if _, err := remote.Remote(vcsurl.SSH); err == nil { // if SSH, use "default" credentials
// use FetchOptions instead of directly RemoteCallbacks
// https://github.com/libgit2/git2go/commit/36e0a256fe79f87447bb730fda53e5cbc90eb47c
cloneOptions.FetchOptions = &git.FetchOptions{
RemoteCallbacks: git.RemoteCallbacks{
CredentialsCallback: func(url string, username string, allowedTypes git.CredType) (*git.Cred, error) {
usr, _ := user.Current()
publicSSH := path.Join(usr.HomeDir, ".ssh/id_rsa.pub")
privateSSH := path.Join(usr.HomeDir, ".ssh/id_rsa")

cred, ret := git.NewCredSshKey("git", publicSSH, privateSSH, "")
return cred, ret
},
CertificateCheckCallback: func(cert *git.Certificate, valid bool, hostname string) git.ErrorCode {
return git.ErrOk
},
}}
}
return cloneOptions
}
4 changes: 1 addition & 3 deletions pkg/gitqlite/git_log.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ func (m *gitLogModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTa
committer_when DATETIME,
parent_id TEXT,
parent_count INT,
tree_id TEXT,
additions INT,
deletions INT
tree_id TEXT
)`, args[0]))
if err != nil {
return nil, err
Expand Down
9 changes: 1 addition & 8 deletions pkg/gitqlite/git_log_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ func (m *gitLogCLIModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.
committer_when DATETIME,
parent_id TEXT,
parent_count INT,
tree_id TEXT,
additions INT,
deletions INT
tree_id TEXT
)`, args[0]))
if err != nil {
return nil, err
Expand Down Expand Up @@ -154,11 +152,6 @@ func (vc *commitCLICursor) Column(c *sqlite3.SQLiteContext, col int) error {
case 11:
//tree_id
c.ResultText(current.TreeID)
case 12:
c.ResultInt(current.Additions)
case 13:
c.ResultInt(current.Deletions)

}
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/gitqlite/git_log_cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestCommitCounts(t *testing.T) {
if err != nil {
t.Fatal(err)
}
expected := 14
expected := 12
if len(columns) != expected {
t.Fatalf("expected %d columns, got: %d", expected, len(columns))
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/gitqlite/git_log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func TestCommits(t *testing.T) {
t.Fatal(err)
}

expected := 14
expected := 12
if len(columns) != expected {
t.Fatalf("expected %d columns, got: %d", expected, len(columns))
}
Expand Down
150 changes: 150 additions & 0 deletions pkg/gitqlite/git_stats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package gitqlite

import (
"fmt"
"io"

git "github.com/libgit2/git2go/v30"
"github.com/mattn/go-sqlite3"
)

type gitStatsModule struct{}

type gitStatsTable struct {
repoPath string
repo *git.Repository
}

func (m *gitStatsModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
err := c.DeclareVTab(fmt.Sprintf(`
CREATE TABLE %q (
commit_id TEXT,
file TEXT,
additions INT,
deletions INT
)`, args[0]))
if err != nil {
return nil, err
}

// the repoPath will be enclosed in double quotes "..." since ensureTables uses %q when setting up the table
// we need to pop those off when referring to the actual directory in the fs
repoPath := args[3][1 : len(args[3])-1]
return &gitStatsTable{repoPath: repoPath}, nil
}

func (m *gitStatsModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
return m.Create(c, args)
}

func (m *gitStatsModule) DestroyModule() {}

func (v *gitStatsTable) Open() (sqlite3.VTabCursor, error) {
repo, err := git.OpenRepository(v.repoPath)
if err != nil {
return nil, err
}
v.repo = repo

return &StatsCursor{repo: v.repo}, nil
}

func (v *gitStatsTable) BestIndex(cst []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) {
used := make([]bool, len(cst))
// TODO implement an index for file name glob patterns?
// TODO this loop construct won't work well for multiple constraints...
for c, constraint := range cst {
switch {
case constraint.Usable && constraint.Column == 0 && constraint.Op == sqlite3.OpEQ:
used[c] = true
return &sqlite3.IndexResult{Used: used, IdxNum: 1, IdxStr: "stats-by-commit-id", EstimatedCost: 1.0, EstimatedRows: 1}, nil
}
}

return &sqlite3.IndexResult{Used: used, EstimatedCost: 100}, nil
}

func (v *gitStatsTable) Disconnect() error {
v.repo = nil
return nil
}
func (v *gitStatsTable) Destroy() error { return nil }

type StatsCursor struct {
repo *git.Repository
iterator *commitStatsIter
current *commitStat
}

func (vc *StatsCursor) Column(c *sqlite3.SQLiteContext, col int) error {
stat := vc.current
switch col {
case 0:
//commit id
c.ResultText(stat.commitID)
case 1:
c.ResultText(stat.file)
case 2:
c.ResultInt(stat.additions)
case 3:
c.ResultInt(stat.deletions)

}

return nil
}
func (vc *StatsCursor) Filter(idxNum int, idxStr string, vals []interface{}) error {
var opt *commitStatsIterOptions

switch idxNum {
case 0:
opt = &commitStatsIterOptions{}
case 1:
opt = &commitStatsIterOptions{commitID: vals[0].(string)}
}

iter, err := NewCommitStatsIter(vc.repo, opt)
if err != nil {
return err
}

vc.iterator = iter

file, err := vc.iterator.Next()
if err != nil {
if err == io.EOF {
vc.current = nil
return nil
}
return err
}

vc.current = file
return nil
}

func (vc *StatsCursor) Next() error {
file, err := vc.iterator.Next()
if err != nil {
if err == io.EOF {
vc.current = nil
return nil
}
return err
}
vc.current = file
return nil
}

func (vc *StatsCursor) EOF() bool {
return vc.current == nil
}

func (vc *StatsCursor) Rowid() (int64, error) {
return int64(0), nil
}

func (vc *StatsCursor) Close() error {
vc.iterator.Close()
return nil
}
Loading