Skip to content

Use Git instead of go-git #7

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

Merged
merged 17 commits into from
Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 3 additions & 7 deletions cmd/git-lzc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@ package main
import (
"fmt"
"os"
"strings"

lazycommit "github.com/spenserblack/git-lazy-commit"
)

func main() {
repo, err := lazycommit.OpenRepo(".")
onError(err)
repo := lazycommit.Repo(".")

noStaged, err := repo.NoStaged()
onError(err)
Expand All @@ -19,12 +17,10 @@ func main() {
onError(repo.StageAll())
}

hash, msg, err := repo.Commit()
out, err := repo.Commit()
onError(err)

msgLines := strings.Split(msg, "\n")

fmt.Printf("[%s] %s\n", hash, msgLines[0])
fmt.Printf("%s", out)
}

func onError(err error) {
Expand Down
76 changes: 29 additions & 47 deletions commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,74 +5,57 @@ import (
"fmt"
"strings"

"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"

"github.com/spenserblack/git-lazy-commit/pkg/fileutils"
)

// Commit commits all changes in the repository.
//
// It returns the commit hash and the commit message.
func (r *LazyRepo) Commit() (hash plumbing.Hash, msg string, err error) {
msg, err = r.CommitMsg()
// It returns the output of the commit command.
func (repo Repo) Commit() ([]byte, error) {
msg, err := repo.CommitMsg()
if err != nil {
return
return nil, err
}

hash, err = r.wt.Commit(msg, &git.CommitOptions{})
return
cmd, err := repo.cmd("commit", "-m", msg)
if err != nil {
return nil, err
}
return cmd.Output()
}

// CommitMsg builds a commit message using the tracked files in the repository.
func (r *LazyRepo) CommitMsg() (string, error) {
status, err := r.status()
func (repo Repo) CommitMsg() (string, error) {
statuses, err := repo.Status()
if err != nil {
return "", err
}
for filename, fileStatus := range status {
if fileStatus.Staging == git.Unmodified || fileStatus.Staging == git.Untracked {
delete(status, filename)

// NOTE: Filtering to only statuses that are staged and can be used for the commit message.
commitableStatuses := make([]StatusRecord, 0, len(statuses))
for _, status := range statuses {
if _, ok := statusMap[status.Staged]; ok {
commitableStatuses = append(commitableStatuses, status)
}
}

if len(status) == 0 {
if len(commitableStatuses) == 0 {
return "", errors.New("no tracked files")
}
if len(status) == 1 {
for filename, fileStatus := range status {
return singleFileMsg(filename, fileStatus), nil
}
}
return multiFileMsg(status), nil
}

func singleFileMsg(filename string, fileStatus *git.FileStatus) string {
statusString := ""
switch fileStatus.Staging {
case git.Added:
statusString = "Create"
case git.Deleted:
statusString = "Delete"
case git.Modified:
statusString = "Update"
case git.Renamed:
statusString = "Rename to"
case git.Copied:
statusString = "Copy to"
default:
statusString = "Do something to"
if len(commitableStatuses) == 1 {
status := commitableStatuses[0]
return status.Message(), nil
}

return fmt.Sprintf("%s %s", statusString, filename)
return multiFileMsg(commitableStatuses), nil
}

func multiFileMsg(status git.Status) string {
// MultiFileMsg builds a commit message from multiple files.
func multiFileMsg(statuses []StatusRecord) string {
var builder strings.Builder

filenames := make([]string, 0, len(status))
for name := range status {
filenames = append(filenames, name)
filenames := make([]string, 0, len(statuses))
for _, status := range statuses {
filenames = append(filenames, status.Path)
}

sharedDir := fileutils.SharedDirectory(filenames)
Expand All @@ -84,9 +67,8 @@ func multiFileMsg(status git.Status) string {
}
builder.WriteRune('\n')

for filename, fileStatus := range status {
msgItem := singleFileMsg(filename, fileStatus)
builder.WriteString(fmt.Sprintf("- %s\n", msgItem))
for _, status := range statuses {
builder.WriteString(fmt.Sprintf("- %s\n", status.Message()))
}

return builder.String()
Expand Down
80 changes: 50 additions & 30 deletions commit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,73 @@ package lazycommit
import (
"strings"
"testing"

gitconfig "github.com/go-git/go-git/v5/config"
)

// Tests that a commit message can't be built when there are no staged changes.
func TestBuildCommitMessageNoStaged(t *testing.T) {
func TestBuildCommitMessage(t *testing.T) {
t.Log("Creating a new repo.")
dir := tempRepo(t)
repo, err := OpenRepo(dir)
repo := Repo(dir)

_, err := repo.CommitMsg()
if err == nil && err.Error() != "no tracked files" {
t.Errorf(`Expected "no tracked files", got %v`, err)
}

f := commitFile(t, dir, "test.txt", "test")
defer f.Close()

t.Log(`Modifying test.txt`)
commitFile(t, dir, "test.txt", "")
addFile(t, dir, "test.txt", "different text")

msg, err := repo.CommitMsg()
if err != nil {
t.Fatal(err)
}
_, err = repo.CommitMsg()
if err == nil {
t.Fatal("expected error")
if msg != "Update test.txt" {
t.Errorf(`Expected "Update test.txt", got %v`, msg)
}
}

// Tests that commit commits all files in the worktree.
func TestCommit(t *testing.T) {
dir := tempRepo(t)
updateConfig(t, dir, func(config *gitconfig.Config) {
config.User.Name = "Test User"
config.User.Email = "test@example.com"
})
addFile(t, dir, "test.txt", "test")
t.Log(`Adding a new file`)
addFile(t, dir, "test2.txt", "test")

repo, err := OpenRepo(dir)
msg, err = repo.CommitMsg()
if err != nil {
t.Fatal(err)
}

_, msg, err := repo.Commit()
if err != nil {
t.Fatal(err)
lines := strings.Split(msg, "\n")
if lines[0] != "Update files" {
t.Errorf(`Expected "Update files" in the header, got %v`, lines[0])
}
if lines[1] != "" {
t.Errorf(`Expected an empty line after the header, got %v`, lines[1])
}
body := strings.Join(lines[2:], "\n")
t.Logf("Body:\n %v", body)
for _, want := range []string{"- Update test.txt", "- Create test2.txt"} {
if !strings.Contains(body, want) {
t.Errorf(`Expected %v in the body`, want)
}
}
}

wantHeader := "Update files"
wantBodyLines := []string{"- Create test.txt", "- Create test2.txt"}
// TestBuildCommitMessageWithRename tests that a commit message can be built when a file is renamed.
func TestBuildCommitMessageWithRename(t *testing.T) {
dir := tempRepo(t)
repo := Repo(dir)

if !strings.HasPrefix(msg, wantHeader) {
t.Errorf("expected commit message to start with %q, got %q", wantHeader, msg)
}
f := commitFile(t, dir, "foo.txt", "test")
defer f.Close()

for _, line := range wantBodyLines {
if !strings.Contains(msg, line) {
t.Errorf("expected commit message to contain %q, got %q", line, msg)
}
t.Log(`Renaming test.txt to test2.txt`)
moveFile(t, dir, "foo.txt", "bar.txt")

msg, err := repo.CommitMsg()
if err != nil {
t.Fatal(err)
}
if msg != "Rename foo.txt to bar.txt" {
t.Errorf(`Expected "Rename foo.txt to bar.txt", got %v`, msg)
}
}
25 changes: 1 addition & 24 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,4 @@ module github.com/spenserblack/git-lazy-commit

go 1.20

require (
github.com/go-git/go-billy/v5 v5.4.1
github.com/go-git/go-git/v5 v5.5.2
)

require (
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/pjbgf/sha1cd v0.2.3 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/skeema/knownhosts v1.1.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.3.0 // indirect
golang.org/x/net v0.2.0 // indirect
golang.org/x/sys v0.3.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
require github.com/cli/safeexec v1.0.1
Loading