-
Notifications
You must be signed in to change notification settings - Fork 10
/
git.go
112 lines (100 loc) · 2.95 KB
/
git.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package actionutil
import (
"fmt"
"os"
"github.com/posener/goaction"
"github.com/posener/goaction/log"
"github.com/posener/script"
)
// GitConfig configures git with name and email to enable git operations.
func GitConfig(name, email string) error {
err := git("config", "user.name", name).ToStdout()
if err != nil {
return err
}
err = git("config", "user.email", email).ToStdout()
if err != nil {
return err
}
// Prevent errors "fatal: detected dubious ownership in repository at ...".
return git("config", "--add", "safe.directory", "'*'").ToStdout()
}
// GitDiff returns diff of changes in a given file.
func GitDiff(path string) (string, error) {
// Add files to git, in case it does not exists
err := git("add", path).ToStdout()
defer func() { git("reset", path).ToStdout() }()
if err != nil {
return "", fmt.Errorf("git add for %s: %s", path, err)
}
return git("diff", "--staged", "--no-color", path).Tail(-5).ToString()
}
type Diff struct {
Path string
Diff string
}
// GitDiffAll returns diff of all changes in a given file.
func GitDiffAll() ([]Diff, error) {
// Add files to git, in case it does not exists
err := git("add", ".").ToStdout()
defer func() { git("reset").ToStdout() }()
if err != nil {
return nil, fmt.Errorf("git add: %s", err)
}
var diffs []Diff
err = git("diff", "--staged", "--name-only").Iterate(func(path []byte) error {
if len(path) == 0 {
return nil
}
diff, err := git("diff", "--staged", "--no-color", string(path)).Tail(-5).ToString()
if err != nil {
return fmt.Errorf("git diff %q: %s", string(path), err)
}
diffs = append(diffs, Diff{Path: string(path), Diff: diff})
return nil
})
return diffs, err
}
// GitCommitPush commits and pushes a list of files.
func GitCommitPush(paths []string, message string) error {
branch := goaction.Branch()
// Reset git if there where any staged files.
err := git("reset").ToStdout()
if err != nil {
return fmt.Errorf("git reset: %s", err)
}
// Add the requested paths.
err = git("add", paths...).ToStdout()
if err != nil {
return fmt.Errorf("git add: %s", err)
}
// Commit the changes.
err = git("commit", "-m", message).ToStdout()
if err != nil {
return fmt.Errorf("git commit: %s", err)
}
retry := 1
maxRetries := 3
for {
// Push the change.
err = git("push", "origin", "HEAD:"+branch).ToStdout()
if err == nil {
return nil
}
if retry > maxRetries {
return fmt.Errorf("push failed %d times: %s", maxRetries, err)
}
retry++
// In case of push error, try to rebase and push again, in case the error was due to other
// changes being pushed to the remote repository.
log.Printf("Push failed, rebasing and trying again...")
err = git("pull", "--rebase", "--autostash", "origin", branch).ToStdout()
if err != nil {
return fmt.Errorf("git pull rebase: %s", err)
}
}
}
func git(subcmd string, args ...string) script.Stream {
args = append([]string{subcmd}, args...)
return script.ExecHandleStderr(os.Stderr, "git", args...)
}