Skip to content

Commit ea0f60a

Browse files
authored
Initial implementation (#1)
2 parents fea84d9 + 866b22e commit ea0f60a

File tree

16 files changed

+745
-7
lines changed

16 files changed

+745
-7
lines changed

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,6 @@ indent_size = 4
2121
[*.{yaml,yml}]
2222
indent_style = space
2323
indent_size = 2
24+
25+
[Makefile]
26+
indent_style = tab

.git-blame-ignore-revs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Format
2+
4b8bab8817edefff3ffa83267de5a2fb9f99cf58

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ jobs:
1414
- uses: actions/setup-go@v3
1515
with:
1616
go-version: '1.20'
17-
# cache: true # NOTE: Requires go.sum
17+
cache: true
18+
- uses: golangci/golangci-lint-action@v3
1819
- run: go build -v ./...
1920
- name: Test and Generate Coverage
2021
run: go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Built assets
2+
git-lzc
3+
git-lzc-*

.golangci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
linters:
2+
enable:
3+
- gofmt

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
git-lzc: cmd/ pkg/ *.go
2+
go build ./cmd/git-lzc/

cmd/git-lzc/main.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strings"
7+
8+
lazycommit "github.com/spenserblack/git-lazy-commit"
9+
)
10+
11+
func main() {
12+
repo, err := lazycommit.OpenRepo(".")
13+
onError(err)
14+
15+
noStaged, err := repo.NoStaged()
16+
onError(err)
17+
18+
if noStaged {
19+
onError(repo.StageAll())
20+
}
21+
22+
hash, msg, err := repo.Commit()
23+
onError(err)
24+
25+
msgLines := strings.Split(msg, "\n")
26+
27+
fmt.Printf("[%s] %s\n", hash, msgLines[0])
28+
}
29+
30+
func onError(err error) {
31+
if err != nil {
32+
fmt.Fprintf(os.Stderr, "error: %v\n", err)
33+
os.Exit(1)
34+
}
35+
}

commit.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package lazycommit
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/go-git/go-git/v5"
9+
"github.com/go-git/go-git/v5/plumbing"
10+
11+
"github.com/spenserblack/git-lazy-commit/pkg/fileutils"
12+
)
13+
14+
// Commit commits all changes in the repository.
15+
//
16+
// It returns the commit hash and the commit message.
17+
func (r *LazyRepo) Commit() (hash plumbing.Hash, msg string, err error) {
18+
msg, err = r.CommitMsg()
19+
if err != nil {
20+
return
21+
}
22+
23+
hash, err = r.wt.Commit(msg, &git.CommitOptions{})
24+
return
25+
}
26+
27+
// CommitMsg builds a commit message using the tracked files in the repository.
28+
func (r *LazyRepo) CommitMsg() (string, error) {
29+
status, err := r.status()
30+
if err != nil {
31+
return "", err
32+
}
33+
for filename, fileStatus := range status {
34+
if fileStatus.Staging == git.Unmodified || fileStatus.Staging == git.Untracked {
35+
delete(status, filename)
36+
}
37+
}
38+
39+
if len(status) == 0 {
40+
return "", errors.New("no tracked files")
41+
}
42+
if len(status) == 1 {
43+
for filename, fileStatus := range status {
44+
return singleFileMsg(filename, fileStatus), nil
45+
}
46+
}
47+
return multiFileMsg(status), nil
48+
}
49+
50+
func singleFileMsg(filename string, fileStatus *git.FileStatus) string {
51+
statusString := ""
52+
switch fileStatus.Staging {
53+
case git.Added:
54+
statusString = "Create"
55+
case git.Deleted:
56+
statusString = "Delete"
57+
case git.Modified:
58+
statusString = "Update"
59+
case git.Renamed:
60+
statusString = "Rename to"
61+
case git.Copied:
62+
statusString = "Copy to"
63+
default:
64+
statusString = "Do something to"
65+
}
66+
67+
return fmt.Sprintf("%s %s", statusString, filename)
68+
}
69+
70+
func multiFileMsg(status git.Status) string {
71+
var builder strings.Builder
72+
73+
filenames := make([]string, 0, len(status))
74+
for name := range status {
75+
filenames = append(filenames, name)
76+
}
77+
78+
sharedDir := fileutils.SharedDirectory(filenames)
79+
80+
if sharedDir == "/" {
81+
builder.WriteString("Update files\n")
82+
} else {
83+
builder.WriteString(fmt.Sprintf("Update %s/\n", sharedDir))
84+
}
85+
builder.WriteRune('\n')
86+
87+
for filename, fileStatus := range status {
88+
msgItem := singleFileMsg(filename, fileStatus)
89+
builder.WriteString(fmt.Sprintf("- %s\n", msgItem))
90+
}
91+
92+
return builder.String()
93+
}

