Skip to content

Commit 644e198

Browse files
committed
first stab at fast-forward, need to fix tests
1 parent 47030e1 commit 644e198

File tree

3 files changed

+53
-16
lines changed

3 files changed

+53
-16
lines changed

git/git.go

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ func CloneRepo(ctx context.Context, logf func(string, ...any), opts CloneRepoOpt
5555
return false, fmt.Errorf("parse url %q: %w", opts.RepoURL, err)
5656
}
5757
logf("Parsed Git URL as %q", parsed.Redacted())
58+
opts.RepoURL = parsed.String()
5859
if parsed.Hostname() == "dev.azure.com" {
5960
// Azure DevOps requires capabilities multi_ack / multi_ack_detailed,
6061
// which are not fully implemented and by default are included in
@@ -100,19 +101,17 @@ func CloneRepo(ctx context.Context, logf func(string, ...any), opts CloneRepoOpt
100101
gitStorage := filesystem.NewStorage(gitDir, cache.NewObjectLRU(cache.DefaultMaxSize*10))
101102
fsStorage := filesystem.NewStorage(fs, cache.NewObjectLRU(cache.DefaultMaxSize*10))
102103
repo, err := git.Open(fsStorage, gitDir)
103-
if errors.Is(err, git.ErrRepositoryNotExists) {
104-
err = nil
104+
if err == nil {
105+
// Repo exists, so fast-forward it.
106+
return fastForwardRepo(ctx, logf, opts, repo, reference)
105107
}
106-
if err != nil {
108+
109+
// Something went wrong opening the repo.
110+
if !errors.Is(err, git.ErrRepositoryNotExists) {
107111
return false, fmt.Errorf("open %q: %w", opts.RepoURL, err)
108112
}
109-
if repo != nil {
110-
if head, err := repo.Head(); err == nil && head != nil {
111-
logf("existing repo HEAD: %s", head.Hash().String())
112-
}
113-
return false, nil
114-
}
115113

114+
// Repo does not exist, so clone it.
116115
repo, err = git.CloneContext(ctx, gitStorage, fs, &git.CloneOptions{
117116
URL: parsed.String(),
118117
Auth: opts.RepoAuth,
@@ -136,6 +135,40 @@ func CloneRepo(ctx context.Context, logf func(string, ...any), opts CloneRepoOpt
136135
return true, nil
137136
}
138137

138+
func fastForwardRepo(ctx context.Context, logf func(string, ...any), opts CloneRepoOptions, repo *git.Repository, referenceName string) (bool, error) {
139+
if head, err := repo.Head(); err == nil && head != nil {
140+
logf("existing repo HEAD: %s", head.Hash().String())
141+
}
142+
wt, err := repo.Worktree()
143+
if err != nil {
144+
return false, fmt.Errorf("worktree: %w", err)
145+
}
146+
err = wt.PullContext(ctx, &git.PullOptions{
147+
RemoteName: "", // use default remote
148+
ReferenceName: plumbing.ReferenceName(referenceName),
149+
SingleBranch: opts.SingleBranch,
150+
Depth: opts.Depth,
151+
Auth: opts.RepoAuth,
152+
Progress: opts.Progress,
153+
Force: false,
154+
InsecureSkipTLS: opts.Insecure,
155+
CABundle: opts.CABundle,
156+
ProxyOptions: opts.ProxyOptions,
157+
})
158+
if err == nil {
159+
if head, err := repo.Head(); err == nil && head != nil {
160+
logf("fast-forwarded to %s", head.Hash().String())
161+
}
162+
return true, nil
163+
}
164+
if errors.Is(err, git.NoErrAlreadyUpToDate) {
165+
logf("existing repo already up-to-date")
166+
return false, nil
167+
}
168+
logf("failed to fast-forward: %s", err.Error())
169+
return false, err
170+
}
171+
139172
// ShallowCloneRepo will clone the repository at the given URL into the given path
140173
// with a depth of 1. If the destination folder exists and is not empty, the
141174
// clone will not be performed.

git/git_test.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"path/filepath"
1212
"regexp"
1313
"testing"
14-
"time"
1514

1615
"github.com/coder/envbuilder/git"
1716
"github.com/coder/envbuilder/options"
@@ -39,6 +38,7 @@ func TestCloneRepo(t *testing.T) {
3938
mungeURL func(*string)
4039
expectError string
4140
expectClone bool
41+
prepFS func(billy.Filesystem)
4242
}{
4343
{
4444
name: "no auth",
@@ -237,19 +237,21 @@ func TestShallowCloneRepo(t *testing.T) {
237237

238238
func TestFetchAfterClone(t *testing.T) {
239239
t.Parallel()
240-
t.Skip()
241-
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
242-
defer cancel()
240+
241+
ctx := context.Background()
242+
// ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
243+
// defer cancel()
244+
243245
// setup a git repo
244246
srvDir := t.TempDir()
245-
srvFS := osfs.New(srvDir, osfs.WithChrootOS())
247+
srvFS := osfs.New(srvDir)
246248
repo := gittest.NewRepo(t, srvFS)
247249
repo.Commit(gittest.Commit(t, "README.md", "Hello, worldd!", "initial commit"))
248250
srv := httptest.NewServer(gittest.NewServer(srvFS))
249251

250252
// clone to a tempdir
251253
clientDir := t.TempDir()
252-
clientFS := osfs.New(clientDir, osfs.WithChrootOS())
254+
clientFS := osfs.New(clientDir)
253255
cloned, err := git.CloneRepo(ctx, t.Logf, git.CloneRepoOptions{
254256
Path: "/repo",
255257
RepoURL: srv.URL,
@@ -273,7 +275,7 @@ func TestFetchAfterClone(t *testing.T) {
273275
require.NoError(t, err)
274276
content, err := io.ReadAll(contentAfter)
275277
require.NoError(t, err)
276-
require.Equal(t, "Hello, worldd!", string(content), "expected client repo to be updated after fetch")
278+
require.Equal(t, "Hello, world!", string(content), "expected client repo to be updated after fetch")
277279
}
278280

279281
func TestCloneRepoSSH(t *testing.T) {

testutil/gittest/gittest.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@ func Commit(t *testing.T, path, content, msg string) CommitFunc {
262262
obj, err := repo.CommitObject(commit)
263263
require.NoError(t, err)
264264
t.Logf("commited object %s", obj.Hash.String())
265+
err = repo.Storer.SetReference(plumbing.NewHashReference("refs/heads/main", obj.Hash))
266+
require.NoError(t, err)
265267
}
266268
}
267269

0 commit comments

Comments
 (0)