Skip to content

Commit 02f9c97

Browse files
committed
git: wrap upstream gitutil cli
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
1 parent 63ebbeb commit 02f9c97

1 file changed

Lines changed: 61 additions & 77 deletions

File tree

util/gitutil/gitutil.go

Lines changed: 61 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,125 @@
11
package gitutil
22

33
import (
4-
"bytes"
54
"context"
65
"net/url"
7-
"os"
8-
"os/exec"
9-
"path/filepath"
106
"strings"
117

128
"github.com/docker/buildx/util/osutil"
9+
bkgitutil "github.com/moby/buildkit/util/gitutil"
1310
"github.com/pkg/errors"
1411
)
1512

16-
// Git represents an active git object
13+
// Git represents an active git object.
1714
type Git struct {
18-
ctx context.Context
19-
wd string
20-
gitpath string
15+
ctx context.Context
16+
cli *bkgitutil.GitCLI
2117
}
2218

2319
// Option provides a variadic option for configuring the git client.
24-
type Option func(b *Git)
20+
type Option func(*Git)
2521

2622
// WithContext sets context.
2723
func WithContext(ctx context.Context) Option {
28-
return func(b *Git) {
29-
b.ctx = ctx
24+
return func(g *Git) {
25+
g.ctx = ctx
3026
}
3127
}
3228

3329
// WithWorkingDir sets working directory.
3430
func WithWorkingDir(wd string) Option {
35-
return func(b *Git) {
36-
b.wd = wd
31+
return func(g *Git) {
32+
if g.cli == nil {
33+
g.cli = bkgitutil.NewGitCLI()
34+
}
35+
g.cli = g.cli.New(bkgitutil.WithDir(wd))
3736
}
3837
}
3938

40-
// New initializes a new git client
39+
// New initializes a new git client.
4140
func New(opts ...Option) (*Git, error) {
42-
var err error
43-
c := &Git{
41+
g := &Git{
4442
ctx: context.Background(),
43+
cli: bkgitutil.NewGitCLI(),
4544
}
4645

4746
for _, opt := range opts {
48-
opt(c)
47+
opt(g)
4948
}
5049

51-
c.gitpath, err = gitPath(c.wd)
50+
gitpath, err := gitPath(g.cli.Dir())
5251
if err != nil {
5352
return nil, err
5453
}
5554

56-
return c, nil
55+
g.cli = g.cli.New(
56+
bkgitutil.WithGitBinary(gitpath),
57+
bkgitutil.WithArgs("-c", "log.showSignature=false"),
58+
)
59+
return g, nil
5760
}
5861

59-
func (c *Git) IsInsideWorkTree() bool {
60-
out, err := c.Run("rev-parse", "--is-inside-work-tree")
62+
func (g *Git) IsInsideWorkTree() bool {
63+
out, err := g.Run("rev-parse", "--is-inside-work-tree")
6164
return out == "true" && err == nil
6265
}
6366

64-
func (c *Git) IsDirty() bool {
65-
out, err := c.Run("status", "--porcelain", "--ignored")
67+
func (g *Git) IsDirty() bool {
68+
out, err := g.Run("status", "--porcelain", "--ignored")
6669
return strings.TrimSpace(out) != "" || err != nil
6770
}
6871

69-
func (c *Git) RootDir() (string, error) {
70-
root, err := c.Run("rev-parse", "--show-toplevel")
72+
func (g *Git) RootDir() (string, error) {
73+
root, err := g.cli.WorkTree(g.ctx)
7174
if err != nil {
7275
return "", err
7376
}
7477
return osutil.SanitizePath(root), nil
7578
}
7679

77-
func (c *Git) GitDir() (string, error) {
78-
dir, err := c.RootDir()
80+
func (g *Git) GitDir() (string, error) {
81+
dir, err := g.cli.GitDir(g.ctx)
7982
if err != nil {
8083
return "", err
8184
}
82-
return filepath.Join(dir, ".git"), nil
85+
return osutil.SanitizePath(dir), nil
8386
}
8487

85-
func (c *Git) RemoteURL() (string, error) {
86-
// Try default remote based on remote tracking branch
87-
if remote, err := c.currentRemote(); err == nil && remote != "" {
88-
if ru, err := c.clean(c.run("remote", "get-url", remote)); err == nil && ru != "" {
88+
func (g *Git) RemoteURL() (string, error) {
89+
// Try default remote based on remote tracking branch.
90+
if remote, err := g.currentRemote(); err == nil && remote != "" {
91+
if ru, err := g.Run("remote", "get-url", remote); err == nil && ru != "" {
8992
return stripCredentials(ru), nil
9093
}
9194
}
92-
// Next try to get the remote URL from the origin remote first
93-
if ru, err := c.clean(c.run("remote", "get-url", "origin")); err == nil && ru != "" {
95+
// Next try to get the remote URL from the origin remote first.
96+
if ru, err := g.Run("remote", "get-url", "origin"); err == nil && ru != "" {
9497
return stripCredentials(ru), nil
9598
}
96-
// If that fails, try to get the remote URL from the upstream remote
97-
if ru, err := c.clean(c.run("remote", "get-url", "upstream")); err == nil && ru != "" {
99+
// If that fails, try to get the remote URL from the upstream remote.
100+
if ru, err := g.Run("remote", "get-url", "upstream"); err == nil && ru != "" {
98101
return stripCredentials(ru), nil
99102
}
100103
return "", errors.New("no remote URL found for either origin or upstream")
101104
}
102105

103-
func (c *Git) FullCommit() (string, error) {
104-
return c.clean(c.run("show", "--format=%H", "HEAD", "--quiet", "--"))
106+
func (g *Git) FullCommit() (string, error) {
107+
return g.Run("show", "--format=%H", "HEAD", "--quiet", "--")
105108
}
106109

107-
func (c *Git) ShortCommit() (string, error) {
108-
return c.clean(c.run("show", "--format=%h", "HEAD", "--quiet", "--"))
110+
func (g *Git) ShortCommit() (string, error) {
111+
return g.Run("show", "--format=%h", "HEAD", "--quiet", "--")
109112
}
110113

111-
func (c *Git) Tag() (string, error) {
114+
func (g *Git) Tag() (string, error) {
112115
var tag string
113116
var err error
114117
for _, fn := range []func() (string, error){
115118
func() (string, error) {
116-
return c.clean(c.run("tag", "--points-at", "HEAD", "--sort", "-version:creatordate"))
119+
return g.Run("tag", "--points-at", "HEAD", "--sort", "-version:creatordate")
117120
},
118121
func() (string, error) {
119-
return c.clean(c.run("describe", "--tags", "--abbrev=0"))
122+
return g.Run("describe", "--tags", "--abbrev=0")
120123
},
121124
} {
122125
tag, err = fn()
@@ -127,53 +130,34 @@ func (c *Git) Tag() (string, error) {
127130
return tag, err
128131
}
129132

130-
func (c *Git) Run(args ...string) (string, error) {
131-
return c.clean(c.run(args...))
132-
}
133-
134-
func (c *Git) run(args ...string) (string, error) {
135-
var extraArgs = []string{
136-
"-c", "log.showSignature=false",
137-
}
138-
139-
args = append(extraArgs, args...)
140-
cmd := exec.CommandContext(c.ctx, c.gitpath, args...)
141-
if c.wd != "" {
142-
cmd.Dir = c.wd
143-
}
144-
145-
// Override the locale to ensure consistent output
146-
cmd.Env = append(os.Environ(), "LC_ALL=C")
147-
148-
stdout := bytes.Buffer{}
149-
stderr := bytes.Buffer{}
150-
cmd.Stdout = &stdout
151-
cmd.Stderr = &stderr
152-
153-
if err := cmd.Run(); err != nil {
154-
return "", errors.New(stderr.String())
155-
}
156-
return stdout.String(), nil
133+
func (g *Git) Run(args ...string) (string, error) {
134+
return g.clean(g.cli.Run(g.ctx, args...))
157135
}
158136

159-
func (c *Git) clean(out string, err error) (string, error) {
160-
out = strings.ReplaceAll(strings.Split(out, "\n")[0], "'", "")
137+
func (g *Git) clean(dt []byte, err error) (string, error) {
138+
out := strings.ReplaceAll(strings.Split(string(dt), "\n")[0], "'", "")
161139
if err != nil {
162-
err = errors.New(strings.TrimSuffix(err.Error(), "\n"))
140+
msg := strings.TrimSuffix(err.Error(), "\n")
141+
if stderr, ok := strings.CutPrefix(msg, "git stderr:\n"); ok {
142+
if idx := strings.LastIndex(stderr, ": exit status "); idx != -1 {
143+
stderr = stderr[:idx]
144+
}
145+
msg = strings.TrimSuffix(stderr, "\n")
146+
}
147+
err = errors.New(msg)
163148
}
164149
return out, err
165150
}
166151

167-
func (c *Git) currentRemote() (string, error) {
168-
symref, err := c.Run("symbolic-ref", "-q", "HEAD")
152+
func (g *Git) currentRemote() (string, error) {
153+
symref, err := g.Run("symbolic-ref", "-q", "HEAD")
169154
if err != nil {
170155
return "", err
171156
}
172157
if symref == "" {
173158
return "", nil
174159
}
175-
// git for-each-ref --format='%(upstream:remotename)'
176-
remote, err := c.Run("for-each-ref", "--format=%(upstream:remotename)", symref)
160+
remote, err := g.Run("for-each-ref", "--format=%(upstream:remotename)", symref)
177161
if err != nil {
178162
return "", err
179163
}

0 commit comments

Comments
 (0)