commit_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package lazycommit
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
gitconfig "github.com/go-git/go-git/v5/config"
8+
)
9+
10+
// Tests that a commit message can't be built when there are no staged changes.
11+
func TestBuildCommitMessageNoStaged(t *testing.T) {
12+
dir := tempRepo(t)
13+
repo, err := OpenRepo(dir)
14+
if err != nil {
15+
t.Fatal(err)
16+
}
17+
_, err = repo.CommitMsg()
18+
if err == nil {
19+
t.Fatal("expected error")
20+
}
21+
}
22+
23+
// Tests that commit commits all files in the worktree.
24+
func TestCommit(t *testing.T) {
25+
dir := tempRepo(t)
26+
updateConfig(t, dir, func(config *gitconfig.Config) {
27+
config.User.Name = "Test User"
28+
config.User.Email = "[email protected]"
29+
})
30+
addFile(t, dir, "test.txt", "test")
31+
addFile(t, dir, "test2.txt", "test")
32+
33+
repo, err := OpenRepo(dir)
34+
if err != nil {
35+
t.Fatal(err)
36+
}
37+
38+
_, msg, err := repo.Commit()
39+
if err != nil {
40+
t.Fatal(err)
41+
}
42+
43+
wantHeader := "Update files"
44+
wantBodyLines := []string{"- Create test.txt", "- Create test2.txt"}
45+
46+
if !strings.HasPrefix(msg, wantHeader) {
47+
t.Errorf("expected commit message to start with %q, got %q", wantHeader, msg)
48+
}
49+
50+
for _, line := range wantBodyLines {
51+
if !strings.Contains(msg, line) {
52+
t.Errorf("expected commit message to contain %q, got %q", line, msg)
53+
}
54+
}
55+
}

go.mod

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,28 @@
11
module github.com/spenserblack/git-lazy-commit
22

33
go 1.20
4+
5+
require (
6+
github.com/go-git/go-billy/v5 v5.4.0
7+
github.com/go-git/go-git/v5 v5.5.2
8+
)
9+
10+
require (
11+
github.com/Microsoft/go-winio v0.5.2 // indirect
12+
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
13+
github.com/acomagu/bufpipe v1.0.3 // indirect
14+
github.com/cloudflare/circl v1.1.0 // indirect
15+
github.com/emirpasic/gods v1.18.1 // indirect
16+
github.com/go-git/gcfg v1.5.0 // indirect
17+
github.com/imdario/mergo v0.3.13 // indirect
18+
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
19+
github.com/kevinburke/ssh_config v1.2.0 // indirect
20+
github.com/pjbgf/sha1cd v0.2.3 // indirect
21+
github.com/sergi/go-diff v1.1.0 // indirect
22+
github.com/skeema/knownhosts v1.1.0 // indirect
23+
github.com/xanzy/ssh-agent v0.3.3 // indirect
24+
golang.org/x/crypto v0.3.0 // indirect
25+
golang.org/x/net v0.2.0 // indirect
26+
golang.org/x/sys v0.3.0 // indirect
27+
gopkg.in/warnings.v0 v0.1.2 // indirect
28+
)

0 commit comments

Comments
 (0)